Unity Shader - 模仿RenderImage制作全屏Quad,可以制作自定义后处理的流程

先尝试GL类来制作

Shader

// jave.lin 2020.04.12 - 绘制一个全屏的Quad
Shader "Custom/DrawFullScreenQuad" {
    SubShader {
        Pass {
            ZTest Always ZWrite Off Cull Off
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            struct appdata {
                float4 vertex : POSITION;
                fixed4 color : COLOR;
            };
            struct v2f {
                float4 vertex : SV_POSITION;
                fixed4 color : COLOR;
            };
            v2f vert (appdata v) {
                v2f o;
                o.vertex = v.vertex;
                o.color = v.color;
                return o;
            }
            fixed4 frag (v2f i) : SV_Target { return i.color; }
            ENDCG
        }
    }
}

CSharp

画个三角型

        GL.PushMatrix();
        mat.SetPass(0);
        GL.LoadOrtho();

        GL.Begin(GL.TRIANGLE_STRIP);

        // 注意顶点顺序,组合为:顺时针即可,Unity 逆时针为正面
        GL.Color(new Color(1, 0, 0, 1));//red
        GL.Vertex3(0, 0, 1);
        GL.Color(new Color(0, 1, 0, 1));//green
        GL.Vertex3(1, 0, 1);
        GL.Color(new Color(0, 0, 1, 1));//blue
        GL.Vertex3(0.5f, 0.5f, 1);

        GL.End();

        GL.Flush();

运行效果
在这里插入图片描述

借用我之前写的一篇:

里面有个图片要注意的是,Unity的三角面正面判定
这里写图片描述

画个全屏的Quad

    private void DrawFullScreenQuadTesting()
    {
        //var camWPos = cam.transform.position;
        mat.SetPass(0);

        GL.Begin(GL.TRIANGLE_STRIP);
        // 注意顶点顺序,组合为:顺时针即可,Unity 逆时针为正面
        GL.Color(new Color(1, 0, 0, 1));//red
        GL.Vertex3(-1, -1, 0);
        GL.Color(new Color(0, 1, 0, 1));//green
        GL.Vertex3(1, -1, 0);
        GL.Color(new Color(0, 0, 1, 1));//blue
        GL.Vertex3(-1, 1, 0);
        GL.Color(new Color(1, 1, 0, 1));//yellow
        GL.Vertex3(1, 1, 0);

        GL.End();
    }

运行效果:
在这里插入图片描述

发现GL没有RenderTarget之类的

全屏的效果算是弄出来的,但是向要GL调用底层绘制的内容想指定一下渲染目标,结果发现没有。。。哈哈,白忙活了。。。连RenderTarget都没有。
这么说Unity GL貌似没啥用了,除了可以话一些Line来调试用。
看来要实现RT功能只能CommandBuffer或是Camera来设置RenderTarget并绘制了。

使用姿势不对,其实可以使用RenderTexture.active来设置当前渲染目标

使用CommandBuffer来绘制全屏的Quad

为何需要全屏Quad并Render到RT的功能,因为后面制作SSSM(Screen Space Shadow Map)做准备的。

CommandBuffer 相关的内容,我写过4篇,可以查看我References的。

CSharp

using UnityEngine;
using UnityEngine.Rendering;
// jave.lin 2020.04.13 - 绘制全屏Quad
public class CmdBuffDrawFullScreen : MonoBehaviour
{
    public Camera cam;
    public Material mat;

