reference:《Game Engine Gems》 1

渲染贴花是3D世界中动态生成细节的一种常用方法。贴花常用于制作子弹孔、血迹、轮胎痕迹以及其它相似的项作为游戏中发生的事件。它们也可以用于设计师使用位于墙上的污迹、破损效果的纹理来丰富场景。

本文介绍了一种延迟贴花技术,它应用于使用延迟渲染生成场景的地方,如图20.1所示。该技术完全基于shader,而对CPU的依赖很小,不需要动态生成三角形,并且对于大多数3D引擎而言是一个直接的叠加效果。

        图20.1 应用在复杂几何体上的贴花

20.1 问题

作为一个好的贴花系统,需要解决如下几个问题:

1. 贴花系统需要和光照很好地结合起来。贴花不仅要能改变颜色,也需要能够改变法线,高光因子,以及其它表面参数,使它们和其它几何体变得无明显区别。

2. 贴花应该能应用在所有表面上,包括静态和动态的。

3. 贴花能够根据几何包围体进行合适的裁剪,甚至能够环绕角落。

4. 对于和应用于碰撞检测的几何体有着很大差异的几何体,贴花系统需要能够生效。

如果图形引擎具备延迟渲染的能力,那么第一个问题很容易解决。对于前向渲染而言,本章中的系统依然可以使用,但是需要做一些必要的修改。如果想要对延迟渲染有所了解,请参照[3,4,5,6]章节。

如果我们能够在G-Buffer中占用一个8位通道,那么我们能够解决第二个问题。如果不能,我们至少可以使其在静态物体上运行。这在章节20.5中讨论。

问题3是我们实际应用中可能遇到的问题的开始。贴花需要根据表面来得到它应用的位置,哪怕表面是高度棋盘格化的。一些现有的贴花技术会根据表面生成三角形网格,但是这在计算量上消耗很大。此外,原始网格在CPU上通常不可用,因为网格数据将加载到顶点着色器中,只能由GPU以合理的速度访问。

问题4是一个我们面临的问题,因为当今的游戏通常会用比较复杂的网格进行渲染,并且使用细节更少的网格用于碰撞检测。难点在于光线投射返回的交点可能与渲染贴花的位置有很大的不同。

20.2 一般思路

我们技术提供的基本解决方法是:使用一个应用到贴花包围体的特殊片元着色器,将贴花投影到表面。而不是在场景几何体上渲染一个新的贴花几何体。实现这一点所需的信息包括贴花的位置和方向、贴花的大小以及当前渲染视区的深度值纹理。此信息允许在顶点和片元着色器中进行所有计算。

当渲染一个贴花的时候,我们很自然地会在“贴花空间”做这一工作,其中x,y轴位于贴花中心下的切线平面上,z轴与贴花中心的表面法向量平行。为了将数据移动到贴花的局部坐标系统,shader中需要乘以贴花变换矩阵的逆矩阵。

20.1中所列出的代码首先在片元位置处从G-buffer中获取了深度,并且用其来重建片元的3D世界坐标。world到decal的常量就是我们使用的贴花渲染转换矩阵的逆。使用这一矩阵,我们可以将片元坐标转换为贴花的局部空间坐标。局部坐标可用于计算贴花纹理的采样坐标。在第一版中,我们只使用(x,y)坐标,它仅沿着本地z坐标投影到下面的几何体上,如图20.2所示。

图20-2 在局部x-y平面上使用简单投影会使贴花沿着局部z轴方向延展。

listing 20-1 : 此代码确定要渲染片段的贴花空间坐标,并将其转换为贴花的纹理坐标。RT-DEPTH纹理包含视区的深度值,worldToDecal常量是贴花从贴花空间到世界空间4x4矩阵转换的逆,recipDecalSize常量为1/s,其中s是场景中decal的大小。

// sample the depth at the current fragment
float pixelDepth = texture2DRect(RT_Depth, gl_FragCoord.xy).x;// compute the fragment's world-space posiiton
vec3 worldPos = ComputeWorldSpacePosition(gl_FragCoord.xy, pixelDepth);// transform into decal space
vec3 decalPos = worldToDecal * worldPos;// use the xy position of the position for the texture lookup
vec2 texcoord = decalPos.xy * recipDecalSize * 0.5 + 0.5;// fetch the decal texture color
gl_FragColor = texture2D(diffuseDecalTexture,texcoord);

20.3 几何体渲染

现在我们有了一些计算基本纹理坐标的代码,我们必须考虑实际需要渲染的几何体类型。使用贴花shader渲染片段,就像透过一个窗口,我们可以看到我们的贴花。所以,我们可以只渲染和贴花的大小和位置相对应的表面齐平的四边形。但是,我们又希望贴花具有深度,因此我们改为渲染边界立方体,如图20-3所示。这允许我们从任何方向观察贴花,并且允许我们在之后添加一些环绕的特性。

