找回密码
 立即注册
查看: 475|回复: 0

[笔记] Unity|体积光散射(下)

[复制链接]
发表于 2022-10-1 14:35 | 显示全部楼层 |阅读模式
这是一篇下文,接上文链接Unity|体积光散射(上)
主要内容就是用unity的通用渲染管线创建自定义的渲染效果来实现体积光散射。


1绘制光源
创建材质后,现在开始绘制实际的光源。
替换// TODO: 1为以下行:
这准备了命令缓冲区,所以你可以开始向其中添加命令。
发出的第一个图形命令将渲染主光源。为简单起见,绘制包含太阳形状的天空盒。这应该会产生非常好的结果。
在前面的代码下面添加这些行:
为ScriptableRenderContext提供DrawSkybox,它需要对相机的引用。你可以从RenderingData 中获得此引用,这是一个提供有关场景的信息的结构。
2实引用unity默认shader
接下来,开始绘制occluders遮挡物,它们是场景中可能阻挡光源的所有对象。你可以使用它们的着色器在渲染期间引用它们,而不是跟踪这些对象。
在这个项目中,对象都使用 Unity 的默认着色器。要支持默认着色器,你必须获取所有默认着色器通道的着色器标签 ID。只需要这样做一次,并使用lists将结果缓存在一个变量中。
要使用 C# 列表,需要在文件顶部添加以下行:
接下来,在顶部声明以下字段LightScatteringPass:
然后,在构造函数中添加以下代码以填充列表:


3绘制occluders遮挡物
获得默认着色器 ID 后,你可以绘制使用这些着色器的对象。
返回DrawSkybox()函数并在其下方添加以下行:


上面代码的主要功能是:

  • 在绘制任何东西之前,你需要设置一些东西。DrawingSettings描述如何对对象进行排序以及允许哪些着色器通道。你可以通过调用来创建它CreateDrawingSettings()。你为该方法提供着色器通道、RenderingData对可见对象的引用和排序标准。
  • 你使用材质覆盖将对象的材质替换为occludersMaterial。
接下来,在前面的代码之后添加以下内容:


DrawRenderers处理实际的绘图调用。它需要知道哪些对象当前是可见的,这就是剔除结果的用途。此外,你必须提供绘图设置和过滤设置。你通过引用传递这两个结构。
你已经定义了绘图设置,但没有定义过滤设置。同样的,你可以声明它们一次,所以在类的顶部添加这一行:
FilteringSettings指示允许哪个渲染队列范围:不透明、透明或全部。使用此行,你可以设置范围以过滤不属于不透明渲染队列的任何对象。
你要做的最后一件事是清理你在执行此渲染过程时分配的资源。为此,请替换OnCameraCleanup()为:
保存脚本,单击“播放”,然后……还是没有效果。不用担心,你将在下一节中检查并找到原因。
4现使用帧调试器进行检查
虽然你在场景中看不到任何新内容,但幕后正在发生一些不同的事情。接下来,使用Frame Debugger检查渲染器并查看纹理是否正确绘制。
确保你仍处于Play模式并选择了Game视图。选择Window  Analysis  Frame Debugger,这将打开一个新窗口。将窗口停靠在“场景”选项卡旁边,按“启用” ,可以看到:
选择VolumetricLightScattering并展开它。你会注意到游戏视图发生了变化。如果RenderTexture在选定的绘制调用中进行渲染,则该绘制的内容将RenderTexture显示在游戏视图中。这就是 occluders map。


如果你保留默认分辨率比例设置,你会看到纹理是屏幕大小的一半。你可以选择各个draw来检查它们的作用。你甚至可以单步执行每个draw。
5在后期处理中细化图像
现在通过在后期处理中模糊来细化图像。
实现径向模糊着色器

径向模糊是通过创建后处理片段着色器来实现的。
转到RW/Shaders,选择Create  Shader  Image Effect Shader并将其命名为RadialBlur。
接下来,打开RadialBlur.shader并将名称声明替换为:


这定义了新的径向模糊着色器。
接下来,你需要告诉着色器你在渲染器功能中定义的设置。在属性块中,添加以下内容_MainTex:
_BlurWidth并_Intensity控制光线的外观。_Center是Vector太阳的屏幕空间坐标,径向模糊的原点。
Combing the Images

下一步,在occluders map上执行此着色器,并将生成的颜色覆盖在主摄像机颜色纹理的顶部。使用混合模式来确定如何组合这两个图像。
首先转到SubShader并删除此代码:
用这个替换它:
此行将混合模式配置为add。这会将两个图像的值添加到颜色通道并将它们限制为最大值 1。
接下来,在上面声明以下属性frag():
第一行定义了为使图像模糊而采用的样本数。数量多会产生更好的结果,但性能也较差。其他行与你在属性块中声明的变量相同。
真正的魔法发生在片段着色器中。替换frag()为以下代码:


在上面的代码中,
1.将color设置成默认值黑色。
2.计算从中心点朝向当前像素 UV 坐标的光线。
3.沿纹理采样ray并累积片段颜色。
4.乘以color并intensity返回结果。
保存着色器并返回 Unity。你将通过创建新材质并将着色器文件拖到其上来测试新着色器。
首先选择材质并在纹理槽中分配occludersMapExample.png。在RW/Textures中找到这个纹理。
现在,你将在预览窗口中看到着色器效果。将 preview shape更改为plane并使用这些值来更好地理解着色器属性。


添加径向模糊材质实例

以下步骤与设置occluders map类似。回到VolumetricLightScattering.cs并在LightScatteringPass构造函数上方声明一个材质:
然后,在构造函数中添加这一行:
到目前为止没有加什么新内容,你只是在创建材质实例。if通过替换此语句确保没有丢失材质:
配置径向模糊材质

模糊着色器需要知道太阳的位置,以便将其用作中心点。在Execute()中,替换// TODO: 2为以下行:


上面看起来很复杂,但实际上非常简单:
1.从RenderSettings中获得对太阳的引用。需要太阳的forward vector,因为directional light在空间中没有位置信息。
2.获取相机位置。
3.这为你提供了一个从相机到太阳的单位矢量。你可以使用它作为太阳的位置。
4.着色器需要一个视口空间位置,但你在世界空间中进行了计算。要解决此问题,你可以使用WorldToViewportPoint()转换点到相机的视口空间。
现在,使用以下代码将数据传递给着色器:


你实际上只需要 x 和 y 分量,sunPositionViewportSpace因为它代表屏幕上的像素位置。
模糊遮挡贴图

最后,你需要模糊遮挡贴图。
添加以下代码行以运行着色器:
上面提供Blit,一个使用着色器将源纹理复制到目标纹理的函数。它使用occluders作为源纹理执行着色器,然后将输出存储在相机颜色目标中。由于你在着色器中定义的混合模式,它结合了输出和颜色目标。
要获取对相机颜色目标的引用,请在顶部添加以下行LightScatteringPass:
在构造函数下面添加一个新函数来设置这个变量:


在VolumetricLightScattering中,在末尾添加这一行AddRenderPasses():


这会将相机颜色目标传递给Blit()需要的渲染通道。
保存脚本并返回编辑器。恭喜完成了效果。
转到场景视图,你将看到你做的照明效果。单击play并在关卡中移动以查看它的运行情况:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-12-22 11:25 , Processed in 0.095401 second(s), 27 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表