Direct3D 12——模板——平面镜效果

news2024/11/18 22:39:51

1.将实物照常渲染到后台缓冲区内(不包括镜子)。注意,此步骤不修改模 板缓冲区。

2.清理模板缓冲区,将其整体置零。
在这里插入图片描述
将实物都绘制到后台缓冲区中,并将模板缓冲区清理为0 (用浅灰色来表示)。
绘制在模板缓冲区中的黑色轮廊线条反映的是:后台缓冲区与模板缓冲区中像素之间的对照关系,而并非模板缓冲区中所绘的实际数据。

3.仅将镜面渲染到模板缓冲区中。若要禁止其他颜色数据写入到后台缓冲区,可用下列设置所创 建的混合状态:
D3D12_RENDER_TARGET_BLEND_DESC::RenderTargetWriteMask = 0;
再通过以下配置来禁止向深度缓冲区的写操作:
D3D12_DEPTH_STENCIL_DESC::DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ZERO;

在向模板缓冲区渲染镜面的时候,我将模板测试设置为每次都成功(D3D12_COMPARISON_ALWAYS), 并且在通过测试时用1 ( StencilRef模板参考值)来替换(通过D3D12_STENCIL_OP_REPLACE来 设置)模板缓冲区元素。如果深度测试失败,则应当采用枚举项D3D12_STENCIL_OP_KEEP,使模板缓冲区中的对应像素保持不变。由于仅向模板缓冲区绘制了镜面,因此在模板缓冲区内,除了镜面可见部分的对应像素为1,其他像素皆为0。图下所示的即为更新后的模板缓冲区。换言之,我们其实就是在模板缓冲区中标记了镜面的可见像素而已。
在这里插入图片描述
图把镜面渲染到模板缓冲区中,其实就是在模板缓冲区中标记出镜面可视部分的对应像素。
模板缓冲区中实心黑色区域的模板元素取值为1。但请注意,由于被立方体挡住部分的深度测试会失败,所以在模板缓冲区中的这一范围内,元素的取值并不为1
(立方体与黑色镜面重合的部分,也就是立方体位于镜面前方的这一部分)

保证先绘制实物,后将镜面渲染至模板缓冲区的顺序是很重要的。这样一来,深度 测试的失败会令镜面的像素被实物的像素所遮挡,因而也就不必再对模板缓冲区进 行二次修改了。我们并不希望把模板缓冲区中镜面被遮挡部分的值设为1,那样将导致在实物位于镜面前方的范围内也能显示出镜面内容。

4.现在我们来将实物的镜像渲染至后台缓冲区及模板缓冲区中。前面曾提到,只有通过模板测 试的像素才能渲染至后台缓冲区。对此,我们便将其设置为:仅当模板缓冲区中的值为1时, 才能通过模板测试。这可以通过令StencilRef为1,且模板运算符为D3D12_COMPARISON_ FUNC_EQUAL来实现。如此一来,只有模板缓冲区中元素数值为1的实物镜像部分才能得以 渲染。由于只有镜面可见部分所对应的模板缓冲区中元素数值为1,所以仅有这一范围内的实物镜像才能被渲染出来。

5.最后,我们像往常那样将镜面渲染到后台缓冲区中。但是,为了能“透过”镜面观察实物的 镜像(它实际位于镜子的背面。虽说展现的是镜面内的镜像,但实际上是镜面背后的反射实物 与镜面透明混合所得到的效果),我们就需要运用透明混合技术来渲染镜面。若非如此,则由于 实物镜像的深度值小于镜面的深度值,理所当然地会致使实物镜像被镜子挡住。为此,我 们只需为镜面定义一个新的材质配置实例:将其漫反射alpha通道分量设为0.3,使镜子的不透 明度达到30%,

	auto icemirror = std::make_unique<Material>();
	icemirror->Name = "icemirror";
	icemirror->MatCBIndex = 2;
	icemirror->DiffuseSrvHeapIndex = 2;
	icemirror->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 0.3f);
	icemirror->FresnelR0 = XMFLOAT3(0.1f, 0.1f, 0.1f);
	icemirror->Roughness = 0.5f;

