深度缓冲区与发光效果的实现

3D图形编程中,向最终用户清晰地展示交互对象是一项挑战性任务。例如,在CAD软件中,如何简洁地显示用户在三维场景中选择的对象?在实时策略游戏中,如何展示用户控制的单位?为了解决这个问题,一种方法是对感兴趣的模型进行轮廓勾勒或发光处理。

在深入探讨发光效果的实现之前,有必要简要概述一下模板缓冲区。具体来说,它是什么?它的作用是什么?

模板缓冲区是一个按像素级别的位掩码。换句话说,它是一个通用的深度缓冲区!为了详细说明,考虑一个过度简化的渲染器中的深度缓冲区逻辑。当需要渲染一个三角形时,首先将三角形的顶点推送到渲染管线中。然后,管线将三角形转换到屏幕空间,将连续的三角形数据光栅化为离散的像素,并为这些像素计算着色方程。最后,渲染器执行深度测试。它将新像素的深度与深度缓冲区中已有像素的深度进行比较。如果新像素的深度较小,则用新像素替换旧像素。

模板测试的工作原理类似。然而,可以定义模板比较操作以及渲染器写入模板缓冲区的信息。

如何制作发光效果

步骤1:将模型渲染到帧缓冲区、深度缓冲区和模板缓冲区中。

步骤2:在模板缓冲区中遮罩像素的同时,将模型的更大尺寸渲染到帧缓冲区和深度缓冲区中。(在这个渲染步骤中,可能需要更改着色方程。例如,如果想要一个红色发光效果,着色方程需要生成红色像素。)

示例/演示要求

由于这个项目基于DirectX10,将需要Windows Vista或更高版本。此外,使用的是DirectX,2010年2月SDK。需要安装2010年2月或更高版本的可再发行版本。最后,虽然不是必需的,但建议拥有一个兼容DirectX 10的GPU。

源代码要求

首先,所有示例要求也适用于源代码。此外,需要安装整个DirectX SDK,2010年2月版本或更高版本。安装SDK后,需要在IDE中包含DirectX和DXUT头文件,并链接它们各自的库。

注意:微软不分发DXUT库。SDK文件夹内将包含一个Visual Studio项目,其中包含DXUT头文件。构建此项目以创建库。

使用代码

首先,不要忘记分配一个模板缓冲区。知道这听起来微不足道;然而,DirectX 10将深度缓冲区和模板缓冲区合并为一个对象,这使得它容易被忽视。以下是初始化缓冲区的一种方法。

C++ // 初始化一个适合发光效果的深度模板缓冲区。 D3D10_TEXTURE2D_DESC descDepth; descDepth.Width = pBufferSurfaceDesc-> Width; descDepth.Height = pBufferSurfaceDesc-> Height; descDepth.MipLevels = 1 ; descDepth.ArraySize = 1 ; // “S8”为模板保留32位中的8位。 descDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; descDepth.SampleDesc.Count = 1 ; descDepth.SampleDesc.Quality = 0 ; descDepth.Usage = D3D10_USAGE_DEFAULT; descDepth.BindFlags = D3D10_BIND_DEPTH_STENCIL; descDepth.CPUAccessFlags = 0 ; descDepth.MiscFlags = 0 ; V_RETURN( pd3dDevice-> CreateTexture2D( &descDepth, NULL, &g_pDepthStencil ) ); // 为深度模板缓冲区设置视图。 D3D10_DEPTH_STENCIL_VIEW_DESC descDSV; ZeroMemory( &descDSV, sizeof ( D3D10_DEPTH_STENCIL_VIEW_DESC ) ); descDSV.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; descDSV.ViewDimension = D3D10_DSV_DIMENSION_TEXTURE2D; descDSV.Texture2D.MipSlice = 0 ; V_RETURN( pd3dDevice-> CreateDepthStencilView ( g_pDepthStencil, &descDSV, &g_pDSView ) );

接下来,DirectX10通过深度模板状态对象控制对这些缓冲区的写入。确保为每个渲染步骤设置几个这样的对象。

顺便说一句,DirectX实现了双面模板。模板缓冲区阴影效果最充分利用了这个特性。因此,对于发光效果,只需忽略“背面”像素操作。但是,确保它们不会干扰并从“正面”操作中调整值。

