游戏中的Decal(贴花)

article/2025/8/29 3:26:52


在游戏中,decal是一种非常常见的效果,常用来实现弹孔,血迹,涂鸦等效果。最近研究了下Decal在游戏引擎中的实现方式,大致总结了一下:

1.基于面片实现:

直接用一个Quat的mesh,加上一张贴图,简单直观的实现。

缺点:只能在平面上贴。

2.修改贴图:

将物体的材质贴图替换成原贴图和decal贴图的混合,适用于静态批量的物体

缺点:只适用于静态物体

3.基于SubMesh:

先获取跟目标投影相交的mesh,然后将mesh根据投影框进行裁剪

1.获取所有可能和投影框相交的mesh,一般游戏引擎都会有Octree或BVH保存mesh的aabb,这一步简单获取aabb相交的mesh即可。

2.将mesh的顶点数据变换到投影框的三维空间中,这样一来是方便裁剪,二来是裁剪完成后可以将变换后的坐标值直接作为uv值使用。

3.得到相交的三角形片:

判断每个点是否在投影框内,如果三角形有任意一个点在框内,则认为三角形与投影框相交。当然这种方法会漏掉一些三角形,比如这中情况:当然如果mesh较小以及要求不精细的话也没有太大问题。
 

三角形顶点都不在框内,但是和框相交


比较严谨的求交算法可以使用SAT算法,参考 RealTimeRendering4 22.12 或者这个https://gdbooks.gitbooks.io/3dcollisions/content/Chapter4/aabb-triangle.html.

4.将所有相交的三角形片,合并成新的IndexBuffer,使用新的decal的纹理重新渲染一次,UV可以直接取映射到框中的xy值,当然要注意在shader中把uv 0~1之外的部分clip掉

5.如果你想的话,也可以对处在边界,不完全在框内部的三角形进行裁剪,最后整理顶点生成新的mesh.

方法如下:

简单的逐边裁剪:
 

逐边裁剪


也可以一次性裁剪所有的方向,参考下面的算法(来自<计算机图形学第三版>6.8.1)
 

一次性裁剪所有边


缺点:比较适用于静态的物体,创建过程可能耗时较长

4.基于Multi-Pass实现:

和上面方法很相似

1.获取所有相交的mesh;

2.在mesh正常渲染结束后,再渲染一次,卖游戏使用decal的shader,向shader中传入一个ClipToDecal的矩阵(=ClipToWorld * WorldToDecal),在FS中计算计算映射到decal框中的坐标,取决于具体的实现,可以将xy坐标作为uv,以及裁剪掉uv0~1之外的部分,将decal渲染出来。

Unity的built-in管线中的Projector就是使用的这种方式。

缺点:如果投影框与多个mesh相交,或者mesh很大,则会产生很大的性能消耗。

5.修改渲染shader实现

判断decal框和某个mesh相交时,将decal标记为需要渲染。修改mesh的shader,传入一个或多个decal投影框矩阵+数张decal贴图。FS得到原始的输出颜色后,再根据decal拿到的颜色进行混合,如果同时有多个Decal,则需要不同数量改变shader变体。

缺点:需要大量调整shader,复杂繁琐,而且一个mesh上的decal数量在运行时发生变化时,需要动态编译shader变体。

6.基于后处理实现:

将decal整体作为一个长方体进行渲染两次来对目标进行贴花

1.首先正常渲染其他的物体,拿到正常渲染的buffer和depth buffer.

2.将投影框作为一个长方体进行渲染,关闭Face Cull,将depth test设置为GreatEqual,输出一个标志位到 stencil buffer(或者任意其他可以标志像素点的方式),不需要输出颜色值

3.再次将投影框作为长方体渲染,打开Face Cull(只绘制长方形靠前的三个面),再上一步中stencil buffer测试通过的位置绘制,与前面方法不同的是,当前像素点的 WorldPosition 通过从depth buffer中读取然后反变换获得(后处理中非常常用的方法).

两种情况下decal绘制示意图

投影面在靠后的位置

投影在靠前的位置


缺点:不支持光照

7.Deferred实现

大致和上面的方法相同

deferred 渲染管线中渲染所有gbuffer之后

和上一个方法中讲到的一样,同样是先渲染长方体,写入stencil buffer,然后再次渲染长方体,根据stencil buffer来改变gbuffer中的数据,根据需要选择修改basecolor,normal等;

因为gbuffer被修改,后面的光照计算会产生decal的效果。

UE4中的DefferedDecal,就是这种方法(未使用Dbuffer时)

