學習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視圖都是一片白色,此時就需要我們為渲染管線填充內容,我們主要在RenderPipeline的Render函數提交渲染,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的學習。