URP | 热扭曲效果
那个人真狗
2022年03月04日 13:19
收录于文集
共43篇

内容偏多,下图是目录

目的

  • URP管线下怎么获取屏幕信息。

  • 实现特效中使用的热扭曲效果。

  • 认识摄像机输出的不同阶段效果图。

概述

  • URP管线环境下,GrabPass是失效的。

  • 在URP管线使用的OpaqueTexture 获取不透明物体的绘制。

  • 在shader Graph 里对应的节点是  SceneColor

注意: OpaqueTexture只能渲染不透明物体。

  • 透明物体是抓取不到的。

1.抓取不透明物体

声明贴图

  • 设置贴图

代码块
JavaScript
自动换行
复制代码
SAMPLER(_CameraOpaqueTexture);            //注意名字
复制成功

片元着色器阶段输出

  • 计算出模型显示UV坐标

  • 全代码

代码块
JavaScript
自动换行
复制代码
Shader "URP/14_OpaqueTexture_01"
{
    Properties
    {

    }
    HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

        CBUFFER_START(UnityPerMaterial)
        CBUFFER_END


        struct appdata
        {
            float4 positionOS : POSITION;
            float2 texcoord : TEXCOORD0;
        };

        struct v2f
        {
            float2 uv : TEXCOORD0;
            float4 positionOS : SV_POSITION;

        };


        SAMPLER(_CameraOpaqueTexture);                   //定义贴图
    ENDHLSL

    SubShader
    {
        Tags { "RenderPipeline"="UniversalPipeline" "RenderType"="Transparent"  "Queue" = "Transparent" "IgnoreProjector" = " True"}  
        LOD 100

        Pass
        {
            Tags{ "LightMode"="UniversalForward" }
            Blend SrcAlpha OneMinusSrcAlpha

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            v2f vert (appdata v)
            {
                v2f o;
                o.positionOS = TransformObjectToHClip(v.positionOS.xyz);
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {

                half2 screenUV = (i.positionOS.xy / _ScreenParams.xy);
                half4 col = tex2D(_CameraOpaqueTexture, screenUV); 
                return col;
            }
            ENDHLSL
        }
    }
}
复制成功

  • 效果

2.热扭曲效果

  • 制作原理,就是我们获取到屏幕颜色以后,对屏幕颜色UV进行偏移达到扭曲的效果。

  • 原来我们是扭曲纹理,就是对纹理坐标UV扭曲。

  • 现在我们获取一张Niose纹理对屏幕UV扭曲。

输入变量

  • 输入需要的变量

           niose纹理,遮罩纹理,niose纹理移动速度,扭曲力度

  • 计算UV信息,输出

  • 片元着色器阶段

    前计算出不同的UV移动方向,

    生成俩个扭曲的纹理,计算出不同的UV扭曲程度,

    加到屏幕UV里。

  • 计算遮罩,

    我们输出设置成遮罩,这样我们就可控扭曲范围大小。

  • 效果

3.特效顶点Alpha可以控制扭曲

  • 准备好特效刀光贴图

定义顶点输入

  • 在结构体输入,输出,如果不清楚可以参考上面的文档。

代码块
JavaScript
自动换行
复制代码
		struct appdata
        {
            float4 positionOS : POSITION;
            float2 texcoord : TEXCOORD0;
            float4 vertexColor : COLOR;
        };

        struct v2f
        {
            float2 uv : TEXCOORD0;
            float4 positionOS : SV_POSITION;
            float4 vertexColor : COLOR;
        };
复制成功
  • 顶点着色器阶段输出

片元着色器阶段

  • 计算出俩个方向,使用顶点Alpha控制扭曲强度。

代码块
JavaScript
自动换行
复制代码
					half4 frag (v2f i) : SV_Target
            {
                
                half4 mainTex = SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,i.uv);

                half2 screenUV = (i.positionOS.xy / _ScreenParams.xy) + half2(mainTex.rg * _Force * i.vertexColor.a);
                half4 col = tex2D(_CameraOpaqueTexture, screenUV); 
                return col;
            }
复制成功
  • 效果

能看到扭曲越来越弱。

扩展 :法线贴图实现扭曲效果

  • 准备贴图

  • 修改变量