缺点: 只能用于Deferred,不支持烘培光(因为烘培光是在渲染gbuffer时加上的).

8.Dbuffer

先进行depth prepass渲染深度图

用上面提到的方法将decal渲染到类似gbuffer的dbuffer上,然后在渲染gbuffer时(或者forward渲染时),直接应用同样位置对dbuffer进行采样,融合到gbuffer中,可以支持烘培光,支持deferred,forward管线。

Unity的HDRP以及UE4中的DeferredDecal(使用Dbuffer),使用该方法
 

UE4中的Decal Material,当选择使用Dbuffer渲染时,会根据不同的类型来使用不同数量的Dbuffer Render Target


缺点:很大的性能消耗

9.常见的问题

1.垂直角度投影时可能出现这样的拉伸,可以加上一个角度判断,丢弃角度超过某个阈值的像素。


 


2.可能需要在边缘处加上一些fade off的效果,防止突兀的边界。

3.blend方式,decal材质可以提供base color,normal等属性,decal是半透明时,需要仔细考虑和原图base color等的混合方式,normal 需要变换下切空间后再进行混合。

4.一般情况下decal无法支持带骨骼动画的物体。


http://chatgpt.dhexx.cn/article/03RIJXMO.shtml

相关文章

UE4 Decal实现简介

Decal 是游戏中常见的一个东西&#xff0c;经常被用在 显示弹痕&#xff0c;地面叠加花纹等。 Decal 绘制流程 Decal可以认为是&#xff0c;将一个面的画面沿Decal的Box的X轴方向投影到物体表面。 Decal的绘制实际只有一个Box绘制。 RHICmdList.DrawIndexedPrimitive(GetUn…

跳出for循环

跳出for循环有三种方式&#xff1a; 1&#xff1a;continue&#xff1b;跳出当次循环&#xff0c;可继续进行下一个循环&#xff1b; function ceshi(){for(var i 0 ; i < 6 ; i){if(i 3){continue;}console.log(,i);} }ceshi(); 效果图&#xff1a; 2&#xff1a;brea…

跳出forEach循环

我们平时用到的循环有很多种。for, map, while, forEach, for...of, for...in等等&#xff0c;每种循环都有在某一次循环语句中跳出本次循环的方法&#xff0c;但是除了forEach。 有小伙伴说不用不就好了。其实这些循环里边&#xff0c;当属for的效率最高&#xff0c;for...in最…

Java 8 跳出foreach循环,跳出本次循环,继续执行,之前的for each循环如何跳出本次循环,跳出循环,跳出多层for循环。

在Java8之前&#xff0c;最开始使用for i 循环&#xff0c;很老旧&#xff0c; 后来有了高级的for each 循环&#xff0c;然后这个跳出本次循环和跳出所有的for循环&#xff0c;都简单&#xff0c;稍微没见过的就是跳出多层for循环。 然后就是Java8出的foreach循环&#xff0…

js中的for循环如何跳出,js中for循环的两种语法

js几种for循环的几种用法 谷歌人工智能写作项目&#xff1a;小发猫 js&#xff0c;for循环是怎么运行的&#xff1f; typescript有哪些变化。 最普遍的介绍&#xff1a;for循环是JavaScript中最常用的循环&#xff0c;标准for循环代码格式为&#xff1a;for(定义变量初始值;…

if/while/do-while/for循环以及跳出循环break/return/continue

流程控制对任何一门编程语言都是至关重要的&#xff0c;它为我们提供了控制程序步骤的基本手段。常见对主要分为&#xff0c;条件语句、循环语句、跳转语句。 1、if语句 if 语句是一种判断语句。 语法&#xff1a; if(条件){条件成立时执行的代码 } if...else 语句当条件成…

Python中跳出循环的两种方法

我们经常遇到循环在进行到某一个特定的值时&#xff0c;需要跳出循环&#xff0c;或跳过这个值&#xff0c;python中早已为我们准备了这样的参数:break,continue 比如下面的for循环&#xff1a; for i in range(1,10):print(循环了,i,次) 结果显而易见如图&#xff1a; 当我…

js foreach与for循环之return跳出循环

因为自己比较大只&#xff0c;容易忘记&#xff0c;仅此用来记录一下~ 各种循环中使用return或者退出循环的机制。 1、forEach 使用 return 可以退出循环吗&#xff1f;下面代码打印啥&#xff1f; const list [1, 2, 3, 4, 5]list.forEach(e > {if (e 3) {return}consol…

JS中如何跳出.forEach循环

