URP学习之一--SRP

学习URP之前首先要知道SRP是什么。

SRP(Scriptable Render Pipeline)Unity向开发者提供的用来组织渲染数据和自定义提交渲染方案的接口,用户可以灵活的根据需求选择自己的渲染信息组织和提交方案。

URP就是Unity官方在SRP的基础上实现好的一套方案。

我们先来创建一个属于自己的渲染管线看看SRP究竟在起的什么作用:

创建两个脚本:

脚本1

 1 using UnityEngine;
 2 
 3 using UnityEngine.Rendering;
 4 
 5  
 6 
 7 [CreateAssetMenu]
 8 
 9 public class SRenderPipelineAsset : RenderPipelineAsset
10 
11 {
12 
13     protected override RenderPipeline CreatePipeline()
14 
15     {
16 
17         return new SRenderPipeline();
18 
19     }
20 
21 }

脚本2

using UnityEngine;

using UnityEngine.Rendering;

 

public class SRenderPipeline : RenderPipeline

{

    protected override void Render(ScriptableRenderContext context, Camera[] cameras)

    {

        

    }

}

Asset Menu中创建自己的PipelineAsset文件:

 

然后将创建出来的Asset放入Graphics Setting

 

接下来你会看到无论是场景视图还是Game视图都是一片白色,此时就需要我们为渲染管线填充内容,我们主要在RenderPipelineRender函数提交渲染,Render函数的第一个参数context可以看作是做渲染提交的上下文,里面包含了很多接口;第二个参数是cameras,把所有的相机(包括场景相机和preview相机)都作为参数传入进来。所以先试一下最简单的:

protected override void Render(ScriptableRenderContext context, Camera[] cameras)
{
        for (int i = 0; i < cameras.Length; i++)
        {
            context.DrawSkybox(cameras[i]);
        }
        context.Submit();
}

Context中有很多draw方法,我们要确定在哪个相机上draw,所以需要逐相机调用,将相机传入方法;context最后必须调用submit方法提交渲染,因为上面调用draw并不是真正提交到GPU了,而是填充了context的内容,只有将context提交上去才会将渲染数据送往GPU。

细心的小伙伴会发现虽然天空盒画出来了,但是转动相机为什么没有任何反映呢?

原来粗心的我在画天空盒之前少了一句这样的代码:

context.SetupCameraProperties(camera);

从代码我们可以看出渲染一切物体之前首先必须设置相机数据到context,否则context无法根据相机矩阵信息算出应该看到的区域。

那么天空盒画完了,该画场景物体了,代码如下:

protected override void Render(ScriptableRenderContext context, Camera[] cameras)
    {
        for (int i = 0; i < cameras.Length; i++)
        {
            Camera camera = cameras[i];
            context.SetupCameraProperties(camera);
            context.DrawSkybox(camera);
            
            //相机裁剪
            camera.TryGetCullingParameters(out var parameters);
            CullingResults results = context.Cull(ref parameters);

            DrawingSettings ds = new DrawingSettings();
            FilteringSettings fs = new FilteringSettings();
            
            context.DrawRenderers(results, ref ds, ref fs);
        }

        context.Submit();
    }

首先相机要进行视锥裁剪,决定哪些物体需要被渲染,裁剪参数从相机中获取,裁剪结果存放在Cull方法的返回值中,然而并不能拿到里面每一个物体的数据,最后调用方法DrawRenderers。然而笔者我总感觉少了什么,回到Unity发现果然不对。如下图:

 

这个cube一下子让我想到了问题所在:材质!

于是经过不知多长时间的鼓捣,加上查百度(重点),才写出以下代码:

protected override void Render(ScriptableRenderContext context, Camera[] cameras)
    {
        for (int i = 0; i < cameras.Length; i++)
        {
            Camera camera = cameras[i];
            context.SetupCameraProperties(camera);
            context.DrawSkybox(camera);
            
            //相机裁剪
            camera.TryGetCullingParameters(out var parameters);
            CullingResults results = context.Cull(ref parameters);

            DrawingSettings ds = new DrawingSettings();
            //指定使用设定的LightMode的Pass
            ds.SetShaderPassName(0, new ShaderTagId("SForward"));
            //排序设置
            ds.sortingSettings = new SortingSettings(camera){criteria = SortingCriteria.CommonOpaque};
            //过滤设置
            FilteringSettings fs = new FilteringSettings(RenderQueueRange.opaque,-1);

            context.DrawRenderers(results, ref ds, ref fs);
        }

        context.Submit();
    }

这里需要注意的是DrawSettings里面的SetShaderPassName查找的不不不(重要的事情说三遍)是Shader的Pass中那个Name “xxx” 不是那个!!!找的是Tags中的LightMode!虽然不知道为什么,但是记住就对了。

还有就是DrawSettings中的sortingSettings,没错,就是这个排序规则,必须设置criteria字段,笔者我一直以为其他地方出问题了,结果查了半天就是因为这个,就是它,大家记住这个可恶的字段!

Shader代码如下:

Shader "Custom/test"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque"}
        LOD 100

        Pass
        {
            Tags{ "LightMode" = "SForward"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }
    }
}

非常常规而又简单的Unlit。

给个贴图效果如下:

那如果我想要一个光照该怎么办呢?

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'

Shader "Custom/diffuse"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque"}
        LOD 100

        Pass
        {
            Tags{ "LightMode" = "SForward"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal :NORMAL;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float3 worldNormal:TEXCOORD1;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                float3 worldNormal = normalize(i.worldNormal);
                float3 lightDir = _WorldSpaceLightPos0.xyz;
                col = col * saturate(dot(worldNormal, lightDir));
                // apply fog
                return col;
            }
            ENDCG
        }
    }
}

按照正常的diffuse写完全没有任何问题,Specular也是一样。

diffuse效果如下:

现在,我们基本上可以通过SRP正常的显示一些简单的效果,通过这个简单的例子来引出我们对URP的学习。

原文地址:https://www.cnblogs.com/shenyibo/p/12485235.html