龙书笔记(8)

chap 8 模版(stencil)缓存
功能:阻止后台缓存中某些特定区域的绘制

1.模版缓存的使用
启用和禁用模版缓存:
		Device->SetRenderState(D3DRS_STENCILENABLE, true);
		Device->SetRenderState(D3DRS_STENCILENABLE, false);


清空模版缓存,此方法也能清空深度缓存和后台缓存:
		Device->Clear(0,0,D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0xff000000, 1.0f, 0);		//最后一个参数说明模板缓存要清空为0

模版缓存的格式:
D3DFMT_D24S8 D3DFMT_D24X4S4 D3DFMT_D15S1

就是在创建设备的时候,D3DPRESENT_PARAMETERS 那个结构里面


模版测试
就是判定是否将某个像素写入后台缓存的决策过程(测试结果为 true 就把此像素写入后台):
(ref(模版参考值) & mask(模版掩码))ComparisonOperation(value(当前模版缓存的数值) & mask(模版掩码))
ComparisonOperation前面的是左值,后面 右值
(1).模版参考值 ref: 默认是0,可以修改:
			Device->SetRenderState(D3DRS_STENCILREF, 0x1);

(2).模版掩码 mask: 屏蔽ref或者value中的某些位,默认是 0xffffffff, 表示不屏蔽任何位
可以修改:
			Device->SetRenderState(D3DRS_STENCILMASK, 0x0000ffff);				//表示屏蔽了高16位

(3).模版值 value: 不能显示的单独设置模版值,但可以对模版进行清空操作,还可以通过 模版的绘制状态 控制写入模版缓存的内容
(4).比较运算: 通过绘制状态 D3DRS_STENCILFUNC 来设置比较运算,常用的值有:
				D3DCMP_NEVER			//模版测试总是失败,比较函数总是返回false
				D3DCMP_LESS				//若左值 < 右值,模版测试成功
				D3DCMP_EQUAL			//若左值 == 右值,模版测试成功
				D3DCMP_LESSEQUAL		//若左值 <= 右值,模版测试成功
				D3DCMP_GREATER			//若左值 > 右值,模版测试成功
				D3DCMP_NOTEQUAL			//若左值 != 右值,模版测试成功
				D3DCMP_GREATEREQUAL		//若左值 >= 右值,模版测试成功
				D3DCMP_ALWAYS			//模版测试总是成功,比较函数总是返回true

模版缓存的更新
(1).某像素的模版测试失败:
		Device->SetRenderState(D3DRS_STENCILFAIL, StencilOperation);

(2).某像素的深度测试失败:
		Device->SetRenderState(D3DRS_STENCILZFAIL, StencilOperation);

(3).某像素的深度测试和模版测试均成功:
		Device->SetRenderState(D3DRS_STENCILPASS, StencilOperation);
		//StencilOperation的取值:
		D3DSTENCILOP_KEEP		//不更新模版缓存中的值
		D3DSTENCILOP_ZERO		//将模版缓存中的值设为0
		D3DSTENCILOP_REPLACE	//用模版参考值代替模版缓存中的值
		//....

模版写掩码
屏蔽将写入模版缓存的某位


2.镜面效果
D3DX提供了如下方法去创建 相对于任意平面的 镜像变换 矩阵:
		D3DXMATRIX* D3DXMatrixReflect(D3DXMATRIX* pout, CONST D3DXPLANE* pplane);

镜面效果实现(理论篇):
(1)绘制场景,包括地板、墙壁、镜面和茶壶,此步 不需修改 模版缓存
(2)模版缓存 清零
(3)将构成镜面的图元 仅 绘制到 模版缓存 中,并且让模版测试总是成功,成功通过的模版缓存值被替换为1
//解析:这时,由于只画了镜面,所以模版缓存中镜面对应的区域像素设为1,其余像素都为0(这就相当于对镜面做了标记)
(4)绘制茶壶的 映像 到 后台缓存 和 模版缓存。(其实是通过先 绘制到模版缓存, 进行判断后, 再写入 后台缓存)
//解析:通过了模版测试,就将茶壶映像绘制到后台缓存中

领悟吧!奥义!
后台还缓存 是 结果, 模版缓存 是手段,
开启了模版缓存,意味着要想在后台缓存中绘制,就得先通过“模版的试练”(模版测试)
把要绘制的内容分别放进 后台缓存 和 模版缓存,在模版缓存中去测试,如果测试结果正确,就在后台缓存中绘制!

