(转)Shadow Mapping
原文:丢失,十分抱歉,这篇是在笔记上发现的。SmaEngine 阴影和级联部分是模仿UE的结构设计
This tutorial will cover how to implement shadow mapping in DirectX 11 using C++ and HLSL. Before proceeding with this tutorial you should first have a clear understanding of the following concepts: Render to Texture (Tutorial 22), Projective Texturing (Tutorial 27), and Depth Buffers (Tutorial 35). Shadow mapping is one of the fastest and CPU/GPU efficient methods for rendering shadows in small to medium sized scenes. It is also one of the simpler methods that gives highly realistic results. To understand how shadow mapping works we will start with a basic scene that is illuminated with a single light: The light that is illuminating the scene is originating from behind and to the right of our current camera position. The light position is very important as the next step is that we will render the scene from the point of view of the light. When we render from the light's point of view we will render just the depth buffer information into a render to texture. This render to texture filled with depth information is called the Shadow Map and looks like the following: Now that we have the depth information of all the objects in the scene that could possibly cast a shadow we can now figure out where the shadows should occur. When we render the scene we will project the shadow map texture back onto the scene to get the depth of any objects that cast shadows and compare it with the position of the light on a per pixel basis in the pixel shader. If we find the light is closer to the camera then we light the pixel. If we find the object is closer to the camera then we shadow the pixel. Doing so produces the following image: We will start the code section of the shadow map tutorial by examining the HLSL shaders first. Shadow.vs The shadow shaders are basic single point light shaders modified to handle shadowing. //////////////////////////////////////////////////////////////////////////////// ///////////// The vertex shader will require two light matrices to transform the vertex based on the light's view point which have been added to the MatrixBuffer. cbuffer MatrixBuffer ////////////////////// ////////////// The PixelInputType has lightViewPosition for sending the light perspective transformed vertex into the pixel shader. struct PixelInputType //////////////////////////////////////////////////////////////////////////////// // Change the position vector to be 4 units for proper matrix calculations. // Calculate the position of the vertex against the world, view, and projection matrices. Here we transform the vertex based on the light's perspective. // Calculate the position of the vertice as viewed by the light source. // Store the texture coordinates for the pixel shader. // Calculate the normal vector against the world matrix only. // Calculate the position of the vertex in the world. // Determine the light position based on the position of the light and the position of the vertex in the world. // Normalize the light position vector. return output;
Shadow.ps //////////////////////////////////////////////////////////////////////////////// ////////////// The depthMapTexture is the shadow map. This texture contains the scene depth buffer rendered from the light's perspective. Texture2D depthMapTexture : register(t1); /////////////////// We require a clamp based sampler when sampling the depth buffer so that it doesn't wrap around and sample incorrect information. SamplerState SampleTypeClamp : register(s0); ////////////////////// ////////////// //////////////////////////////////////////////////////////////////////////////// Shadow mapping requires a bias adjustment when comparing the depth of the light and the depth of the object due to the low floating point precision of the depth map. // Set the bias value for fixing the floating point precision issues. // Set the default output color to the ambient light value for all pixels. Calculate the projected texture coordinates for sampling the shadow map (depth buffer texture) based on the light's viewing position. // Calculate the projected texture coordinates. Check if the projected coordinates are in the view of the light, if not then the pixel gets just an ambient value. // Determine if the projected coordinates are in the 0 to 1 range. If so then this pixel is in the view of the light. Now that we are in the view of the light we will retrieve the depth value from the shadow map (depthMapTexture). We only sample the red component since this is a grey scale texture. The depth value we get from the texture translates into the distance to the nearest object. This is important since objects are what cast the shadows and hence why it is called a shadow map. // Sample the shadow map depth value from the depth texture using the sampler at the projected texture coordinate location. Now that we have the depth of the object for this pixel we need the depth of the light to determine if it is in front or behind the object. We get this from the lightViewPosition. Note that we need to subtract the bias from this or we will get the floating point precision issue. // Calculate the depth of the light. // Subtract the bias from the lightDepthValue. Now we perform the comparison between the light depth and the object depth. If the light is closer to us then no shadow. But if the light is behind an object in the shadow map then it gets shadowed. Note that a shadow just means we only apply ambient light, we don't color it black or anything. // Compare the depth of the shadow map value and the depth of the light to determine whether to shadow or to light this pixel. If the light was in front of the object then there is no shadow and we do regular lighting. // Calculate the amount of light on this pixel. if(lightIntensity > 0.0f) // Saturate the final light color. // Sample the pixel color from the texture using the sampler at this texture coordinate location. // Combine the light and texture color. return color;
Shadowshaderclass.h The ShadowShaderClass is just the LightShaderClass modified for shadows. //////////////////////////////////////////////////////////////////////////////// ////////////// //////////////////////////////////////////////////////////////////////////////// The MatrixBufferType has been expanded to hold the view and projection matrix of the light. struct MatrixBufferType struct LightBufferType struct LightBufferType2 public: bool Initialize(ID3D11Device*, HWND); private: bool SetShaderParameters(ID3D11DeviceContext*, D3DXMATRIX, D3DXMATRIX, D3DXMATRIX, D3DXMATRIX, D3DXMATRIX, ID3D11ShaderResourceView*, private: We now have a second clamp based sampler state. ID3D11SamplerState* m_sampleStateClamp; #endif
Shadowshaderclass.cpp //////////////////////////////////////////////////////////////////////////////// ShadowShaderClass::ShadowShaderClass() Initialize the new sampler state to null in the class constructor. m_sampleStateClamp = 0; ShadowShaderClass::ShadowShaderClass(const ShadowShaderClass& other) ShadowShaderClass::~ShadowShaderClass() bool ShadowShaderClass::Initialize(ID3D11Device* device, HWND hwnd) We load the shadow HLSL shader files here. // Initialize the vertex and pixel shaders. return true; void ShadowShaderClass::Shutdown() return; The shadow map texture is sent in as input into the Render function. We then send it into the SetShaderParameters to set it in the shader before rendering. bool ShadowShaderClass::Render(ID3D11DeviceContext* deviceContext, int indexCount, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, // Set the shader parameters that it will use for rendering. // Now render the prepared buffers with the shader. return true; bool ShadowShaderClass::InitializeShader(ID3D11Device* device, HWND hwnd, WCHAR* vsFilename, WCHAR* psFilename) There is a new description for the second clamp sampler state used in the pixel shader. D3D11_BUFFER_DESC lightBufferDesc2; // Initialize the pointers this function will use to null. Load the shadow vertex shader program. // Compile the vertex shader code. return false; Load the shadow pixel shader program. // Compile the pixel shader code. return false; // Create the vertex shader from the buffer. // Create the pixel shader from the buffer. // Create the vertex input layout description. polygonLayout[1].SemanticName = "TEXCOORD"; polygonLayout[2].SemanticName = "NORMAL"; // Get a count of the elements in the layout. // Create the vertex input layout. // Release the vertex shader buffer and pixel shader buffer since they are no longer needed. pixelShaderBuffer->Release(); // Create a wrap texture sampler state description. // Create the texture sampler state. Create the new clamp based sampler state here. // Create a clamp texture sampler state description. // Create the texture sampler state. // Setup the description of the dynamic matrix constant buffer that is in the vertex shader. // Create the constant buffer pointer so we can access the vertex shader constant buffer from within this class. // Setup the description of the light dynamic constant buffer that is in the pixel shader. // Create the constant buffer pointer so we can access the pixel shader constant buffer from within this class. // Setup the description of the light dynamic constant buffer that is in the vertex shader. // Create the constant buffer pointer so we can access the vertex shader constant buffer from within this class. return true; void ShadowShaderClass::ShutdownShader() if(m_lightBuffer2) // Release the matrix constant buffer. // Release the sampler states. Release the new clamp sampler state here. if(m_sampleStateClamp) // Release the layout. // Release the pixel shader. // Release the vertex shader. return; void ShadowShaderClass::OutputShaderErrorMessage(ID3D10Blob* errorMessage, HWND hwnd, WCHAR* shaderFilename) // Get a pointer to the error message text buffer. // Get the length of the message. // Open a file to write the error message to. // Write out the error message. // Close the file. // Release the error message. // Pop a message up on the screen to notify the user to check the text file for compile errors. return; bool ShadowShaderClass::SetShaderParameters(ID3D11DeviceContext* deviceContext, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, // Transpose the matrices to prepare them for the shader. Transpose the light matrices before sending them into the vertex shader. D3DXMatrixTranspose(&lightViewMatrix, &lightViewMatrix); // Lock the constant buffer so it can be written to. // Get a pointer to the data in the constant buffer. // Copy the matrices into the constant buffer. Copy the light matrices into the matrix constant buffer. dataPtr->lightView = lightViewMatrix; // Unlock the constant buffer. // Set the position of the constant buffer in the vertex shader. // Now set the constant buffer in the vertex shader with the updated values. // Set shader texture resource in the pixel shader. The shadow map texture is set in the pixel shader here. deviceContext->PSSetShaderResources(1, 1, &depthMapTexture); // Lock the light constant buffer so it can be written to. // Get a pointer to the data in the constant buffer. // Copy the lighting variables into the constant buffer. // Unlock the constant buffer. // Set the position of the light constant buffer in the pixel shader. // Finally set the light constant buffer in the pixel shader with the updated values. // Lock the second light constant buffer so it can be written to. // Get a pointer to the data in the constant buffer. // Copy the lighting variables into the constant buffer. // Unlock the constant buffer. // Set the position of the light constant buffer in the vertex shader. // Finally set the light constant buffer in the pixel shader with the updated values. return true; void ShadowShaderClass::RenderShader(ID3D11DeviceContext* deviceContext, int indexCount) // Set the vertex and pixel shaders that will be used to render this triangle. The new clamp sampler state is set in the pixel shader here. // Set the sampler states in the pixel shader. // Render the triangle. return;
Lightclass.h The LightClass was modified for this tutorial so that lights can have their own view and projection matrices associated with them. //////////////////////////////////////////////////////////////////////////////// ////////////// //////////////////////////////////////////////////////////////////////////////// void SetAmbientColor(float, float, float, float); D3DXVECTOR4 GetAmbientColor(); void GenerateViewMatrix(); void GetViewMatrix(D3DXMATRIX&); private: #endif
Lightclass.cpp //////////////////////////////////////////////////////////////////////////////// LightClass::LightClass() LightClass::LightClass(const LightClass& other) LightClass::~LightClass() void LightClass::SetAmbientColor(float red, float green, float blue, float alpha) void LightClass::SetDiffuseColor(float red, float green, float blue, float alpha) void LightClass::SetPosition(float x, float y, float z) The SetLookAt function sets the m_lookAt vector so that we can set where the light is looking at. This vector is used to build the light's view matrix. void LightClass::SetLookAt(float x, float y, float z) D3DXVECTOR4 LightClass::GetAmbientColor() D3DXVECTOR4 LightClass::GetDiffuseColor() D3DXVECTOR3 LightClass::GetPosition() The view matrix for the light is built using the up vector, the lookAt vector, and the position of the light. void LightClass::GenerateViewMatrix() // Setup the vector that points upwards. // Create the view matrix from the three vectors. The projection matrix for the light is built using the field of view, viewing aspect ratio, and the near and far plane of the light range. The light we are projecting is more of a square spotlight than a true point light, but this is necessary since we need to align with the sampling from a square shadow map texture. That is why the field of view and aspect ratio are setup for a square projection. void LightClass::GenerateProjectionMatrix(float screenDepth, float screenNear) // Setup field of view and screen aspect for a square light source. // Create the projection matrix for the light. return; We also have two new functions to return the view and projection matrices. void LightClass::GetViewMatrix(D3DXMATRIX& viewMatrix) void LightClass::GetProjectionMatrix(D3DXMATRIX& projectionMatrix)
Graphicsclass.h //////////////////////////////////////////////////////////////////////////////// /////////////////////// We have included headers for the render to texture, depth shader, and shadow shader classes. #include "rendertextureclass.h" ///////////// We also added a new define for the shadow map size so it can be easily tweaked. const int SHADOWMAP_WIDTH = 1024; //////////////////////////////////////////////////////////////////////////////// bool Initialize(int, int, HWND); private: private: We will use a cube, a sphere, and a ground model for the scene. ModelClass *m_CubeModel, *m_GroundModel, *m_SphereModel; The new render to texture, depth shader, and shadow shader objects are defined here. RenderTextureClass* m_RenderTexture; #endif
Graphicsclass.cpp //////////////////////////////////////////////////////////////////////////////// GraphicsClass::GraphicsClass() GraphicsClass::GraphicsClass(const GraphicsClass& other) GraphicsClass::~GraphicsClass() bool GraphicsClass::Initialize(int screenWidth, int screenHeight, HWND hwnd) // Create the Direct3D object. // Initialize the Direct3D object. // Create the camera object. // Set the initial position of the camera. We setup the cube model here. // Create the cube model object. // Initialize the cube model object. // Set the position for the cube model. We setup the sphere model here. // Create the sphere model object. // Initialize the sphere model object. // Set the position for the sphere model. We setup the ground model here. // Create the ground model object. // Initialize the ground model object. // Set the position for the ground model. // Create the light object. // Initialize the light object. The lookAt is now set in the light object and then we can generate the projection matrix that this light will have. m_Light->SetLookAt(0.0f, 0.0f, 0.0f); We create a render to texture object that will be used as the shadow map. The depth buffer of the scene will be rendered from the light's perspective onto this render to texture object. // Create the render to texture object. // Initialize the render to texture object. The depth shader object is created and initialized here. // Create the depth shader object. // Initialize the depth shader object. The shadow shader object is created and initialized here. // Create the shadow shader object. // Initialize the shadow shader object. return true; void GraphicsClass::Shutdown() // Release the depth shader object. // Release the render to texture object. // Release the light object. // Release the ground model object. // Release the sphere model object. // Release the cube model object. // Release the camera object. // Release the D3D object. return; bool GraphicsClass::Frame(float posX, float posY, float posZ, float rotX, float rotY, float rotZ) // Set the position of the camera. During the frame we move the light from left to right to see the shadows move in accordance with the light's position. // Update the position of the light each frame. // Update the position of the light. // Render the graphics scene. return true; The RenderSceneToTexture function is called at the beginning of the frame rendering. We basically render the depth buffer of the scene from the perspective of the light into the render to texture object which then becomes our shadow map. bool GraphicsClass::RenderSceneToTexture() Set the render to texture to be the rendering target. // Set the render target to be the render to texture. // Clear the render to texture. Generate the view matrix of the light. // Generate the light view matrix based on the light's position. // Get the world matrix from the d3d object. // Get the view and orthographic matrices from the light object. Render all the objects in the scene using the depth shader and the light view and projection matrices. // Setup the translation matrix for the cube model. // Render the cube model with the depth shader. // Reset the world matrix. // Setup the translation matrix for the sphere model. // Render the sphere model with the depth shader. // Reset the world matrix. // Setup the translation matrix for the ground model. // Render the ground model with the depth shader. Set the rendering target back to normal. // Reset the render target back to the original back buffer and not the render to texture anymore. // Reset the viewport back to the original. return true; bool GraphicsClass::Render() First render the depth information to the render to texture object to get our shadow map. // First render the scene to a texture. // Clear the buffers to begin the scene. // Generate the view matrix based on the camera's position. Generate a light view matrix. // Generate the light view matrix based on the light's position. // Get the world, view, and projection matrices from the camera and d3d objects. Retrieve the two matrices for the light. // Get the light's view and projection matrices from the light object. Now render each model using the shadow map shader, the light matrices, and the shadow map texture. // Setup the translation matrix for the cube model. // Render the model using the shadow shader. // Reset the world matrix. // Setup the translation matrix for the sphere model. // Put the model vertex and index buffers on the graphics pipeline to prepare them for drawing. // Reset the world matrix. // Setup the translation matrix for the ground model. // Render the ground model using the shadow shader. // Present the rendered scene to the screen. return true;
Summary We can now add shadows to any of our scenes using a render to texture of the depth information from the light's perspective. The variable inputs and same checks can be used with any other shader program to add shadows. To Do Exercises 1. Compile the code and run the program. Use the arrow keys, A, Z, PgUp, and PgDn keys to navigate around the scene and examine the shadows. Press escape to quit. 2. Set the bias to 0.0f in the pixel shader to see the effect when no bias is set. 3. Set the bias to a higher value such as 0.3f to see how it moves the shadows away from their expected position. 4. Set the SHADOWMAP_WIDTH and SHADOWMAP_HEIGHT to a lower value such as 256 to see the effect a low resolution shadow map produces. 5. Move the sphere closer to the cube to see that they shadow each other. |
(转)Shadow Mapping的更多相关文章
- Tutorial - Deferred Rendering Shadow Mapping 转
http://www.codinglabs.net/tutorial_opengl_deferred_rendering_shadow_mapping.aspx Tutorial - Deferred ...
- OpenGL阴影,Shadow Mapping(附源程序)
实验平台:Win7,VS2010 先上结果截图(文章最后下载程序,解压后直接运行BIN文件夹下的EXE程序): 本文描述图形学的两个最常用的阴影技术之一,Shadow Mapping方法(另一种是Sh ...
- Shadow mapping
http://www.cnblogs.com/cxrs/archive/2009/10/17/1585038.html 1.什么是Shadow Maping? Shadow Mapping是 ...
- opengl 教程(24) shadow mapping (2)
原帖地址:http://ogldev.atspace.co.uk/www/tutorial24/tutorial24.html 本篇教程中,我们通过shadowmap来实现阴影渲染. 我们知道shad ...
- OpenGL 阴影之Shadow Mapping和Shadow Volumes
先说下开发环境.VS2013,C++空项目,引用glut,glew.glut包含基本窗口操作,免去我们自己新建win32窗口一些操作.glew使我们能使用最新opengl的API,因winodw本身只 ...
- shadow mapping实现动态shadow实现记录 【转】
http://blog.csdn.net/iaccepted/article/details/45826539 前段时间一直在弄一个室内场景,首先完成了render,效果还可以.然后给其加上shado ...
- OpenGL核心技术之Shadow Mapping改进版
笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者;已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D实战核心技术详解 ...
- OpenGL核心技术之Shadow Mapping
笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D ...
- Shadow Mapping 的原理与实践(二)
3) 定义并生成Shadow Map纹理 texture2D Lamp0ShadowMapColor : RENDERCOLORTARGET < float2 ViewPortRatio = { ...
随机推荐
- 对于dequeueReusableCellWithIdentifier:的理解
Table Data Source Methods中的一个必要实现的方法tableView: cellForRowAtIndexPath: 中经常会包含一段代码: static NSString ...
- 小修改,让mvc的验证锦上添点花
首先,mvc的客户端验证用的是jquery.validate.js, jquery.validate本身已经提供了很好的扩展功能,通过简单点配置就可以做得更好看些. 而Microsoft通过jquer ...
- 产线事故:删除创建mysql索引
单表数据量:670W: 删除一个老的索引,新建一个新的索引. 事故原因: 先删除索引,应用访问量大,没有索引自然慢,数据库CPU飚到100%:新索引创建失败. 直接造成交易打烊. 日志: ------ ...
- OSD仿真_MFC程序01
Windows系统具有强大的绘图功能,可以用来模拟OSD显示.接下来将设计一个简单的模拟显示终端的程序,用于后续显示功能和菜单系统的开发.说明一下,对于Windows下的MFC编程我不怎么了解,只知道 ...
- 用HTML编写迪士尼乐园页面
<!DOCTYPE html><html xmlns="http://www.w3.org/1999/html"><head lang="e ...
- Flex布局(一)flex-direction
采用Flex布局的元素,被称为Flex容器(flex container),简称"容器".其所有子元素自动成为容器成员,成为Flex项目(Flex item),简称"项目 ...
- DW CS5序列号
先要改host文件,以防止其连接 Adobe 的激活验证服务器 1. 用记事本编辑“C:\Windows\System32\Drivers\etc\”目录下的 hosts 文件, 在末尾加上: 127 ...
- 执行上下文--变量、函数、this
原文地址:https://www.xingkongbj.com/blog/js/execution-context.html JavaScript 中的执行上下文和调用栈 ES6 变量作用域与提升:变 ...
- ubuntu安装flashplayer插件三步走
1.去官网下载flash;2.解压3.复制.so文件到~/.mozilla/plugins/
- ORALCE表的约束条件
一.主键:(PRIMARY KEY) 主键是表中的一列或多列.为表定义主键有如下几个作用: 1.主键包含的列不能输入重复的值,以此来保证一个表的所有行的唯一性: 2.主键也不允许定义此约束的列为NUL ...