65.9K
CodeProject 正在变化。 阅读更多。
Home

模板缓冲区发光 - 第一部分

starIconstarIconstarIconstarIconstarIcon

5.00/5 (13投票s)

2010年11月18日

CPOL

3分钟阅读

viewsIcon

50665

downloadIcon

1448

通过利用模板缓冲区实现的辉光效果。

引言

作为一名软件工程师,有效地与最终用户沟通可能是一项艰巨的任务。例如,在 CAD 软件包中,您如何简洁地显示用户在三维场景中选择的对象?在实时策略游戏中,您如何显示用户正在控制哪些单位?为了解决这个问题,一种方法是勾勒出或发光感兴趣的模型。

背景

在深入研究发光的实现之前,我觉得有必要简要概述一下模板缓冲。具体来说,它是什么,它有什么作用?

模板缓冲是一个逐像素级别的位掩码。换句话说,它是一个广义的深度缓冲!

为了详细说明,考虑一下简化渲染器中深度缓冲的逻辑。当我想渲染一个三角形时,我首先将我的三角形的顶点推入渲染管线。然后,管线将我的三角形转换为屏幕空间,将连续的三角形数据栅格化为离散的像素,并为每个像素计算我的着色方程。最后,渲染器执行深度测试。它将我的每个新像素的深度与深度缓冲中已有的像素的深度进行比较。如果新像素的深度较小,它将用新像素替换旧像素。

模板测试的操作类似。但是,我可以定义模板比较操作以及渲染器写入模板缓冲的信息。

考虑到这一点,我如何发光?

步骤 1

将模型渲染到帧、深度和模板缓冲中。

第二步

将更大比例的模型渲染到帧和深度缓冲中,同时根据模板缓冲中当前的值屏蔽像素。(在这一渲染步骤中,改变你的着色方程可能是明智之举。例如,如果我想要一个红色的发光,我的着色方程需要生成红色的像素。)

示例/演示要求

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

源代码要求

首先,所有示例要求也适用于源代码。此外,您将需要安装整个 DirectX SDK,2010 年 2 月或更新版本。安装 SDK 后,在您的 IDE 中包含 DirectX 和 DXUT 标头并链接它们各自的库。
注意:微软不分发 DXUT 库。SDK 文件夹中将有一个 Visual Studio 项目,其中包含 DXUT 标头。构建此项目以创建库。

Using the Code

首先,不要忘记分配一个模板缓冲。我知道这听起来微不足道;然而,DirectX 10 将深度和模板缓冲组合成一个单独的对象,这使得它很容易被忽略。这是初始化缓冲的一种方法。

// Initialize a depth stencil buffere suitable for glows.
D3D10_TEXTURE2D_DESC descDepth;
descDepth.Width = pBufferSurfaceDesc->Width;
descDepth.Height = pBufferSurfaceDesc->Height;
descDepth.MipLevels = 1;
descDepth.ArraySize = 1;

// "S8" reserves eight of the thirty-two bits for stenciling.
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 ) );

// Set up a view for the depth stencil buffer.
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 ) );

接下来,DirectX 10 通过深度模板状态对象控制对这些缓冲的写入。确保设置了几个对应于每个渲染步骤的状态对象。

顺便说一句,DirectX 实现了一个双面模板。模板缓冲阴影效果最能利用此功能。所以,对于发光效果,只需忽略“BackFacing”像素操作。但是,确保它们不干扰并调整“FrontFacing”操作的值。

// Create depth stencil state corresponding to step one of the article.
D3D10_DEPTH_STENCIL_DESC dsDesc;
dsDesc.DepthEnable = true;
dsDesc.DepthWriteMask = D3D10_DEPTH_WRITE_MASK_ALL;
dsDesc.DepthFunc = D3D10_COMPARISON_LESS;

// Stencil test parameters
dsDesc.StencilEnable = true;
dsDesc.StencilReadMask = 0xFF;
dsDesc.StencilWriteMask = 0xFF;

// Stencil operations if pixel is front-facing.

// Keep original value on fail.
dsDesc.FrontFace.StencilFailOp = D3D10_STENCIL_OP_KEEP;      
dsDesc.FrontFace.StencilDepthFailOp = D3D10_STENCIL_OP_KEEP; 
// Write to the stencil on pass.
dsDesc.FrontFace.StencilPassOp = D3D10_STENCIL_OP_INCR_SAT;
dsDesc.FrontFace.StencilFunc = D3D10_COMPARISON_ALWAYS;

// Stencil operations if pixel is back-facing.
// Since we do not care about back-facing pixels, always keep original value.
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;

// Create the depth stencil state.
V_RETURN( pd3dDevice->CreateDepthStencilState(&dsDesc, &g_pDSState) );

// Create depth stencil state corresponding to step two of the article.
dsDesc.DepthEnable = true;
dsDesc.DepthWriteMask = D3D10_DEPTH_WRITE_MASK_ALL;
dsDesc.DepthFunc = D3D10_COMPARISON_LESS;

dsDesc.StencilEnable = true;
dsDesc.StencilReadMask = 0xFF;
dsDesc.StencilWriteMask = 0xFF;

// It does not matter what we write since we are not using the values after this step.
// In other words, we are only using the values to mask pixels.
dsDesc.FrontFace.StencilFailOp = D3D10_STENCIL_OP_KEEP;
dsDesc.FrontFace.StencilDepthFailOp = D3D10_STENCIL_OP_KEEP;
dsDesc.FrontFace.StencilPassOp = D3D10_STENCIL_OP_KEEP;
// The stencil test passes if the passed parameter is equal to value in the buffer.
dsDesc.FrontFace.StencilFunc = D3D10_COMPARISON_EQUAL;

// Again, we do not care about back-facing pixels.
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;

// Create the depth stencil state.
V_RETURN( pd3dDevice->CreateDepthStencilState(&dsDesc, &g_pDSStateOutline) );

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

// Set the stencil state corresponding to step one of the article.
pd3dDevice->OMSetDepthStencilState( g_pDSState, 0 );

// This technique renders step one.
g_pOutlineTechnique->GetPassByIndex( 0 )->Apply( 0 );
pd3dDevice->DrawIndexed( 36, 0, 0 );
	
// Set the stencil state corresponding to step two of the article.
pd3dDevice->OMSetDepthStencilState( g_pDSStateOutline, 0 );

// This technique renders step two.
// Remember, this pass needs to do some extra scaling and to output
// the correct color pixels for the glow.
g_pOutlineTechnique->GetPassByIndex( 1 )->Apply( 0 );
pd3dDevice->DrawIndexed( 36, 0, 0 );	

结论

虽然这种实现对立方体或球体产生了出色的效果,但让我们考虑一下环面。当缩放时,内环上的像素将落入原始的、未缩放的环面内部。这将导致一个有趣的效果,但并不是一个完整的发光。

此外,任何不围绕原点均匀缩放的网格都会出现类似的视觉瑕疵。

因此,在第二篇文章中,我将研究一种更稳健的方法来生成发光的位置。然而,现在,专注于理解模板缓冲。具体来说,我们如何利用它来剔除第二个渲染步骤中的像素。

历史

  • 当前修订版:1.0 版
© . All rights reserved.