UE4 Decal实现简介

article/2025/8/29 5:35:47

Decal 是游戏中常见的一个东西,经常被用在 显示弹痕,地面叠加花纹等。

在这里插入图片描述
在这里插入图片描述

Decal 绘制流程

Decal可以认为是,将一个面的画面沿Decal的Box的X轴方向投影到物体表面。
在这里插入图片描述

Decal的绘制实际只有一个Box绘制。

RHICmdList.DrawIndexedPrimitive(GetUnitCubeIndexBuffer(), 0, 0, 8, 0, UE_ARRAY_COUNT(GCubeIndices) / 3, 1);

为什么不用一个平面来绘制呢?这样做主要是为了,覆盖不同视角,裁剪也变得简单。

Decal Pass是在Base Pass之后,Translucency Pass之前,Decal只能投射在非透明物体上。

Vertex过程

// from component to clip space (for decal frustum)
float4x4 FrustumComponentToClip;// decal vertex shader
void MainVS(in float4 InPosition : ATTRIBUTE0,out float4 OutPosition : SV_POSITION)
{OutPosition = mul(InPosition, FrustumComponentToClip);
}

Vertex Shader只是乘以FrustumComponentToClip变换矩阵,变换到Clip空间。

FMatrix FDecalRendering::ComputeComponentToClipMatrix(const FViewInfo& View, const FMatrix& DecalComponentToWorld)
{FMatrix ComponentToWorldMatrixTrans = DecalComponentToWorld.ConcatTranslation(View.ViewMatrices.GetPreViewTranslation());return ComponentToWorldMatrixTrans * View.ViewMatrices.GetTranslatedViewProjectionMatrix();
}

FrustumComponentToClip实际就是WorldViewProjection 组合变换矩阵(包含了DecalSize)

	FTransform GetTransformIncludingDecalSize() const{FTransform Ret = GetComponentToWorld();Ret.SetScale3D(Ret.GetScale3D() * DecalSize);return Ret;}

Pixel 处理过程

先获取屏幕UV

float2 ScreenUV = SvPositionToBufferUV(In.SvPosition);

SvPosition 是从Vertex Shader里输出,插值后 再传入Pixel Shader,

