Direct3D模板缓存

模板缓存是一个用于获得某种特效的离屏缓存,模板缓存的分辨率与后台缓存和深度缓存的分辨率完全相同,所以像素也是一一对应的,模板缓存允许我们动态的,有针对性的决定是否将某个像素写入后台缓存中。

例如实现镜面效果时,我们只需在在镜子所在平面中绘制某个特定物体的映像,但是如果想只在镜面所对应的子区域中显示物体的映像,这是就可用模板缓存来阻止物体映像在非镜面区域中的绘制,a中镜面和墙壁映像都会被绘制,b中阻止了非镜面区域的绘制

模板缓存的使用

为了使用模板缓存,在Direct3D初始化时需要查询当前设备是否支持模板缓存,如果支持还需要将其启用。

Device->SetRenderState(D3DRS_STENCILENABLE, true);
//do stencil work
Device->SetRenderState(D3DRS_STENCILENABLE, false);

我们可以使用IDirect3DDevice9::Clear方法将模板缓存清空为一个默认值,该方法也可对后台缓存和深度缓存进行清空操作

Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0x00000000, 1.0f, 0);

在第三个参数中增加了标记D3DClEAR_DTENCIL表明我们要对模板缓存、目标缓存(后台缓存)和深度缓存进行清空操作,第6个参数用于指定要将模板缓存清为何值。

模板缓存格式的查询

模板缓存可与深度缓存一同创建,为深度缓存指定格式时,我们可以同时指定模板缓存的格式,模板缓存和深度缓存共享同一个离屏的表面缓存,而每个像素的内存段被划分为若干部分,分别于某种特定缓存相对应例如:

D3DFMT_D24S8:创建一个32位深度/模板缓存,其中每个像素的24位指定给深度缓存,8位指定给模板缓存
D3DFMT_D24X4S4:创建一个32位深度/模板缓存,每个像素的24位指定给深度缓存,4位指定给模板缓存,其余4位不使用
D3DFMT_D15S1:创建一个16位深度/模板缓存,每个像素的15位指定给深度缓存,1位指定给模板缓存
一些格式没有模板缓存分配任何空间,例如D3DFMT_D32格式仅创建一个32位的深度缓存

模板测试

判定是否将某个像素写入后台缓存的决策过程过程称为模板测试,假定模板已处于启用状态则每个像素都需要进行模板测试。

(ref & mask) ComparisonOperation (value & mask)
左操作数LHS:由应用程序定义的模板参考值ref和模板掩码mask通过按位与运算得到
右操作数RHS:由当前进行测试的像素的模板缓存中的数值value与模板掩码mask按位与得到
运算结果为true则将该像素写入后台缓存,如果为false将阻止该像素被写入后台缓存,当一个像素不被写入后台缓存时,也 不会被写入深度缓存。

模板测试的控制

模板参考值
模板参考值ref的默认值为0,我们可用D3DRS_STENCILREF绘制状态改变该值,我们倾向于使用16进制数,这样可使整数的位排列一目了然,方便按位逻辑运算

Device->SetRenderState(D3DRS_STENCILREF, 0x1);

模板掩码
模板掩码用于屏蔽ref和value变量中某些位,默认值为0xffffffff,表会不屏蔽任何位,可借助绘制状态D3DRS_STENCILMASK来修改

//屏蔽了高16位
Device->SetRenderState(D3DRS_STENCILMASK, 0x0000ffff);

模板值
该值是当前待测试像素在模板缓存中的对应值,不能显式地单独设置模版值,但是可对模板缓存进行清空操作,还可以用模板的绘制状态控制将要写入模板缓存的内容。

比较运算
可通过绘制状态D3DRS_STENCILFUNC来设置比较运算符函数,参数可用D3DCMPFUNC类型枚举
D3DCMP_NEVER:模板测试总是失败,即比较函数总是返回false
D3DCMP_LESS:LHS<RHS,则模板测试成功
D3DCMP_EQUAL:LHS=RHS,则模板测试成功
D3DCMP_LESSEQUAL:LHS<=RHS
D3DCMP_GREATER:LHS>RHS
D3DCMP_NOTEQUAL:LHS!=RHS
D3DCMP_GREATEREQUAL:LHS>=RHS
D3DCMP_ALWAYS:模板测试总是成功,返回true

