【UE5】将2D切片图渲染为体积纹理,最终实现使用RT实时绘制体积纹理【第五篇-着色器投影-投射阴影部分】

投射阴影

最初打算将投影内容放在上一篇中,因为实现非常快速简单,没必要单独成篇。不过因为这里面涉及一些问题,我觉得还是单独作为一篇讲一下比较好。

原理

这里要用到的是 Shadow Pass Switch ,它可以为非不透明的材质替换阴影

某些版本UE只能搜中文"阴影通道切换"

在这里插入图片描述

简单的演示一下 Shadow Pass Switch 的功能
做一个这样的材质:
在这里插入图片描述
效果如下:
在这里插入图片描述

制作Shader

创建一个 Custom ,起名为 ShadowRayMarching ,输入节点如图,输出单通道
在这里插入图片描述

老样子,这些可以直接右键粘贴到输入:

((InputName="Tex"),(InputName="XYFrames"),(InputName="NumFrames"),(InputName="MaxSteps"),(InputName="StepSize"),(InputName="LightVector"),(InputName="CurPos"),(InputName="LocalObjectBoundsMax",Input=(OutputIndex=3,Mask=1,MaskR=1,MaskG=1,MaskB=1)))

代码如下:

float accumdens = 0;
for (int i = 0; i < MaxSteps; i++)
{float cursample = PseudoVolumeTexture(Tex, TexSampler, saturate(CurPos), XYFrames, NumFrames).r;accumdens += cursample * StepSize ;CurPos -= LightVector * StepSize ; // 步进方向换成了 LightVector
}
//返回累计结果
return accumdens ;

是不是很熟悉,这就是我们梦开始的地方。还记得我们的起点吗,我们第一步就做了这样一个步进,只是当时是"从相机方向"进行步进。因为现在做的是阴影,也就需要改为从光源方向步进,因此代码中现在是:

CurPos -= LightVector * StepSize;//这里与之前不同

从相机方向步进:
在这里插入图片描述

从光源方向步进:
在这里插入图片描述