假设已经将实物镜像的像素置于后台缓冲区内,那么,此时我们所看到的镜像颜色30%来自镜子 (源像素),70%出自实物镜像(目标像素)。

定义镜像的深度/模板状态

为了实现上述算法,我们要用到两个PSO对象。第一个用于在绘制镜面时标记模板缓冲区内镜面部 分的像素,第二个则用于绘制镜面可见部分(即不被前侧实物所遮挡部分)内的实物镜像。

	//
	//
	// PSO for marking stencil mirrors.禁止对渲染目标的写操作
	//

	CD3DX12_BLEND_DESC mirrorBlendState(D3D12_DEFAULT);
	mirrorBlendState.RenderTarget[0].RenderTargetWriteMask = 0; //禁止对渲染目标的写操作

	D3D12_DEPTH_STENCIL_DESC mirrorDSS;
	mirrorDSS.DepthEnable = true;
	mirrorDSS.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ZERO;//禁止对渲染目标的写操作
	mirrorDSS.DepthFunc = D3D12_COMPARISON_FUNC_LESS;
	mirrorDSS.StencilEnable = true;
	mirrorDSS.StencilReadMask = 0xff;
	mirrorDSS.StencilWriteMask = 0xff;
	
	mirrorDSS.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
	mirrorDSS.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
	mirrorDSS.FrontFace.StencilPassOp = D3D12_STENCIL_OP_REPLACE;
	mirrorDSS.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
	

	//我们不渲染背面朝向的多边形,因而对这些参数血置并不关心
	// We are not rendering backfacing polygons, so these settings do not matter.]

	mirrorDSS.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
	mirrorDSS.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
	mirrorDSS.BackFace.StencilPassOp = D3D12_STENCIL_OP_REPLACE;
	mirrorDSS.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;

	D3D12_GRAPHICS_PIPELINE_STATE_DESC markMirrorsPsoDesc = opaquePsoDesc;
	markMirrorsPsoDesc.BlendState = mirrorBlendState;
	markMirrorsPsoDesc.DepthStencilState = mirrorDSS;
	ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&markMirrorsPsoDesc, IID_PPV_ARGS(&mPSOs["markStencilMirrors"])));

	//
	// PSO for stencil reflections.用于渲染模板缓冲区中反射镜像的PSO
	//

	D3D12_DEPTH_STENCIL_DESC reflectionsDSS;
	reflectionsDSS.DepthEnable = true;
	reflectionsDSS.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
	reflectionsDSS.DepthFunc = D3D12_COMPARISON_FUNC_LESS;
	reflectionsDSS.StencilEnable = true;
	reflectionsDSS.StencilReadMask = 0xff;
	reflectionsDSS.StencilWriteMask = 0xff;

	reflectionsDSS.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
	reflectionsDSS.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
	reflectionsDSS.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
	reflectionsDSS.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL;

	// We are not rendering backfacing polygons, so these settings do not matter.
	reflectionsDSS.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
	reflectionsDSS.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
	reflectionsDSS.BackFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
	reflectionsDSS.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL;

	D3D12_GRAPHICS_PIPELINE_STATE_DESC drawReflectionsPsoDesc = opaquePsoDesc;
	drawReflectionsPsoDesc.DepthStencilState = reflectionsDSS;
	drawReflectionsPsoDesc.RasterizerState.CullMode = D3D12_CULL_MODE_BACK;
	drawReflectionsPsoDesc.RasterizerState.FrontCounterClockwise = true;
	ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&drawReflectionsPsoDesc, IID_PPV_ARGS(&mPSOs["drawStencilReflections"])));

