DirectX/10.0/Direct3D/环境光照
本教程将介绍如何在 DirectX 11 中使用 HLSL 实现环境光照。
我将通过一个示例来解释环境光照。想象一下你在一个房间里,唯一的灯光来源是从窗户射入的阳光。阳光不会直接照射到房间里的所有表面,但由于光粒子的反射,房间里的所有东西都会被照亮到一定程度。这种作用在阳光没有直接照射到的表面上的光照效果被称为环境光照。
现在,为了模拟环境光照,我们使用一个非常简单的公式。我们只需将每个像素设置为像素着色器开始时环境光的颜色值。从那时起,所有其他操作都会将它们的值添加到环境色中。这样,我们就可以确保使用环境色值对所有内容进行最小程度的照明。
环境光照还可以为 3D 场景增添更多真实感。例如,以下图片只使用了指向正 X 轴的漫射光照,照射到一个立方体上
产生的图像看起来并不真实,因为环境光几乎无处不在,即使只是轻微的照明,也能赋予所有物体其应有的形状。现在,如果我们只在同一个场景中添加 15% 的白色环境光,我们会得到以下图像
现在,我们得到了更真实的照明效果,这是我们人类所习惯的。
现在,让我们来看看代码中实现环境光照的更改。本教程是在之前使用漫射光照的教程基础上构建的。我们现在将添加环境分量,只需进行一些更改。
光照着色器只是从之前的教程中更新的漫射光照着色器。顶点着色器没有代码更改,只有像素着色器进行了修改。
//////////////////////////////////////////////////////////////////////////////// // Filename: light.vs //////////////////////////////////////////////////////////////////////////////// ///////////// // GLOBALS // ///////////// cbuffer MatrixBuffer { matrix worldMatrix; matrix viewMatrix; matrix projectionMatrix; }; ////////////// // TYPEDEFS // ////////////// struct VertexInputType { float4 position : POSITION; float2 tex : TEXCOORD0; float3 normal : NORMAL; }; struct PixelInputType { float4 position : SV_POSITION; float2 tex : TEXCOORD0; float3 normal : NORMAL; }; //////////////////////////////////////////////////////////////////////////////// // Vertex Shader //////////////////////////////////////////////////////////////////////////////// PixelInputType LightVertexShader(VertexInputType input) { PixelInputType output; // 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); return output; }
//////////////////////////////////////////////////////////////////////////////// // Filename: light.ps //////////////////////////////////////////////////////////////////////////////// ///////////// // GLOBALS // ///////////// Texture2D shaderTexture; SamplerState SampleType;
光照常量缓冲区更新了包含一个新的 4 个浮点环境色值的组件。这将允许外部类在此着色器中设置环境色。
cbuffer LightBuffer {
float4 ambientColor;
float4 diffuseColor; float3 lightDirection; float padding; }; ////////////// // TYPEDEFS // ////////////// struct PixelInputType { float4 position : SV_POSITION; float2 tex : TEXCOORD0; float3 normal : NORMAL; }; //////////////////////////////////////////////////////////////////////////////// // Pixel Shader //////////////////////////////////////////////////////////////////////////////// float4 LightPixelShader(PixelInputType input) : SV_TARGET { float4 textureColor; float3 lightDir; float lightIntensity; float4 color; // 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;
// Invert the light direction for calculations. lightDir = -lightDirection; // Calculate the amount of light on this pixel. lightIntensity = saturate(dot(input.normal, lightDir));
检查 N 点乘 L 是否大于零。如果是,则将漫射色添加到环境色。如果不是,则需要谨慎,不要添加漫射色。原因是漫射色可能是负的,它会从加法中减去一些环境色,这是不正确的。
if(lightIntensity > 0.0f) { // Determine the final diffuse color based on the diffuse color and the amount of light intensity. color += (diffuseColor * lightIntensity); }
确保对最终的输出光照颜色进行饱和处理,因为环境色和漫射色的组合可能大于 1。
// Saturate the final light color. color = saturate(color);
// Multiply the texture pixel and the final diffuse color to get the final pixel color result. color = color * textureColor; return color; }
//////////////////////////////////////////////////////////////////////////////// // 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; };
LightBufferType 更新了包含环境色分量。
struct LightBufferType {
D3DXVECTOR4 ambientColor;
D3DXVECTOR4 diffuseColor; D3DXVECTOR3 lightDirection; float padding; }; public: LightShaderClass(); LightShaderClass(const LightShaderClass&); ~LightShaderClass(); bool Initialize(ID3D11Device*, HWND); void Shutdown(); bool Render(ID3D11DeviceContext*, int, D3DXMATRIX, D3DXMATRIX, D3DXMATRIX, ID3D11ShaderResourceView*, D3DXVECTOR3, D3DXVECTOR4, D3DXVECTOR4); private: bool InitializeShader(ID3D11Device*, HWND, WCHAR*, WCHAR*); void ShutdownShader(); void OutputShaderErrorMessage(ID3D10Blob*, HWND, WCHAR*); bool SetShaderParameters(ID3D11DeviceContext*, D3DXMATRIX, D3DXMATRIX, D3DXMATRIX, ID3D11ShaderResourceView*, D3DXVECTOR3, D3DXVECTOR4, D3DXVECTOR4); void RenderShader(ID3D11DeviceContext*, int); private: ID3D11VertexShader* m_vertexShader; ID3D11PixelShader* m_pixelShader; ID3D11InputLayout* m_layout; ID3D11SamplerState* m_sampleState; ID3D11Buffer* m_matrixBuffer; ID3D11Buffer* m_lightBuffer; }; #endif
//////////////////////////////////////////////////////////////////////////////// // Filename: lightshaderclass.cpp //////////////////////////////////////////////////////////////////////////////// #include "lightshaderclass.h" LightShaderClass::LightShaderClass() { m_vertexShader = 0; m_pixelShader = 0; m_layout = 0; m_sampleState = 0; m_matrixBuffer = 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 函数现在接收一个环境色值,然后在渲染之前将其设置到着色器中。
bool LightShaderClass::Render(ID3D11DeviceContext* deviceContext, int indexCount, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix, ID3D11ShaderResourceView* texture, D3DXVECTOR3 lightDirection, D3DXVECTOR4 ambientColor, D3DXVECTOR4 diffuseColor)
{ bool result; // Set the shader parameters that it will use for rendering.
result = SetShaderParameters(deviceContext, worldMatrix, viewMatrix, projectionMatrix, texture, lightDirection, ambientColor, diffuseColor);
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 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 structure 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; } // 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; } // 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 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 函数现在接收一个环境光照颜色值。
bool LightShaderClass::SetShaderParameters(ID3D11DeviceContext* deviceContext, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix, ID3D11ShaderResourceView* texture, D3DXVECTOR3 lightDirection, D3DXVECTOR4 ambientColor, D3DXVECTOR4 diffuseColor)
{ HRESULT result; D3D11_MAPPED_SUBRESOURCE mappedResource; unsigned int bufferNumber; MatrixBufferType* dataPtr; LightBufferType* dataPtr2; // 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); // 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 constant buffer. dataPtr2 = (LightBufferType*)mappedResource.pData;
环境光照颜色被映射到光照缓冲区中,然后在渲染之前被设置为像素着色器中的一个常量。
// Copy the lighting variables into the constant buffer.
dataPtr2->ambientColor = ambientColor;
dataPtr2->diffuseColor = diffuseColor; dataPtr2->lightDirection = lightDirection; dataPtr2->padding = 0.0f; // Unlock the 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 在本教程中进行了更新,以包含环境分量和相关的辅助函数。
//////////////////////////////////////////////////////////////////////////////// // 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);
D3DXVECTOR4 GetAmbientColor();
D3DXVECTOR4 GetDiffuseColor(); D3DXVECTOR3 GetDirection(); private:
D3DXVECTOR4 m_ambientColor;
D3DXVECTOR4 m_diffuseColor; D3DXVECTOR3 m_direction; }; #endif
//////////////////////////////////////////////////////////////////////////////// // 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; }
D3DXVECTOR4 LightClass::GetAmbientColor() { return m_ambientColor; }
D3DXVECTOR4 LightClass::GetDiffuseColor() { return m_diffuseColor; } D3DXVECTOR3 LightClass::GetDirection() { return m_direction; }
本教程中,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
//////////////////////////////////////////////////////////////////////////////// // 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; }
将环境光的强度设置为 15% 的白色。还将光的指向设置为指向正 X 轴,以便我们能够直接看到环境光照对立方体的影响。
// 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(1.0f, 0.0f, 0.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());
光照着色器现在接收环境光照颜色作为输入。
// 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());
if(!result) { return false; } // Present the rendered scene to the screen. m_D3D->EndScene(); return true; }
通过添加环境光照,所有表面现在都以最小的程度进行照明,从而产生更真实的照明效果。
1. 重新编译代码,并确保你得到一个旋转的立方体,现在它的暗面也被照亮了。
2. 将环境光值更改为 (0.0f, 0.0f, 0.0f, 1.0f),以再次只看到漫射分量。
3. 在像素着色器中注释掉 color = color * textureColor; 行,以仅查看照明效果。