镜面效果实现(实战篇):
			//(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_ZFAIL, D3DSTENCILOP_KEEP);
					Device->SetRenderState(D3DRS_FAIL, D3DSTENCILOP_KEEP);
					Device->SetRenderState(D3DRS_PASS, D3DSTENCILOP_REPLACE);
					/*	D3DCMP_ALWAYS说明了模版测试总是成功,D3DSTENCILOP_REPLACE则说明模版测试成功后,用模版参考值D3DRS_STENCILREF代替模版缓存中的值	*/
			//(2)
					Device->SetRenderState(D3DRS_ZWRITEENABLE, false);				//关闭深度缓存
					Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
					Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
					Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);			//禁止更新后台缓存
					//说明:这里只向模板缓存中绘制茶壶,开启alpha是为了不绘制到后台缓存中
					Device->SetStreamSource(0, VB, 0, sizeof(Vertex));
					Device->SetFVF(Vertex::FVF);
					Device->SetMaterial(&MirrorMtrl);
					Device->SetTexture(0, MirrorTex);
					D3DXMatrixIdentity(&I);
					Device->SetTransform(D3DTS_WORLD,&I);
					Device->DrawPrimitive(D3DPT_TRIANGLELIST, 18, 2);
					Device->SetRenderState(D3DRS_ZWRITEENABLE, true);				//重新打开深度缓存
			//(3)
					Device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL);
					Device->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP);	//测试成功保留模版缓存中的值
			//(4)
					D3DXMATRIX W,T,R;
					D3DXPLANE plane(0.0f, 0.0f, 1.0f, 0.0f);
					D3DXMatrixReflect(&R, &plane);									//获得镜面矩阵
					D3DXMatrixTranslation(
						&T,															//获得茶壶位置矩阵
						TeapotPosition.x,	
						TeapotPosition.y,
						TeapotPosition.z,
					);
					W = T * R;														
			//(5)
					Device->Clear(0,0,D3DCLEAR_ZBUFFER,0,1.0f,0);					//由于映像的位置在镜面之后,所以先清空深度缓存
					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);											//由于前面的设置,这里绘制的已经不是原来的茶壶了,而是茶壶的镜像
					Device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
					Device->SetRenderState(D3DRS_STENCILENABLE, false);
					Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);			//改回原先的背面消隐
				}


3.阴影效果
Direct3D中的阴影矩阵:
		D3DXMATRIX * D3DXMatrixShadow(
			D3DXMATRIX * pout,
			const D3DXVECTOR4 * pLight,
			const D3DXPLANE * pPlane
		);

使用 模版缓存 防止二次融合
二次融合:就是一个物体的阴影中,同一个位置出现阴影加强的情况
阴影效果实现:
			void RenderShadow()
			{
				Device->SetRenderState(D3DRS_STENCILENABLE, true);
				Device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL);
				Device->SetRenderState(D3DRS_STENCILREF, 0x0);
				Device->SetRenderState(D3DRS_STENCILMASK, 0xffffffff);
				Device->SetRenderState(D3DRS_STENCILWRITEMASK, 0xffffffff);
				Device->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP);
				Device->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCOLOP_KEEP);
				Device->SetRenderState(D3DRS_STENCILPASS, D3DSTENCOLOP_INCR);		//注意这里设为增加模板缓存中的对应值是为了防止二次融合
				
				D3DXVECTOR4 lightDirection(0.707f, -0.707f, 0.707f, 0.0f);
				D3DXPLANE groundPlane(0.0f, -1.0f, 0.0f, 0.0f);
				D3DXMATRIX S;
				D3DXMatrixShadow(&S, &lightDirection, &groundPlane);
				D3DXMATRIX T;
				D3DXMatrixTranslation(&T, TeapotPosition.X, TeapotPosition.Y, TeapotPosition.Z);
				D3DXMATRIX W = T * S;
				Device->SetTransform(D3DTS_WORLD, &W);
				
				Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);				//让阴影和地面融合
				Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
				Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
				D3DMATERIAL9 mtrl = d3d::InitMtrl(d3d::BLACK, d3d::BLACK, d3d::BLACK, d3d::BLACK, 0.0f);
				mtrl.Diffuse.a = 0.5f;
				Device->SetRenderState(D3DRS_ZENABLE, false);		//先绘制地面,然后禁用深度缓存,最后绘制阴影,这是为了防止深度冲突
				Device->SetMaterial(&mrtl);
				Device->SetTexture(0,0);
				Teapot->DrawSubset(0);
				
				Device->SetRenderState(D3DRS_ZENABLE, true);
				Device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
				Device->SetRenderState(D3DRS_STENCILENABLE, false);
			}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值