绘制场景

	//绘制不透明的物体
	// Draw opaque items--floors, walls, skull.
	auto passCB = mCurrFrameResource->PassCB->Resource();
	mCommandList->SetGraphicsRootConstantBufferView(2, passCB->GetGPUVirtualAddress());
    DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::Opaque]);
	
	//将模板缓冲区中可见的镜面像素标记为1
	 Mark the visible mirror pixels in the stencil buffer with the value 1
	mCommandList->OMSetStencilRef(1);
	mCommandList->SetPipelineState(mPSOs["markStencilMirrors"].Get());
	DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::Mirrors]);

	//只绘制镜子范围内的镜像(即仅绘制模板缓冲区中标记为1的像素)
   //注意,我们必须使用两个单独的渲染过程常量缓冲区(per-pass constant buffer)来完成此工作,
   //一个存储物体镜像,另一个保存光照镜像
	// Draw the reflection into the mirror only (only for pixels where the stencil buffer is 1).
	// Note that we must supply a different per-pass constant buffer--one with the lights reflected.
	mCommandList->SetGraphicsRootConstantBufferView(2, passCB->GetGPUVirtualAddress() + 1 * passCBByteSize);
	mCommandList->SetPipelineState(mPSOs["drawStencilReflections"].Get());
	DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::Reflected]);

	//恢复主渲染过程常量数据以及模板参考值 
	// Restore main pass constants and stencil ref.
	mCommandList->SetGraphicsRootConstantBufferView(2, passCB->GetGPUVirtualAddress());
	mCommandList->OMSetStencilRef(0);

	//绘制透明的镜面,使镜像可以与之混合
	// Draw mirror with transparency so reflection blends through.
	mCommandList->SetPipelineState(mPSOs["transparent"].Get());
	DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::Transparent]);

关于以上代码还有一点需要注意,即在绘制RenderLayer: :Reflected层的时候如何来修改其渲染过程常量缓冲区。这是因为在绘制物体镜像的同时,还涉及场景中光照的镜像(即,物体的镜像也 要有与之对应的光照)。光源本存于渲染过程常量缓冲区中,因此我们可以再额外创建一个渲染过程常量 缓冲区,用以存储场景中光照的镜像。该常量缓冲区的设置方法如下:

  PassConstants mMainPassCB;
  PassConstants mReflectedPassCB;
void StencilApp::UpdateReflectedPassCB(const GameTimer& gt)
{
	mReflectedPassCB = mMainPassCB;

	XMVECTOR mirrorPlane = XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f); // xy plane
	XMMATRIX R = XMMatrixReflect(mirrorPlane);

	// 光照镜像
	for(int i = 0; i < 3; ++i)
	{
		XMVECTOR lightDir = XMLoadFloat3(&mMainPassCB.Lights[i].Direction);
		XMVECTOR reflectedLightDir = XMVector3TransformNormal(lightDir, R);
		XMStoreFloat3(&mReflectedPassCB.Lights[i].Direction, reflectedLightDir);
	}

	// 将光照镜像的渲染过程常量数据存于渲染过程常量缓冲区中索引1的位置
	auto currPassCB = mCurrFrameResource->PassCB.get();
	currPassCB->CopyData(1, mReflectedPassCB);
}

绕序与镜像

当一个三角形被反射到某个平面上时(也就是此三角形在这一平面上的镜像),其绕序(winding order ) 并不会发生改变,正因如此,其平面法线的方向同样保持不变。所以,实际物体的外向法线在镜像中则变为 了内向法线。此时,为了纠正这一点,我们会告知Direct3D将逆时针绕序的三角形看作是正面 朝向,而将顺时针绕序的三角形看作背面朝向。这实际上 是对法线的方向也进行了 “反射”,以此使镜像成为外向朝向。我们可以通过设置下列PSO光栅化属性来改 变绕序的约定:

drawReflectionsPsoDesc.RasterizerState.FrontCounterClockwise = true;

在这里插入图片描述

多边形的法线不会随着反射操作而调转过来,这使得镜像的法线都变为内向朝向

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

socked编程

socket是什么&#xff1f;套接字是什么&#xff1f; 什么是 socket&#xff1f; socket 的原意是“插座”&#xff0c;在计算机通信领域&#xff0c;socket 被翻译为“套接字”&#xff0c;它是计算机之间进行通信的一种约定或一种方式。通过 socket 这种约定&#xff0c;一台…