图20.3  我们可以渲染以贴花为中心的边界立方体,以确保从任何视角都能捕获到贴花的影响。

对于包含大量贴花的场景做优化,通过硬件实例来渲染贴花会更有优势。因此,最好简单地渲染一个单位立方体,并将其转换为顶点着色器中的正确大小和位置,如list20.2所示。

listing20.2 此顶点着色器将单位立方体缩放为贴花的实际大小,并将其转换为贴花的世界空间位置。

uniform float decalSize;
uniform mat4  decalToWorld;// scale the unit cube and position it in world space
vec4 worldPos = decalToWorld * vec4(gl_Vertex.xyz * decalSize, gl_Vertex.w);// output the position in homogeneous clip space
gl_Position = gl_ModelViewProjectionMatrix * worldPos;

对于小的贴花,这种技术非常有效。但是,当一个立方体在启用背面剔除的情况下渲染,并且深度测试设置为GL_LESS时,相机进入立方体时贴花就会消失。该程序的一个解决方案是,剔除正面,并将深度测试设置为GL_GREATER。这样将只渲染其世界位置实际位于曲面后面的片段,但当许多片段被靠近摄像机的几何体遮挡时,会导致过度渲染。

另一种解决方案是,找到最靠近摄像机的立方体,并在该深度处渲染一个与摄像机对齐的四边形,该四边形的大小足以包围整个立方体。如果最近的立方体角位于近平面的前面,则应在近平面处渲染全屏四边形。

20.4 淡出和环绕

我们有一个基本的顶点和片元着色器,但是我们是沿着贴花的z轴无限投影的,会产生如图20.2所示的拉伸。有两种方法可以用来解决这个问题。更简单的方法是使用片元位置到贴花平面的距离作为淡出参数。这个距离在decalPos.z中可用,我们只需将其缩放到decal的大小,如list20.3所示。

list 20.3 .片元位置的贴花空间z坐标的绝对值将缩放到贴花大小,并用作贴花颜色的淡出参数。和以前一样,recipDecalSize常量大小为1/s,其中s是场景中的贴花大小。

// compute the distance of the fragment to the decal's plane
float distance = abs(decalPos.z);// scale the distance into the [0,1] range
// according to the size of the decal
float scaledDistance = max(distance * recipDecalSize * 2.0, 1.0);// somehow use that scaled distance to fade out
// here : simple linear fade out
float fadeOut = 1.0 - scaledDistance;vec4 diffuseColor = texture2D(diffuseDecalTexture, texcoord):gl_FragColor = vec4(diffuseColor.rgb, diffuseolor.a * fadeOut);

当贴花是平的而不是环绕几何图形时,此方法非常有用。用于淡出贴花的实际公式可以更改,以产生不同贴花的最佳外观,建议在游戏引擎内部可对此进行配置。

处理拉伸问题的一个更有趣的方法是,使贴花环绕拐角,并跟随复杂曲面的曲率。这对于血液和其他液体尤其有用,因为如果这样的贴花覆盖了整个表面,而不受其曲率的影响,会更有说服力。

这是很容易做到的。我们所需要的只是贴花中每个片元位置处的表面法向量。如果将该法向量旋转到贴花空间中,它的(x y)分量会给出曲面相对于贴花平面的梯度。我们可以使用这个梯度和片元到贴花平面的距离来修改纹理坐标。在没有相对坡度的区域,纹理查找保持不变,而在坡度较大的区域(例如,在拐角处),纹理坐标会根据到贴花平面的距离向外移动。List 20.4中说明了这一技术,结果如图20.4所示。

图20.4 环绕角落的贴花基于表面法线计算,根据离贴花平面的距离淡出。

Listing 20.4. 此代码演示了如何根据底部曲面的法向来调整纹理坐标,使得贴花围绕曲线和拐角。RT_NORMAL纹理包含在RGB通道编码的视区的法向量。

// get the world-space normal at the fragment position
vec3 worldNormal = texture2DRect(RT_Normal, gl_FragCoord.xy).xyz;// rotate it into the local space of the decal
vec3 decalNormal = (worldToDecal * vec4(worldNormal.xyz, 0.0)).xyz;vec2 texcoord = decalPos.xy;// use the distance and gradient to modify the texture lookup
texcoord -= decalPos.z * decalNormal.xy;// scale and center the texture coordinates
texcoord += vec2(decalSize);
texcoord *= recipDecalSize * 0.5;gl_FragColor = texture2D(diffuseDecalTexture, texcoord);

