DirectX 10 教程9:环境光照
原文地址:Tutorial 9: Ambient Lighting(http://www.rastertek.com/dx10tut09.html)。
源代码:dx10tut09.zip。
本教程介绍如何使用环境光照。
我使用了一个例子解释环境光照。加入你在一个房间里,唯一的光源是通过窗口照射进来的太阳光。阳光并没有直接照射到房间的每个面上,但房间中的每样东西都会被散射光照亮,这种在阳光并没有直接照射在物体表面的光照效果称之为环境光照。
我们使用了一个非常简单的公式模拟环境光照,只是在像素着色器的开始将每个像素的颜色设置为环境光颜色,之后再进行其他颜色操作叠加在环境光颜色上。通过这个方法我们让每个对象至少都有环境光的颜色。
环境光照还能让3D场景更加真实。下图中的立方体只被X轴方向的漫反射光照射:
图像看起来不够真实,因为即使光照很弱,充斥在周围环境光也会让物体有一个大致的轮廓。现在,如果我们添加15%的白色环境光照,则图像如下图所示:
这样就获得了一个更加真实的光照效果。
本教程是建立在上一个漫反射光照教程的基础上的。要添加环境光分量只需做很小一点的改动。
Light.fx
//////////////////////////////////////////////////////////////////////////////// // Filename: light.fx //////////////////////////////////////////////////////////////////////////////// ///////////// // GLOBALS // ///////////// matrix worldMatrix; matrix viewMatrix; matrix projectionMatrix; Texture2D shaderTexture;
首先添加一个float4类型的环境光颜色变量,这样环境光就可以被这个shader之外的类进行设置。
float4 ambientColor; float4 diffuseColor; float3 lightDirection; /////////////////// // SAMPLE STATES // /////////////////// SamplerState SampleType { Filter = MIN_MAG_MIP_LINEAR; AddressU = Wrap; AddressV = Wrap; }; ////////////// // 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; } //////////////////////////////////////////////////////////////////////////////// // 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的点乘是否大于零。如果是则将漫反射颜色加上环境光颜色,如果不是则无需加上漫反射颜色,这是因为漫反射强度可能为负值,导致会在环境光颜色中减去漫反射颜色,导致错误的光照效果。
// 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)); 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操作。
// 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; } //////////////////////////////////////////////////////////////////////////////// // Technique //////////////////////////////////////////////////////////////////////////////// technique10 LightTechnique { pass pass0 { SetVertexShader(CompileShader(vs_4_0, LightVertexShader())); SetPixelShader(CompileShader(ps_4_0, LightPixelShader())); SetGeometryShader(NULL); } }
Lightshaderclass.h
//////////////////////////////////////////////////////////////////////////////// // Filename: lightshaderclass.h //////////////////////////////////////////////////////////////////////////////// #ifndef _LIGHTSHADERCLASS_H_ #define _LIGHTSHADERCLASS_H_ ////////////// // INCLUDES // ////////////// #include <d3d10.h> #include <d3dx10math.h> #include <fstream> using namespace std; //////////////////////////////////////////////////////////////////////////////// // Class name: LightShaderClass //////////////////////////////////////////////////////////////////////////////// class LightShaderClass { public: LightShaderClass(); LightShaderClass(const LightShaderClass&); ~LightShaderClass(); bool Initialize(ID3D10Device*, HWND); void Shutdown(); void Render(ID3D10Device*, int, D3DXMATRIX, D3DXMATRIX, D3DXMATRIX, ID3D10ShaderResourceView*, D3DXVECTOR3, D3DXVECTOR4, D3DXVECTOR4); private: bool InitializeShader(ID3D10Device*, HWND, WCHAR*); void ShutdownShader(); void OutputShaderErrorMessage(ID3D10Blob*, HWND, WCHAR*); void SetShaderParameters(D3DXMATRIX, D3DXMATRIX, D3DXMATRIX, ID3D10ShaderResourceView*, D3DXVECTOR3, D3DXVECTOR4, D3DXVECTOR4); void RenderShader(ID3D10Device*, int); private: ID3D10Effect* m_effect; ID3D10EffectTechnique* m_technique; ID3D10InputLayout* m_layout; ID3D10EffectMatrixVariable* m_worldMatrixPtr; ID3D10EffectMatrixVariable* m_viewMatrixPtr; ID3D10EffectMatrixVariable* m_projectionMatrixPtr; ID3D10EffectShaderResourceVariable* m_texturePtr; ID3D10EffectVectorVariable* m_lightDirectionPtr;
LightShaderClass新添了一个指向环境光颜色的私有指针。
ID3D10EffectVectorVariable* m_ambientColorPtr; ID3D10EffectVectorVariable* m_diffuseColorPtr; }; #endif
Lightshaderclass.cpp
//////////////////////////////////////////////////////////////////////////////// // Filename: lightshaderclass.cpp //////////////////////////////////////////////////////////////////////////////// #include "lightshaderclass.h" LightShaderClass::LightShaderClass() { m_effect = 0; m_technique = 0; m_layout = 0; m_worldMatrixPtr = 0; m_viewMatrixPtr = 0; m_projectionMatrixPtr = 0; m_texturePtr = 0; m_lightDirectionPtr = 0;
在构造函数中将ambient light指针设置为null。
m_ambientColorPtr = 0; m_diffuseColorPtr = 0; } LightShaderClass::LightShaderClass(const LightShaderClass& other) { } LightShaderClass::~LightShaderClass() { } bool LightShaderClass::Initialize(ID3D10Device* device, HWND hwnd) { bool result; // Initialize the shader that will be used to draw the triangle. result = InitializeShader(device, hwnd, L"../Engine/light.fx"); if(!result) { return false; } return true; } void LightShaderClass::Shutdown() { // Shutdown the shader effect. ShutdownShader(); return; }
Render方法的参数中新包含了环境光颜色,它会被传递到shader中进行设置。
void LightShaderClass::Render(ID3D10Device* device, int indexCount, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix, ID3D10ShaderResourceView* texture, D3DXVECTOR3 lightDirection, D3DXVECTOR4 ambientColor, D3DXVECTOR4 diffuseColor) { // Set the shader parameters that it will use for rendering. SetShaderParameters(worldMatrix, viewMatrix, projectionMatrix, texture, lightDirection, ambientColor, diffuseColor); // Now render the prepared buffers with the shader. RenderShader(device, indexCount); return; } bool LightShaderClass::InitializeShader(ID3D10Device* device, HWND hwnd, WCHAR* filename) { HRESULT result; ID3D10Blob* errorMessage; D3D10_INPUT_ELEMENT_DESC polygonLayout[3]; unsigned int numElements; D3D10_PASS_DESC passDesc; // Initialize the error message. errorMessage = 0; // Load the shader in from the file. result = D3DX10CreateEffectFromFile(filename, NULL, NULL, "fx_4_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, device, NULL, NULL, &m_effect, &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, filename); } // If there was nothing in the error message then it simply could not find the shader file itself. else { MessageBox(hwnd, filename, L"Missing Shader File", MB_OK); } return false; } // Get a pointer to the technique inside the shader. m_technique = m_effect->GetTechniqueByName("LightTechnique"); if(!m_technique) { return false; } // Now setup the layout of the data that goes into the shader. // 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 = D3D10_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 = D3D10_APPEND_ALIGNED_ELEMENT; polygonLayout[1].InputSlotClass = D3D10_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 = D3D10_APPEND_ALIGNED_ELEMENT; polygonLayout[2].InputSlotClass = D3D10_INPUT_PER_VERTEX_DATA; polygonLayout[2].InstanceDataStepRate = 0; // Get a count of the elements in the layout. numElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]); // Get the description of the first pass described in the shader technique. m_technique->GetPassByIndex(0)->GetDesc(&passDesc); // Create the input layout. result = device->CreateInputLayout(polygonLayout, numElements, passDesc.pIAInputSignature, passDesc.IAInputSignatureSize, &m_layout); if(FAILED(result)) { return false; } // Get pointers to the three matrices inside the shader so we can update them from this class. m_worldMatrixPtr = m_effect->GetVariableByName("worldMatrix")->AsMatrix(); m_viewMatrixPtr = m_effect->GetVariableByName("viewMatrix")->AsMatrix(); m_projectionMatrixPtr = m_effect->GetVariableByName("projectionMatrix")->AsMatrix(); // Get pointer to the texture resource inside the shader. m_texturePtr = m_effect->GetVariableByName("shaderTexture")->AsShaderResource(); // Get pointers to the light direction and diffuse color variables inside the shader. m_lightDirectionPtr = m_effect->GetVariableByName("lightDirection")->AsVector();
新的环境光指针被设置到shader中的环境光颜色变量。
m_ambientColorPtr = m_effect->GetVariableByName("ambientColor")->AsVector(); m_diffuseColorPtr = m_effect->GetVariableByName("diffuseColor")->AsVector(); return true; } void LightShaderClass::ShutdownShader() { // Release the light pointers. m_lightDirectionPtr = 0;
在ShutdownShader方法中释放环境光指针。
m_ambientColorPtr = 0; m_diffuseColorPtr = 0; // Release the pointer to the texture in the shader file. m_texturePtr = 0; // Release the pointers to the matrices inside the shader. m_worldMatrixPtr = 0; m_viewMatrixPtr = 0; m_projectionMatrixPtr = 0; // Release the pointer to the shader layout. if(m_layout) { m_layout->Release(); m_layout = 0; } // Release the pointer to the shader technique. m_technique = 0; // Release the pointer to the shader. if(m_effect) { m_effect->Release(); m_effect = 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; } void LightShaderClass::SetShaderParameters(D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix, ID3D10ShaderResourceView* texture, D3DXVECTOR3 lightDirection, D3DXVECTOR4 ambientColor, D3DXVECTOR4 diffuseColor) { // Set the world matrix variable inside the shader. m_worldMatrixPtr->SetMatrix((float*)&worldMatrix); // Set the view matrix variable inside the shader. m_viewMatrixPtr->SetMatrix((float*)&viewMatrix); // Set the projection matrix variable inside the shader. m_projectionMatrixPtr->SetMatrix((float*)&projectionMatrix); // Bind the texture. m_texturePtr->SetResource(texture); // Set the direction of the light inside the shader. m_lightDirectionPtr->SetFloatVector((float*)&lightDirection); // Set the ambient color of the light. m_ambientColorPtr->SetFloatVector((float*)&ambientColor); // Set the diffuse color of the light inside the shader. m_diffuseColorPtr->SetFloatVector((float*)&diffuseColor); return; } void LightShaderClass::RenderShader(ID3D10Device* device, int indexCount) { D3D10_TECHNIQUE_DESC techniqueDesc; unsigned int i; // Set the input layout. device->IASetInputLayout(m_layout); // Get the description structure of the technique from inside the shader so it can be used for rendering. m_technique->GetDesc(&techniqueDesc); // Go through each pass in the technique (should be just one currently) and render the triangles. for(i=0; i<techniqueDesc.Passes; ++i) { m_technique->GetPassByIndex(i)->Apply(0); device->DrawIndexed(indexCount, 0, 0); } return; }
Lightclass.h
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
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; } D3DXVECTOR4 LightClass::GetAmbientColor() { return m_ambientColor; } D3DXVECTOR4 LightClass::GetDiffuseColor() { return m_diffuseColor; } D3DXVECTOR3 LightClass::GetDirection() { return m_direction; }
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; }
将环境光颜色设置为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; // 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->GetDevice());
light shader的Render方法的参数中新添了环境光颜色。
// Render the model using the light shader. m_LightShader->Render(m_D3D->GetDevice(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix, m_Model->GetTexture(), m_Light->GetDirection(), m_Light->GetAmbientColor(), m_Light->GetDiffuseColor()); // 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;”注释掉,就不会显示纹理了。
文件下载(已下载 939 次)发布时间:2012/7/17 下午4:29:16 阅读次数:6286