Motion Planning学习笔记一:配置空间、图、图搜索、图遍历

学习高飞博士的路径规划课程所总结的学习笔记。 目录 1、配置空间&#xff08;Configuration Space, C-space&#xff09; 2、图&#xff08;Graphs&#xff09; 3、图搜索&#xff08;Graph Search Basis&#xff09; 3.1、总体框架 3.2、两种基本的图遍历算法 3.3、启…

漫谈大数据 - HiveSQL总结(二)查询操作

导语&#xff1a;HiveSQL各关键字详解&#xff0c;hive函数大全&#xff0c;类似于个人记录工具书&#xff0c;后续遇到其他的也会继续加进来。 有关hive库表操作请见上篇&#xff1a;漫谈大数据 - HiveSQL总结&#xff08;一&#xff09;库表操作_昊昊该干饭了的博客-CSDN博客…

一条SQL如何被MySQL架构中的各个组件操作执行的?

文章目录 1. 单表查询SQL在MySQL架构中的各个组件的执行过程2. SELECT的各个关键字在哪里执行&#xff1f;3. 表关联查询SQL在MySQL架构中的各个组件的执行过程4. LEFT JOIN将过滤条件放在子查询中再关联和放在WHERE子句上有什么区别&#xff1f;5. 聚集索引和全表扫描有什么区…

推动开发者平台本土化,高通加速中国XR内容生态发展

随着VR和AR技术快速发展&#xff0c;产品不断成熟&#xff0c;体验也变得越来越优秀。据悉&#xff0c;Meta Quest系列VR头显出货量超2000万台&#xff0c;基本证明了VR开始在消费类电子产品中占据一席之地。与此同时&#xff0c;近两年AR眼镜也在逐渐升温&#xff0c;成为了创…

day17_异常

今日内容 零、 复习昨日 一、作业 二、异常 三、自定义异常 零、 复习昨日 见晨考,重点是String类的方法 StringBuffer和StringBuiler面试问 日期解析和格式化 int i Integer.parseInt(“111”); 一、作业 略,见答案二、异常 2.1 介绍 异常,就是程序出现的不正常的情况. 2.2…

RPA流程自动化技术在金融机构的落地方案详解

金融机构在面向数字化运营的转型过程中&#xff0c;需将智能流程自动化技术整合到数字化转型战略中&#xff0c;规划建设统一的企业流程自动化处理平台&#xff0c;作为数字化运营的辅助支撑类系统&#xff0c;明确流程治理方法和运营模式&#xff0c;确保足够的规模弹性&#…

【深度学习】Softmax回归及前馈神经网络

1 实验内容简介 1.1 实验目的 &#xff08;1&#xff09;熟练掌握tensor相关各种操作&#xff1b; &#xff08;2&#xff09;掌握广义线性回归模型&#xff08;logistic模型、sofmax模型&#xff09;、前馈神经网络模型的原理&#xff1b; &#xff08;3&#xff09;熟练掌…

第一章: uniapp引入axios异步框架

第一章&#xff1a; uniapp引入axios异步框架 在学习uniapp的过程中&#xff0c;发现uniapp框架默认集成request请求框架存在问题&#xff0c;发送请求时在header中塞入token值&#xff0c;而后台接收不到&#xff0c;也就是说uniapp默认的request请求框架&#xff0c;不支持在…

巨烽数字化采购项目启动,甄云助力医疗影像显示领军企业数智化升级

近日&#xff0c;医疗影像显示行业的领军者深圳市巨烽显示科技有限公司&#xff08;以下简称“巨烽”&#xff09;联合甄云科技举办数字化采购管理项目启动会&#xff0c;双方相关部门负责人及项目组成员参加了此次会议。 会上&#xff0c;就巨烽的数字化采购管理系统建设升级…

AI写作机器人-ai文章生成器在线