C++ // 创建与文章第一步对应的深度模板状态。 D3D10_DEPTH_STENCIL_DESC dsDesc; dsDesc.DepthEnable = true ; dsDesc.DepthWriteMask = D3D10_DEPTH_WRITE_MASK_ALL; dsDesc.DepthFunc = D3D10_COMPARISON_LESS; // 模板测试参数 dsDesc.StencilEnable = true ; dsDesc.StencilReadMask = 0xFF ; dsDesc.StencilWriteMask = 0xFF ; // 如果像素是正面的,模板操作如下。 // 在失败时保留原始值。 dsDesc.FrontFace.StencilFailOp = D3D10_STENCIL_OP_KEEP; dsDesc.FrontFace.StencilDepthFailOp = D3D10_STENCIL_OP_KEEP; // 在通过时写入模板。 dsDesc.FrontFace.StencilPassOp = D3D10_STENCIL_OP_INCR_SAT; dsDesc.FrontFace.StencilFunc = D3D10_COMPARISON_ALWAYS; // 如果像素是背面的,模板操作如下。 // 由于不关心背面像素,总是保留原始值。 dsDesc.BackFace.StencilFailOp = D3D10_STENCIL_OP_KEEP; dsDesc.BackFace.StencilDepthFailOp = D3D10_STENCIL_OP_KEEP; dsDesc.BackFace.StencilPassOp = D3D10_STENCIL_OP_KEEP; dsDesc.BackFace.StencilFunc = D3D10_COMPARISON_NEVER; // 创建深度模板状态。 V_RETURN( pd3dDevice-> CreateDepthStencilState(&dsDesc, &g_pDSState) ); // 创建与文章第二步对应的深度模板状态。 dsDesc.DepthEnable = true ; dsDesc.DepthWriteMask = D3D10_DEPTH_WRITE_MASK_ALL; dsDesc.DepthFunc = D3D10_COMPARISON_LESS; dsDesc.StencilEnable = true ; dsDesc.StencilReadMask = 0xFF ; dsDesc.StencilWriteMask = 0xFF ; // 写入的内容无关紧要,因为在此步骤之后不使用这些值。 // 换句话说,只是使用这些值来遮罩像素。 dsDesc.FrontFace.StencilFailOp = D3D10_STENCIL_OP_KEEP; dsDesc.FrontFace.StencilDepthFailOp = D3D10_STENCIL_OP_KEEP; dsDesc.FrontFace.StencilPassOp = D3D10_STENCIL_OP_KEEP; // 如果通过的参数等于缓冲区中的值,则模板测试通过。 dsDesc.FrontFace.StencilFunc = D3D10_COMPARISON_EQUAL; // 同样,不关心背面像素。 dsDesc.BackFace.StencilFailOp = D3D10_STENCIL_OP_KEEP; dsDesc.BackFace.StencilDepthFailOp = D3D10_STENCIL_OP_KEEP; dsDesc.BackFace.StencilPassOp = D3D10_STENCIL_OP_KEEP; dsDesc.BackFace.StencilFunc = D3D10_COMPARISON_NEVER; // 创建深度模板状态。 V_RETURN( pd3dDevice-> CreateDepthStencilState(&dsDesc, &g_pDSStateOutline) );

最后,将所有内容整合在一起,开始渲染一些发光效果。

C++ // 设置与文章第一步对应的模板状态。 pd3dDevice-> OMSetDepthStencilState( g_pDSState, 0 ); // 此技术渲染第一步。 g_pOutlineTechnique-> GetPassByIndex( 0 )-> Apply( 0 ); pd3dDevice-> DrawIndexed( 36 , 0 , 0 ); // 设置与文章第二步对应的模板状态。 pd3dDevice-> OMSetDepthStencilState( g_pDSStateOutline, 0 ); // 此技术渲染第二步。 // 请记住,此步骤需要进行一些额外的缩放,并输出正确的颜色像素以实现发光效果。 g_pOutlineTechnique-> GetPassByIndex( 1 )-> Apply( 0 ); pd3dDevice-> DrawIndexed( 36 , 0 , 0 );

虽然这种实现对于立方体或球体来说效果很好,但让考虑一下环面。当缩放时,内环的像素将落在原始未缩放的环面内。这将产生一个有趣的效果,但并不是完全的发光效果。

此外,任何不均匀缩放的网格都会有类似的视觉伪影。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485