学习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的学习。