连接变量:
只有 ShadowMask_MaxSteps 是新建变量,其余都是已有变量,我们直接使用
将结果连入 BeersLaw (还记得吗,这是介质吸收),连到Mask输出打印看看(材质当然也换到了 已遮罩 ,因为现在是法线向内的模型,因此也要开启双面

在这里插入图片描述

在这里插入图片描述

看看效果,这就是用来形成阴影的Mask

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

制作Mesh

到这一步,我们会遇到一个问题
我们目前使用的模型是法线向内的,如果不开启 双面 ,你看的实际会是这样:
在这里插入图片描述

背面透明的材质无法阻挡光照并投射阴影。
如果继续使用"双面"呢?
可惜投影是不区分 TwoSidedSign 的,实际上投影对很多东西都不支持,稍后会提到

  1. 创建双面材质
    在这里插入图片描述
  2. 打光,可以看到正反面的不同并不会影响阴影
    在这里插入图片描述

况且,由于体积渲染本身已经很耗资源,开启双面会导致性能难以接受。或者,你可能考虑使用 TwoSidedSign 来从视觉上剔除体积雾的“外部”渲染,但这对性能没有改善。

举例来说,这就像在计算 (1+2+3) * 0
虽然结果是 0 ,但 1+2+3 是会被计算的
简单的因果律

总之,这个办法是无论如何都行不通的。那么我们现在有两个选择:

两种方案

方案1

使用两个Cube模型:一个法线向内的,用来渲染体积;一个法线向外的,渲染阴影遮罩。然后将他们重叠放在一起。
你可以选择这种方法,这会让内外两个mesh有更为独立的静态网格体组件的控制。缺点是整合两个模型,需要制作一个Actor蓝图,类似这样:
在这里插入图片描述

方案2

和1的思路一样,制作一个 双面的模型,并为模型内外表面分别设置材质ID

为保证纯粹性,教程采用"方案2"

建立内外法线cube

在UE直接建模也是可以的,像之前一样
但我发现UE建模功能的更新频繁,用它做教程没啥制导意义,可能过几个版本就没人看得懂了,所以这次直接用3dsMax做演示

  1. 建立一个 100cm 长宽高的 box ,模型的轴在中心 。复制出一个,增加 法线 修改器翻转法线,现在我们拥有一正一反两个模型
    在这里插入图片描述
    2.分别为它们增加材质修改器,分别指定材质ID 12
    (注意,图中两个都为1,是错的)
    在这里插入图片描述
    在这里插入图片描述

  2. 为他们增加平滑修改器,这是为了优化顶点数量
    在这里插入图片描述

  3. 将它们移回中心,并选中两个模型,塌陷
    在这里插入图片描述

  4. 创建多维子材质并赋予模型,这是为了导出时能正确导出材质ID。(图里给了一个切片,是为了让读者可以看到内外的不同材质。实际不要切哦!)
    在这里插入图片描述

  5. 导出FBX并导入UE,注意不要开启这个模型的 Nanite ,可以看到两个材质通道(元素)
    在这里插入图片描述

组合在一起

模型放入场景

  1. 将模型放入场景,注意要关掉 影响距离场光照
    还记得吗,接收阴影 是使用距离场实现的,关掉它避免影响自身
    在这里插入图片描述
  2. 创建子实例
    1.为主材质 M_VolRayMarching 创建子材质 MI_VolRayMarching
    2.再为子材质 MI_VolRayMarching 创建子材质 MI_VolRayMarching_Shadow

注意父子关系为:
M_VolRayMarchingMI_VolRayMarchingMI_VolRayMarching_Shadow

  1. MI_VolRayMarching 放入法线向内的材质通道,MI_VolRayMarching_Shadow 放入法线向外的材质通道
    在这里插入图片描述

制作材质

现在把主材质连接好,这个材质需要同时实现半透明和遮罩材质,并在子材质中切换:

主材质

直接看图:
在这里插入图片描述

  1. 增加 Static Switch Parameter 节点(图中1),起名为 IsShadow ,用来做子实例切换。
  2. 注意图中的"2"和"3"是不一样的,"2"是不透明度 Opacity,"3"是不透明蒙版 Opacity Mask
  3. 主材质是半透明,因此"不透明蒙版"是灰色,但是同样需要连上。稍后会在子材质切换到Mask材质。
  4. Switch节点是"Is Shadow",因此下面的实现阴影的部分要连到True,如图中1,别连反了。
  5. 再最后检查一下材质设置:
    在这里插入图片描述
    在这里插入图片描述
子材质

MI_VolRayMarching 目前不需要修改,直接打开 MI_VolRayMarching_Shadow

  1. 勾选IsShadow
    在这里插入图片描述
  2. 修改材质重载,混合模式改为遮罩在这里插入图片描述
  3. 检查结果:在这里插入图片描述
    在这里插入图片描述
    检查一下,可以看到,外层的Mask材质为我们投下阴影

Tip:
当你快速改变光照或者改变模型位置时,你可能会发现阴影更新不及时,这是阴影缓存造成的,将模型的阴影缓存无效化改为 始终
在这里插入图片描述

隐藏Mask材质

两套模型方案有所不同,

如果你选择的是另外一个方案(方案1),也就是"内外是两个独立模型"的方案:
选择法线向外的模型,为其勾选 隐藏阴影 (它的意思是"隐藏时阴影"),关闭 可视
就可以在非可视情况投射阴影。
在这里插入图片描述
在这里插入图片描述
我没实际做方案1,因此下图中,对一个圆柱模型进行了设置,模型被隐藏了,但阴影还在:
在这里插入图片描述

这里使用老朋友Shadow Pass Switch
也就是在视图里,Default 输入的 Mask0。阴影 Shadow 则使用我们制作的光线方向步进出来的蒙版(下图1)
留下debug
考虑到Debug,为了在需要的时候还能看到这个黑色的mask,这里做了一个切换,ShowShadowMask (上图2)

现在效果如下:

在这里插入图片描述
请添加图片描述
自阴影,接收投影,投射阴影,一切都OOKK的

Done

一些问题

投影的实现本身并不是一件简单的事情,因此我们这种快速实现方式也不可避免地存在一些缺点。
以下是一些试图修正这些问题的无用尝试和昂贵的方法
(也许在某次UE的版本更新后,这些方法会有变得有效。所以先记录下来)

当体积模型与物体穿插时,阴影会相接
在这里插入图片描述
MI_VolRayMarching_Shadow Debug一下
在这里插入图片描述
能看到这就是问题的原因
在这里插入图片描述
阴影是通过Mask实现,也就代表它无法使用 SceneDepth 等数据来修复(体积雾里我们使用了它)。

那如果使用距离场呢?
ShadowRayMarching 增加CameraPosWS输入
在这里插入图片描述
代码如下:

// 创建变量,累加步进过程中的总密度
float accumdens = 0;// 使用 MaxSteps 作为最大步数进行循环,每次循环执行以下操作
for (int i = 0; i < MaxSteps; i++)
{// PseudoVolumeTexture 函数用于伪体积纹理采样,函数需要的参数在括号内传递float cursample = PseudoVolumeTexture(Tex, TexSampler, saturate(CurPos), XYFrames, NumFrames).r;// 使用距离场排除体积float3 RayPointPos = 2 * (CurPos - 0.5) * LocalObjectBoundsMax - 0.5 * 2;RayPointPos = LWCToFloat(TransformLocalPositionToWorld(Parameters,RayPointPos)) - CameraPosWS;// 在调用 GetDistanceToNearestSurfaceGlobal 时减去 CameraPosWSfloat SDFDistance = GetDistanceToNearestSurfaceGlobal(RayPointPos);if (SDFDistance < 0) break;accumdens += cursample * StepSize;// 为下次循环更新射线位置,沿着相机方向步进CurPos -= LightVector * StepSize;
}// 返回累计结果
return accumdens;

这里添加了获取距离场的代码。在沿光线步进的过程中,一旦距离场小于0,就可以判断射线已到达表面,此时直接结束并退出射线,返回累积的体积密度结果。
效果如下:
在这里插入图片描述
可以看到蒙版确实表达了正确的深度。但意外的,阴影实际上未受影响。
Why?我们做一个快速的实验,制作一个这样简单的材质
在这里插入图片描述
在这里插入图片描述
能看到使用了距离场作为Mask的面片,未投下任何阴影,那么试着直接看看值
在这里插入图片描述
在这里插入图片描述
这就是根本原因,投影和距离场是冲突的

如果不使用距离场,还有什么方法可以获取场景深度呢?那就只能采用代价高昂的2D捕获来实现真实的阴影。

在这里插入图片描述
这是我的测试场景,我通过Actor实现了一种沿光照方向捕获深度的相机,这样就可以拍摄到目标并实现正确的投影,然后使用贴花进行投射。
不过,这种方法对我来说实在过于昂贵,我认为并不值得尝试。这里只是给你提供一个思路,如果你有一个无情的甲方爸爸,你可以考虑使用这种方法。


下一篇是对目前阶段简短的收拾和整理的总结篇。

再然后就开始制作动态编辑体积雾的功能的新篇章咯!
(收拾它↓)
在这里插入图片描述

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

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

相关文章

Python3 接口自动化测试,HTTPS下载文件(GET方法和POST方法)

Python3 接口自动化测试,HTTPS下载文件(GET方法和POST方法) requests-pkcs12 PyPI python中如何使用requests模块下载文件并获取进度提示 1、GET方法 1.1、调用 # 下载客户端(GET)def download_client_get(self, header_all):try:url = self.host + "/xxx/v1/xxx-mod…

【MySQL】索引的机制、使用

在学习索引知识之前&#xff0c;我们可以先了解一下什么是索引。实际上&#xff0c;索引就是数据库中一个或多个列存储的结构&#xff0c;能够支持数据库管理系统在不扫描整张表的情况下也能查询到数据行&#xff0c;能够大大提升查询效率。举个例子&#xff0c;我们想要找到一…

WPF入门_02依赖属性

1、依赖属性主要有以下三个优点 1)依赖属性加入了属性变化通知、限制、验证等功能。这样可以使我们更方便地实现应用,同时大大减少了代码量 2)节约内存:在WinForm中,每个UI控件的属性都赋予了初始值,这样每个相同的控件在内存中都会保存一份初始值。而WPF依赖属性很好地…

