8.2实例程序:镜子
在自然界中的很多表面象镜子一样允许我们通过它的反射来看物体,我们来看看怎样用3D应用程序来模拟镜子,为了简单起见我们只模拟平面镜。举个例子,一辆擦亮的小汽车能够反射;然而车的车身是光滑的圆的,不是一个平面。我们渲染的是那些光滑的大理石地板、挂在墙上的镜子的反射,换句话说就是在一个平面的镜子。
实现镜子的程序需要我们解决两个问题。第一,我们必须学习沿着一个面怎样反射一个物体以便能够正确地绘制反射结果。第二,我们必须只能在一个镜子范围内显示反射结果。即,我们必须标记一个表面作为镜子,且只渲染那些在镜子里物体。图8.1就是说的这个内容。
第一个问题只需要用一些几何向量就可以简单解决,我们能够利用模板缓存解决第二个问题。下两小节分别介绍怎样解决这两个问题。第三小节把它们柔和在一起并且介绍一下本章的第一个应用程序实例代码——镜子。
8.2.1反射数学
我们现在演示怎样计算点V=(Vx,
Vy, Vz)被平面n*p+d=0反射的点V’=(V’x,
V’y, V’z),请参阅图8.2:
根据Part I中的“平面”部分,我们能够知道q=v-kn,这里k是有符号的从v到平面的距离。下面是v相对与平面(n,d)的反射推导:
在D3DX库中用下面的函数来创建反射矩阵R。
D3DXMATRIX
*D3DXMatrixReflect(
D3DXMATRIX *pOut,
// The resulting reflection matrix.
CONST D3DXPLANE
*pPlane // The plane to reflect about.
);
|
一旦我们说到反射变换的话题,就让我们看看其他3种特殊的反射变换。它们是关于三个坐标平面的反射—yz平面,xz平面,和xy平面—它们分别通过下面三个矩阵来表现:
通过yz平面反射一个点,我们只需要简单的将x分量取反就可以了。同样的,通过xz平面反射一个点,我们只需要简单的将y分量取反。通过xy平面反射一个点,我们只需要简单的将z分量取反。这种反射是非常容易理解的。
8.2.2镜面实现流程
当实现一个镜面,一个物体假如在一面镜子前那么它就会被反射。然而,我们不想测试空间一个物体是否在一面镜子前,要做它是非常复杂的。因此,为了简化事情,我们总是反射物体并且无限制地渲染它。但是这样就有一个象本章开头的图8.1一样的问题。即,物体反射被渲染到了没有镜子的表面。我们能够用模板缓存来解决这个问题,因为模板缓存允许我们阻止渲染在后缓存中的特定区域。因此,我们使用模板缓存来阻止渲染被反射的不在镜子里的茶壶。下面的步骤简要的说明了怎样实现:
1、
正常渲染所有的场景——地板,墙,镜子和茶壶——不包含反射的茶壶。注意这一步没有修改模板缓存。
2、
清除模板缓存为0。图8.3显示了后台缓存和模板缓存。
3、
渲染只有镜子部分的图元到模板缓存中。设置模板测试总是成功,并且假如测试成功就指定模板缓存入口为1。我们仅仅渲染镜子,在模板缓存中的所有像素都将为0,除了镜子部分为1以外。图8.4显示了更新以后的模板缓存。也就是说,我们在模板缓存中对镜子像素做了标记。
4、
现在我们渲染被反射的茶壶到后台缓存和模板缓存中。但是假如模板测试通过,我们就只渲染后台缓存。假如在模板缓存中的值为1,那么我们设置模板测试通过。这样,茶壶就仅仅被渲染到模板缓存为1的地方了。因为只有镜子对应的模板缓存值为1,所以反射的茶壶就只能被渲染到镜子里。
8.2.3代码和解释
这个例子的相关代码在RenderMirror函数中,它首先渲染镜子图元到模板缓存,然后渲染那些能被渲染到镜子里的反射茶壶。我们现在一行一行的分析RenderMirror函数的代码,并解释为什么要这么做。
假如你想使用8.2.2部分的步骤实现代码,注意我们从第3步开始,因为对模板缓存来说1和2步已经没有什么事做了。同样我们通过这个解释来讨论通过镜子渲染的信息。
注意我们将分成几个部分来讨论它。
8.2.3.1第一部分
我们通过允许模板缓存和设置渲染状态来开始:
void
RenderMirror()
{
Device->SetRenderState(D3DRS_STENCILENABLE,
true);
Device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS);
Device->SetRenderState(D3DRS_STENCILREF, 0x1);
Device->SetRenderState(D3DRS_STENCILMASK, 0xffffffff);
Device->SetRenderState(D3DRS_STENCILWRITEMASK,0xffffffff);
Device->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP);
Device->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP);
Device->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE);
|
这是非常容易理解的。我们设置模板比较运算为D3DCMP_ALWAYS,这就是说让所有模板测试都通过。
假如深度测试失败了,我们指定D3DSTENCILOP_KEEP,它表明不更新模板缓存入口。即,我们保存当前值。这样做的原因是假如深度测试失败了,那么就意味着像素被“遮挡”了。我们不想渲染被“遮挡”的反射像素。
同样假如模板测试失败了,我们也指定D3DSTENCILOP_KEEP。但是在这里这样做不是必须的,因为我们指定的是D3DCMP_ALWAYS,当然这样的测试也就永远不会失败。然而,我们只改变比较运算的一位,那么设置模板失败渲染状态是必须的。我们现在就这样做。
假如深度测试和模板测试都通过了,我们就指定D3DSTENCILOP_REPLACE,更新模板缓存入口,设置模板参考值为0x1。
8.2.3.2第二部分
这下一步阻止渲染镜子代码,除了模板缓存。我们通过设置D3DRS_ZWRITEENABLE并指定为false来阻止写深度缓存。我们能够防止更新后台缓存,混合和设置源混合要素为D3DBLEND_ZERO目的混合要素为D3DBLEND_ONE。将这些混合要素代入混合等式,我们得到后台缓存是不会改变的:
// disable writes to the depth and back
buffers
Device->SetRenderState(D3DRS_ZWRITEENABLE,
false);
Device->SetRenderState(D3DRS_ALPHABLENDENABLE,
true);
Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
// draw the mirror to the stencil buffer
Device->SetStreamSource(0, VB, 0, sizeof(Vertex));
Device->SetFVF(Vertex::FVF);
Device->SetMaterial(&MirrorMtrl);
Device->SetTexture(0, MirrorTex);
D3DXMATRIX I;
D3DXMatrixIdentity(&I);
Device->SetTransform(D3DTS_WORLD, &I);
Device->DrawPrimitive(D3DPT_TRIANGLELIST, 18, 2);
// re-enable depth writes
Device->SetRenderState(D3DRS_ZWRITEENABLE, true);
|
8.2.3.3第三部分
在模板缓存中,符合镜子可视像素的为0x1,因此对已经渲染的镜子区域做记号。我们现在准备渲染被反射的茶壶。回忆一下,我们仅仅想渲染镜子范围内的反射像素。我们现在可以很容易的做到了,因为在模板缓存中这些像素已经被做了记号。
我们设置下面的渲染状态:
Device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL);
Device->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP);
|
用一个新的比较运算设置,我们进行下面的模板测试:
(ref & mask ==
(value & mask)
(0x1 & 0xffffffff)
== (value & 0xffffffff)
(0x1)== (value &
0xffffffff)
|
这说明了只有当value=0x1时模板测试才成功。因为在模板缓存中只有镜子相应位置的值才是0x1,若我们渲染这些地方那么测试将会成功。因此,被反射的茶壶只会在镜子里绘制而不会在镜子以外的表面上绘制。
注意我们已经将渲染状态由D3DRS_STENCILPASS变为了D3DSTENCILOP_KEEP,简单的说就是假如测试通过那么就保存模板缓存的值。因此,在下一步的渲染中,我们不改变模板缓存的值。我们仅仅使用模板缓存来对镜子相应位置的像素做标记。
8.2.3.4第四部分
RenderMirror函数的下一部分就是计算在场景中反射位置的矩阵:
// position reflection
D3DXMATRIX W, T, R;
D3DXPLANE
plane(0.0f, 0.0f, 1.0f, 0.0f); // xy plane
D3DXMatrixReflect(&R, &plane);
D3DXMatrixTranslation(&T,
TeapotPosition.x,
TeapotPosition.y,
TeapotPosition.z);
W = T * R;
|
注意我们首先确定没有反射的茶壶位置,然后就通过xy平面来反射。这种变换规则是通过矩阵相乘来指定的。
8.2.3.5第五部分
我们已经为渲染反射茶壶做好了准备。然而,假如我们现在就渲染它,它是不会被显示的。为什么呢?因为被反射的茶壶的深度比镜子的深度大,因此镜子的图元将把被反射茶壶的图元弄模糊。为了避免这种情况,我们清除深度缓存:
Device->Clear(0, 0,
D3DCLEAR_ZBUFFER, 0, 1.0f, 0);
|
并不是所有问题都解决了。假如我们简单的清除深度缓存,被反射的茶壶会被绘制到镜子的前面,物体看起来就不对了。我们想做的是清除深度缓存并且要混合被反射的茶壶和镜子。这样,被反射的茶壶看起来就象在镜子里了。我们能够通过下面的混合等式来混合被反射的茶壶和镜子:
因为原像素(sourcePixel)来自被反射的茶壶,目的像素(DestPixel)来自镜子,我们能够通过这个等式明白它们是怎么被混合到一起的。我们有如下的代码:
Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_DESTCOLOR);
Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO);
|
最后,我们准备绘制被反射的茶壶:
Device->SetTransform(D3DTS_WORLD, &W);
Device->SetMaterial(&TeapotMtrl);
Device->SetTexture(0, 0);
Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
Teapot->DrawSubset(0);
|
回顾一下8.2.3.4部分的W,它能够正确的将被反射的茶壶变换到场景中恰当的位置。同样,我们也要改变背面拣选模式。必须这样做的原因是当一个物体被反射以后,它的正面和背面将会被交换。因此为了改变这种情况,我们必须改变背面拣选模式。
Device->SetRenderState(D3DRS_ALPHABLENDENABLE,
false);
Device->SetRenderState( D3DRS_STENCILENABLE,
false);
Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
}
// end RenderMirror()
|
运行截图:
主程序:
/**************************************************************************************
Demonstrates mirrors with stencils.
Use the arrow keys and the 'A' and 'S' key to navigate the scene and translate the teapot.
**************************************************************************************/
#include "d3dUtility.h"
#pragma warning(disable : 4100)
class cTextureVertex
{
public:
float _x, _y, _z;
float _nx, _ny, _nz;
float _u, _v;
cTextureVertex() { }
cTextureVertex(float x, float y, float z,
float nx, float ny, float nz,
float u, float v)
{
_x = x; _y = y; _z = z;
_nx = nx; _ny = ny; _nz = nz;
_u = u; _v = v;
}
};
const DWORD TEXTURE_VERTEX_FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1;
const int WIDTH = 640;
const int HEIGHT = 480;
IDirect3DDevice9* g_d3d_device;
IDirect3DVertexBuffer9* g_vertex_buffer;
IDirect3DTexture9* g_floor_texture;
IDirect3DTexture9* g_wall_texture;
IDirect3DTexture9* g_mirror_texture;
D3DMATERIAL9 g_floor_material = WHITE_MATERIAL;
D3DMATERIAL9 g_wall_material = WHITE_MATERIAL;
D3DMATERIAL9 g_mirror_material = WHITE_MATERIAL;
ID3DXMesh* g_teapot_mesh;
D3DXVECTOR3 g_teapot_pos(0.0f, 3.0f, -7.5f);
D3DMATERIAL9 g_teapot_material = YELLOW_MATERIAL;
void render_scene();
void render_mirror();
////////////////////////////////////////////////////////////////////////////////////////////////////
bool setup()
{
g_wall_material.Specular = WHITE * 0.2f; // make walls have low specular reflectance - 20%
D3DXCreateTeapot(g_d3d_device, &g_teapot_mesh, NULL);
// Create and specify geometry. For this sample we draw a floor and a wall with a mirror on it.
// We put the floor, wall, and mirror geometry in one vertex buffer.
//
// |----|----|----|
// |Wall|Mirr|Wall|
// | | or | |
// /--------------/
// / Floor /
// /--------------/
g_d3d_device->CreateVertexBuffer(24 * sizeof(cTextureVertex), 0, TEXTURE_VERTEX_FVF, D3DPOOL_MANAGED,
&g_vertex_buffer, NULL);
cTextureVertex* v;
g_vertex_buffer->Lock(0, 0, (void**)&v, 0);
// floor
v[0] = cTextureVertex(-7.5f, 0.0f, -10.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f);
v[1] = cTextureVertex(-7.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
v[2] = cTextureVertex( 7.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);
v[3] = cTextureVertex(-7.5f, 0.0f, -10.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f);
v[4] = cTextureVertex( 7.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);
v[5] = cTextureVertex( 7.5f, 0.0f, -10.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f);
// wall
v[6] = cTextureVertex(-7.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f);
v[7] = cTextureVertex(-7.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f);
v[8] = cTextureVertex(-2.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f);
v[9] = cTextureVertex(-7.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f);
v[10] = cTextureVertex(-2.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f);
v[11] = cTextureVertex(-2.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f);
// Note: We leave gap in middle of walls for mirror
v[12] = cTextureVertex(2.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f);
v[13] = cTextureVertex(2.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f);
v[14] = cTextureVertex(7.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f);
v[15] = cTextureVertex(2.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f);
v[16] = cTextureVertex(7.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f);
v[17] = cTextureVertex(7.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f);
// mirror
v[18] = cTextureVertex(-2.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f);
v[19] = cTextureVertex(-2.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f);
v[20] = cTextureVertex( 2.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f);
v[21] = cTextureVertex(-2.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f);
v[22] = cTextureVertex( 2.5f, 5.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f);
v[23] = cTextureVertex( 2.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f);
g_vertex_buffer->Unlock();
// create the texture and set filters
D3DXCreateTextureFromFile(g_d3d_device, "checker.jpg", &g_floor_texture);
D3DXCreateTextureFromFile(g_d3d_device, "brick0.jpg", &g_wall_texture);
D3DXCreateTextureFromFile(g_d3d_device, "ice.bmp", &g_mirror_texture);
g_d3d_device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
g_d3d_device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
g_d3d_device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
// lights
D3DXVECTOR3 light_dir(0.707f, -0.707f, 0.707f);
D3DXCOLOR color(1.0f, 1.0f, 1.0f, 1.0f);
D3DLIGHT9 light = init_directional_light(&light_dir, &color);
g_d3d_device->SetLight(0, &light);
g_d3d_device->LightEnable(0, TRUE);
g_d3d_device->SetRenderState(D3DRS_NORMALIZENORMALS, TRUE);
g_d3d_device->SetRenderState(D3DRS_SPECULARENABLE, TRUE);
// set the projection matrix
D3DXMATRIX proj;
D3DXMatrixPerspectiveFovLH(&proj, D3DX_PI/4.0f, (float)WIDTH/HEIGHT, 1.0f, 1000.0f);
g_d3d_device->SetTransform(D3DTS_PROJECTION, &proj);
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
void cleanup()
{
safe_release<IDirect3DVertexBuffer9*>(g_vertex_buffer);
safe_release<IDirect3DTexture9*>(g_floor_texture);
safe_release<IDirect3DTexture9*>(g_wall_texture);
safe_release<IDirect3DTexture9*>(g_mirror_texture);
safe_release<ID3DXMesh*>(g_teapot_mesh);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
bool display(float time_delta)
{
// update the scene
if(GetAsyncKeyState(VK_LEFT) & 0x80000f)
g_teapot_pos.x -= 3.0f * time_delta;
if(GetAsyncKeyState(VK_RIGHT) & 0x80000f)
g_teapot_pos.x += 3.0f * time_delta;
static float radius = 20.0f;
if(GetAsyncKeyState(VK_UP) & 0x80000f)
radius -= 2.0f * time_delta;
if(GetAsyncKeyState(VK_DOWN) & 0x80000f)
radius += 2.0f * time_delta;
static float angle = (3.0f * D3DX_PI) / 2.0f;
if(GetAsyncKeyState('A') & 0x80000f)
angle -= 0.5f * time_delta;
if(GetAsyncKeyState('S') & 0x80000f)
angle += 0.5f * time_delta;
D3DXVECTOR3 position(cosf(angle) * radius, 3.0f, sinf(angle) * radius);
D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
D3DXMATRIX view_matrix;
D3DXMatrixLookAtLH(&view_matrix, &position, &target, &up);
g_d3d_device->SetTransform(D3DTS_VIEW, &view_matrix);
// render now
g_d3d_device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0xff000000, 1.0f, 0);
g_d3d_device->BeginScene();
render_scene();
render_mirror();
g_d3d_device->EndScene();
g_d3d_device->Present(NULL, NULL, NULL, NULL);
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
void render_scene()
{
// draw teapot
g_d3d_device->SetMaterial(&g_teapot_material);
g_d3d_device->SetTexture(0, NULL);
D3DXMATRIX world_matrix;
D3DXMatrixTranslation(&world_matrix, g_teapot_pos.x, g_teapot_pos.y, g_teapot_pos.z);
g_d3d_device->SetTransform(D3DTS_WORLD, &world_matrix);
g_teapot_mesh->DrawSubset(0);
D3DXMATRIX identity_matrix;
D3DXMatrixIdentity(&identity_matrix);
g_d3d_device->SetTransform(D3DTS_WORLD, &identity_matrix);
g_d3d_device->SetStreamSource(0, g_vertex_buffer, 0, sizeof(cTextureVertex));
g_d3d_device->SetFVF(TEXTURE_VERTEX_FVF);
// draw the floor
g_d3d_device->SetMaterial(&g_floor_material);
g_d3d_device->SetTexture(0, g_floor_texture);
g_d3d_device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);
// draw the walls
g_d3d_device->SetMaterial(&g_wall_material);
g_d3d_device->SetTexture(0, g_wall_texture);
g_d3d_device->DrawPrimitive(D3DPT_TRIANGLELIST, 6, 4);
// draw the mirror
g_d3d_device->SetMaterial(&g_mirror_material);
g_d3d_device->SetTexture(0, g_mirror_texture);
g_d3d_device->DrawPrimitive(D3DPT_TRIANGLELIST, 18, 2);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
void render_mirror()
{
// Draw Mirror quad to stencil buffer ONLY. In this way only the stencil bits that
// correspond to the mirror will be on. Therefore, the reflected teapot can only be
// rendered where the stencil bits are turned on, and thus on the mirror only.
g_d3d_device->SetRenderState(D3DRS_STENCILENABLE, TRUE);
g_d3d_device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS);
g_d3d_device->SetRenderState(D3DRS_STENCILREF, 0x1);
g_d3d_device->SetRenderState(D3DRS_STENCILMASK, 0xffffffff);
g_d3d_device->SetRenderState(D3DRS_STENCILWRITEMASK, 0xffffffff);
g_d3d_device->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP);
g_d3d_device->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP);
g_d3d_device->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE);
//// Disable writes to the depth and back buffers,
//// enable alpha blending so mirror will overwrite dest areas.
//g_d3d_device->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
//g_d3d_device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
//g_d3d_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
//g_d3d_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
//// draw the mirror to the stencil buffer
//g_d3d_device->SetStreamSource(0, g_vertex_buffer, 0, sizeof(cTextureVertex));
//g_d3d_device->SetFVF(TEXTURE_VERTEX_FVF);
//g_d3d_device->SetMaterial(&g_mirror_material);
//g_d3d_device->SetTexture(0, g_mirror_texture);
D3DXMATRIX identity_matrix;
D3DXMatrixIdentity(&identity_matrix);
g_d3d_device->SetTransform(D3DTS_WORLD, &identity_matrix);
g_d3d_device->DrawPrimitive(D3DPT_TRIANGLELIST, 18, 2);
// re-enalbe depth writes
g_d3d_device->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
// only draw reflected teapot to the pixels where the mirror was drawn to
g_d3d_device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL);
g_d3d_device->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP);
// clear depth buffer and blend the reflected teapot with the mirror
g_d3d_device->Clear(0, NULL, D3DCLEAR_ZBUFFER, 0, 1.0f, 0);
g_d3d_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_DESTCOLOR);
g_d3d_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO);
// position reflection
D3DXMATRIX world_matrix, translation_matrix, reflect_matrix;
D3DXPLANE plane(0.0f, 0.0f, 1.0f, 0.0f); // xy plane
D3DXMatrixReflect(&reflect_matrix, &plane);
D3DXMatrixTranslation(&translation_matrix, g_teapot_pos.x, g_teapot_pos.y, g_teapot_pos.z);
world_matrix = translation_matrix * reflect_matrix;
// Finally, draw the reflected teapot.
g_d3d_device->SetTransform(D3DTS_WORLD, &world_matrix);
g_d3d_device->SetMaterial(&g_teapot_material);
g_d3d_device->SetTexture(0, NULL);
g_d3d_device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
g_teapot_mesh->DrawSubset(0);
// restore render states
g_d3d_device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
g_d3d_device->SetRenderState(D3DRS_STENCILENABLE, FALSE);
g_d3d_device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK wnd_proc(HWND hwnd, UINT msg, WPARAM word_param, LPARAM long_param)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
if(word_param == VK_ESCAPE)
DestroyWindow(hwnd);
break;
}
return DefWindowProc(hwnd, msg, word_param, long_param);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE inst, HINSTANCE, PSTR cmd_line, int cmd_show)
{
if(! init_d3d(inst, WIDTH, HEIGHT, true, D3DDEVTYPE_HAL, &g_d3d_device))
{
MessageBox(NULL, "init_d3d() - failed.", 0, MB_OK);
return 0;
}
if(! setup())
{
MessageBox(NULL, "Steup() - failed.", 0, MB_OK);
return 0;
}
enter_msg_loop(display);
cleanup();
g_d3d_device->Release();
return 0;
}
下载源程序