平面阴影技术 DirectX例子 图形学2

平面阴影虽然不能满足所有的阴影要求,但是如果是相对简单的场景的话,用平面阴影也是个不错的选择,它的优点是速度快,并不需占用多少运算时间。

步骤如下:

1. 定位需要渲染阴影的物体,和阴影所在平面

2. 计算出平面上阴影的物体定点,定义一定透明度,如50%透明度

3. 渲染

一、首先解决问题是如何计算

方法就是利用向量代数和空间解析几何的知识,知道了光照的方向向量,物体定点,和平面位置,就可以计算了。这里就不给出具体的计算了,基础不好的恶补下,这里主要讲图形学的技术,而且大多数的API,如DirectX和OpenGL都有现场的API去自动计算,记得目的就是为了定义出在平面上的阴影的定点。图形学中一般的做法就是先计算出这个转变向量,然后利用这个转变向量,把原物体转换成阴影物体,就可以把阴影渲染在平面上了。

而在DirectX中就是可以调用现成的API生成所需要的矩阵:

D3DXMATRIX *D3DXMatrixShadow(
    D3DXMATRIX *pOut,              //我们所需要的矩阵
    CONST D3DXVECTOR4 *pLight,     // 光源
    CONST D3DXPLANE *pPlane        // 阴影所在的平面
);

利用Stencil 缓冲技术来防止多重Blending


我们画阴影需要再一个平面上画的,但是原图形是立体的,那么会发生多个几何图形重叠的情况,比如一个物体的正反面重叠子一起,那么通过多次画图就会使得阴影物体更加黑。为了得到正确明暗程度的阴影就要用到Stencil技术了。当然如果不用也可以画出来,不过就是阴影部分更加黑罢了。

具体做法就是:我们把阴影部分标记到stencil buffer中,然后如果第二次再次写像素到同样的阴影部分的时候就拒绝写入,也就是stencil test会失败。(当然程序还是正常运行的)。这样的话,我们就只画了一次阴影部分。

Direct3D代码示例:

1.参数设置

gd3dDevice->SetRenderState(D3DRS_STENCILENABLE, true);//开启Stencil buffer
HR(gd3dDevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL);//设置stencil测试函数为等于函数
HR(gd3dDevice->SetRenderState(D3DRS_STENCILREF, 0x0);//设置ref的值为0
HR(gd3dDevice->SetRenderState(D3DRS_STENCILMASK, 0xffffffff);//Mask蒙蔽层的值为1
HR(gd3dDevice->SetRenderState(D3DRS_STENCILWRITEMASK, 0xffffffff);//写入蒙蔽层也为1
HR(gd3dDevice->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP);//stencil测试失败则保持原有的颜色
HR(gd3dDevice->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP);
HR(gd3dDevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_INCR);//这要设置,已经写入一次的像素位置就不能再次写入其他像素了

2. 计算和定位阴影

// Position shadow.
D3DXVECTOR4 lightDirection(0.577f, -0.577f, 0.577f, 0.0f);
D3DXPLANE groundPlane(0.0f, -1.0f, 0.0f, 0.0f);

D3DXMATRIX S;
D3DXMatrixShadow(&S, &lightDirection, &groundPlane);//这里得出阴影的转换矩阵

// Offset the shadow up slightly so that there is no
// z-fighting with the shadow and ground.
D3DXMATRIX eps;
D3DXMatrixTranslation(&eps, 0.0f, 0.001f, 0.0f);//这里是Z-fighting技术,简单的来说就是当两个物体(这里是指阴影和平面)的深度测试(depth testing)发生重复的时候,就可能会出问题,例如像素会闪烁,这个时候,可以稍微把其中一个物体(这里是阴影)的位置移一点点。但是据我的实验可知,问题不会很大,还是可以正常显示的,无闪烁,不过为了安全起见,那么移动一下吧。
// Save the original teapot world matrix.
D3DXMATRIX oldTeapotWorld = mTeapotWorld;

// Add shadow projection transform.
mTeapotWorld = mTeapotWorld * S * eps;//这里进行正式转换
 


3. 最后我们设置颜色,黑色,透明度50%,然后恢复原来的参数设置:

// Alpha blend the shadow.
gd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);//开启Alpha blending
gd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA));
gd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

drawTeapot();//这里是画一个茶壶

// Restore settings.
mTeapotWorld = oldTeapotWorld;
gd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
gd3dDevice->SetRenderState(D3DRS_STENCILENABLE, false);


总结:

这里是平面阴影技术,如果要做复杂的阴影的话,比如水面上的,和乱石中的阴影那就要用更加深的技术了,需要更多的知识,后续在继续贴出来吧。

Reference:

Introduction to 3D Game Programming with DirectX9.0C Shader Approach (DirectX 的 龙书 封面是条红龙的)

你可能感兴趣的:(平面阴影技术 DirectX例子 图形学2)