模板缓存的更新

除了决定一个具体像素是否应被写入后台缓存,我们还可以基于以下3种可能的情形定义模板缓存中的值如何进行更新

第i行、第j列的像素模板测试失败,可借助绘制状态D3DRS_STENCILFAIL将模板缓存中处于同样位置的项的更新方式定义如下

Device->SetRenderState(D3DRS_STENCILFAIL, StencilOperation);

第i行、第j列的像素深度测试失败,可借助绘制状态D3DRS_STENCILZFAIL将模板缓存中处于同样位置的项的更新方式定义如下

Device->SetRenderState(D3DRS_STENCILZFAIL, StencilOperation);

第i行、第j列的像素深度测试、模板测试均成功,可借助绘制状态D3DRS_STENCILPASS将模板缓存中处于同样位置的项的更新方式定义如下

Device->SetRenderState(D3DRS_STENCILPASS, StencilOperation);

StencilOperation可取以下预定义常量
D3DSTENCILOP_KEEP:不更新模板缓存中的值(保留当前值)
D3DSTENCILOP_ZERO:将模板缓存中的值设为0
D3DSTENCILOP_REPLACE:用模板参考值替代模板缓存中的对应值
D3DSTENCILOP_INCRSAT:增加模板缓存中的对应数值,如果超过最大值,则取最大值
D3DSTENCILOP_DECRSAT:减少模板缓存中的对应数值,如果小于最小值,则取最小值
D3DSTENCILOP_INVERT:模板缓存中的对应值按位取反
D3DSTENCILOP_INCR:增加模板缓存中的对应数值,如果超过最大值,则取0
D3DSTENCILOP_DECR:减少模板缓存中的对应数值,如果小于0,则取最大值

模板写掩码

除了模板绘制状态,还可设置写掩码,该值可屏蔽我们将写入模板缓存的任何值的某些位,可用绘制状态D3DRS_STENCILWRITEMASK来设定写掩码的值,默认值为0xffffffff

Device->SetRenderState(D3DRS_STENCILWRITEMASK, 0x0000ffff);

镜面效果例程

程序中实现镜面效果需要解决俩个问题
1.了解对于任意平面物体如何成像 
2.必须将某一表面区域"标记"为镜面

D3DX库提供了用于创建对于任意平面的镜像变换矩阵

D3DXMATRIX* D3DXMatrixReflect(D3DXMATRIX *pOut,		CONST D3DXPLANE *pPlane
);

3种其他类型的镜像变换矩阵,分别为相对于3个标准坐标平面(yz平面、xz平面及xy平面)所做的镜像变换

R_{yz}=\begin{bmatrix} -1 & 0 & 0 &0 \\ 0&1 & 0 &0 \\ 0& 0& 1 &0 \\ 0& 0 &0 &1 \end{bmatrix}  R_{xz}=\begin{bmatrix} 1 & 0 & 0 &0 \\ 0&-1 & 0 &0 \\ 0& 0& 1 &0 \\ 0& 0 &0 &1 \end{bmatrix}  R_{xy}=\begin{bmatrix} 1 & 0 & 0 &0 \\ 0&1 & 0 &0 \\ 0& 0& -1 &0 \\ 0& 0 &0 &1 \end{bmatrix}

为求得一个点相对于yz平面所成的像,只需将该点的x分量取反,类似xz平面将y分量取反,xy平面将z分量取反

效果实现

实现镜面效果时,一个物体仅当位于镜面之前时才对其进行成像计算,然而我们不想进行空间测试以判断某物体是否位于镜面之前,因为这样会使问题变得更加复杂,为了简化简化这个问题,无论物体在何处,都计算其成像并进行绘制,然后借助模板缓存区阻止部分区域的绘制