作为最后一笔,我们添加了颜色淡出,这取决于到贴花平面的距离。我们还需要考虑贴花平面法向量和下平面法向量之间的角度,否则,贴花会出现在较薄墙壁的背面。完整的shader例子可以在附带的CD中找到。

20.5 表面裁剪

我们所描述的技术通过在视区空间中应用贴花来工作,并且不区分投影到的曲面。通过使用“投影和环绕”的方法,可以将贴花附加到任何类型的曲面,甚至附加到动画的角色上。然而,贴花永远不会被裁剪掉,这意味着贴在盒子上的贴花也会投射到盒子所在的地面上。如果盒子带着贴花移动,那么地面上的投影也会移动。

这个问题有两种可能的解决方案。一种解决方案是将贴花渲染限制为仅由静态几何体覆盖的像素。在这种情况下,我们需要先渲染所有静态几何体,然后应用贴花,然后再渲染动态对象。

第二个解决方案要求在G-Buffer中使用一个额外的通道来为渲染的每个不同对象保存一个贴花ID。在填充G-Buffer的渲染过程中,我们写入每个对象的贴花ID以及漫反射颜色、法线等,这样我们就有一个每像素的遮罩来标识每个像素属于哪个对象。当贴花应用于对象时,我们查找对象的ID并将其与贴花关联。当显示贴花时,我们将此ID与其它Uniform常量一起传递,并将其与从G-Buffer读取的ID进行比较。如果两者匹配,那么我们就知道像素属于贴花附着的对象,然后我们就正常的渲染。否则,我们丢弃片元,因为它将对象覆盖的像素集之外绘制。

如果不需要区分静态几何图形,那么它们都可以共享相同的ID,比如0。所有的动态对象都应该有不同的ID,但这些ID不需要是唯一的。我们所要做的是使两个相邻的动态对象不可能具有相同的ID,然后就可以使用一个8位通道,简单地从范围内给动态对象随机的ID。

这种对象管理也可以通过模板缓冲区来完成。但是,测试模板值将阻碍我们使用Instancing渲染贴花,因为我们无法将模板比较值作为uniform常量统一传递给着色器,并根据每个片元来修改模版测试。

20.6 局限性

我们应该注意到一些限制。这种技术并不会创建起体积贴花,它只改进了二维投影,使它在许多情况下看起来更加真实。环绕特征使用曲面的法线来更改贴花内的纹理坐标。图20.4所示的结果比图20.2所示的结果要好很多,但是在垂直柱的边缘仍然有一个清晰可见的切口。为了防止这种伪影,我们需要使用真正的体积贴花。图20.5显示了应用于更复杂对象的贴花,在左侧图像中,对象使用面法线,在右侧图像中,对象使用平滑法线。

在这两幅图像中,都未考虑法线映射,显然,下垫面的法线会对贴花的外观有着很大的影响。因此,具有强法线映射额曲面往往会扭曲贴花,有时会导致视觉瑕疵。不过,对于某些类型的贴花而言,这并不是一个问题,因为一个血迹不管在表面上多扭曲,它看起来仍然像一个血迹。

图20.5(左)基于法线的环绕(右)基于平滑插值的法线的环绕。

关于性能:通过测试边界体积很容易剔除贴花,GPU可以通过实例批量呈现贴花。但是,你应该小心不要让许多贴花互相层叠,因为这样会消耗所有可用的片元渲染能力。可以尝试通过在一定时间后移除贴花,或一次检索屏幕上的贴花数量来防止这种情况。后一种方法允许我们保留世界上的许多贴花,只要它们在同一时间不可见即可。大多数游戏只需在固定时间后移除贴花即可清理场景,但在此过程中会降低沉浸式的质量。游戏Max Payne对在一个区域创建的贴花数量进行了限制,这有一个巨大的影响,即如果玩家回到之前所在的房间,所有的血迹和弹孔仍然存在。

20.7 额外功能

由于我们是在任何光源计算之前将贴花渲染到G-Buffer中,我们不仅可以更改漫反射颜色,还可以替换(或修改)法线、光泽度以及其他数据。计算贴花的切线和次法线向量在顶点着色器中非常简单,不需要额外的顶点属性。有关详细信息,请参见附带CD上的shader代码。