使用AI续写生成器&#xff0c;让内容创作事半功倍&#xff01; 随着人工智能技术的不断进步和应用&#xff0c;AI续写生成器的出现为内容创作带来了全新的革命。这种技术可以让你的写作事半功倍&#xff0c;让你轻松生成高质量的文章和内容。在这篇文章中&#xff0c;我们将介绍…

如何伪原创-自媒体伪原创软件

批量文字伪原创的作用 批量文字伪原创是指通过对原文进行修改、改写、调整等方式&#xff0c;生成大量新的类似原文的文本。其作用主要包括以下几个方面&#xff1a; 提高文本的独创性&#xff1a;批量伪原创可以有效地避免大量相似内容的出现&#xff0c;从而提高文本的独创性…

我如何学习使用 Jetpack Compose 开发 Android 应用程序

我如何学习使用 Jetpack Compose 开发 Android 应用程序 Jetpack Compose 和 Android 开发简介 2021 年 7 月&#xff0c;Google 发布了用于为 Android 应用构建原生 UI 的全新工具包 1.0 版。Jetpack Compose 是 Android 开发人员的游戏规则改变者&#xff0c;因为它从通过 X…

C#如何解决项目打开问题 error : 找不到指定的 SDK“Microsoft.NET.Sdk.WindowsDesktop”

错误提示&#xff0c;问题描述 后来发现&#xff0c;直接安装rider还是不能解决解决&#xff08;会自动配置关联&#xff08;path等&#xff09;&#xff0c;甚至自动下载的&#xff0c;官方的visual studio反而不会&#xff0c;之后再详细看怎么弄了&#xff09; VS2022项目…

IDEA 用上这款免费 GPT4 插件,生产力爆表了

大家好&#xff0c;我是一航&#xff01; 早前给大家分享过GPT的一些玩法&#xff0c;但是依旧有很多铁子没有掌握魔法的奥秘&#xff0c;始终没有用上&#xff1b;前两天&#xff0c;一兄台分享给我一款 IDE 插件&#xff1a;Bito-ChatGPT &#xff0c;安装就能直接在IDE中使…

如何选择合适的网络自动化工具

通过网络自动化工具实现网络自动化是所有网络组织的关键。如果没有合适的网络自动化工具&#xff0c;拥有由许多设备组成的大型网络环境的组织将无法执行重要操作&#xff0c;例如按时备份配置、实时跟踪不需要的更改以及遵守行业法规。当组织未能使用正确的网络自动化工具来执…

yolov8训练自己的数据集遇到的问题

训练分类模型 1.如何更改模型的类别数nc 根据本地模型配置文件.yaml可以设置nc 但是&#xff0c;这里无法用到预训练模型.pt模型文件&#xff0c;预训练模型的权重参数是在大数据集上训练得到的&#xff0c;泛化性能可能比较好&#xff0c;所以&#xff0c;下载了官方的分类…

Flink+Kafka、Pulsar实现端到端的exactly-once语义

End-to-End Exactly-Once Processing in Apache Flink with Apache Kafka 2017年12月Apache Flink社区发布了1.4版本。该版本正式引入了一个里程碑式的功能&#xff1a;两阶段提交Sink&#xff0c;即TwoPhaseCommitSinkFunction。该SinkFunction提取并封装了两阶段提交协议中的…

【离散数学】测试五 图论

1. n层正则m叉树一共有()片树叶。 A. nm B. mn C. mn 正确答案: B 2. 下图是一棵最优二叉树 A. 对 B. 错 正确答案: B 3. 要构造权为1,4,9,16,25,36,49,64,81,100一棵最优二叉树,则必须先构造权为5,9,16,25,36,49,64,81,100一棵最优二叉树. A. 对 B. 错 …

视频剪辑必备,这6个网站承包你一年的音效素材

视频剪辑中需要用到各种声音、音效素材&#xff0c;这些音效不仅能让你的视频更丰富&#xff0c;还能更好的表达视频内容&#xff0c;传递情绪让观者感到共鸣。很多朋友剪辑过程中为了找到好的配乐、音效&#xff0c;往往会花费大量的时间&#xff0c;找到了还有可能受版权限制…