    [SerializeField] private RenderTexture sssmRT;
    private Mesh mesh;
    private CommandBuffer cmdBuff;
    private void Start()
    {
        cam.depthTextureMode |= DepthTextureMode.Depth;

        mesh = new Mesh();
        mesh.vertices = new Vector3[] 
        {
        	// 这里的第三个z分量最好不要在这里设置,最好在shader中判断是GL还是DX平台来设置为近截面的z值就好
            new Vector3(-1,1,0),    // bl
            new Vector3(-1,-1,0),   // tl
            new Vector3(1,-1,0),    // tr
            new Vector3(1,1,0),     // br
        };
        mesh.uv = new Vector2[]
        {
            new Vector2(0,0),       // bl
            new Vector2(0,1),       // tl
            new Vector2(1,1),       // tr
            new Vector2(1,0),       // br
        };
        mesh.triangles = new int[] 
        {
            0, 1, 2,
            0, 2, 3
        };
    }
    private void OnPreRender()
    {
        if (sssmRT == null || sssmRT.width != Screen.width || sssmRT.height != Screen.height)
        {
            if (sssmRT) RenderTexture.ReleaseTemporary(sssmRT);
            sssmRT = RenderTexture.GetTemporary(Screen.width, Screen.height, 0);//, RenderTextureFormat.R8);
            sssmRT.name = "sssmRT";

            if (cmdBuff == null)
            {
                cmdBuff = new CommandBuffer();
                cmdBuff.name = "After Depth TEX";
            }

            cmdBuff.Clear();

            cmdBuff.SetRenderTarget(sssmRT);
            cmdBuff.ClearRenderTarget(true, true, Color.white);

            cmdBuff.DrawMesh(mesh, Matrix4x4.identity, mat);

            cam.RemoveCommandBuffer(CameraEvent.AfterDepthTexture, cmdBuff);
            cam.AddCommandBuffer(CameraEvent.AfterDepthTexture, cmdBuff);
        }
    }

    private void OnDestroy()
    {
        if (sssmRT) RenderTexture.ReleaseTemporary(sssmRT);

        if (cam != null && cmdBuff != null)
        {
            cam.RemoveCommandBuffer(CameraEvent.AfterDepthTexture, cmdBuff);
            cmdBuff.Release();
        }
    }

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        Graphics.Blit(sssmRT, (RenderTexture)null);
    }
}

其中需要注意的地方

        mesh.vertices = new Vector3[] 
        {
        	// 这里的第三个z分量最好不要在这里设置,最好在shader中判断是GL还是DX平台来设置为近截面的z值就好
            new Vector3(-1,1,0),    // bl
            new Vector3(-1,-1,0),   // tl
            new Vector3(1,-1,0),    // tr
            new Vector3(1,1,0),     // br
        };

留意注意:这里的第三个z分量最好不要在这里设置,最好在shader中判断是GL还是DX平台来设置为近截面的z值就好

Shader

// jave.lin 
Shader "Custom/CmdBuffDrawFullScreenQuadMat" {
    SubShader {
        ZTest Always ZWrite Off Cull Off
        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            struct appdata {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
            struct v2f {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
            };
            sampler2D _CameraDepthTexture;
            v2f vert (appdata v) {
                v2f o;
                
                // jave.lin : 在这里我们处理GL与DX的差异
                // GL的Z:-1~1,DX的Z:0~1
                #if defined (SHADER_TARGET_GLSL)
                v.vertex.z = -1;
                #else
                v.vertex.z = 0;
                #endif
                
                o.vertex = v.vertex;
                o.uv = v.uv;
                return o;
            }
            fixed4 frag (v2f i) : SV_Target {
                float depth = tex2D(_CameraDepthTexture, i.uv).r;
                #if defined (UNITY_REVERSED_Z)
                depth = 1 - depth;
                #endif
                return depth;
            }
            ENDCG
        }
    }
}

shader的vert函数中注意这么一段:

				// CSharp层脚本的第三个z分量最好不要在这里设置
				// 最好在shader中判断是GL还是DX平台来设置为近截面的z值就好
                // jave.lin : 在这里我们处理GL与DX的差异
                // GL的Z:-1~1,DX的Z:0~1
                #if defined (SHADER_TARGET_GLSL)
                v.vertex.z = -1;
                #else
                v.vertex.z = 0;
                #endif

运行效果

在这里插入图片描述

GL渲染到目标

CSharp,主要调用的是:RenderTexture.active = RT;

using UnityEngine;
// jave.lin 2020.04.22 - 绘制一个全屏的Quad
public class DrawFullScreenQuadGL_Correct : MonoBehaviour
{
    private static int _TestFullscreenTex_hash = Shader.PropertyToID("_TestFullscreenTex");
    public enum TransformType
    {
        None,       // 无,就是VS不处理任何变化,a2v的坐标当做clip space的坐标用
        Generic     // 一般化
    }

    public Camera cam;

    public TransformType transType;
    public Material noneTransformMat;
    public Material genericTransformMat;