upload-labs靶场Pass-13

upload-labs靶场Pass-13 查看源码 $is_upload false; $msg null; if(isset($_POST[submit])){$ext_arr array(jpg,png,gif);$file_ext substr($_FILES[upload_file][name],strrpos($_FILES[upload_file][name],".")1);if(in_array($file_ext,$ext_arr)){$temp_…

WSL2-轻量级AI训练场景最佳生产环境

WSL2 只适用于 Win 10 、Win11 在运行 AI 软件、AI 模型训练&#xff0c;Linux 是最佳的操作系统。 在运行各种软件&#xff0c;如&#xff1a;Stable Diffusion Web UI 等&#xff0c;使用 Docker 容器运行也更方便后期的快速复用&#xff0c;同样的 Docker 容器在 Linux 中…

安装vue发生异常:npm ERR! the command again as root/Administrator.

一、异常 npm ERR! The operation was rejected by your operating system. npm ERR! Its possible that the file was already in use (by a text editor or antivirus), npm ERR! or that you lack permissions to access it. npm ERR! npm ERR! If you believe this might b…

入门!Linux 常见指令及权限管理全面指南

Linux 操作系统在现代计算机应用中扮演着重要的角色&#xff0c;广泛用于服务器、桌面系统、嵌入式设备及云计算平台等领域。理解和掌握 Linux 常见指令及权限管理机制&#xff0c;是每一位系统管理员和开发人员的基础技能。本文将详细介绍 Linux 系统的基本背景、常用指令、权…

初试PostgreSQL数据库

文章目录 一、PostgreSQL数据库概述1.1 PostgreSQL的历史1.2 PostgreSQL安装1.3 安装PostgreSQL二、PostgreSQL起步2.1 连接数据库2.1.1 SQL Shell2.1.2 执行SQL语句2.2 pgAdmin 42.2.1 打开pgAdmin 42.2.2 查找数据库2.2.3 打开查询工具2.2.4 执行SQL语句三、实战小结文章目录…

【leetcode练习·二叉树】用「遍历」思维解题 III

本文参考labuladong算法笔记[【强化练习】用「遍历」思维解题 III | labuladong 的算法笔记] 437. 路径总和 III | 力扣 | LeetCode | 给定一个二叉树的根节点 root &#xff0c;和一个整数 targetSum &#xff0c;求该二叉树里节点值之和等于 targetSum 的 路径 的数目。 路…

c语言基础程序——经典100道实例(二)

前面 52 题可以看下 《c语言基础程序——经典100道实例。》 c语言基础程序——经典100道实例 053&#xff0c;按位异或 ^054&#xff0c;取数右端4~7位055&#xff0c;按位取反~056&#xff0c;画圆形057&#xff0c;画直线058&#xff0c;画矩形059&#xff0c;画椭圆060&…

Git上传命令汇总

进入企业&#xff0c;每日需要上传执行用例记录到gitlab平台上&#xff0c;本文记录了常用git上传命令&#xff0c; 并用github演示。 1、本地建立分支&#xff0c;克隆远程仓库 在gitlab中&#xff0c;每个人需要创建自己的分支&#xff0c;一般以自己的名字命名&#xff0c;…

FineReport 页面设置

点击菜单栏中的「模板>页面设置」&#xff0c;弹出页面设置对话框&#xff0c;就可以对当前 sheet 进行页面设置&#xff0c;一个报表的每个 sheet 页面设置可以不同&#xff1a; 1 方向 指纸张方向&#xff0c;通常与打印结合使用。A4 纸横向预览效果和纵向预览效果 2、…

HCIP-HarmonyOS Application Developer 习题(十四)

&#xff08;多选&#xff09;1、HarmonyOs为应用提供丰富的Al(Artificial Intelligence)能力&#xff0c;支持开箱即用。下列哪些是它拥有的AI能力? A、通用文字识别 B、词性标注 C、实体识别 D、语音播报 答案&#xff1a;ABCD 分析&#xff1a; AI能力简介二维码生成根据开…

为什么软件维护成本比软件的开发成本高?

很多项目的软件维护成本比软件的开发成本高出很多 一、需求变更频繁 业务需求变化 随着市场环境的变化和业务的发展&#xff0c;客户的需求可能会不断调整和改变。例如&#xff0c;企业的业务模式发生调整&#xff0c;需要软件系统增加新的功能模块或对现有功能进行重大修改…

为什么一条Java命令,JVM就可以执行Java程序了(串联JVM面试知识点)

文章目录 前言从面试题说起JVM做了哪些事&#xff1f;“翻译”的工作不仅仅“翻译” JVM 各部件如何协同工作&#xff1f;类加载器先工作执行引擎开始工作执行引擎工作模式Main方法什么时候被执行&#xff1f; 运行时数据区域开始工作线程私有的空间大名鼎鼎的堆内存 就这么一直…

FineReport 条件属性

条件属性主要指&#xff1a;给报表的属性添加条件&#xff0c;当满足该条件时&#xff0c;对属性的属性值进行修改&#xff0c;从而达到改变报表样式的目的。 条件属性共有 12 种&#xff0c;分别是&#xff1a;颜色、字体、超级链接、形态、缩进、行高、分页、列宽、背景、边框…

Oracle 使用位图索引 Cost降低200倍! 探讨位图索引的利与弊

一.简介 位图索引&#xff08;Bitmap Index&#xff09; 是 Oracle 数据库中一种特殊类型的索引&#xff0c;适用于低基数&#xff08;Low Cardinality&#xff09;列&#xff0c;即那些列中可选值相对较少的情况下使用。它与常规的 B-tree 索引不同&#xff0c;位图索引通过位…

Columns Page “列”页面

“列”页提供了列管理工具&#xff0c;其中包括用于添加和删除列的按钮、显示绑定数据源中字段名称的列表框以及网格列、提供对所选列属性的访问的属性网格。 Columns 页面提供 Column properties &#xff08;列属性&#xff09;、Column options &#xff08;列选项&#xff…

【Git】远程操作-标签管理-多人协作

远程操作 分布式版本控制系统 概念理解 Git就像正在看的一本书。每当看完一章&#xff0c;可以将其保存起来&#xff0c;如果后面想修改或者查看以前自己看到哪里&#xff0c;随时可以翻看。Git就是帮助记录这些修改的工具&#xff0c;主要负责记录每次改动&#xff0c;就类似…

AnaTraf | 网络质量分析与DNS响应时间

http://www.anatraf.com 在当今的数字化时代&#xff0c;网络是任何企业正常运转的核心。而网络的质量直接影响着业务的连续性和用户体验。当网络性能不佳时&#xff0c;可能会导致网站加载缓慢、应用响应滞后&#xff0c;甚至影响企业的生产力。特别是在互联网世界中&#xf…