1.往常绘制整个场景(地板、墙壁、镜面、茶壶)但先不绘制茶壶的映像
2.将模板缓存清0
3.将构成镜面的图元绘制到模板缓存中,将模板测试设置为总是true,并指定如果测试通过,模板缓存值被替换为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_STENCILZFAIL, D3DSTENCILOP_KEEP);		//深度测试失败则模板缓存保持原状Device->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP);		//模板测试失败则模板缓存保持原状Device->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE);	//通过模板/深度测试则模板缓存替换为模板参考值//绘制镜面到模板缓存Device->SetRenderState(D3DRS_ZWRITEENABLE, false);					//将绘制状态D3DRS_ZWRITEENABLE设为false阻止对深度缓存中进行写操作Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);				//开启融合运算Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);				//设置源融合因子Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);				//设置目标融合因子Device->SetStreamSource(0, Mirror, 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);Device->SetRenderState(D3DRS_ZWRITEENABLE, true);					//取消阻止对深度缓存的写操作//绘制茶壶映像Device->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL);			//设置比较运算符函数Device->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP);		//设置测试成功后保留模板缓存中的值//为物体的映像进行定位的镜像变换矩阵D3DXMATRIX W, T, R;D3DXPLANE plane(0.0f, 0.0f, 1.0f, 0.0f);							//xy planeD3DXMatrixReflect(&R, &plane);										//创建平面变换矩阵D3DXMatrixTransformation(&T, TeapotPosition, x, TeapotPosition.y, TeapotPosition.z);		//平移到尚未进行镜像变换的茶壶所在位置W = T * R;//如果现在就进行绘制,映像不会显示出来,因为镜子中的茶壶映像的深度大于镜面的深度(经过镜像变换矩阵后,镜壶位置在镜面的另一端)//所以构成镜面的图元就遮挡了镜壶映像,为了解决这个问题,需要将深度缓存清空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);
}

茶壶映像与镜面融合公式

FinalPixel=sourcePixel\bigotimes destPixel+destPixel\bigotimes (0,0,0,0)=sourcePixel\bigotimes destPixel

背面消隐模式修改原因

当一个物体在镜面中成像时,其正面和背面将会相互调换,但绕序并未发生变化,所以"新"正面的绕序将使Direct3D误认为它们是背面,"新"背面的绕序被误认为正面,为了此修正,所以修改消隐模式。

平行光阴影、点光源阴影、阴影矩阵

平行光通过顶点p的射线:r(t)=p+tL   点光源通过顶点p的射线:r(t)=p+t(p-L)

对于点光源和平行光光源,L具有不同的含义,点光源中L用来定义该点光源的空间位置,平行光源中L用来定义平行光的的方向。由图可看出平行光产生的阴影实质上是物体在平面上沿特定方向的平行投影,点光源产生的阴影是以光源为视点,物体在平面上的透视投影。

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, D3DSTENCILOP_KEEP);Device->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_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(&mtrl);Device->SetTexture(0, 0);Teapot->DrawSubset(0);Device->SetRenderState(D3DRS_ZENABLE, true);Device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);Device->SetRenderState(D3DRS_STENCILENABLE, false);
}

防止深度冲突的另一个方法是使用Direct3D深度偏置机制

模板缓存还可以实现一些其他类型的程序

1.阴影体
2.消融与淡入淡出
3.深度复杂性的可视化
4.轮廓图和侧影效果
5.几何实体的构建
6.修正由共面引起的深度冲突

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/120352.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

常用中间件-OAuth2

1. 微服务权限校验Session共享 1.1 微服务权限校验 实现2号方案。使用Redis作为Session统一存储。 首先配置一下Nacos&#xff0c;参考https://blog.csdn.net/weixin_43917045/article/details/132852850 然后为每个服务添加验证机制&#xff0c;先导入依赖 <!-- SpringS…

打开深度学习的锁:(2)单隐藏层的神经网络

