当前位置: 首页 > news >正文

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

上一章中实现了体积渲染的光照与自阴影,那我们这篇来实现投影

回顾

勘误

在开始本篇内容之前,我已经对上一章中的内容的错误进行了修改。为了确保不会错过这些更正,同时也避免大家重新阅读一遍,我将在这里为大家演示一下修改的具体内容。

  • 错误连接:在之前的文章里,SkyLightEnvMapSample 错误地连接到了“光源方向”。
  • 正确连接:实际上,需要沿垂直方向 (0,0,-1) 进行采样,这样才能与 SkyAtmosphere 的结果保持一致。
    在这里插入图片描述
    (图为SkyLightEnvMapSample 正确的输入)

扩展:
快速的制作一个左右分屏,对比一下两者:
在这里插入图片描述
在材质编辑器没有SkyAtmosphere,因此左侧没有环境光:
在这里插入图片描述
在场景中,可以看到左右两侧的天光是基本一致的:
在这里插入图片描述


准备工作:整理和完善

再开始本章工作前,先对我们的凌乱的Shader做一次整理和完善吧!

整理

整理Freams

首先我们正式将XYFrames拆成两个参数,因为怕有人忘记这是一个float2

在这里插入图片描述
我们有一段时间不会再见到他们了,把他们 折叠到节点 ,收纳起来
在这里插入图片描述
起个名改个引脚,后续的相同操作就不多说明了
在这里插入图片描述

整理Base

本章中还需要修改这里,所以先不打包
但我们把Density移动到LightVecor上方
在这里插入图片描述

之前之后
在这里插入图片描述在这里插入图片描述

Tip:
在这里拖动
在这里插入图片描述

Tip:
小懒蛋们,在文章最后展示全部代码的分,完整的输入节点的"快速粘贴"

完善光照输入

在上一章中介绍了多种对环境光照的取值方式,我们现在为他们制作切换函数

1.整理输入

调整位置,将LightVector LightColor SkyColor 三个相关输入摆放在一起:
(图中演示的是不基于SkyAtmosphere,都做相同操作)

之前之后
在这里插入图片描述在这里插入图片描述
2.制作函数

在这里插入图片描述
将它们折叠到函数,起名VolumeLight
(如果你使用的是早期UE5版本,则需要手动创建材质函数)
在这里插入图片描述
为其增加Input ,输入类型为StaticBool,三个输入分别命名为
UseSkyAtmosphere CustomLightVector CustomColor
在这里插入图片描述

我们可以使用一个小技巧,在材质中创建一个“结构体”,然后通过Switch节点进行切换,从而减少Switch节点的数量。
现在,我们已经制作了一个函数,使其可以在几种方式之间进行切换。
需要特别说明的是,实际传递的内容与MaterialAttributes节点使用的名字无关。例如,在全局位置偏移中实际放置的是LightVector
在这里插入图片描述

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

上面这几张整体截图中,Input的输入类型是 Bool ,但应该像第一张图一样是 StaticBool

归一化

为了出于对转换结果的安全考虑,这里增加一个归一化
在这里插入图片描述

世界空间LightVectorWS

我们保留一个未转换为本地空间的光照方向作为输出,稍后会用到
在这里插入图片描述

Tip:为了避免造成误解,需要再次说明,实际传递的内容与 MaterialAttributes 节点上使用的名字无关。例如,在 全局位置偏移 中,实际放置的是 LightVector 。只是将它作为"结构体"使用。

完成后是这个样子
在这里插入图片描述
在这里插入图片描述

3.整理SelfShadow

好现在我么可以整理剩下的东西了
在这里插入图片描述
如上图,直接折叠,改好名即可


Done
清爽啦
现在已经把环境整理妥当,那就开始本章内容吧!


制作Shader

简单的概述

接下来的阴影效果会将着色器的复杂度提升一大截。

