원문 : http://www.rastertek.com/dx11tut10.html
Tutorial 10: Specular Lighting
이번 강좌는 DirectX 11 에서 HLSL로 specular 조명을 사용해볼 것입니다. 이번 강좌의 코드는 이전 강좌의 코드에서 이어집니다.
Specular 조명은 광원 위치에 대한 시각적 단서를 주기위해 "밝은 곳 강조"의 사용입니다. 예를들어 ambient와 diffuse 조명만 적용된 빨간 구가 다음과 같이 있습니다.
이제 백색 specular 조명을 추가하면 이런 결과를 얻습니다.
Specular 조명은 대부분 흔히 거울이나 아주 윤이나고 반사하는 금속면같은 것에 빛이 반사되는 효과를 주는데 사용됩니다. 또 수면 위 태양빛이 반사하는것과 같은 다른 물질에도 사용됩니다. 잘 사용하면 3D 씬에 사진같은 사실감을 더합니다.
다음은 specular 조명에 대한 식입니다.
SpecularLighting = SpecularColor * (SpecularColorOfLight * ((NormalVector dot HalfWayVector) power SpecularReflectionPower) * Attentuation * Spotlight)
우리는 다음과 같이 기본 specular 조명을 주기 위해 식을 수정할 것입니다.
SpecularLighting = SpecularLightColor * (ViewingDirection dot ReflectionVector) power SpecularReflectionPower
이 식에서 반사 벡터(ReflectionVector)는 광도(LightIntensity)의 두배에 법선 벡터를 곱하는 것으로 구해집니다. 빛의 방향(LightDirection)을 빼줌으로 광원과 보는 곳 사이 각을 구할 수 있습니다.
ReflectionVector = 2 * LightIntensity * VertexNormal - LightDirection
식에서 보는 방향(ViewingDirection)은 카메라 위치(CameraPosition)에 정점 위치(VertexPosition)을 빼줌으로 구해집니다.
ViewingDirection = CameraPosition - VertexPosition
이제 수정된 라이트 쉐이더가 어떻게 동작하는지 살펴보겠습니다.
Light.vs
////////////////////////////////////////////////////////////////////////////////
// Filename: light.vs
////////////////////////////////////////////////////////////////////////////////
/////////////
// GLOBALS //
/////////////
cbuffer MatrixBuffer
{
matrix worldMatrix;
matrix viewMatrix;
matrix projectionMatrix;
};
카메라 정보를 가질 새 상수 버퍼를 추가하였습니다. 이번 쉐이더에서 specular 계산에서 해당 정점이 어디에서 보여지고 있는지를 결정하기 위해 카메라의 위치가 필요합니다.
cbuffer CameraBuffer { float3 cameraPosition; float padding; }; ////////////// // TYPEDEFS // ////////////// struct VertexInputType { float4 position : POSITION; float2 tex : TEXCOORD0; float3 normal : NORMAL; };
PixelInputType 구조체는 보는 방향을 버텍스 쉐이더에서 계산하여 specular 조명 계산을 위해 픽셀 쉐이더로 보내도록 수정되었습니다.
struct PixelInputType { float4 position : SV_POSITION; float2 tex : TEXCOORD0; float3 normal : NORMAL; float3 viewDirection : TEXCOORD1; }; //////////////////////////////////////////////////////////////////////////////// // Vertex Shader //////////////////////////////////////////////////////////////////////////////// PixelInputType LightVertexShader(VertexInputType input) { PixelInputType output; float4 worldPosition; // Change the position vector to be 4 units for proper matrix calculations. input.position.w = 1.0f; // Calculate the position of the vertex against the world, view, and projection matrices. output.position = mul(input.position, worldMatrix); output.position = mul(output.position, viewMatrix); output.position = mul(output.position, projectionMatrix); // Store the texture coordinates for the pixel shader. output.tex = input.tex; // Calculate the normal vector against the world matrix only. output.normal = mul(input.normal, (float3x3)worldMatrix); // Normalize the normal vector. output.normal = normalize(output.normal);
버텍스 쉐이더에서 보는 방향은 여기서 계산됩니다. 정점의 월드 위치를 계산하고 우리가 어디서 보고있는지를 결정하기 위해 카메라 위치에 월드 위치를 빼줍니다. 최종 값은 정규화되어 픽셀 쉐이더로 보내집니다.
// Calculate the position of the vertex in the world. worldPosition = mul(input.position, worldMatrix); // Determine the viewing direction based on the position of the camera and the position of the vertex in the world. output.viewDirection = cameraPosition.xyz - worldPosition.xyz; // Normalize the viewing direction vector. output.viewDirection = normalize(output.viewDirection); return output; }
Light.ps
////////////////////////////////////////////////////////////////////////////////
// Filename: light.ps
////////////////////////////////////////////////////////////////////////////////
/////////////
// GLOBALS //
/////////////
Texture2D shaderTexture;
SamplerState SampleType;
라이트 버퍼는 specular 조명 계산을 위한 specularColor와 specularPower값을 가지도록 바뀌었습니다.
cbuffer LightBuffer { float4 ambientColor; float4 diffuseColor; float3 lightDirection; float specularPower; float4 specularColor; }; ////////////// // TYPEDEFS // //////////////
PixelInputType 구조체는 버텍스 쉐이더에서의 변화를 반영하도록 바뀌었습니다.
struct PixelInputType { float4 position : SV_POSITION; float2 tex : TEXCOORD0; float3 normal : NORMAL; float3 viewDirection : TEXCOORD1; }; //////////////////////////////////////////////////////////////////////////////// // Pixel Shader //////////////////////////////////////////////////////////////////////////////// float4 LightPixelShader(PixelInputType input) : SV_TARGET { float4 textureColor; float3 lightDir; float lightIntensity; float4 color; float3 reflection; float4 specular; // Sample the pixel color from the texture using the sampler at this texture coordinate location. textureColor = shaderTexture.Sample(SampleType, input.tex); // Set the default output color to the ambient light value for all pixels. color = ambientColor; // Initialize the specular color. specular = float4(0.0f, 0.0f, 0.0f, 0.0f); // Invert the light direction for calculations. lightDir = -lightDirection; // Calculate the amount of light on this pixel. lightIntensity = saturate(dot(input.normal, lightDir)); if(lightIntensity > 0.0f) { // Determine the final diffuse color based on the diffuse color and the amount of light intensity. color += (diffuseColor * lightIntensity); // Saturate the ambient and diffuse color. color = saturate(color);
specular 조명 계산에 대한 반사 벡터는 여기 픽셀쉐이더에서 제공된 광도가 0보다 클때 계산된다. 강좌 초반에 나열된 식과 같습니다.
// Calculate the reflection vector based on the light intensity, normal vector, and light direction.
reflection = normalize(2 * lightIntensity * input.normal - lightDir);
specular 빛의 양은 반사 벡터와 보는 방향 벡터를 이용하여 계산됩니다. 광원과 보는 방향 사이 각이 작을수록 specular 빛 반사는 강해집니다. 결과로 specularPower 값 만큼의 거듭제곱을 취합니다. specularPower 값이 작을수록 최종 효과는 강해집니다. (코사인 함수는 제곱하면 경사도가 커져서 하이라이트 효과범위가 좁아집니다. )
// Determine the amount of specular light based on the reflection vector, viewing direction, and specular power. specular = pow(saturate(dot(reflection, input.viewDirection)), specularPower); } // Multiply the texture pixel and the input color to get the textured result. color = color * textureColor;
마지막 전까지 specular 효과를 더하지 않습니다. 이 효과는 강조이기때문에 마지막 최종 결과값에 더해져야 하며 그렇지 않으면 적절히 보이지 않습니다.
// Add the specular component last to the output color. color = saturate(color + specular); return color; }
Lightshaderclass.h
LightShaderClass는 이전 강좌에서 specular 조명을 처리하도록 바뀌었습니다.
////////////////////////////////////////////////////////////////////////////////
// Filename: lightshaderclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _LIGHTSHADERCLASS_H_
#define _LIGHTSHADERCLASS_H_
//////////////
// INCLUDES //
//////////////
#include <d3d11.h>
#include <d3dx10math.h>
#include <d3dx11async.h>
#include <fstream>
using namespace std;
////////////////////////////////////////////////////////////////////////////////
// Class name: LightShaderClass
////////////////////////////////////////////////////////////////////////////////
class LightShaderClass
{
private:
struct MatrixBufferType
{
D3DXMATRIX world;
D3DXMATRIX view;
D3DXMATRIX projection;
};
새로운 카메라 버퍼 구조체를 버텍스 쉐이더의 새 카메라 상수 버퍼와 맞추기 위해 추가하였습니다. CreateBuffer() 함수 실패를 예방하기 위해 16의 배수로 구조체 크기를 맞추도록 패딩값을 추가하는것을 잊지마세요.
struct CameraBufferType
{
D3DXVECTOR3 cameraPosition;
float padding;
};
LightBufferType은 픽셀 쉐이더의 라이트 상수 버퍼와 맞추도록 specular color와 specular power를 추가하였습니다. 구조체 크기를 16바이트의 배수로 유지하기 위해 저번 강좌에서는 lightDirection 옆에 패딩값을 넣었는데 이번에는 대신에 specularPower를 위치시킨 것을 주의하시기 바랍니다. specularPower를 맨 아래에 위치시킬 수도 있지만(전체 크기는 맞으니까) lightDirection밑에 패딩이 사용되지 않으면 쉐이더가 올바르게 동작하지 않을 수 있습니다. 그 이유는 비록 구조체는 16바이트의 배수이지만 각 슬롯(구조체내 변수들) 들은 논리적으로 16바이트에 맞추어져 있지 않기때문입니다.
struct LightBufferType { D3DXVECTOR4 ambientColor; D3DXVECTOR4 diffuseColor; D3DXVECTOR3 lightDirection; float specularPower; D3DXVECTOR4 specularColor; }; public: LightShaderClass(); LightShaderClass(const LightShaderClass&); ~LightShaderClass(); bool Initialize(ID3D11Device*, HWND); void Shutdown(); bool Render(ID3D11DeviceContext*, int, D3DXMATRIX, D3DXMATRIX, D3DXMATRIX, ID3D11ShaderResourceView*, D3DXVECTOR3, D3DXVECTOR4, D3DXVECTOR4, D3DXVECTOR3, D3DXVECTOR4, float); private: bool InitializeShader(ID3D11Device*, HWND, WCHAR*, WCHAR*); void ShutdownShader(); void OutputShaderErrorMessage(ID3D10Blob*, HWND, WCHAR*); bool SetShaderParameters(ID3D11DeviceContext*, D3DXMATRIX, D3DXMATRIX, D3DXMATRIX, ID3D11ShaderResourceView*, D3DXVECTOR3, D3DXVECTOR4, D3DXVECTOR4, D3DXVECTOR3, D3DXVECTOR4, float); void RenderShader(ID3D11DeviceContext*, int); private: ID3D11VertexShader* m_vertexShader; ID3D11PixelShader* m_pixelShader; ID3D11InputLayout* m_layout; ID3D11SamplerState* m_sampleState; ID3D11Buffer* m_matrixBuffer;
버텍스 쉐이더에 카메라 위치를 설정할 새로운 카메라 상수 버퍼 포인터를 추가하였습니다.
ID3D11Buffer* m_cameraBuffer; ID3D11Buffer* m_lightBuffer; }; #endif
Lightshaderclass.cpp
////////////////////////////////////////////////////////////////////////////////
// Filename: lightshaderclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "lightshaderclass.h"
LightShaderClass::LightShaderClass()
{
m_vertexShader = 0;
m_pixelShader = 0;
m_layout = 0;
m_sampleState = 0;
m_matrixBuffer = 0;
생성자에서 카메라 상수 버퍼를 null로 초기화합니다.
m_cameraBuffer = 0; m_lightBuffer = 0; } LightShaderClass::LightShaderClass(const LightShaderClass& other) { } LightShaderClass::~LightShaderClass() { } bool LightShaderClass::Initialize(ID3D11Device* device, HWND hwnd) { bool result; // Initialize the vertex and pixel shaders. result = InitializeShader(device, hwnd, L"../Engine/light.vs", L"../Engine/light.ps"); if(!result) { return false; } return true; } void LightShaderClass::Shutdown() { // Shutdown the vertex and pixel shaders as well as the related objects. ShutdownShader(); return; }
Render() 함수는 이제 cameraPosition, specularColor, specularPower 값을 받고 렌더링전 라이트 쉐이더에 활성화 시키도록 SetShaderParameters() 함수로 넘깁니다.
bool LightShaderClass::Render(ID3D11DeviceContext* deviceContext, int indexCount, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix, ID3D11ShaderResourceView* texture, D3DXVECTOR3 lightDirection, D3DXVECTOR4 ambientColor, D3DXVECTOR4 diffuseColor, D3DXVECTOR3 cameraPosition, D3DXVECTOR4 specularColor, float specularPower) { bool result; // Set the shader parameters that it will use for rendering. result = SetShaderParameters(deviceContext, worldMatrix, viewMatrix, projectionMatrix, texture, lightDirection, ambientColor, diffuseColor, cameraPosition, specularColor, specularPower); if(!result) { return false; } // Now render the prepared buffers with the shader. RenderShader(deviceContext, indexCount); return true; } bool LightShaderClass::InitializeShader(ID3D11Device* device, HWND hwnd, WCHAR* vsFilename, WCHAR* psFilename) { HRESULT result; ID3D10Blob* errorMessage; ID3D10Blob* vertexShaderBuffer; ID3D10Blob* pixelShaderBuffer; D3D11_INPUT_ELEMENT_DESC polygonLayout[3]; unsigned int numElements; D3D11_SAMPLER_DESC samplerDesc; D3D11_BUFFER_DESC matrixBufferDesc; D3D11_BUFFER_DESC cameraBufferDesc; D3D11_BUFFER_DESC lightBufferDesc; // Initialize the pointers this function will use to null. errorMessage = 0; vertexShaderBuffer = 0; pixelShaderBuffer = 0; // Compile the vertex shader code. result = D3DX11CompileFromFile(vsFilename, NULL, NULL, "LightVertexShader", "vs_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL, &vertexShaderBuffer, &errorMessage, NULL); if(FAILED(result)) { // If the shader failed to compile it should have writen something to the error message. if(errorMessage) { OutputShaderErrorMessage(errorMessage, hwnd, vsFilename); } // If there was nothing in the error message then it simply could not find the shader file itself. else { MessageBox(hwnd, vsFilename, L"Missing Shader File", MB_OK); } return false; } // Compile the pixel shader code. result = D3DX11CompileFromFile(psFilename, NULL, NULL, "LightPixelShader", "ps_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL, &pixelShaderBuffer, &errorMessage, NULL); if(FAILED(result)) { // If the shader failed to compile it should have writen something to the error message. if(errorMessage) { OutputShaderErrorMessage(errorMessage, hwnd, psFilename); } // If there was nothing in the error message then it simply could not find the file itself. else { MessageBox(hwnd, psFilename, L"Missing Shader File", MB_OK); } return false; } // Create the vertex shader from the buffer. result = device->CreateVertexShader(vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), NULL, &m_vertexShader); if(FAILED(result)) { return false; } // Create the pixel shader from the buffer. result = device->CreatePixelShader(pixelShaderBuffer->GetBufferPointer(), pixelShaderBuffer->GetBufferSize(), NULL, &m_pixelShader); if(FAILED(result)) { return false; } // Create the vertex input layout description. // This setup needs to match the VertexType stucture in the ModelClass and in the shader. polygonLayout[0].SemanticName = "POSITION"; polygonLayout[0].SemanticIndex = 0; polygonLayout[0].Format = DXGI_FORMAT_R32G32B32_FLOAT; polygonLayout[0].InputSlot = 0; polygonLayout[0].AlignedByteOffset = 0; polygonLayout[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; polygonLayout[0].InstanceDataStepRate = 0; polygonLayout[1].SemanticName = "TEXCOORD"; polygonLayout[1].SemanticIndex = 0; polygonLayout[1].Format = DXGI_FORMAT_R32G32_FLOAT; polygonLayout[1].InputSlot = 0; polygonLayout[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT; polygonLayout[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; polygonLayout[1].InstanceDataStepRate = 0; polygonLayout[2].SemanticName = "NORMAL"; polygonLayout[2].SemanticIndex = 0; polygonLayout[2].Format = DXGI_FORMAT_R32G32B32_FLOAT; polygonLayout[2].InputSlot = 0; polygonLayout[2].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT; polygonLayout[2].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; polygonLayout[2].InstanceDataStepRate = 0; // Get a count of the elements in the layout. numElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]); // Create the vertex input layout. result = device->CreateInputLayout(polygonLayout, numElements, vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), &m_layout); if(FAILED(result)) { return false; } // Release the vertex shader buffer and pixel shader buffer since they are no longer needed. vertexShaderBuffer->Release(); vertexShaderBuffer = 0; pixelShaderBuffer->Release(); pixelShaderBuffer = 0; // Create a texture sampler state description. samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; samplerDesc.MipLODBias = 0.0f; samplerDesc.MaxAnisotropy = 1; samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; samplerDesc.BorderColor[0] = 0; samplerDesc.BorderColor[1] = 0; samplerDesc.BorderColor[2] = 0; samplerDesc.BorderColor[3] = 0; samplerDesc.MinLOD = 0; samplerDesc.MaxLOD = D3D11_FLOAT32_MAX; // Create the texture sampler state. result = device->CreateSamplerState(&samplerDesc, &m_sampleState); if(FAILED(result)) { return false; } // Setup the description of the dynamic matrix constant buffer that is in the vertex shader. matrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC; matrixBufferDesc.ByteWidth = sizeof(MatrixBufferType); matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; matrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; matrixBufferDesc.MiscFlags = 0; matrixBufferDesc.StructureByteStride = 0; // Create the constant buffer pointer so we can access the vertex shader constant buffer from within this class. result = device->CreateBuffer(&matrixBufferDesc, NULL, &m_matrixBuffer); if(FAILED(result)) { return false; }
새 카메라 버퍼의 description을 설정하고 그 description을 이용하여 버퍼를 생성합니다. 버텍스 쉐이더에 카메라 위치를 설정할 수 있습니다.
// Setup the description of the camera dynamic constant buffer that is in the vertex shader. cameraBufferDesc.Usage = D3D11_USAGE_DYNAMIC; cameraBufferDesc.ByteWidth = sizeof(CameraBufferType); cameraBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; cameraBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; cameraBufferDesc.MiscFlags = 0; cameraBufferDesc.StructureByteStride = 0; // Create the camera constant buffer pointer so we can access the vertex shader constant buffer from within this class. result = device->CreateBuffer(&cameraBufferDesc, NULL, &m_cameraBuffer); if(FAILED(result)) { return false; } // Setup the description of the light dynamic constant buffer that is in the pixel shader. // Note that ByteWidth always needs to be a multiple of 16 if using D3D11_BIND_CONSTANT_BUFFER or CreateBuffer will fail. lightBufferDesc.Usage = D3D11_USAGE_DYNAMIC; lightBufferDesc.ByteWidth = sizeof(LightBufferType); lightBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; lightBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; lightBufferDesc.MiscFlags = 0; lightBufferDesc.StructureByteStride = 0; // Create the constant buffer pointer so we can access the vertex shader constant buffer from within this class. result = device->CreateBuffer(&lightBufferDesc, NULL, &m_lightBuffer); if(FAILED(result)) { return false; } return true; } void LightShaderClass::ShutdownShader() { // Release the light constant buffer. if(m_lightBuffer) { m_lightBuffer->Release(); m_lightBuffer = 0; }
ShutdownShader() 함수에서 카메라 상수 버퍼를 해제합니다.
// Release the camera constant buffer. if(m_cameraBuffer) { m_cameraBuffer->Release(); m_cameraBuffer = 0; } // Release the matrix constant buffer. if(m_matrixBuffer) { m_matrixBuffer->Release(); m_matrixBuffer = 0; } // Release the sampler state. if(m_sampleState) { m_sampleState->Release(); m_sampleState = 0; } // Release the layout. if(m_layout) { m_layout->Release(); m_layout = 0; } // Release the pixel shader. if(m_pixelShader) { m_pixelShader->Release(); m_pixelShader = 0; } // Release the vertex shader. if(m_vertexShader) { m_vertexShader->Release(); m_vertexShader = 0; } return; } void LightShaderClass::OutputShaderErrorMessage(ID3D10Blob* errorMessage, HWND hwnd, WCHAR* shaderFilename) { char* compileErrors; unsigned long bufferSize, i; ofstream fout; // Get a pointer to the error message text buffer. compileErrors = (char*)(errorMessage->GetBufferPointer()); // Get the length of the message. bufferSize = errorMessage->GetBufferSize(); // Open a file to write the error message to. fout.open("shader-error.txt"); // Write out the error message. for(i=0; i<bufferSize; i++) { fout << compileErrors[i]; } // Close the file. fout.close(); // Release the error message. errorMessage->Release(); errorMessage = 0; // Pop a message up on the screen to notify the user to check the text file for compile errors. MessageBox(hwnd, L"Error compiling shader. Check shader-error.txt for message.", shaderFilename, MB_OK); return; }
SetShaderParameters() 함수는 입력으로 cameraPosition, specularColor, specularPower 값을 받도록 바뀌었습니다.
bool LightShaderClass::SetShaderParameters(ID3D11DeviceContext* deviceContext, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix, ID3D11ShaderResourceView* texture, D3DXVECTOR3 lightDirection, D3DXVECTOR4 ambientColor, D3DXVECTOR4 diffuseColor, D3DXVECTOR3 cameraPosition, D3DXVECTOR4 specularColor, float specularPower) { HRESULT result; D3D11_MAPPED_SUBRESOURCE mappedResource; unsigned int bufferNumber; MatrixBufferType* dataPtr; LightBufferType* dataPtr2; CameraBufferType* dataPtr3; // Transpose the matrices to prepare them for the shader. D3DXMatrixTranspose(&worldMatrix, &worldMatrix); D3DXMatrixTranspose(&viewMatrix, &viewMatrix); D3DXMatrixTranspose(&projectionMatrix, &projectionMatrix); // Lock the constant buffer so it can be written to. result = deviceContext->Map(m_matrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); if(FAILED(result)) { return false; } // Get a pointer to the data in the constant buffer. dataPtr = (MatrixBufferType*)mappedResource.pData; // Copy the matrices into the constant buffer. dataPtr->world = worldMatrix; dataPtr->view = viewMatrix; dataPtr->projection = projectionMatrix; // Unlock the constant buffer. deviceContext->Unmap(m_matrixBuffer, 0); // Set the position of the constant buffer in the vertex shader. bufferNumber = 0; // Now set the constant buffer in the vertex shader with the updated values. deviceContext->VSSetConstantBuffers(bufferNumber, 1, &m_matrixBuffer);
카메라 버퍼를 잠그고 카메라 위치 값을 설정합니다.
// Lock the camera constant buffer so it can be written to.
result = deviceContext->Map(m_cameraBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if(FAILED(result))
{
return false;
}
// Get a pointer to the data in the constant buffer.
dataPtr3 = (CameraBufferType*)mappedResource.pData;
// Copy the camera position into the constant buffer.
dataPtr3->cameraPosition = cameraPosition;
dataPtr3->padding = 0.0f;
// Unlock the camera constant buffer.
deviceContext->Unmap(m_cameraBuffer, 0);
카메라 상수 버퍼 설정 전에 bufferNumber를 0대신 1로 설정합니다. 버텍스 쉐이더의 두번째 버퍼이기 때문입니다. (첫번째(0번)는 행렬 버퍼)
// Set the position of the camera constant buffer in the vertex shader. bufferNumber = 1; // Now set the camera constant buffer in the vertex shader with the updated values. deviceContext->VSSetConstantBuffers(bufferNumber, 1, &m_cameraBuffer); // Set shader texture resource in the pixel shader. deviceContext->PSSetShaderResources(0, 1, &texture); // Lock the light constant buffer so it can be written to. result = deviceContext->Map(m_lightBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); if(FAILED(result)) { return false; } // Get a pointer to the data in the light constant buffer. dataPtr2 = (LightBufferType*)mappedResource.pData;
쉐이더가 specular 조명 계산을 할 수 있도록 라이트 상수 버퍼는 이제 specularColor와 specularPower를 설정합니다.
// Copy the lighting variables into the light constant buffer. dataPtr2->ambientColor = ambientColor; dataPtr2->diffuseColor = diffuseColor; dataPtr2->lightDirection = lightDirection; dataPtr2->specularColor = specularColor; dataPtr2->specularPower = specularPower; // Unlock the light constant buffer. deviceContext->Unmap(m_lightBuffer, 0); // Set the position of the light constant buffer in the pixel shader. bufferNumber = 0; // Finally set the light constant buffer in the pixel shader with the updated values. deviceContext->PSSetConstantBuffers(bufferNumber, 1, &m_lightBuffer); return true; } void LightShaderClass::RenderShader(ID3D11DeviceContext* deviceContext, int indexCount) { // Set the vertex input layout. deviceContext->IASetInputLayout(m_layout); // Set the vertex and pixel shaders that will be used to render this triangle. deviceContext->VSSetShader(m_vertexShader, NULL, 0); deviceContext->PSSetShader(m_pixelShader, NULL, 0); // Set the sampler state in the pixel shader. deviceContext->PSSetSamplers(0, 1, &m_sampleState); // Render the triangle. deviceContext->DrawIndexed(indexCount, 0, 0); return; }
Lightclass.h
이번 강좌에서 LightClass는 specular 요소와 관련 함수를 포함하도록 바뀌었습니다.
//////////////////////////////////////////////////////////////////////////////// // Filename: lightclass.h //////////////////////////////////////////////////////////////////////////////// #ifndef _LIGHTCLASS_H_ #define _LIGHTCLASS_H_ ////////////// // INCLUDES // ////////////// #include <d3dx10math.h> //////////////////////////////////////////////////////////////////////////////// // Class name: LightClass //////////////////////////////////////////////////////////////////////////////// class LightClass { public: LightClass(); LightClass(const LightClass&); ~LightClass(); void SetAmbientColor(float, float, float, float); void SetDiffuseColor(float, float, float, float); void SetDirection(float, float, float); void SetSpecularColor(float, float, float, float); void SetSpecularPower(float); D3DXVECTOR4 GetAmbientColor(); D3DXVECTOR4 GetDiffuseColor(); D3DXVECTOR3 GetDirection(); D3DXVECTOR4 GetSpecularColor(); float GetSpecularPower(); private: D3DXVECTOR4 m_ambientColor; D3DXVECTOR4 m_diffuseColor; D3DXVECTOR3 m_direction; D3DXVECTOR4 m_specularColor; float m_specularPower; }; #endif
Lightclass.cpp
//////////////////////////////////////////////////////////////////////////////// // Filename: lightclass.cpp //////////////////////////////////////////////////////////////////////////////// #include "lightclass.h" LightClass::LightClass() { } LightClass::LightClass(const LightClass& other) { } LightClass::~LightClass() { } void LightClass::SetAmbientColor(float red, float green, float blue, float alpha) { m_ambientColor = D3DXVECTOR4(red, green, blue, alpha); return; } void LightClass::SetDiffuseColor(float red, float green, float blue, float alpha) { m_diffuseColor = D3DXVECTOR4(red, green, blue, alpha); return; } void LightClass::SetDirection(float x, float y, float z) { m_direction = D3DXVECTOR3(x, y, z); return; } void LightClass::SetSpecularColor(float red, float green, float blue, float alpha) { m_specularColor = D3DXVECTOR4(red, green, blue, alpha); return; } void LightClass::SetSpecularPower(float power) { m_specularPower = power; return; } D3DXVECTOR4 LightClass::GetAmbientColor() { return m_ambientColor; } D3DXVECTOR4 LightClass::GetDiffuseColor() { return m_diffuseColor; } D3DXVECTOR3 LightClass::GetDirection() { return m_direction; } D3DXVECTOR4 LightClass::GetSpecularColor() { return m_specularColor; } float LightClass::GetSpecularPower() { return m_specularPower; }
Graphicsclass.h
GraphicsClass 헤더는 바뀐게 없습니다.
////////////////////////////////////////////////////////////////////////////////
// Filename: graphicsclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _GRAPHICSCLASS_H_
#define _GRAPHICSCLASS_H_
///////////////////////
// MY CLASS INCLUDES //
///////////////////////
#include "d3dclass.h"
#include "cameraclass.h"
#include "modelclass.h"
#include "lightshaderclass.h"
#include "lightclass.h"
/////////////
// GLOBALS //
/////////////
const bool FULL_SCREEN = true;
const bool VSYNC_ENABLED = true;
const float SCREEN_DEPTH = 1000.0f;
const float SCREEN_NEAR = 0.1f;
////////////////////////////////////////////////////////////////////////////////
// Class name: GraphicsClass
////////////////////////////////////////////////////////////////////////////////
class GraphicsClass
{
public:
GraphicsClass();
GraphicsClass(const GraphicsClass&);
~GraphicsClass();
bool Initialize(int, int, HWND);
void Shutdown();
bool Frame();
private:
bool Render(float);
private:
D3DClass* m_D3D;
CameraClass* m_Camera;
ModelClass* m_Model;
LightShaderClass* m_LightShader;
LightClass* m_Light;
};
#endif
Graphicsclass.cpp
////////////////////////////////////////////////////////////////////////////////
// Filename: graphicsclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "graphicsclass.h"
GraphicsClass::GraphicsClass()
{
m_D3D = 0;
m_Camera = 0;
m_Model = 0;
m_LightShader = 0;
m_Light = 0;
}
GraphicsClass::GraphicsClass(const GraphicsClass& other)
{
}
GraphicsClass::~GraphicsClass()
{
}
bool GraphicsClass::Initialize(int screenWidth, int screenHeight, HWND hwnd)
{
bool result;
// Create the Direct3D object.
m_D3D = new D3DClass;
if(!m_D3D)
{
return false;
}
// Initialize the Direct3D object.
result = m_D3D->Initialize(screenWidth, screenHeight, VSYNC_ENABLED, hwnd, FULL_SCREEN, SCREEN_DEPTH, SCREEN_NEAR);
if(!result)
{
MessageBox(hwnd, L"Could not initialize Direct3D.", L"Error", MB_OK);
return false;
}
// Create the camera object.
m_Camera = new CameraClass;
if(!m_Camera)
{
return false;
}
// Set the initial position of the camera.
m_Camera->SetPosition(0.0f, 0.0f, -10.0f);
// Create the model object.
m_Model = new ModelClass;
if(!m_Model)
{
return false;
}
// Initialize the model object.
result = m_Model->Initialize(m_D3D->GetDevice(), "../Engine/data/cube.txt", L"../Engine/data/seafloor.dds");
if(!result)
{
MessageBox(hwnd, L"Could not initialize the model object.", L"Error", MB_OK);
return false;
}
// Create the light shader object.
m_LightShader = new LightShaderClass;
if(!m_LightShader)
{
return false;
}
// Initialize the light shader object.
result = m_LightShader->Initialize(m_D3D->GetDevice(), hwnd);
if(!result)
{
MessageBox(hwnd, L"Could not initialize the light shader object.", L"Error", MB_OK);
return false;
}
// Create the light object.
m_Light = new LightClass;
if(!m_Light)
{
return false;
}
light 클래스 객체 초기화시 specular color와 specular power를 설정해줍니다. 이번 강좌에서 specular color는 흰색으로하고 specular power는 32로 설정합니다. specular power 값이 작아야 specular 효과 범위가 넓다는걸 기억하세요. (눈부셩)
// Initialize the light object. m_Light->SetAmbientColor(0.15f, 0.15f, 0.15f, 1.0f); m_Light->SetDiffuseColor(1.0f, 1.0f, 1.0f, 1.0f); m_Light->SetDirection(0.0f, 0.0f, 1.0f); m_Light->SetSpecularColor(1.0f, 1.0f, 1.0f, 1.0f); m_Light->SetSpecularPower(32.0f); return true; } void GraphicsClass::Shutdown() { // Release the light object. if(m_Light) { delete m_Light; m_Light = 0; } // Release the light shader object. if(m_LightShader) { m_LightShader->Shutdown(); delete m_LightShader; m_LightShader = 0; } // Release the model object. if(m_Model) { m_Model->Shutdown(); delete m_Model; m_Model = 0; } // Release the camera object. if(m_Camera) { delete m_Camera; m_Camera = 0; } // Release the D3D object. if(m_D3D) { m_D3D->Shutdown(); delete m_D3D; m_D3D = 0; } return; } bool GraphicsClass::Frame() { bool result; static float rotation = 0.0f; // Update the rotation variable each frame. rotation += (float)D3DX_PI * 0.005f; if(rotation > 360.0f) { rotation -= 360.0f; } // Render the graphics scene. result = Render(rotation); if(!result) { return false; } return true; } bool GraphicsClass::Render(float rotation) { D3DXMATRIX worldMatrix, viewMatrix, projectionMatrix; bool result; // Clear the buffers to begin the scene. m_D3D->BeginScene(0.0f, 0.0f, 0.0f, 1.0f); // Generate the view matrix based on the camera's position. m_Camera->Render(); // Get the world, view, and projection matrices from the camera and d3d objects. m_Camera->GetViewMatrix(viewMatrix); m_D3D->GetWorldMatrix(worldMatrix); m_D3D->GetProjectionMatrix(projectionMatrix); // Rotate the world matrix by the rotation value so that the triangle will spin. D3DXMatrixRotationY(&worldMatrix, rotation); // Put the model vertex and index buffers on the graphics pipeline to prepare them for drawing. m_Model->Render(m_D3D->GetDeviceContext());
light 쉐이더의 render() 함수는 이제 카메라 위치, specular color, specular power를 받습니다.
// Render the model using the light shader. result = m_LightShader->Render(m_D3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, m_Model->GetTexture(), m_Light->GetDirection(), m_Light->GetAmbientColor(), m_Light->GetDiffuseColor(), m_Camera->GetPosition(), m_Light->GetSpecularColor(), m_Light->GetSpecularPower()); if(!result) { return false; } // Present the rendered scene to the screen. m_D3D->EndScene(); return true; }
요약
specular 조명의 추가로 큐브 표면이 카메라가 보는 방향과 고르게 직면할 때마다 밝은 백색 플래쉬를 보실 수 있습니다.
연습하기
1. 재컴파일하고 실행해보고 카메라와 직면하면 밝은 specular 하이라이트가 반짝이며 회전하는 큐브가 보이는지 확인 해 보세요.
2. 광원이 다를때 효과를 보기 위해 빛의 방향을 m_Light->SetDirection(1.0f, 0.0f, 1.0f) 이런식으로 바꿔보세요.
3. 강좌 상단에 구 이미지를 재현하기 위해 폴리곤이 5000개 이상에 빨간 텍스쳐가 적용된 구 모델을 만들어보세요.
'프로그래밍 > directx' 카테고리의 다른 글
DirectX 11 Tutorials - 뼈대 애니메이션 (MD5 포맷을 기반으로 한) (0) | 2016.06.04 |
---|---|
DirectX 11 Tutorials - MD5 모델 로딩 (0) | 2016.05.28 |
DirectX 11 Tutorials - 9 : 주변 조명 (0) | 2016.05.09 |
DirectX 11 Tutorials - 8 : 마야 2011 모델 로딩 (0) | 2016.05.06 |
DirectX 11 Tutorials - 7 : 3D 모델 렌더링 (0) | 2016.05.05 |