打开深度学习的锁 导言PS&#xff1a;神经网络的训练过程一、数据集和包的说明1.1准备文件1.2 需要导入的包 二、构建神经网络的架构三、初始化函数四、激活函数4.1 tanh&#xff08;双曲正切函数&#xff09;函数 五&#xff0c;前向传播六、损失函数七、后向传播八、梯度下降…

修改vscode底部栏背景和字体颜色

修改vscode底部栏背景和字体颜色 如图&#xff1a; 首先打开齿轮&#xff0c;打开设置搜索workbench.colorCustomizations,然后点击编辑setting.json修改setting.json内内容 "workbench.colorCustomizations": {"statusBar.foreground": "#FFFFFF…

AI类APP能做什么

AI类APP可以实现多种功能&#xff0c;涵盖了各种领域和用途。以下是一些常见的AI类APP示例以及它们主要实现的功能&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1.语音助手&#xff08;Voice Assis…

微信小游戏从零到上线系列文章整理,建议收藏

引言 本系列是《从零开始开发贪吃蛇小游戏到上线系列》&#xff0c;欢迎大家关注分享收藏订阅。 大家中秋快乐&#xff0c;我是亿元程序员&#xff0c;一位有着8年游戏行业经验的主程。前面笔者给大家讲解了微信小游戏如何从零到上线的流程。可能很多小伙伴都还没有看到。 本…

Nginx负载均衡详解

一、负载均衡介绍 1、负载均衡的定义 单体服务器解决不了并发量大的请求&#xff0c;所以&#xff0c;我们可以横向增加服务器的数量&#xff08;集群&#xff09;&#xff0c;然后将请求分发到各个服务器上&#xff0c;将原先请求集中到单个服务器上的情况改为将请求分发到多…

Idea上传项目到gitlab并创建使用分支

Idea上传项目到gitlab并创建使用分支 1 配置git 在idea的setting中&#xff0c;找到git&#xff0c;配置好git的位置&#xff0c;点击Test按钮显示出git版本号&#xff0c;则说明配置成功。 2 项目中引入git Idea通过VCS&#xff0c;选择Create Git Repository 在弹出的对话框…

探索ClickHouse——连接Kafka和Clickhouse

安装Kafka 新增用户 sudo adduser kafka sudo adduser kafka sudo su -l kafka安装JDK sudo apt-get install openjdk-8-jre下载解压kafka 可以从https://downloads.apache.org/kafka/下找到希望安装的版本。需要注意的是&#xff0c;不要下载路径包含src的包&#xff0c;否…

stl格式-3D三角形

文章目录 什么是stl文件?格式首选stl的语法1.这是一个stl格式的文件:(ASCII码)2.下面先举个例子(难度略微提示)补充:关于\<\<我试了一下:这个法线你随便写好像也没问题\>> 3.来个立方体4.最后再写一个由三个直角形组成的立方体(直棱锥)5.amend 修正(右手定则,法线…

redis介绍

一、简介 Redis 与其他 key - value 缓存产品有以下三个特点&#xff1a; Redis支持数据的持久化&#xff0c;可以将内存中的数据保存在磁盘中&#xff0c;重启的时候可以再次加载进行使用。 Redis不仅仅支持简单的key-value类型的数据&#xff0c;同时还提供list&#xff0c;…

宝塔 php修改了php.ini配置不生效

最近在使用hypref&#xff0c;php的版本是7.4 服务器linux&#xff0c;用宝塔安装完php,并装完swoole插件后 安装了swoole后&#xff0c;需要在php.ini中修改一下配置文件 添加 swoole.use_shortnameOff 但是添加了&#xff0c;重启php,依然不生效 解决方法是&#xff1a; 同时…

C语言学习(1)—— 环境安装和配置

运行C语言和C程序需要安装MinGW和VSCode。 一. 安装MinGW 1、进入官网下载MinGW&#xff1a;https://sourceforge.net/projects/mingw-w64/files/ 2、解压缩 3、配置环境变量 4、检查是否安装成功 二. 安装VSCode 1、进入官网下载VSCode&#xff1a;https://code.visualstud…