需要注意的是,着色器讲究的是“看上去对的”就是“对的”。除非你确实有重大需求,否则没有必要无脑地加入更多功能,以免增加不必要的复杂度。目前,体积雾的着色器已经在计算光照的过程中实现了“自阴影”,这在很多情况已经足够了。

接下来,我们要为其增加另外两种阴影效果,分别是“体积雾对其他物体投射的阴影(投射阴影)”和“其他物体在体积雾上的阴影(接收阴影)”

接收阴影

在材质球中实现真正的“接收阴影”是一项非常复杂的任务,这对于Shader来说是非常昂贵和不切实际的。然而,我们可以利用“距离场阴影”技术来实现相似的效果。UE引擎已经广泛应用了这种技术,它计算成本低且能够生成柔和的软阴影,在性能和视觉效果之间取得了良好的平衡。

因此,我们的“接收阴影”本质上是实现一个“距离场阴影”。不过,在这篇文章中,我不会详细介绍“距离场阴影”的具体实现,因为相关信息已经很容易找到。未来,我计划写一些基于距离场的效果,届时会对“距离场阴影”进行更详细的介绍。

接下来的工作是采样全局距离场,以实现所需的阴影效果。

增加变量

在这里插入图片描述

为 RayMarching 新增4个输入

输入说明
LightVectorWS世界空间光方向
CameraPosWS世界空间相机位置
LocalObjectBoundsMax本地空间的Bound框大小
LightTangent光切线,也就是对距离场步进时的距离,这将决定阴影边缘的模糊程度
DFSStepsDistanceFieldShdowSteps 的缩写,距离场阴影的采样步数

快速粘贴