写在前面 提到在一段程序中如果碰到需要终止&#xff0c;结束一个循环&#xff0c;函数或者一段代码&#xff0c;一般会想到以下这几个关键字return、continue、break 简述一下三者的区别&#xff1a; break: 终止整个循环(有内层循环时终止的是内层循环)&#xff0c;退出swi…

JavaScript foreach 方法跳出循环

通常&#xff0c;在 for循环中跳出循环可以用 break或者 continue 来跳出循环。 break&#xff1a;跳出循环&#xff1b; continue&#xff1a;跳过当次循环。 而有时候需要在 foreach 中跳出循环&#xff0c;该怎么做呢&#xff1f; forEach() 方法用于调用数组的每个元素&am…

C++跳出for循环的方式

注&#xff1a;continue只能跳出当前层的循环&#xff0c;无法直接跳出for循环 方法1&#xff1a; for循环中满足条件后使用break语句&#xff1b; #include <iostream> using namespace std;int main(int argc,char *argv[]) {int i;for(i0;i<5;i){if(i2)break;}cou…

Js之跳出for循环,跳出多次for循环详解

一、for循环退出方式 首先我们都知道循环中最常用的就是continue;break; continue:表示跳出本次循环&#xff0c;也就是不执行本次循环continue之后的操作 break:表示跳出当前的循环&#xff0c;针对整个循环体终止后续的遍历&#xff1b; 最简单的应用效果如下&#xff1a; …

layui遮罩

layui遮罩 在执行某个按钮功能时,可能出现网络延迟或是其他原因时 避免重复表单提交,用遮罩.此次请求不响应,执行完毕,就一直遮罩住其他功能 var loading layer.load(1, {shade: [0.8, #393D49], time: 0}); layer.close(loading);写的位置 在请求开启之前遮住 在请求响应之…

遮罩Mask

一、遮罩&#xff1a;遮罩层范围内可看见被遮罩层图层&#xff0c;遮挡层范围可看见笼罩层图形 遮罩层必须至少有两个图层&#xff0c;上面的一个图层为“遮罩层”&#xff0c;下面的称“被遮罩层”&#xff1b;这两个图层中只有相重叠的地方才会被显示。也就是说在遮罩层中有…

基于vue3.0的遮罩

前言 最近在uniapp中要写一个弹窗&#xff0c;弹窗好写&#xff0c;但是遮罩没有接触过。找到了下面这篇文章&#xff0c;推荐仔细阅读一下。在这篇文章的基础下&#xff0c;简单封装了一个遮罩。 推荐文章&#xff1a;论如何用Vue实现一个弹窗-一个简单的组件实现 遮罩 备…

html5 div遮罩,CSS3 遮罩属性

遮罩(Mask)和Photoshop里的蒙版效果含义一致&#xff0c;用于为指定元素建立一个覆盖在上面的遮罩层。它显示在元素之上&#xff0c;和背景效果使用起来正好相反。我们可以使用遮罩在元素上方叠加一幅图或者一个渐变效果。 遮罩、内容和背景效果的显示层次如下图所示&#xff1…

html添加遮罩

html添加遮罩的代码如下所示 使用divcss &#xff0c;加载中的图片是网上下载的动图&#xff0c;可以根据自己需要进行修改 <!DOCTYPE html> <html> <head><title>DIV CSS遮罩层</title><script language"javascript" type"t…

Android渐变遮罩

Android Studio版本&#xff1a; Android Studio Chipmunk 这篇文章介绍如何在Android中实现这样的渐变遮罩 依赖项 1.CardView 在app下的build.gradle中添加依赖&#xff0c;并且完成编译 dependencies {implementation androidx.cardview:cardview:1.0.0 } 2.两张svg图片…

Blender 利用遮罩剔除顶点

&#x1f369;mask &#x1f367;效果&#x1f961;步骤&#x1f366;mask贴图制作&#x1f364; 添加顶点权重编辑&#x1f957;添加mask&#x1f35f;应用修改 &#x1f367;效果 &#x1f961;步骤 &#x1f366;mask贴图制作 &#x1f4a1; 本次教程参考以下贴图制作mas…

Unity之ASE 图片遮罩

前言 当一张图片,我们希望它只显示一部分时,就可以使用一张特定的遮罩图来控制它的显示。 比如下图,我们希望它的边缘变成圆角,且过渡自然。 我们就可以和下面的遮罩图进行融合。 最后出来的效果如下图所示: 原理 原理很简单,我们只使用了一个Multiply相乘即可 我们…