[图形学] 延迟贴花渲染技术 (A Deffered Decal Rendering Technique)相关推荐

  1. 【游戏开发小技】Unity中实现Dota里的角色技能地面贴花效果(URP | ShaderGraph | Decal)

    本文最终效果 文章目录 一.前言 二.环境准备 1.URP环境准备 2.技能范围图案 二.方案一:写Shader实现 1.Shader脚本:UrpDecal.shader 2.材质球 3.创建Cube ...

  2. 【游戏开发小技】Unity中实现Dota里的角色技能地面贴花效果(URP ShaderGraph Decal)

    本文最终效果 文章目录 一.前言 二.环境准备 1.URP环境准备 2.技能范围图案 二.方案一:写Shader实现 1.Shader脚本:UrpDecal.shader 2.材质球 3.创建Cube ...

  3. Deferred Decal(延迟贴花)

    Decal渲染是一个引擎中重要的一部分,记忆中印象最深刻的就是以前CS中的弹痕与爆炸痕迹了.目前来说,Decal的实现方法也比较多,而且感觉还跟游戏类型有关,比如子弹乱飞的射击类FPS游戏中对贴花系统 ...

  4. 游戏渲染技术:前向渲染 vs 延迟渲染 vs Forward+渲染(二)

    GTA5 2 前向渲染 前向渲染是三个光照技术中最简单的,也是游戏图形渲染中最常见的技术.出于这个原因,也是光照计算最昂贵的技术,它不允许在场景中出现大量的动态光源. 大部分使用前向渲染的图形引擎会采 ...

  5. 真实感皮肤渲染技术总结

    <GPU Gems 3>中的"Chapter 14. Advanced Techniques for Realistic Real-Time Skin Rendering&quo ...

  6. 【转载】【《Real-Time Rendering 3rd》 提炼总结】(九) 第十章 · 游戏开发中基于图像的渲染技术总结

    本文由@浅墨_毛星云 出品,转载请注明出处.   文章链接: http://blog.csdn.net/poem_qianmo/article/details/78309500 这是一篇近万字的总结式 ...

  7. 【《Real-Time Rendering 3rd》 提炼总结】(九) 第十章 · 游戏开发中基于图像的渲染技术总结

    本文由@浅墨_毛星云 出品,转载请注明出处.   文章链接: http://blog.csdn.net/poem_qianmo/article/details/78309500 这是一篇近万字的总结式 ...

  8. unity着色器和屏幕特效开发秘笈_Oculus研发分享:开发移动VR内容时应避免的PC渲染技术...

    查看引用/信息源请点击:映维网 开发移动VR内容时应避免的PC渲染技术 (映维网 2019年11月25日)有不少开发者都是以与PC相同的方式来开发Quest游戏,但这可能会导致优化性能方面出现大量困难 ...

  9. 斯图加特大学GPU光线投射体渲染技术提携

    斯图加特大学GPU光线投射体渲染技术介绍 前言:在以往人们的印象中,美国的CG技术是一世界第一流的,而没有注意德国CG技术的发展.事实上,德国大学的CG是相当高的,与美国第一流的大学学术交往非常频繁. ...

最新文章

  1. 打造 AI Beings,和微信合作…第七代微软小冰的成长之路
  2. 从电商用户触点看服务设计趋势
  3. 简化 Pod 故障诊断:kubectl-debug 介绍
  4. 盘点那些让程序员目瞪口呆的Bug都有什么?
  5. 计算机网路【2】数据链路层
  6. interface not supported怎么解决_这20个婚礼伴手礼,怎么看都比喜糖有创意!
  7. oracle 提取连续数字,oracle 得到连续不重复的数字序列
  8. 未经授权的ADB Android设备
  9. Python爬虫之(一):爬虫简介
  10. sql server 2000:不能打开到主机的连接,在端口1433:连接失败
  11. 如果物联网平台一直不盈利,行业集体该怎么活?
  12. 拷贝(添加)本地音乐到iPhone、iPad设备(最新iTunes12.7)
  13. SPEC 2000使用的详细介绍(1)
  14. 均值和方差的计算(已知两样本标准差,求总体标准差)
  15. ZYNQ-7000概述
  16. 各大工业机器人品牌大全
  17. xml文件读写(C#)
  18. SuperMap大数据GIS技术白皮书 v2.1
  19. python控制步进电机转动_python-树莓派通过按键控制步进电机正反转
  20. LeetCode 540 Single Element in a Sorted Array 在分类好的数组里寻找单个元素 C语言 异或的巧妙使用

热门文章

  1. Jenkins 打包项目出错汇总(持续)
  2. 强烈推荐这些值得下载的神仙工具,每一个都让人惊喜
  3. python使用什么来区分代码块_Python 小数据池、代码块以及代码块缓存机制
  4. 拆字在线版-扌斥字在纟戋片反
  5. Lsyncd:负载均衡之后,服务器的文件双向同步
  6. vc++ 编程 经典电子书
  7. 测试的发展前景怎么样?
  8. 国庆节上映的电影有哪些?2014国庆节上映的动画电影盘点
  9. 前端模拟自动解析手机号姓名地址
  10. Thinkpad E450c WIN8 重装系统 如何U盘启动