DirectX 11 学习笔记-Demo-02

Basic.fx 

基本的Effect文件,包括一个顶点着色器,一个像素着色器和若干Cbuffer。Cbuffer按照更新频率分组有助于提高效率。这里有两组Cbuffer,第一组主要是环境相关参数,一般而言每帧只更新一次。 

第二组是和具体绘制对象有关系的参数,包括世界坐标下的对象的矩阵信息,纹理变换矩阵和光照材质等。 

cbuffer cbPerFrame 

{ 

    DirectionalLight gDirLights[3]; 

    float3 gEyePosW; 



    float  gFogStart; 

    float  gFogRange; 

    float4 gFogColor; 

}; 



cbuffer cbPerObject 

{ 

    float4x4 gWorld; 

    float4x4 gWorldInvTranspose; 

    float4x4 gWorldViewProj; 

    float4x4 gTexTransform; 

    Material gMaterial; 

};  

另外还包含一个纹理贴图。因为纹理贴图无法包含在cbuffer中所以单独定义。 顶点着色器的输入包括一个本地位置,本地法线和纹理UV,经过顶点着色器后变换为世界坐标和屏幕空间坐标。(世界坐标用于光照计算) 

struct VertexIn 

{ 

    float3 PosL    : POSITION; 

    float3 NormalL : NORMAL; 

    float2 Tex     : TEXCOORD; 

}; 

struct VertexOut 

{ 

    float4 PosH    : SV_POSITION; 

    float3 PosW    : POSITION; 

    float3 NormalW : NORMAL; 

    float2 Tex     : TEXCOORD; 

}; 

像素着色器主要作用是纹理采样和光照计算。这里参数中有一个uniform关键字,主要用于将输入参数标记为不变量,从而使编译时能对分支语句等进行优化。 

光照的部分不详细叙述,书中介绍得很清楚,主要是使用了兰伯特余弦定理推导光照公式。 

Mount&Water 

Mount较为简单,生成好纹理网格,获取随机的高度并计算法线和纹理UV,创建顶点和索引坐标即可。绘制时先设置好BasicEffect的参数,然后绘制即可。由于我们有两种顶点坐标,所以每次绘制前都会设置一次输入布局。 

Water类似于Mount。关于水波的算法有兴趣可以详细学习《3D游戏与计算机图形学中的数学方法》。由于我们要定期更新水波坐标,所以这里需要一个Dynamic的顶点坐标,然后在顶点更新之后使用Map的方式来更新数据即可。 

绘制时,我们的水是半透明的,所以在DrawTransparency时绘制。绘制透明物体需要开启混合之外,还应该关闭深度信息的写入。 

pD3dContext->OMSetDepthStencilState(RenderStates::NoDepthDSS, 1); 

pD3dContext->OMSetBlendState( RenderStates::TransparentBS , blendFactor, 0xffffffff ); 


D3D11_DEPTH_STENCIL_DESC noDepthDesc; 

noDepthDesc.DepthEnable      = true; 

noDepthDesc.DepthWriteMask   = D3D11_DEPTH_WRITE_MASK_ZERO; 

noDepthDesc.DepthFunc        = D3D11_COMPARISON_LESS;  

noDepthDesc.StencilEnable    = false; 

noDepthDesc.StencilReadMask  = 0xff; 

noDepthDesc.StencilWriteMask = 0xff; 



noDepthDesc.FrontFace.StencilFailOp      = D3D11_STENCIL_OP_KEEP; 

noDepthDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; 

noDepthDesc.FrontFace.StencilPassOp      = D3D11_STENCIL_OP_INCR; 

noDepthDesc.FrontFace.StencilFunc        = D3D11_COMPARISON_EQUAL; 



// We are not rendering backfacing polygons, so these settings do not matter. 

noDepthDesc.BackFace.StencilFailOp      = D3D11_STENCIL_OP_KEEP; 

noDepthDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; 

noDepthDesc.BackFace.StencilPassOp      = D3D11_STENCIL_OP_INCR; 

noDepthDesc.BackFace.StencilFunc        = D3D11_COMPARISON_EQUAL; 



HR(device->CreateDepthStencilState(&noDepthDesc, &NoDepthDSS)); 

为了说明混合/深度测试/深度写入之间的关系,我这里举一个例子,假设我们某个像素位置有6个像素片段ABCDEF,它们的绘制顺序按字母顺序,绘制时状态如下表所示 

像素片段 

透明 

混合 

深度值 

深度测试 

深度写入 

模板深度值 

缓冲区像素 

否 

否 

0.8 

是 

是 

0.8 

否 

否 

0.4 

是 

是 

0.4 

否 

否 

0.5 

是 

是 

0.4 

是 

是 

0.3 

是 

否 

0.4 

B+D 

是 

是 

0.45 

是 

否 

0.4 

B+D 

是 

是 

0.35 

是 

否 

0.4 

B+D+F 

根据深度信息我们可以判断,从近到远的顺序排列物体应该是DFBECA,由于ABC都是不透明物体,所以ECA都被B遮挡了,而DF是透明的,所以可以透过D和F看见B,实际上可见的物体是三个DFB,这是我们想要的结果。

考虑不关闭深度写入的情况,当绘制到D时,深度值被写为0.3,那么F将无法通过深度测试,所以F会被剔除,最终可见的只剩下DB,显然不正确。

那是否能在透明物体绘制时关闭深度测试呢?关闭的情况下,在B物体后面的E也会被绘制出来,实际绘制的物体变为DFBE,显然也是不正确的。 

你可能感兴趣的:(C++,DirectX)