    public bool drawMergeColor = true;
    public Material depthMergeFullscreenColorMat;

    private RenderTexture rt;

    private void Start()
    {
        cam.depthTextureMode |= DepthTextureMode.DepthNormals;
    }

    private void OnRenderObject()
    {
        if (rt == null || rt.width != Screen.width || rt.height != Screen.height)
        {
            if (rt != null) RenderTexture.ReleaseTemporary(rt);
            rt = RenderTexture.GetTemporary(Screen.width, Screen.height, 0);
        }

        RenderTexture src = RenderTexture.active;
        if (drawMergeColor)
        {
            RenderTexture.active = rt;
        }

        if (transType == TransformType.None) NoneTransformDraw();
        else GenericTransformDraw();

        if (drawMergeColor)
        {
            RenderTexture.active = src;
        }
    }

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (drawMergeColor)
        {
            depthMergeFullscreenColorMat.SetTexture(_TestFullscreenTex_hash, rt);
            Graphics.Blit(source, destination, depthMergeFullscreenColorMat);
        }
        else
        {
            Graphics.Blit(source, destination);
        }
    }

    private void OnDestroy()
    {
        if (rt != null) {
            RenderTexture.ReleaseTemporary(rt);
            rt = null;
        }
    }

    private void NoneTransformDraw()
    {
        noneTransformMat.SetPass(0);
        GL.Begin(GL.TRIANGLE_STRIP);
        // 注意顶点顺序,组合为:顺时针即可,Unity 逆时针为正面
        GL.Color(new Color(1, 0, 0, 1));//red
        GL.Vertex3(-1, -1, 0);
        GL.Color(new Color(0, 1, 0, 1));//green
        GL.Vertex3(1, -1, 0);
        GL.Color(new Color(0, 0, 1, 1));//blue
        GL.Vertex3(-1, 1, 0);
        GL.Color(new Color(1, 1, 0, 1));//yellow
        GL.Vertex3(1, 1, 0);
        GL.End();
    }

    private void GenericTransformDraw()
    {
        genericTransformMat.SetPass(0);
        GL.PushMatrix();
        GL.LoadOrtho();
        GL.Begin(GL.QUADS);
        // 注意顶点顺序,组合为:顺时针即可,Unity 逆时针为正面
        GL.Color(new Color(1, 0, 0, 1));//red
        GL.Vertex3(0, 0, 0); // bl
        GL.Color(new Color(0, 1, 0, 1));//green
        GL.Vertex3(0, 1, 0); // tl
        GL.Color(new Color(0, 0, 1, 1));//blue
        GL.Vertex3(1, 1, 0); // tr
        GL.Color(new Color(1, 1, 0, 1));//yellow
        GL.Vertex3(1, 0, 0); // br
        GL.End();
        GL.PopMatrix();
    }
}

另外优化

后期渲染我们一般的会使用4个顶点,两个三角形来绘制全屏图像。

但后来发现这部分竟然还可以优化,说是因为tile-based渲染相关。

所以可以使用一个三角形来替代一个Quad(两个三角形)的方式来优化。

引用一张图:
在这里插入图片描述

在 Unity Shader URP 中,可以看到:Common.hlsl 中的 GetFullScreenTriangleTexCoordGetFullScreenTriangleVertexPosition

也有类似的优化处理

// Generates a triangle in homogeneous clip space, s.t.
// v0 = (-1, -1, 1), v1 = (3, -1, 1), v2 = (-1, 3, 1).
float2 GetFullScreenTriangleTexCoord(uint vertexID)
{
#if UNITY_UV_STARTS_AT_TOP
    return float2((vertexID << 1) & 2, 1.0 - (vertexID & 2));
#else
    return float2((vertexID << 1) & 2, vertexID & 2);
#endif
}

float4 GetFullScreenTriangleVertexPosition(uint vertexID, float z = UNITY_NEAR_CLIP_VALUE)
{
    float2 uv = float2((vertexID << 1) & 2, vertexID & 2);
    return float4(uv * 2.0 - 1.0, z, 1.0);
}

具体查看:用三角形代替四边形-后处理优化

Project

backup : UnityShader_RenderImageLikeTesting_2018.3.0f2

References

GL

CommandBuffer相关的之前几篇:

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值