第4章 URP着色器基础
理论讲解
URP Shader结构与Built-in区别
Unity的Universal Render Pipeline (URP)着色器与传统的Built-in Render Pipeline着色器在结构和功能上存在显著差异。URP着色器是为Scriptable Render Pipeline (SRP)框架专门设计的,具有更好的性能和更灵活的扩展性。
Built-in Render Pipeline着色器特点:
- 固定渲染流程:渲染管线是硬编码在Unity引擎中的,开发者无法修改渲染流程
- 复杂的内置函数:使用大量的内置函数如
Lighting.cginc中的函数
- 不支持SRP Batcher:无法利用URP的批处理优化
- 渲染状态管理复杂:渲染状态由引擎自动管理,难以精确控制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| // Built-in Render Pipeline着色器示例 Shader "Custom/BuiltInExample" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo", 2D) = "white" {} _Glossiness ("Smoothness", Range(0,1)) = 0.5 _Metallic ("Metallic", Range(0,1)) = 0.0 } SubShader { Tags { "RenderType"="Opaque" } LOD 200
CGPROGRAM #pragma surface surf Standard fullforwardshadows #pragma target 3.0
sampler2D _MainTex; fixed4 _Color; half _Glossiness; half _Metallic;
struct Input { float2 uv_MainTex; };
void surf (Input IN, inout SurfaceOutputStandard o) { fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c.a; } ENDCG } }
|
URP Render Pipeline着色器特点:
- 可编程渲染管线:渲染流程可以通过C#脚本完全自定义
- 模块化设计:使用HLSL工具库,代码更清晰
- 支持SRP Batcher:能够利用批处理优化提升性能
- 渲染状态精确控制:可以精确控制渲染状态和流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
| // URP Render Pipeline着色器示例 Shader "URP/Custom/URPExample" { Properties { _BaseMap("Texture", 2D) = "white" {} _BaseColor("Color", Color) = (1, 1, 1, 1) _Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5 _Smoothness("Smoothness", Range(0.0, 1.0)) = 0.5 _Metallic("Metallic", Range(0.0, 1.0)) = 0.0 [Toggle(_SPECULAR_SETUP)] _SpecularSetup("Specular Setup", Float) = 0 }
SubShader { Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" "UniversalMaterialType" = "Lit" "IgnoreProjector" = "True" } Pass { Name "ForwardLit" Tags { "LightMode" = "UniversalForward" } Blend[_SrcBlend][_DstBlend] ZWrite[_ZWrite] Cull[_Cull] HLSLPROGRAM #pragma exclude_renderers gles gles3 glcore #pragma target 4.5 #pragma vertex LitPassVertex #pragma fragment LitPassFragment #pragma multi_compile _ _MAIN_LIGHT_SHADOWS #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE #pragma multi_compile _ _SHADOWS_SOFT #pragma multi_compile _ _MIXED_LIGHTING_SUBTRACTIVE #pragma multi_compile _ DIRLIGHTMAP_COMBINED #pragma multi_compile _ LIGHTMAP_ON #pragma multi_compile _ DYNAMICLIGHTMAP_ON #pragma multi_compile_fragment _ _SHADOWS_SOFT #pragma multi_compile_fragment _ _SCREEN_SPACE_OCCLUSION #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityMath.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/SurfaceInput.hlsl" CBUFFER_START(UnityPerMaterial) float4 _BaseMap_ST; half4 _BaseColor; half _Cutoff; half _Smoothness; half _Metallic; CBUFFER_END struct Attributes { float4 positionOS : POSITION; float3 normalOS : NORMAL; float4 tangentOS : TANGENT; float2 uv : TEXCOORD0; float2 lightmapUV : TEXCOORD1; float4 shadowCoord : TEXCOORD2; }; struct Varyings { float4 positionCS : SV_POSITION; float3 positionWS : TEXCOORD0; float3 normalWS : TEXCOORD1; float4 tangentWS : TEXCOORD2; float2 uv : TEXCOORD3; float3 viewDirWS : TEXCOORD4; float4 shadowCoord : TEXCOORD5; float4 fogFactorAndVertexLight : TEXCOORD6; }; TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap); Varyings LitPassVertex(Attributes input) { Varyings output = (Varyings)0; VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz); VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS, input.tangentOS); half3 viewDirWS = GetWorldSpaceNormalizeViewDir(vertexInput.positionWS); output.positionCS = vertexInput.positionCS; output.positionWS = vertexInput.positionWS; output.normalWS = normalInput.normalWS; output.tangentWS = float4(normalInput.tangentWS, normalInput.sign); output.uv = TRANSFORM_TEX(input.uv, _BaseMap); output.viewDirWS = viewDirWS; output.shadowCoord = TransformWorldToShadowCoord(output.positionWS); output.fogFactorAndVertexLight = half4(ComputeFogFactor(output.positionCS.z), 0, 0, 0); return output; } half4 LitPassFragment(Varyings input) : SV_Target { SurfaceData surfaceData; InitializeStandardLitSurfaceData(input.uv, surfaceData); surfaceData.albedo = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv).rgb * _BaseColor.rgb; surfaceData.alpha = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv).a * _BaseColor.a; surfaceData.metallic = _Metallic; surfaceData.smoothness = _Smoothness; surfaceData.normalTS = float3(0.0h, 0.0h, 1.0h); surfaceData.occlusion = 1.0h; surfaceData.emission = half3(0.0, 0.0, 0.0); InputData inputData; InitializeInputData(input, surfaceData.normalTS, inputData); half4 color = UniversalFragmentPBR( inputData, surfaceData.albedo, surfaceData.metallic, surfaceData.specular, surfaceData.smoothness, surfaceData.normalTS, surfaceData.emission, surfaceData.alpha ); color.rgb = MixFog(color.rgb, inputData.fogCoord); return color; } ENDHLSL } } Fallback "Universal Render Pipeline/Lit" }
|
Lit、SimpleLit、Unlit Shader详解
URP提供了三种主要的着色器类型,每种都有其特定的用途和性能特征:
1. Lit Shader
Lit Shader是URP中最完整的着色器,支持完整的PBR(Physically Based Rendering)光照模型,包括:
- 主光源和附加光源
- 阴影支持
- 环境光遮蔽
- 法线贴图
- 金属度/光滑度工作流
- 光照贴图支持
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
| // Lit Shader的关键特性 Shader "Universal Render Pipeline/Lit" { Properties { // 主要材质属性 [MainTexture] _BaseMap("Albedo", 2D) = "white" {} [MainColor] _BaseColor("Color", Color) = (1,1,1,1) _Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5 [HDR] _SpecColor("Specular", Color) = (0.2, 0.2, 0.2) _Smoothness("Smoothness", Range(0.0, 1.0)) = 0.5 _BumpScale("Normal Scale", Range(0.0, 2.0)) = 1.0 [Normal] _BumpMap("Normal Map", 2D) = "bump" {} _EmissionColor("Emission", Color) = (0,0,0) [HDR] _EmissionMap("Emission", 2D) = "white" {} _OcclusionStrength("Occlusion", Range(0.0, 1.0)) = 1.0 _OcclusionMap("Occlusion", 2D) = "white" {} _DetailMask("Detail Mask", 2D) = "white" {} _DetailAlbedoMap("Detail Albedo x2", 2D) = "linearGrey" {} _DetailNormalMapScale("Scale", Range(0.0, 2.0)) = 1.0 [Normal] _DetailNormalMap("Normal Map", 2D) = "bump" {} [Enum(UV0, 0, UV1, 1)] _UVSec("UV Set for secondary textures", Float) = 0 [HideInInspector] _AlphaClip("Alpha Clipping", Float) = 0 [HideInInspector] _Blend("Blending State", Float) = 0 [HideInInspector] _SrcBlend("SrcBlend", Float) = 1.0 [HideInInspector] _DstBlend("DstBlend", Float) = 0.0 [HideInInspector] _ZWrite("ZWrite", Float) = 1.0 [HideInInspector] _Cull("Cull Mode", Float) = 2.0 [HideInInspector] _CastShadows("Cast Shadows", Float) = 1.0 } SubShader { // 渲染标签,指定使用URP Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" "UniversalMaterialType" = "Lit" } // 多个Pass处理不同的渲染情况 Pass { Name "ForwardLit" Tags { "LightMode" = "UniversalForward" } // 渲染状态设置 Blend[_SrcBlend][_DstBlend] ZWrite[_ZWrite] Cull[_Cull] HLSLPROGRAM // 编译指令 #pragma multi_compile _ _MAIN_LIGHT_SHADOWS #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS #pragma multi_compile_fragment _ _SHADOWS_SOFT #pragma multi_compile_fragment _ _SCREEN_SPACE_OCCLUSION // 包含必要的头文件 #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/LitInput.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/LitForwardPass.hlsl" ENDHLSL } // 阴影投射Pass Pass { Name "ShadowCaster" Tags { "LightMode" = "ShadowCaster" } ZWrite On ZTest LEqual ColorMask 0 HLSLPROGRAM #pragma multi_compile _ _CASTING_PUNCTUAL_LIGHT_SHADOW #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/LitInput.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShadowCasterPass.hlsl" ENDHLSL } // 深度预通道Pass Pass { Name "DepthOnly" Tags { "LightMode" = "DepthOnly" } ZWrite On ColorMask 0 HLSLPROGRAM #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/LitInput.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DepthOnlyPass.hlsl" ENDHLSL } } }
|
2. SimpleLit Shader
SimpleLit Shader是简化版的光照着色器,相比Lit Shader移除了一些高级特性以提高性能:
- 简化的光照计算
- 不支持光照贴图
- 不支持法线贴图的高级特性
- 更少的变体组合
- 适合移动设备和性能敏感场景
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| // SimpleLit Shader示例 Shader "Universal Render Pipeline/SimpleLit" { Properties { [MainTexture] _BaseMap("Albedo", 2D) = "white" {} [MainColor] _BaseColor("Color", Color) = (1,1,1,1) _Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5 _SpecColor("Specular", Color) = (0.5, 0.5, 0.5) _Smoothness("Smoothness", Range(0.0, 1.0)) = 0.5 _SpecularHighlights("Specular Highlights", Float) = 1.0 [ToggleOff] _EnvironmentReflections("Environment Reflections", Float) = 1.0 _BumpScale("Normal Scale", Range(0.0, 2.0)) = 1.0 [Normal] _BumpMap("Normal Map", 2D) = "bump" {} _EmissionColor("Emission", Color) = (0,0,0) [HDR] _EmissionMap("Emission", 2D) = "white" {} } SubShader { Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" "UniversalMaterialType" = "SimpleLit" } Pass { Name "ForwardLit" Tags { "LightMode" = "UniversalForward" } Blend[_SrcBlend][_DstBlend] ZWrite[_ZWrite] Cull[_Cull] HLSLPROGRAM // 简化的多编译指令 #pragma multi_compile _ _MAIN_LIGHT_SHADOWS #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS #pragma multi_compile_fragment _ _SHADOWS_SOFT #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/SimpleLitInput.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/SimpleLitForwardPass.hlsl" ENDHLSL } } }
|
3. Unlit Shader
Unlit Shader是最简单的着色器类型,不进行任何光照计算:
- 无光照计算
- 最高性能
- 适合UI元素、特效、不需要光照的物体
- 支持透明度和颜色调整
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| // Unlit Shader示例 Shader "Universal Render Pipeline/Unlit" { Properties { [MainTexture] _BaseMap("Texture", 2D) = "white" {} [MainColor] _BaseColor("Color", Color) = (1,1,1,1) _Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5 [Toggle(_ALPHATEST_ON)] _AlphaTest("Alpha Test", Float) = 0.0 [Toggle(_ALPHAPREMULTIPLY_ON)] _SrcBlend("SrcBlend", Float) = 1.0 [Toggle(_ALPHAMODULATE_ON)] _DstBlend("DstBlend", Float) = 0.0 [Toggle(_ZWRITE_ON)] _ZWrite("ZWrite", Float) = 1.0 } SubShader { Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" } Pass { Name "Unlit" Tags { "LightMode" = "UniversalForward" } Blend[_SrcBlend][_DstBlend] ZWrite[_ZWrite] Cull[_Cull] HLSLPROGRAM #pragma multi_compile _ _ALPHATEST_ON #pragma multi_compile _ _ALPHAPREMULTIPLY_ON #pragma multi_compile _ _ALPHAMODULATE_ON #pragma multi_compile _ _ZWRITE_ON #pragma vertex UnlitVertex #pragma fragment UnlitFragment #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" CBUFFER_START(UnityPerMaterial) float4 _BaseMap_ST; half4 _BaseColor; half _Cutoff; CBUFFER_END TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap); struct Attributes { float4 positionOS : POSITION; float2 uv : TEXCOORD0; }; struct Varyings { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; }; Varyings UnlitVertex(Attributes input) { Varyings output; output.positionCS = TransformObjectToHClip(input.positionOS.xyz); output.uv = TRANSFORM_TEX(input.uv, _BaseMap); return output; } half4 UnlitFragment(Varyings input) : SV_Target { half4 texColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv); half4 color = texColor * _BaseColor; #ifdef _ALPHATEST_ON clip(color.a - _Cutoff); #endif return color; } ENDHLSL } } }
|
ShaderGraph在URP中的应用
ShaderGraph是Unity提供的可视化着色器编辑工具,特别适合URP使用。它允许开发者通过节点图的方式创建着色器,无需编写代码。
ShaderGraph在URP中的优势:
- 可视化编辑:通过拖拽节点创建着色器逻辑
- 实时预览:即时查看着色器效果
- 易于修改:快速调整参数和效果
- 性能优化:自动生成优化的着色器代码
创建URP ShaderGraph的步骤:
- 创建Shader Graph:右键Assets → Create → Shader → Universal Render Pipeline → Lit Shader Graph
- 配置节点:添加和连接各种节点
- 设置材质属性:通过Property节点暴露参数
- 连接输出:将结果连接到Master节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| using UnityEngine; using UnityEngine.Rendering.Universal;
public class ShaderGraphExample : MonoBehaviour { [Header("Material References")] public Material litShaderGraphMaterial; public Material unlitShaderGraphMaterial; [Header("Animation Parameters")] public float pulseSpeed = 2.0f; public float pulseAmount = 0.5f; private float baseEmission; private MaterialPropertyBlock propertyBlock; void Start() { propertyBlock = new MaterialPropertyBlock(); if(litShaderGraphMaterial != null) { baseEmission = litShaderGraphMaterial.GetFloat("_Emission"); } } void Update() { AnimateMaterials(); } void AnimateMaterials() { if(litShaderGraphMaterial != null) { float emission = baseEmission + Mathf.Sin(Time.time * pulseSpeed) * pulseAmount; litShaderGraphMaterial.SetFloat("_Emission", emission); propertyBlock.SetFloat("_Emission", emission); propertyBlock.SetColor("_Tint", Color.red); GetComponent<Renderer>().SetPropertyBlock(propertyBlock); } } public void SwitchToUnlit() { if(GetComponent<Renderer>() != null && unlitShaderGraphMaterial != null) { GetComponent<Renderer>().material = unlitShaderGraphMaterial; } } public void SwitchToLit() { if(GetComponent<Renderer>() != null && litShaderGraphMaterial != null) { GetComponent<Renderer>().material = litShaderGraphMaterial; } } }
|
常用URP Shader属性与关键字
URP着色器使用特定的属性名称和关键字来确保与渲染管线的兼容性:
常用属性名称:
_BaseMap / _MainTex:基础贴图
_BaseColor / _Color:基础颜色
_BumpMap:法线贴图
_Metallic:金属度
_Smoothness:光滑度
_EmissionMap:发射贴图
_OcclusionMap:遮蔽贴图
常用编译关键字:
_MAIN_LIGHT_SHADOWS:主光源阴影
_MAIN_LIGHT_SHADOWS_CASCADE:级联阴影
_ADDITIONAL_LIGHTS:附加光源
_SHADOWS_SOFT:软阴影
_SCREEN_SPACE_OCCLUSION:屏幕空间遮蔽
URP材质工作流程
URP的材质工作流程与传统管线有所不同,主要体现在:
- 材质创建:使用URP特定的着色器
- 参数映射:URP使用标准化的参数名称
- 渲染队列:通过渲染标签控制渲染顺序
- 材质变体:根据启用的功能生成不同的着色器变体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
| using UnityEngine; using UnityEngine.Rendering.Universal;
public class URPMaterialWorkflow : MonoBehaviour { [Header("Material Configuration")] public Shader urpLitShader; public Shader urpUnlitShader; [Header("Material Properties")] public Color baseColor = Color.white; public Texture2D baseTexture; public float metallic = 0.0f; public float smoothness = 0.5f; public Texture2D normalMap; private Material dynamicMaterial; void Start() { CreateDynamicMaterial(); } void CreateDynamicMaterial() { if(urpLitShader != null) { dynamicMaterial = new Material(urpLitShader); dynamicMaterial.SetColor("_BaseColor", baseColor); dynamicMaterial.SetTexture("_BaseMap", baseTexture); dynamicMaterial.SetFloat("_Metallic", metallic); dynamicMaterial.SetFloat("_Smoothness", smoothness); if(normalMap != null) { dynamicMaterial.SetTexture("_BumpMap", normalMap); dynamicMaterial.EnableKeyword("_NORMALMAP"); } var renderer = GetComponent<Renderer>(); if(renderer != null) { renderer.material = dynamicMaterial; } } } public void UpdateMaterialProperties() { if(dynamicMaterial != null) { dynamicMaterial.SetColor("_BaseColor", baseColor); dynamicMaterial.SetFloat("_Metallic", metallic); dynamicMaterial.SetFloat("_Smoothness", smoothness); if(metallic > 0.0f) { dynamicMaterial.EnableKeyword("_METALLICSPECGLOSSMAP"); } else { dynamicMaterial.DisableKeyword("_METALLICSPECGLOSSMAP"); } } } public void SetRenderQueue(int queue) { if(dynamicMaterial != null) { dynamicMaterial.renderQueue = queue; } } public void SetTransparent(bool transparent) { if(dynamicMaterial != null) { if(transparent) { dynamicMaterial.SetFloat("_Surface", 1); dynamicMaterial.SetOverrideTag("RenderType", "Transparent"); dynamicMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha); dynamicMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); dynamicMaterial.SetInt("_ZWrite", 0); dynamicMaterial.DisableKeyword("_ALPHATEST_ON"); dynamicMaterial.EnableKeyword("_ALPHAPREMULTIPLY_ON"); } else { dynamicMaterial.SetFloat("_Surface", 0); dynamicMaterial.SetOverrideTag("RenderType", "Opaque"); dynamicMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One); dynamicMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero); dynamicMaterial.SetInt("_ZWrite", 1); dynamicMaterial.DisableKeyword("_ALPHAPREMULTIPLY_ON"); } } } }
|
代码示例
自定义URP着色器实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
| // 自定义URP Toon着色器 Shader "URP/Custom/ToonShader" { Properties { _BaseMap ("Texture", 2D) = "white" {} _BaseColor ("Color", Color) = (1, 1, 1, 1) _Ramp ("Ramp Texture", 2D) = "white" {} _RampThreshold ("Ramp Threshold", Range(0, 1)) = 0.5 _RampSmooth ("Ramp Smooth", Range(0, 1)) = 0.1 _OutlineWidth ("Outline Width", Range(0, 1)) = 0.1 _OutlineColor ("Outline Color", Color) = (0, 0, 0, 1) _SpecularThreshold ("Specular Threshold", Range(0, 1)) = 0.8 }
SubShader { Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" } // 轮廓Pass Pass { Name "Outline" Tags { "LightMode" = "UniversalForward" } Cull Front ZWrite On HLSLPROGRAM #pragma vertex OutlineVertex #pragma fragment OutlineFragment #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" struct Attributes { float4 positionOS : POSITION; float3 normalOS : NORMAL; }; struct Varyings { float4 positionCS : SV_POSITION; }; float _OutlineWidth; float4 _OutlineColor; Varyings OutlineVertex(Attributes input) { Varyings output; float3 normalWS = TransformObjectToWorldNormal(input.normalOS); float3 viewDirWS = GetWorldSpaceNormalizeViewDir(input.positionOS.xyz); float3 outlineOffset = normalWS * _OutlineWidth; float4 posWS = TransformObjectToWorld(input.positionOS.xyz); posWS.xyz += outlineOffset; output.positionCS = TransformWorldToHClip(posWS.xyz); return output; } half4 OutlineFragment(Varyings input) : SV_Target { return _OutlineColor; } ENDHLSL } // 主渲染Pass Pass { Name "ForwardLit" Tags { "LightMode" = "UniversalForward" } Cull Back ZWrite On HLSLPROGRAM #pragma vertex LitPassVertex #pragma fragment LitPassFragment #pragma multi_compile _ _MAIN_LIGHT_SHADOWS #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityMath.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap); TEXTURE2D(_Ramp); SAMPLER(sampler_Ramp); CBUFFER_START(UnityPerMaterial) float4 _BaseMap_ST; half4 _BaseColor; float _RampThreshold; float _RampSmooth; float _SpecularThreshold; CBUFFER_END struct Attributes { float4 positionOS : POSITION; float3 normalOS : NORMAL; float2 uv : TEXCOORD0; }; struct Varyings { float4 positionCS : SV_POSITION; float3 positionWS : TEXCOORD0; float3 normalWS : TEXCOORD1; float2 uv : TEXCOORD2; }; Varyings LitPassVertex(Attributes input) { Varyings output; VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz); VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS, float4(0, 0, 0, 0)); output.positionCS = vertexInput.positionCS; output.positionWS = vertexInput.positionWS; output.normalWS = normalInput.normalWS; output.uv = TRANSFORM_TEX(input.uv, _BaseMap); return output; } half4 LitPassFragment(Varyings input) : SV_Target { // 采样基础贴图 half4 baseColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv) * _BaseColor; // 计算光照 float3 normalWS = NormalizeNormalPerPixel(input.normalWS); float3 viewDirWS = GetWorldSpaceNormalizeViewDir(input.positionWS); // 主光源 Light mainLight = GetMainLight(); float3 lightDir = mainLight.direction; // 计算漫反射 float NdotL = dot(normalWS, lightDir); NdotL = saturate(NdotL); // 使用渐变贴图创建卡通效果 float ramp = smoothstep(_RampThreshold - _RampSmooth, _RampThreshold + _RampSmooth, NdotL); ramp = SAMPLE_TEXTURE2D(_Ramp, sampler_Ramp, float2(ramp, 0)).r; // 计算镜面反射 float3 reflectDir = reflect(-lightDir, normalWS); float specular = pow(max(dot(viewDirWS, reflectDir), 0.0), 32.0); specular = step(_SpecularThreshold, specular); // 组合最终颜色 half3 finalColor = baseColor.rgb * mainLight.color.rgb * ramp; finalColor += specular * mainLight.color.rgb; return half4(finalColor, baseColor.a); } ENDHLSL } } Fallback "Universal Render Pipeline/Lit" }
|
材质切换和管理脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
| using UnityEngine; using UnityEngine.Rendering.Universal; using System.Collections.Generic;
public class URPShaderManager : MonoBehaviour { [System.Serializable] public class MaterialPreset { public string presetName; public Material material; public Shader shader; public Color baseColor = Color.white; public float metallic = 0.0f; public float smoothness = 0.5f; public Texture2D texture; } [Header("Material Presets")] public List<MaterialPreset> materialPresets = new List<MaterialPreset>(); [Header("Current Material")] public int currentPresetIndex = 0; private Renderer targetRenderer; private Material currentMaterial; private Dictionary<string, Material> materialCache = new Dictionary<string, Material>(); void Start() { targetRenderer = GetComponent<Renderer>(); if(targetRenderer != null) { ApplyMaterialPreset(currentPresetIndex); } } public void ApplyMaterialPreset(int index) { if(index < 0 || index >= materialPresets.Count) { Debug.LogError($"Invalid preset index: {index}"); return; } var preset = materialPresets[index]; currentPresetIndex = index; if(targetRenderer != null) { if(materialCache.ContainsKey(preset.presetName)) { currentMaterial = materialCache[preset.presetName]; } else { if(preset.material != null) { currentMaterial = new Material(preset.material); } else if(preset.shader != null) { currentMaterial = new Material(preset.shader); } else { Debug.LogError("No material or shader specified for preset: " + preset.presetName); return; } ApplyMaterialProperties(currentMaterial, preset); materialCache[preset.presetName] = currentMaterial; } targetRenderer.material = currentMaterial; Debug.Log($"Applied material preset: {preset.presetName}"); } } void ApplyMaterialProperties(Material material, MaterialPreset preset) { if(material == null) return; if(material.HasProperty("_BaseColor")) { material.SetColor("_BaseColor", preset.baseColor); } if(material.HasProperty("_Color")) { material.SetColor("_Color", preset.baseColor); } if(material.HasProperty("_Metallic")) { material.SetFloat("_Metallic", preset.metallic); } if(material.HasProperty("_Smoothness")) { material.SetFloat("_Smoothness", preset.smoothness); } if(material.HasProperty("_BaseMap") && preset.texture != null) { material.SetTexture("_BaseMap", preset.texture); } if(material.HasProperty("_MainTex") && preset.texture != null) { material.SetTexture("_MainTex", preset.texture); } if(preset.metallic > 0.0f) { material.EnableKeyword("_METALLICSPECGLOSSMAP"); } else { material.DisableKeyword("_METALLICSPECGLOSSMAP"); } } public void ModifyCurrentMaterial(float metallic, float smoothness) { if(currentMaterial != null) { currentMaterial.SetFloat("_Metallic", metallic); currentMaterial.SetFloat("_Smoothness", smoothness); if(metallic > 0.0f) { currentMaterial.EnableKeyword("_METALLICSPECGLOSSMAP"); } else { currentMaterial.DisableKeyword("_METALLICSPECGLOSSMAP"); } } } public string GetCurrentMaterialInfo() { if(currentMaterial != null) { return $"Material: {currentMaterial.name}\n" + $"Shader: {currentMaterial.shader.name}\n" + $"Render Queue: {currentMaterial.renderQueue}"; } return "No material assigned"; } public void NextPreset() { currentPresetIndex = (currentPresetIndex + 1) % materialPresets.Count; ApplyMaterialPreset(currentPresetIndex); } public void PreviousPreset() { currentPresetIndex = (currentPresetIndex - 1 + materialPresets.Count) % materialPresets.Count; ApplyMaterialPreset(currentPresetIndex); } }
|
实践练习
练习1:创建自定义URP透明着色器
目标:创建一个支持透明度和裁剪的URP着色器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
| Shader "URP/Custom/TransparentCutout" { Properties { _BaseMap ("Texture", 2D) = "white" {} _BaseColor ("Color", Color) = (1, 1, 1, 1) _Cutoff ("Alpha Cutoff", Range(0.0, 1.0)) = 0.5 _Transparency ("Transparency", Range(0.0, 1.0)) = 0.5 [Toggle(_ALPHA_CLIP)] _UseAlphaClip ("Alpha Clip", Float) = 0.0 }
SubShader { Tags { "RenderType" = "TransparentCutout" "RenderPipeline" = "UniversalPipeline" "IgnoreProjector" = "True" } Pass { Name "ForwardLit" Tags { "LightMode" = "UniversalForward" } Blend SrcAlpha OneMinusSrcAlpha ZWrite[_ZWrite] Cull[_Cull] HLSLPROGRAM #pragma vertex LitPassVertex #pragma fragment LitPassFragment #pragma multi_compile _ _MAIN_LIGHT_SHADOWS #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS #pragma multi_compile_fragment _ _SHADOWS_SOFT #pragma shader_feature_local _ALPHA_CLIP #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityMath.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap); CBUFFER_START(UnityPerMaterial) float4 _BaseMap_ST; half4 _BaseColor; half _Cutoff; half _Transparency; CBUFFER_END struct Attributes { float4 positionOS : POSITION; float3 normalOS : NORMAL; float2 uv : TEXCOORD0; }; struct Varyings { float4 positionCS : SV_POSITION; float3 positionWS : TEXCOORD0; float3 normalWS : TEXCOORD1; float2 uv : TEXCOORD2; }; Varyings LitPassVertex(Attributes input) { Varyings output; VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz); VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS, float4(0, 0, 0, 0)); output.positionCS = vertexInput.positionCS; output.positionWS = vertexInput.positionWS; output.normalWS = normalInput.normalWS; output.uv = TRANSFORM_TEX(input.uv, _BaseMap); return output; } half4 LitPassFragment(Varyings input) : SV_Target { half4 baseColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv) * _BaseColor; // 应用透明度 baseColor.a *= (1.0 - _Transparency); // Alpha裁剪 #ifdef _ALPHA_CLIP clip(baseColor.a - _Cutoff); #endif // 如果没有启用Alpha裁剪,使用透明度混合 #ifndef _ALPHA_CLIP if (baseColor.a < _Cutoff) discard; #endif // 简单的光照计算 float3 normalWS = NormalizeNormalPerPixel(input.normalWS); Light mainLight = GetMainLight(); float3 lightDir = mainLight.direction; float NdotL = saturate(dot(normalWS, lightDir)); half3 finalColor = baseColor.rgb * mainLight.color.rgb * NdotL; return half4(finalColor, baseColor.a); } ENDHLSL } } Fallback "Universal Render Pipeline/Unlit" }
|
练习2:创建材质切换系统
目标:创建一个完整的材质切换和管理系统
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
| using UnityEngine; using UnityEngine.Rendering.Universal; using System.Collections.Generic; using System.Linq;
public class AdvancedMaterialSystem : MonoBehaviour { [System.Serializable] public class MaterialConfiguration { public string configName; public Shader shader; public Dictionary<string, object> properties = new Dictionary<string, object>(); public List<string> enabledKeywords = new List<string>(); } [Header("Material Configurations")] public List<MaterialConfiguration> configurations = new List<MaterialConfiguration>(); [Header("Animation Settings")] public bool animateProperties = false; public float animationSpeed = 1.0f; private Renderer targetRenderer; private Material currentMaterial; private int currentConfigIndex = 0; private float animationTime = 0f; void Start() { targetRenderer = GetComponent<Renderer>(); if(targetRenderer != null && configurations.Count > 0) { ApplyConfiguration(0); } } void Update() { if(animateProperties && currentMaterial != null) { AnimateProperties(); } } public void ApplyConfiguration(int index) { if(index < 0 || index >= configurations.Count) { Debug.LogError($"Invalid configuration index: {index}"); return; } var config = configurations[index]; currentConfigIndex = index; if(targetRenderer != null) { if(currentMaterial == null || currentMaterial.shader != config.shader) { currentMaterial = new Material(config.shader); } ApplyConfigurationProperties(currentMaterial, config); ApplyKeywords(currentMaterial, config.enabledKeywords); targetRenderer.material = currentMaterial; Debug.Log($"Applied configuration: {config.configName}"); } } void ApplyConfigurationProperties(Material material, MaterialConfiguration config) { foreach(var property in config.properties) { if(property.Value is Color) { material.SetColor(property.Key, (Color)property.Value); } else if(property.Value is float) { material.SetFloat(property.Key, (float)property.Value); } else if(property.Value is Vector4) { material.SetVector(property.Key, (Vector4)property.Value); } else if(property.Value is Texture) { material.SetTexture(property.Key, (Texture)property.Value); } } } void ApplyKeywords(Material material, List<string> keywords) { var currentKeywords = material.shaderKeywords.ToList(); foreach(var keyword in currentKeywords) { material.DisableKeyword(keyword); } foreach(var keyword in keywords) { material.EnableKeyword(keyword); } } void AnimateProperties() { animationTime += Time.deltaTime * animationSpeed; if(currentMaterial != null) { Color animatedColor = Color.HSVToRGB( (Mathf.Sin(animationTime) + 1) * 0.5f, 1.0f, 1.0f ); currentMaterial.SetColor("_BaseColor", animatedColor); currentMaterial.SetFloat("_Smoothness", (Mathf.Sin(animationTime * 2) + 1) * 0.25f + 0.25f); } } public void AddConfiguration(MaterialConfiguration config) { configurations.Add(config); } public string GetCurrentConfigurationInfo() { if(currentConfigIndex >= 0 && currentConfigIndex < configurations.Count) { var config = configurations[currentConfigIndex]; return $"Configuration: {config.configName}\n" + $"Shader: {config.shader.name}\n" + $"Properties: {config.properties.Count}\n" + $"Keywords: {config.enabledKeywords.Count}"; } return "No configuration applied"; } public void NextConfiguration() { int nextIndex = (currentConfigIndex + 1) % configurations.Count; ApplyConfiguration(nextIndex); } public void PreviousConfiguration() { int prevIndex = (currentConfigIndex - 1 + configurations.Count) % configurations.Count; ApplyConfiguration(prevIndex); } public void SaveCurrentAsConfiguration(string name) { if(currentMaterial != null) { var newConfig = new MaterialConfiguration { configName = name, shader = currentMaterial.shader }; newConfig.properties.Add("_BaseColor", currentMaterial.GetColor("_BaseColor")); newConfig.properties.Add("_Metallic", currentMaterial.GetFloat("_Metallic")); newConfig.properties.Add("_Smoothness", currentMaterial.GetFloat("_Smoothness")); foreach(var keyword in currentMaterial.shaderKeywords) { newConfig.enabledKeywords.Add(keyword); } configurations.Add(newConfig); Debug.Log($"Saved current material as configuration: {name}"); } } }
|
总结
本章详细介绍了URP着色器的基础知识,包括URP Shader与Built-in Shader的区别、三种主要Shader类型(Lit、SimpleLit、Unlit)的特点和用途、ShaderGraph在URP中的应用,以及常用的URP Shader属性和关键字。
通过理论讲解、代码示例和实践练习,我们学习了:
URP Shader结构:理解了URP着色器与传统Built-in着色器在结构和功能上的差异,URP着色器利用了SRP框架的优势,提供了更好的性能和更灵活的扩展性。
Shader类型对比:掌握了Lit、SimpleLit和Unlit三种主要Shader类型的特点和适用场景,能够根据项目需求选择合适的Shader类型。
ShaderGraph应用:了解了ShaderGraph在URP中的优势和使用方法,这是一种强大的可视化着色器创建工具。
URP材质工作流程:学习了URP中材质的创建、配置和管理方法,包括属性映射、渲染队列控制和材质变体管理。
实践应用:通过创建自定义着色器和材质管理系统,将理论知识转化为实际应用。
URP着色器是实现高质量视觉效果的关键组件,掌握URP着色器的使用对于开发优秀的Unity项目至关重要。在下一章中,我们将深入探讨URP光照系统,了解如何在URP中实现各种光照效果。