((InputName="Tex"),(InputName="XYFrames"),(InputName="NumFrames"),(InputName="MaxSteps"),(InputName="StepSize"),(InputName="LocalCamVec"),(InputName="CurPos"),(InputName="FinalStepSize"),(InputName="Density"),(InputName="LightVector"),(InputName="LightColor",Input=(OutputIndex=1)),(InputName="SkyColor",Input=(OutputIndex=2)),(InputName="ShadowSteps"),(InputName="ShadowStepSize"),(InputName="ShadowDensity"),(InputName="ShadowThreshold"),(InputName="AmbientDensity"),(InputName="LightVectorWS"),(InputName="CameraPosWS"),(InputName="LocalObjectBoundsMax",Input=(OutputIndex=3,Mask=1,MaskR=1,MaskG=1,MaskB=1)),(InputName="LightTangent"),(InputName="DFSSteps")

增加代码

RayMarching代码如下:

//创建变量,从0开始累加沿相机方向步进过程中的总密度
float accumdens = 0;//Shadow部分
//创建变量,透射率和光线的能量
float transmittance =1;
float3 lightenergy = 0;
//基本和相机方向步进一样,但这些都是常量,不需要写进for里
Density *= StepSize;
LightVector *= ShadowStepSize;
ShadowDensity *= ShadowStepSize;
//一个对数来计算阈值,用来判断光线是否还值得计算
float shadowthresh = -log(ShadowThreshold)/ShadowDensity;//使用 MaxSteps 作为最大步数进行循环,每次循环执行以下操作
for (int i = 0; i < MaxSteps; i++)
{float cursample = PseudoVolumeTexture(Tex, TexSampler, saturate(CurPos), XYFrames, NumFrames).r;// 在当前步进位置进行纹理采样//Shadow部分if(cursample > 0.001)//如果采样位置没有密度,则跳过{float3 Lpos = CurPos;//Lpos将作为光线步进的起始位置float shadowdist = 0;//和之前的accumdens一样,积累阴影//自阴影for(int s = 0; s < ShadowSteps; s++){Lpos += LightVector;//移动步进位置float Lsample = PseudoVolumeTexture(Tex, TexSampler, saturate(Lpos), XYFrames, NumFrames).r;//采样//判断是否在框内,不是则直接break退出forfloat3 shadowboxtest = floor( 0.5+ (abs(0.5-Lpos)));//float exitshadowbox = shadowboxtest.x + shadowboxtest.y + shadowboxtest.z;float exitshadowbox = dot(shadowboxtest,1);//简短的通道相加if(shadowdist > shadowthresh || exitshadowbox >= 1) break;shadowdist += Lsample;//累计}//接收阴影float3 dfpos = 2 * (CurPos -0.5) * LocalObjectBoundsMax;//-0.5 * 2,得到一个居中的Bounddfpos = LWCToFloat(TransformLocalPositionToWorld(Parameters,dfpos)) - CameraPosWS;//将dfpos转换为世界空间,需要LWC精度所以在代码里转换,减去相机位置float dftracedist = 1; //创建四个变量float dfshadow = 1;//这是我们最终要的float curdist = 0;float DistanceAlongTrace = 0;for (int d = 0; d < DFSSteps; d++)//又一次的光线步进{DistanceAlongTrace += curdist;//增加距离curdist = GetDistanceToNearestSurfaceGlobal(dfpos);//采样全局距离场,他和蓝图里`DistanceToNearestSurface`是相同函数float SphereSize = DistanceAlongTrace * LightTangent;//采样距离场软阴影的球形距离dfshadow = min( saturate(curdist/SphereSize),dfshadow);//用小于它的结果来更新变量dfpos.xyz += LightVectorWS * dftracedist * curdist;//继续移动位置dftracedist *= 1.0001;//增加一个很小的因子}//更新样本和光能,算法是BeersLaw函数cursample = 1 -exp(-cursample * Density);lightenergy += exp(-shadowdist * ShadowDensity) * cursample * transmittance * LightColor * dfshadow;//在结果上乘dfshadowtransmittance *= 1-cursample;//环境光照部分shadowdist = 0;//重置一下阴影距离,继续利用它计算光照Lpos = CurPos + float3(0,0,0.025);//新位置float Lsample = PseudoVolumeTexture(Tex, TexSampler, saturate(Lpos), XYFrames, NumFrames).r;//采样shadowdist += Lsample;Lpos = CurPos + float3(0,0,0.05);Lsample = PseudoVolumeTexture(Tex, TexSampler, saturate(Lpos), XYFrames, NumFrames).r;//采样shadowdist += Lsample;Lpos = CurPos + float3(0,0,0.15);Lsample = PseudoVolumeTexture(Tex, TexSampler, saturate(Lpos), XYFrames, NumFrames).r;//采样shadowdist += Lsample;lightenergy += exp(-shadowdist * AmbientDensity) *cursample * SkyColor * transmittance;//累计到光}CurPos += -LocalCamVec;
}CurPos += LocalCamVec * (1 - FinalStepSize);
float cursample = PseudoVolumeTexture(Tex, TexSampler, saturate(CurPos), XYFrames, NumFrames).r;return float4(lightenergy, transmittance);

这是增加的部分:
在这里插入图片描述

正像之前说的,这里不介绍距离场阴影的原理,总之我们在上一章自阴影的下方又写了接收阴影,并在后面lightenergy的累计里带上了dfshadow

Tip:
使用custom写hlsl的一大缺点就是没啥向后兼容性,EPIC改不改函数名字完全取决于心情。这里使用了一个函数 GetDistanceToNearestSurfaceGlobal() 他就是蓝图中的 DistanceToNearestSurface , 如果将来哪个版本里函数报错了,希望你知道你需要找什么。
在这里插入图片描述

有关性能

尽管距离场阴影的性能相对较好,但它也并非真正的低成本。DistanceFieldShadowSteps 参数设置过低时,通常会出现一些奇怪的问题。为了避免这些问题,我在使用时一般将这个参数设置为大于32。不过需要注意的是,这意味着在主循环的每一步中都会执行32次计算。

目前效果

在这里插入图片描述
一个接收阴影就做好了

Done


最近工作比较忙,这导致这个Shader写的比较慢。原本计划在一篇文章中同时做接收和投影阴影的实现,不过现在看来得把它们分开写。
为了避免拖得太久让大家着急,我们将在下一章再制作另一部分投影阴影的内容。


相关文章:

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

上一章中实现了体积渲染的光照与自阴影&#xff0c;那我们这篇来实现投影 回顾 勘误 在开始本篇内容之前&#xff0c;我已经对上一章中的内容的错误进行了修改。为了确保不会错过这些更正&#xff0c;同时也避免大家重新阅读一遍&#xff0c;我将在这里为大家演示一下修改的…...

Shell脚本基础——实训项目任务

项目一 项目实训 (初始Shell脚本) 项目一 项目实训 (初始Shell脚本)项目实施任务一 输入输出重定向任务二 数据输入输出操作任务三 Shell变量操作任务四 算术运算符操作任务五 设置环境变量 【实训任务】 本实训的主要任务是通过编写简单的shell脚本&#xff0c;完成使用数据…...

Eclipse Memory Analyzer (MAT)提示No java virtual machine was found ...解决办法

1&#xff0c;下载mat后安装&#xff0c;打开时提示 jdk版本低&#xff0c;需要升级到jdk17及以上版本&#xff0c;无奈就下载了jdk17&#xff0c;结果安装后提示没有jre环境&#xff0c;然后手动生成jre目录&#xff0c;命令如下&#xff1a; 进入jdk17目录&#xff1a;执行&…...

【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器

文章目录 C list 容器详解&#xff1a;从入门到精通前言第一章&#xff1a;C list 容器简介1.1 C STL 容器概述1.2 list 的特点 第二章&#xff1a;list 的构造方法2.1 常见构造函数2.1.1 示例&#xff1a;不同构造方法2.1.2 相关文档 第三章&#xff1a;list 迭代器的使用3.1 …...

植物大战僵尸杂交版V2.5.1下载(最新版)

2.5.1版本更新公告&#xff1a; 在最新的2.5.1版本中&#xff0c;游戏对“两面夹击”关卡进行了多项重要调整。出怪倍率和种类均有所降低&#xff0c;部分关卡的初始阳光量也得到了调整&#xff0c;以增强玩家的策略性。同时&#xff0c;玩家可以在这些关卡中使用投手类植物&a…...

基于nodejs+vue的游戏陪玩系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目…...

SVN文件不显示修改状态图标

今天安装试用SVN时发现文件不显示修改状态 以下为解决方法&#xff1a; 1&#xff0c;在有.svn的文件夹中右键--tortoiseSvn--setting 2&#xff0c;选中icon Overlays&#xff0c;右侧的status cache 选shell 3&#xff0c;点击icon set 如下图所示 4&#xff0c;修改icon…...

GB28181语音对讲协议详解

GB28181-2016语音对讲流程如下图1所示&#xff1a; 图1.语音对讲流程。 其中, 信令 1 、2 、 3 、 4 为语音广播通知、 语音广播应答消息流程; 信令 5 、 1 2 、 1 3 、 1 4 、 1 5 、 1 6 为 S I P 服务器接收到客户端的呼叫请求通过 B 2 B UA 代理方式建立语音流接收者与媒…...

JavaScript 数据可视化:前端开发的核心工具

随着互联网和大数据的快速发展&#xff0c;数据呈爆炸式增长&#xff0c;如何有效地展示和理解数据成为了一项关键技能。JavaScript 作为前端开发的主要语言&#xff0c;不仅在构建网页方面无可替代&#xff0c;也在数据可视化领域发挥了重要作用。从简单的图表到复杂的交互式展…...

[Redis][哨兵][上]详细讲解

目录 0.前言1.基本概念1.相关名词解释2.主从复制的问题3.人工恢复主节点故障4.哨兵自动恢复主节点故障 0.前言 说明&#xff1a;该章节相关操作不需要记忆&#xff0c;理解流程和原理即可&#xff0c;用的时候能自主查到即可Redis的主从复制模式下&#xff0c;⼀旦主节点由于故…...

如何展开浏览器开发者模式的Fetch/XHR

说明&#xff1a;大多数程序员都用浏览器的F12&#xff0c;开发者模式查看接口&#xff0c;我也不例外。我常用下面这个选项&#xff0c;它会过滤掉掉其他文档、样式请求&#xff0c;只展示访问服务器的接口请求 有次&#xff0c;不知道点了什么&#xff0c;这个菜单消失找不…...

Pydantic 是一个强大的 Python 库

Pydantic 是一个强大的 Python 库&#xff0c;专门用于数据验证和设置管理。以下是对 Pydantic 的详细介绍&#xff1a; 一、主要功能和特点 数据验证&#xff1a; Pydantic 通过 Python 类型注解来定义数据模型&#xff0c;并自动验证输入数据是否符合预定义的类型和结构。提…...

每日OJ题_牛客_NC40链表相加(二)_链表+高精度加法_C++_Java

目录 牛客_NC40链表相加&#xff08;二&#xff09;_链表高精度加法 题目解析 C代码 Java代码 牛客_NC40链表相加&#xff08;二&#xff09;_链表高精度加法 链表相加(二)_牛客题霸_牛客网 题目解析 模拟⾼精度加法的过程&#xff0c;只不过是在链表中模拟。 C代码 /*…...

Dubbo快速入门(一):分布式与微服务、Dubbo基本概念

文章目录 一、分布式与微服务概念1.大型互联网架构目标2.集群和分布式&#xff08;1&#xff09;集群 (Cluster)&#xff08;2&#xff09;分布式计算 (Distributed Computing)&#xff08;3&#xff09;集群与分布式的关系&#xff08;4&#xff09;实践中的应用案例 3.架构演…...

jmeter性能测试---csv数据文件设置

&#xff08;1&#xff09;什么时候使用CSV数据文件设置&#xff1f; 当不同的用户&#xff0c;或者同一用户多次循环时&#xff0c;都可以获取到不同的值 &#xff08;2&#xff09;使用CSV数据文件设置进行参数化的步骤&#xff1f; 实例&#xff1a; 请求&#xff1a;htt…...

交换基础【计算机网络】

交换基础 1、交换机的工作原理有哪4项操作&#xff0c;地址表如何建立的&#xff1f; 4项基本操作 丢弃 当本端口下的主机访问已知本端口下的主机时丢弃 转发 当某端口下的主机访问已知某端口下的主机时转发 扩散 当某端口下的主机访问未知端口下的主机时要扩散 广播 当某…...

Android12的netd分析

1.文件位置 system/netd/server/目录下的main.cpp和Android.bp 可知编译会生成netd的可执行程序。 2.main函数的流程 int main() {Stopwatch s; 。。。。。。。 // 启动NetlinkManager服务NetlinkManager *nm NetlinkManager::Instance();if (nm nullptr) {ALOGE("Una…...

OpenCV图像文件读写(6)将图像数据写入文件的函数imwrite()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 将图像保存到指定的文件中。 函数 imwrite 将图像保存到指定的文件中。图像格式是根据文件名扩展名选择的&#xff08;参见 cv::imread 获取扩展…...

JVM(HotSpot):方法区(Method Area)

文章目录 一、内存结构图二、方法区定义三、内存溢出问题四、常量池与运行时常量池 一、内存结构图 1.6 方法区详细结构图 1.8方法区详细结构图 1.8后&#xff0c;方法区是JVM内存的一个逻辑结构&#xff0c;真实内存用的本地物理内存。 且字符串常量池从常量池中移入堆中。 …...

JWT的基础与使用

JWT&#xff08;JSON Web Token&#xff09; 是一种用于在各方之间传输信息的紧凑、安全的方式&#xff0c;常用于身份验证和授权。它以令牌的形式将用户信息编码后传输&#xff0c;可以确保数据的完整性和安全性。 1.JWT的结构 JWT 是一个基于 JSON 的令牌&#xff0c;由三部…...

处理 VA02修改行项目计划行(SCHEDULE LINES )报错:不可能确定一个消耗帐户

用户修改销售订单行项目 计划行(SCHEDULE LINES) &#xff0c;从 “CN”改为“CB”时候发生报错提示&#xff1a;不可能确定一个消耗帐户。 Not possible to determine a consumption account Message No. 06138 Diagnosis The system was not able to determine a consumpti…...

【AI基础】pytorch lightning 基础学习

传统pytorch工作流是首先定义模型框架&#xff0c;然后写训练和验证&#xff0c;测试循环代码。训练&#xff0c;验证&#xff0c;测试代码写起来比较繁琐。这里介绍使用pytorch lightning 部署模型&#xff0c;加速模型训练和验证&#xff0c;记录。 准备工作 1 安装pytorch…...

高通量测序技术--组蛋白甲基化修饰、DNA亲和纯化测序,教授(优青)团队指导:从实验设计、结果分析到SCI论文辅助

组蛋白甲基化修饰工具(H3K4me3 ChIP-seq)组蛋白甲基化类型也有很多种&#xff0c;包括赖氨酸甲基化位点H3K4、H3K9、H3K27、H3K36、H3K79和H4K20等。组蛋白H3第4位赖氨酸的甲基化修饰(H3K4)在进化上高度保守&#xff0c;是被研究最多的组蛋白修饰之一。 DNA亲和纯化测序 DNA亲…...

[极客大挑战 2019]RCE ME1

<?php error_reporting(0); if(isset($_GET[code])){$code$_GET[code];if(strlen($code)>40){die("This is too Long.");}if(preg_match("/[A-Za-z0-9]/",$code)){die("NO.");}eval($code); } else{highlight_file(__FILE__); }// ?>…...

计算机毕业设计 中医院问诊系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…...

FME辅助规划选址

1.需求&#xff1a;新建运动场馆 用地需求&#xff1a;至少1km*2km 找到符合要求的储备地块 2.已有资源&#xff1a;储备用地 现在城市地块储备比较充足&#xff0c;但都是不规则地块 找出可以建大型场馆的地块 3.问题分析 图斑内部可以放下1000*2000的矩形 4.解决思路…...

Unity中的GUIStyle错误:SerializedObject of SerializedProperty has been Disposed.

一运行就循环打印这个报错&#xff0c; 解决办法&#xff0c;每次改参数之后在HIerarchy中手动保存&#xff0c;就会停止循环打印&#xff0c;style中的字体也显示出来了&#xff0c; 或者 直接换个低版本的...

实战篇 | WSL迁移Linux系统到非系统盘(完整实操版)

1. 操作步骤 1.1 确认要导出的Linux系统是否存在&#xff08;可跳过&#xff09; # 终端命令 # 查看通过WSL安装的Linux系统列表 wsl -l1.2 导出Linux系统为tar包 # 终端命令 # 格式&#xff08;过&#xff09; wsl --export <Distribution Name> <File Name> #…...

增量式编码器实现原理

目录 概述 1 认识增量式编码器 1.1 概述 1.2 增量式编码器的特性 1.3 编码器的硬件 2 增量式编码器实现原理 2.1 编码器信号 2.2 正反转判断 概述 本文主要介绍增量式编码器实现原理&#xff0c;包括增量式编码器的特性&#xff0c;信号特性&#xff0c;以及如何使用编…...

数据集-目标检测系列-口罩检测数据集 mask>> DataBall

数据集-目标检测系列-口罩检测数据集 mask>> DataBall 数据集-目标检测系列-口罩检测数据集 mask 数据量&#xff1a;1W DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;享有百种数据集&#xff0c;持续增加中。 数据项目地址&#xff1a; gitcode: https…...