> 文章列表 > 【Unity UPR】造个获取深度法线纹理的轮子

【Unity UPR】造个获取深度法线纹理的轮子

【Unity UPR】造个获取深度法线纹理的轮子

描边需要深度+法线纹理的加持,效果才能达到最好,但URP下很多版本不支持直接获取_CameraNormalsTexture,而我本人也尝试了一下在12.1.7下偷懒直接拿SSAO里的Depth Normal图,

虽然也能实现吧,但是需要打开SSAO的同时,再在shader中加入指定的Tag为"DepthNormals"的Pass才能实现:

稍微有点麻烦,而且总有种用别人东西的感觉。

那就尝试一下自己动手吧!动手造一个获取深度法线纹理的轮子!

贴一下项目环境:

URP12.1.7

Unity2021.3.8f1


浅看两篇手动获取深度法线纹理的文章:URP深度法线纹理 - 简书 (jianshu.com)和雪风大佬的urp管线的自学hlsl之路 第二十四篇 科幻扫描效果后篇 - 哔哩哔哩 (bilibili.com),实现都是依靠build-in底下的shader,然后将绘制出来的纹理传递给URP下自己项目定义的shader使用。

1 定义RenderFeature获取法线深度图

这个是参考了上述的过程,说实话,内容太过复杂。只有不断多学习,多做,每次都好好做备注,总有一天会完全理解的:

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;public class OutlineRenderFeature : ScriptableRendererFeature
{[System.Serializable]// 定义3个共有变量public class Settings{public Shader shader; // 设置后处理shader//public Material material; //后处理Materialpublic RenderPassEvent renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing; // 定义事件位置,放在了官方的后处理之前}// 初始化一个刚刚定义的Settings类public Settings settings = new Settings(); // 初始化PassOutlinePass outlinePass;// 给pass传递变量,并加入渲染管线中public override void Create(){this.name = "OutlinePass"; // 外部显示的名字outlinePass = new OutlinePass(settings.renderPassEvent, settings.shader);}public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData){renderer.EnqueuePass(outlinePass);}}public class OutlinePass : ScriptableRenderPass
{static readonly string renderTag = "Post Effects"; // 定义渲染TagMaterial tmaterial;OutlineVolume outlineVolume;  // 传递到volume,OutlineVolume是Volume那个类定义的类名public OutlinePass(RenderPassEvent evt, Shader tshader){renderPassEvent = evt; // 设置渲染事件位置var shader = tshader;  // 输入shader信息//var material = tmaterial;if (shader == null){Debug.LogError("没有指定Shader");return;}tmaterial = CoreUtils.CreateEngineMaterial(tshader);}// 后处理逻辑和渲染核心函数,相当于build-in 的OnRenderImage()public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData){if(tmaterial == null){Debug.LogError("Shader有错误,请检查!");return;}// 判断是否开启后处理if (!renderingData.cameraData.postProcessEnabled){return;}// 渲染设置var stack = VolumeManager.instance.stack;             // 传入volumeoutlineVolume = stack.GetComponent<OutlineVolume>();  // 拿到我们的volumeif (outlineVolume == null){Debug.LogError("Volume组件获取失败");return;}var cmd = CommandBufferPool.Get(renderTag);     // 设置渲染标签Render(cmd, ref renderingData);                 // 设置渲染函数context.ExecuteCommandBuffer(cmd);              // 执行函数CommandBufferPool.Release(cmd);                 // 释放}void Render(CommandBuffer cmd, ref RenderingData renderingData){RenderTargetIdentifier source = renderingData.cameraData.renderer.cameraColorTarget;                 // 定义RTRenderTextureDescriptor inRTDesc = renderingData.cameraData.cameraTargetDescriptor;inRTDesc.depthBufferBits = 0;                                                                          // 清除深度var camera = renderingData.cameraData.camera;                         // 传入摄像机Matrix4x4 clipToView = GL.GetGPUProjectionMatrix(camera.projectionMatrix, true).inverse;tmaterial.SetColor("_Color", outlineVolume.OutlineColor.value);   // 获取value 组件的颜色tmaterial.SetMatrix("_ClipToView", clipToView);   // 反向输出到Shadertmaterial.SetFloat("_OutlineScale", outlineVolume.OutlineScale.value);tmaterial.SetFloat("_DepthThreshold", outlineVolume.DepthThreshold.value);tmaterial.SetFloat("_NormalThreshold", outlineVolume.NormalThreshold.value);tmaterial.SetFloat("_DepthNormalThreshold", outlineVolume.DepthNormalThreshold.value);tmaterial.SetFloat("_DepthNormalThresholdScale", outlineVolume.DepthNormalThresholdScale.value);int destination = Shader.PropertyToID("Temp1");// 获取一张临时RTcmd.GetTemporaryRT(destination, inRTDesc.width, inRTDesc.height, 0, FilterMode.Bilinear, RenderTextureFormat.DefaultHDR); //申请一个临时图像,并设置相机rt的参数进去cmd.Blit(source, destination);                            // 设置后处理cmd.Blit(destination, source, tmaterial, 0);}
}

2 在Shader中使用

上述RenderFeature我们获得了一个全局的_CameraDepthNormalsTexture变量,我们就可以像Build-in下一样访问啦!

但是,一些之前固定管线下的一些采样、解码Texture函数在URP下不能直接用,要自己定义,主要需要一个解码函数。固定管线下函数:

其中:

直接搬运!完全没问题~

我给他合起来了,合成了一个函数,返回的时候用就行:

还要注意,采样要是屏幕空间的UV,不然乱七八糟。

然后shader后面必须也要加上一个自定义的LightTag:

突然发现这个复杂程度跟SSAO那个差不多。。。

看看效果,我们单独输出深度和法线:

一切正常!终于可以进行下一步了。

参考

URP深度法线纹理 - 简书 (jianshu.com)

八度软件下载