代码块
JavaScript
自动换行
复制代码
		Properties
    {
        _NormalTex("_NormalTex",2D) = "bump" {}
        _NormalScale("_NormalScale",range(0,0.05)) = 0.01
    }
复制成功

注意:法线贴图使用**"bump" {}  法线标签**

  • 在片元着色器阶段我们处理法线的方法。

代码块
JavaScript
自动换行
复制代码
					half4 frag (v2f i) : SV_Target
            {
                
                half3 NormalTex = UnpackNormalScale(SAMPLE_TEXTURE2D(_NormalTex,sampler_NormalTex,i.uv),_NormalScale);  

                half2 screenUV = (i.positionOS.xy / _ScreenParams.xy) + half2(NormalTex.rg  * i.vertexColor.a);
                half4 col = tex2D(_CameraOpaqueTexture, screenUV); 
                return col;
            }
复制成功

这里和普通贴图不一样,使用一个内置函数,UnpackNormalScale 这函数是控制法线强度。

  • 效果

  • 代码

代码块
JavaScript
自动换行
复制代码
Shader "URP/14_OpaqueTexture_Normal"
{
    Properties
    {
        _NormalTex("_NormalTex",2D) = "bump" {}
        _NormalScale("_NormalScale",range(0,0.05)) = 0.01
    }
    HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

        CBUFFER_START(UnityPerMaterial)
            float4 _NormalTex_ST;
            float _NormalScale;
        CBUFFER_END


        struct appdata
        {
            float4 positionOS : POSITION;
            float2 texcoord : TEXCOORD0;
            float4 vertexColor : COLOR;
        };

        struct v2f
        {
            float2 uv : TEXCOORD0;
            float4 positionOS : SV_POSITION;
            float4 vertexColor : COLOR;
        };

        TEXTURE2D (_NormalTex);
        SAMPLER(sampler_NormalTex);

        SAMPLER(_CameraOpaqueTexture);                   //定义贴图
    ENDHLSL

    SubShader
    {
        Tags { "RenderPipeline"="UniversalPipeline" "RenderType"="Transparent"  "Queue" = "Transparent" "IgnoreProjector" = " True"}  
        LOD 100

        Pass
        {
            Tags{ "LightMode"="UniversalForward" }
            Blend SrcAlpha OneMinusSrcAlpha

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            v2f vert (appdata v)
            {
                v2f o;
                o.positionOS = TransformObjectToHClip(v.positionOS.xyz);
                o.uv = TRANSFORM_TEX(v.texcoord, _NormalTex);
                o.vertexColor = v.vertexColor;
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                
                half3 NormalTex = UnpackNormalScale(SAMPLE_TEXTURE2D(_NormalTex,sampler_NormalTex,i.uv),_NormalScale);  

                half2 screenUV = (i.positionOS.xy / _ScreenParams.xy) + half2(NormalTex.rg  * i.vertexColor.a);
                half4 col = tex2D(_CameraOpaqueTexture, screenUV); 
                return col;
            }
            ENDHLSL
        }
    }
}
复制成功

4.半透明物体支持热扭曲

  • 上面方法是不支持半透明物体。但是我们特效中都是半透明物体。

  • 怎么让支持半透明物体扭曲。

思路

  • 实际上URP管线中给我提供了很多COPY的图,我们这里使用**_AfterPostProcessTexture**这张贴图

  • 这张图是后处理完保存的一张图。

    • 然后利用RendererFeatures新建一个渲染时机 ,并新建一种LightMode Tags类型.

    • 这样所有Tags是Grab的shader都会在后期处理完成之后在渲染。

简单理解:我们创建一个状态,让这个状态是在摄像机渲染完以后在渲染。

设置管线

  • 找到URP管线设置——设置后处理资源

  • 设置一个渲染状态

  • 获取屏幕渲染出的一张图。

  • 设置叠加模式和渲染所有层,设置标签

设置相机

  • 我们前面设置渲染状态了,我们现在还需要创建一个摄像机。

    这个摄像机是获取主相机渲染的画面。

  • 设置管线,关闭所有渲染图层。

扩展Overlay Camera 是将其视图呈现在另一个摄影机的输出之上的摄影机.

  • 主相机里绑定 这个子相机

  • 在FrameDebug里我们可以看到俩个摄像机,第二个摄像机就是看到前面渲染效果。

设置shader

  • 定义渲染后处理图

