URP 渲染实战 / 第 3 章 第一个 URP Shader / 案例 3.9
案例 3.9迁移实战:把 Built-in 的滚动贴图 Shader 搬到 URP进阶
目标:拿一个典型的 Built-in 管线 Unlit Shader(滚动贴图 + 顶点色 + 雾),逐行改成 URP 写法。很多项目升级 URP 时,最多的工作量就是这类“小 Shader”。下面是 Built-in 原版(它在 URP 工程里不能正确工作,仅作对照):
Built-in 原版(对照用)
Shader "Legacy/ScrollUnlit" { Properties { _MainTex ("Texture", 2D) = "white" {} _Color ("Tint", Color) = (1,1,1,1) _ScrollSpeed ("Scroll Speed (xy)", Vector) = (0.2, 0, 0, 0) } SubShader { Tags { "RenderType"="Opaque" } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fog #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; fixed4 color : COLOR; }; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; fixed4 color : COLOR; UNITY_FOG_COORDS(1) }; sampler2D _MainTex; float4 _MainTex_ST; fixed4 _Color; float4 _ScrollSpeed; v2f vert (appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex) + _ScrollSpeed.xy * _Time.y; o.color = v.color; UNITY_TRANSFER_FOG(o, o.pos); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv) * _Color * i.color; UNITY_APPLY_FOG(i.fogCoord, col); return col; } ENDCG } } Fallback "Unlit/Texture" }
URP 版本的完整文件是 Ch03/C03_09_ScrollMigrated.shader,颜色 Pass 如下,每一处改动都在注释里标出了对应的 Built-in 写法:
Assets/Cookbook/Ch03/C03_09_ScrollMigrated.shader(节选:pass)
Pass { Name "Unlit" // Unlit 用 UniversalForwardOnly:切到 Deferred 渲染路径也不会消失(见正文 3.4 节) Tags { "LightMode"="UniversalForwardOnly" } HLSLPROGRAM // CGPROGRAM → HLSLPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fog struct Attributes // appdata → Attributes { float4 positionOS : POSITION; // vertex → positionOS float2 uv : TEXCOORD0; half4 color : COLOR; // fixed4 → half4(URP 不再有 fixed) }; struct Varyings // v2f → Varyings { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; half4 color : COLOR; float fogFactor : TEXCOORD1; // UNITY_FOG_COORDS(1) → 自己声明 }; Varyings vert (Attributes input) { Varyings output; // UnityObjectToClipPos → TransformObjectToHClip output.positionCS = TransformObjectToHClip(input.positionOS.xyz); // _Time.y 的含义与 Built-in 相同:游戏开始后的秒数 float2 scroll = _ScrollSpeed.xy * _Time.y + _ScrollOffset.xy; output.uv = TRANSFORM_TEX(input.uv, _BaseMap) + scroll; output.color = input.color; // UNITY_TRANSFER_FOG(o, o.pos) → ComputeFogFactor output.fogFactor = ComputeFogFactor(output.positionCS.z); return output; } half4 frag (Varyings input) : SV_Target { // tex2D(_MainTex, uv) → SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, uv) half4 col = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv) * _BaseColor * input.color; // UNITY_APPLY_FOG(i.fogCoord, col) → MixFog col.rgb = MixFog(col.rgb, input.fogFactor); return half4(col.rgb, 1); } ENDHLSL }
Pass { Name "Unlit" // Unlit 用 UniversalForwardOnly:切到 Deferred 渲染路径也不会消失(见正文 3.4 节) Tags { "LightMode"="UniversalForwardOnly" } GLSLPROGRAM // CGPROGRAM → HLSLPROGRAM(网页版:GLSLPROGRAM) #pragma multi_compile_fog // appdata → Attributes(网页版:固定名字的顶点输入 positionOS / texcoord0 / color) // v2f → Varyings(网页版:逐个 VARYING) VARYING vec2 uv; VARYING vec4 vColor; // fixed4 → half4(URP 不再有 fixed) VARYING float fogFactor; // UNITY_FOG_COORDS(1) → 自己声明 #ifdef VERTEX void main() { // UnityObjectToClipPos → TransformObjectToHClip gl_Position = TransformObjectToHClip(positionOS.xyz); // _Time.y 的含义与 Built-in 相同:游戏开始后的秒数 vec2 scroll = _ScrollSpeed.xy * _Time.y + _ScrollOffset.xy; uv = TransformTex(texcoord0.xy, _BaseMap_ST) + scroll; vColor = color; // UNITY_TRANSFER_FOG(o, o.pos) → ComputeFogFactor fogFactor = ComputeFogFactor(gl_Position.z); } #endif #ifdef FRAGMENT void main() { // tex2D(_MainTex, uv) → SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, uv)(网页版:texture) vec4 col = texture(_BaseMap, uv) * _BaseColor * vColor; // UNITY_APPLY_FOG(i.fogCoord, col) → MixFog col.rgb = MixFog(col.rgb, fogFactor); fragColor = vec4(col.rgb, 1); } #endif ENDGLSL }
除了逐行替换,还有四处“结构性”改动:
- SubShader 加上
RenderPipeline标签,Pass 加上 LightMode(Unlit 用UniversalForwardOnly,见 3.4 节)。 - 属性改名为
_BaseMap/_BaseColor并加[MainTexture]/[MainColor]。这不是语法要求,但遵循 URP 命名后,材质在 URP 自带 Shader 与你的 Shader 之间切换时贴图和颜色能自动保留,URP 的材质升级工具和许多插件也按这个名字找属性。如果项目里有大量旧材质,也可以保留_MainTex这个名字,换取零迁移成本——两种选择都可以,团队统一即可。 - 所有属性进
UnityPerMaterial,放在 HLSLINCLUDE 里供三个 Pass 共享。 - 删掉 Fallback,手写 ShadowCaster 和 DepthOnly。Built-in 版本靠 Fallback 借用别的 Shader 的阴影 Pass;URP 下应该自己提供。
另外,为了能在书里拍静帧,我们加了一个 _ScrollOffset 参数:_Time 在编辑器截图时接近 0,用它可以手动指定“滚到哪了”。实际使用时 _ScrollSpeed 驱动自动滚动,_ScrollOffset 也可以交给动画曲线或脚本控制,做出“按事件推进”的传送带。

_ScrollOffset.x 分别为 0、0.25、0.5,贴图沿 U 方向绕圆柱滚动