void MainPS(
#if PIXELSHADEROUTPUT_INTERPOLANTS || PIXELSHADEROUTPUT_BASEPASSFVertexFactoryInterpolantsVSToPS Interpolants,
#endif
#if PIXELSHADEROUTPUT_BASEPASSFBasePassInterpolantsVSToPS BasePassInterpolants,
#elif PIXELSHADEROUTPUT_MESHDECALPASSFMeshDecalInterpolants MeshDecalInterpolants,
#endifin INPUT_POSITION_QUALIFIERS float4 SvPosition : SV_Position        // after all interpolators

SvPositionToBufferUV函数是将SvPosition变换到屏幕空间UV坐标。

float2 SvPositionToBufferUV(float4 SvPosition)
{return SvPosition.xy * View.BufferSizeAndInvSize.zw;
}

注意SvPosition.xy是FrameBuffer像素坐标xy

获取设备Z(深度缓存里Depth值),这样可以还原那个像素点的WorldPosition。

// make SvPosition appear to be rasterized with the depth from the depth bufferIn.SvPosition.z = LookupDeviceZ(ScreenUV);

转换到Decal Box局部坐标系

float4 DecalVectorHom = mul(float4(In.SvPosition.xyz,1), SvPositionToDecal);OSPosition = DecalVectorHom.xyz / DecalVectorHom.w;// clip content outside the decal// not needed if we stencil out the decal but we do that only on large (screen space) onesclip(OSPosition.xyz + 1.0f);clip(1.0f - OSPosition.xyz);// todo: TranslatedWorld would be better for qualityWSPosition = SvPositionToWorld(In.SvPosition);

有两个clip,裁剪掉DecalBox以外的东西(不是Decal自己的像素点,前面有个步骤还原获取了原来Scene里的DeviceZ(In.SvPosition.z = LookupDeviceZ(ScreenUV)))
如下图所示

在这里插入图片描述

红色是X轴,绿色是Y轴,蓝色是Z轴。中心点坐标是(0, 0, 0)。

			FVector2D InvViewSize = FVector2D(1.0f / View.ViewRect.Width(), 1.0f / View.ViewRect.Height());// setup a matrix to transform float4(SvPosition.xyz,1) directly to Decal (quality, performance as we don't need to convert or use interpolator)//	new_xy = (xy - ViewRectMin.xy) * ViewSizeAndInvSize.zw * float2(2,-2) + float2(-1, 1);//  transformed into one MAD:  new_xy = xy * ViewSizeAndInvSize.zw * float2(2,-2)      +       (-ViewRectMin.xy) * ViewSizeAndInvSize.zw * float2(2,-2) + float2(-1, 1);float Mx = 2.0f * InvViewSize.X;float My = -2.0f * InvViewSize.Y;float Ax = -1.0f - 2.0f * View.ViewRect.Min.X * InvViewSize.X;float Ay = 1.0f + 2.0f * View.ViewRect.Min.Y * InvViewSize.Y;// todo: we could use InvTranslatedViewProjectionMatrix and TranslatedWorldToComponent for better qualityconst FMatrix SvPositionToDecalValue = FMatrix(FPlane(Mx,  0,   0,  0),FPlane( 0, My,   0,  0),FPlane( 0,  0,   1,  0),FPlane(Ax, Ay,   0,  1)) * View.ViewMatrices.GetInvViewProjectionMatrix() * WorldToComponent;

先将Sv_Position(Scene的Sv_Position,不是Decal Box的)转换到Projection空间,再InvViewProjectionMatrix 转换到WorldSpace,最后WorldToComponent到Decal局部坐标系。

DecalVectorHom.xyz / DecalVectorHom.w 从其次坐标系,到正常的坐标空间。

DecalUV的计算

// can be optimizedfloat3 DecalVector = OSPosition * 0.5f + 0.5f;// Swizzle so that DecalVector.xy are perpendicular to the projection direction and DecalVector.z is distance along the projection directionfloat3 SwizzlePos = DecalVector.zyx;// By default, map textures using the vectors perpendicular to the projection directionfloat2 DecalUVs = SwizzlePos.xy;

首先将-1 到 1范围的,转换到0到1。

在这里插入图片描述

最后是输出颜色和Alpha。

不接受Decal投射的物体处理

不接受Decal的物件,绘制的时候,写入0x80到Stencil Buffer里。

	if (bEnableReceiveDecalOutput){const uint8 StencilValue = (PrimitiveSceneProxy && !PrimitiveSceneProxy->ReceivesDecals() ? 0x01 : 0x00);DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<true, CF_DepthNearOrEqual,true, CF_Always, SO_Keep, SO_Keep, SO_Replace,false, CF_Always, SO_Keep, SO_Keep, SO_Keep,// decals atm are singe user of stencil in mobile base pass// don't use masking as it has significant performance hit on Mali GPUs (T860MP2)0x00, 0xff /*GET_STENCIL_BIT_MASK(RECEIVE_DECAL, 1)*/ >::GetRHI());DrawRenderState.SetStencilRef(GET_STENCIL_BIT_MASK(RECEIVE_DECAL, StencilValue)); // we hash the stencil group because we only have 6 bits.}else{// default depth state should be already set}

Decal在绘制的时候,设置Read Mask 0x80,和0比较相等,如果不等于就不进行绘制。

						GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_DepthNearOrEqual,true, CF_Equal, SO_Keep, SO_Keep, SO_Keep,false, CF_Always, SO_Keep, SO_Keep, SO_Keep,GET_STENCIL_BIT_MASK(RECEIVE_DECAL, 1), 0x00>::GetRHI();

http://chatgpt.dhexx.cn/article/bwGptxUy.shtml

相关文章

跳出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相乘即可 我们…

手写遮罩层

&#xff08;注&#xff1a;图文不符&#xff0c;仅供参考&#xff09; 遮罩层原理 就是当出现弹窗或者确认框的时候&#xff0c;点击弹窗外的地方不会触发别的响应&#xff0c;只是单纯地关闭弹窗&#xff08;或者不关闭也不触发其他响应&#xff09; 遮罩层的简单写法 任意…