注意:这里是和上面不一样的图。

  • 标签里修改渲染方式。

  • 片元着色器阶段,替换后处理素材。

代码块
JavaScript
自动换行
复制代码
			half4 frag (v2f i) : SV_Target
            {
                
                half3 normal = UnpackNormalScale(SAMPLE_TEXTURE2D(_NormalTex,sampler_NormalTex,i.uv),_NormalScale);  
                normal.z = lerp(1,normal.z,_NormalScale);
                half2 screenUV = (i.positionOS.xy / _ScreenParams.xy) + half2(normal.rg  * i.vertexColor.a);
                half4 col = tex2D(_AfterPostProcessTexture, screenUV); 
                return col;
            }
复制成功
  • 效果

    现在半透明物体也可以支持扭曲效果。

代码

代码块
JavaScript
自动换行
复制代码
Shader "URP/14_OpaqueTexture_01"
{
    Properties
    {
        _NormalTex("_NormalTex",2D) = "bump" {}
        _NormalScale("_NormalScale",range(0,0.05)) = 0.01
    }
    HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

        CBUFFER_START(UnityPerMaterial)
            float4 _NormalTex_ST;
            float _NormalScale;
        CBUFFER_END


        struct appdata
        {
            float4 positionOS : POSITION;
            float2 texcoord : TEXCOORD0;
            float4 vertexColor : COLOR;
        };

        struct v2f
        {
            float2 uv : TEXCOORD0;
            float4 positionOS : SV_POSITION;
            float4 vertexColor : COLOR;
        };

        TEXTURE2D (_NormalTex);
        SAMPLER(sampler_NormalTex);

        SAMPLER(_AfterPostProcessTexture);                   //定义贴图

    ENDHLSL

    SubShader
    {
        Tags { "RenderPipeline"="UniversalPipeline" "RenderType"="Transparent"  "Queue" = "Transparent" "IgnoreProjector" = " True"}  
        LOD 100

        Pass
        {
            Tags{ "LightMode"= "Grab" }
            Blend SrcAlpha OneMinusSrcAlpha

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            v2f vert (appdata v)
            {
                v2f o;
                o.positionOS = TransformObjectToHClip(v.positionOS.xyz);
                o.uv = TRANSFORM_TEX(v.texcoord, _NormalTex);
                o.vertexColor = v.vertexColor;
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                half3 normal = UnpackNormalScale(SAMPLE_TEXTURE2D(_NormalTex,sampler_NormalTex,i.uv),_NormalScale);  

                half2 screenUV = (i.positionOS.xy / _ScreenParams.xy) + half2(normal.rg  * i.vertexColor.a);
                half4 col = tex2D(_AfterPostProcessTexture, screenUV); 
                return col;
            }
            ENDHLSL
        }
    }
}
复制成功

参考资料

1.人人有功练 Unity URP管线透明折射扭曲材质的一系列实践(一)两种渲染时机的扭曲材质 - 知乎 (zhihu.com)

2. URP 系列教程 | 多相机玩法攻略 - 知乎 (zhihu.com)

5.扩展:渲染阶段的输出

CameraColorTexture 和 _CameraOpaqueTexture区别

  • 在Debug状态下查看这俩个

  • _CameraColorTexture   是场景渲染后生成的纹理截图,

  • _CameraOpaqueTexture  是在不透明物体渲染后截图,所以截取不到透明物体。

AfterPostProcessTexture

  • _AfterPostProcessTexture 是后处理渲染结果输出的图。

问题:这三个阶段渲染输出的图前后顺序是什么?

  • 第一,我们可以看到最前渲染的是CameraColorTexture

  • 第二是 _CameraOpaqueTexture   不透明物体渲染后截图

  • 最后是后处理渲染出结果在输出的图。

总结

  • URP管线下怎么抓取渲染图,默认URP管线提供了设置,这里有俩个一个是获取渲染图,一个是渲染深度。

  • 渲染结果输出图的认识,会产生CameraColorTexture ,_CameraOpaqueTexture   ,AfterPostProcessTexture ,他们的前后顺序,具体什么情况使用上面图。

  • 变透明实现扭曲,是在管线里,增加一种状态,使用第二个摄像机渲染输出AfterPostProcessTexture 图。在进行扭曲处理。

    在编辑窗口是有问题的,需要运行状态查看效果。