// For more information, visit -> https://github.com/ColinLeung-NiloCat/UnityURPToonLitShaderExample /* This shader is a simple example showing you how to write your first URP custom lit shader with "minimum" shader code. You can use this shader as a starting point, add/edit code to develop your own custom lit shader for URP10.3.2 or above. *Usually, just by editing "SimpleURPToonLitOutlineExample_LightingEquation.hlsl" alone can control most of the visual result. This shader includes 5 passes: 0.ForwardLit pass (this pass will always render to the color buffer _CameraColorTexture) 1.Outline pass (this pass will always render to the color buffer _CameraColorTexture) 2.ShadowCaster pass (only for URP's shadow mapping, this pass won't render at all if your character don't cast shadow) 3.DepthOnly pass (only for URP's depth texture _CameraDepthTexture's rendering, this pass won't render at all if your project don't render URP's offscreen depth prepass) 4.DepthNormals pass (only for URP's normal texture _CameraNormalsTexture's rendering) *Because most of the time, you use this toon lit shader for unique characters, so all lightmap & GPU instancing related code are removed for simplicity. *For batching, we only rely on SRP batcher, which is the most practical batching method in URP for rendering lots of unique skinnedmesh characters *In this shader, we choose static uniform branching over "shader_feature & multi_compile" for some of the togglable feature like "_UseEmission","_UseOcclusion"..., because: - we want to avoid this shader's build time takes too long (2^n) - we want to avoid rendering spike when a new shader variant was seen by the camera first time (create GPU program) - we want to avoid increasing ShaderVarientCollection's complexity - we want to avoid shader size becomes too large easily (2^n) - we want to avoid breaking SRP batcher's batching because SRP batcher is per shader variant batching, not per shader - all modern GPU(include newer mobile devices) can handle static uniform branching with "almost" no performance cost */ Shader "SimpleURPToonLitExample(With Outline)" { Properties { [Header(High Level Setting)] [ToggleUI]_IsFace("Is Face? (please turn on if this is a face material)", Float) = 0 // all properties will try to follow URP Lit shader's naming convention // so switching your URP lit material's shader to this toon lit shader will preserve most of the original properties if defined in this shader // for URP Lit shader's naming convention, see URP's Lit.shader [Header(Base Color)] [MainTexture]_BaseMap("_BaseMap (Albedo)", 2D) = "white" {} [HDR][MainColor]_BaseColor("_BaseColor", Color) = (1,1,1,1) [Header(Alpha)] [Toggle(_UseAlphaClipping)]_UseAlphaClipping("_UseAlphaClipping", Float) = 0 _Cutoff("_Cutoff (Alpha Cutoff)", Range(0.0, 1.0)) = 0.5 [Header(Emission)] [Toggle]_UseEmission("_UseEmission (on/off Emission completely)", Float) = 0 [HDR] _EmissionColor("_EmissionColor", Color) = (0,0,0) _EmissionMulByBaseColor("_EmissionMulByBaseColor", Range(0,1)) = 0 [NoScaleOffset]_EmissionMap("_EmissionMap", 2D) = "white" {} _EmissionMapChannelMask("_EmissionMapChannelMask", Vector) = (1,1,1,0) [Header(Occlusion)] [Toggle]_UseOcclusion("_UseOcclusion (on/off Occlusion completely)", Float) = 0 _OcclusionStrength("_OcclusionStrength", Range(0.0, 1.0)) = 1.0 [NoScaleOffset]_OcclusionMap("_OcclusionMap", 2D) = "white" {} _OcclusionMapChannelMask("_OcclusionMapChannelMask", Vector) = (1,0,0,0) _OcclusionRemapStart("_OcclusionRemapStart", Range(0,1)) = 0 _OcclusionRemapEnd("_OcclusionRemapEnd", Range(0,1)) = 1 [Header(Lighting)] _IndirectLightMinColor("_IndirectLightMinColor", Color) = (0.1,0.1,0.1,1) // can prevent completely black if lightprobe not baked _IndirectLightMultiplier("_IndirectLightMultiplier", Range(0,1)) = 1 _DirectLightMultiplier("_DirectLightMultiplier", Range(0,1)) = 1 _CelShadeMidPoint("_CelShadeMidPoint", Range(-1,1)) = -0.5 _CelShadeSoftness("_CelShadeSoftness", Range(0,1)) = 0.05 _MainLightIgnoreCelShade("_MainLightIgnoreCelShade", Range(0,1)) = 0 _AdditionalLightIgnoreCelShade("_AdditionalLightIgnoreCelShade", Range(0,1)) = 0.9 [Header(Shadow mapping)] _ReceiveShadowMappingAmount("_ReceiveShadowMappingAmount", Range(0,1)) = 0.65 _ReceiveShadowMappingPosOffset("_ReceiveShadowMappingPosOffset", Float) = 0 _ShadowMapColor("_ShadowMapColor", Color) = (1,0.825,0.78) [Header(Outline)] _OutlineWidth("_OutlineWidth (World Space)", Range(0,4)) = 1 _OutlineColor("_OutlineColor", Color) = (0.5,0.5,0.5,1) _OutlineZOffset("_OutlineZOffset (View Space)", Range(0,1)) = 0.0001 [NoScaleOffset]_OutlineZOffsetMaskTex("_OutlineZOffsetMask (black is apply ZOffset)", 2D) = "black" {} _OutlineZOffsetMaskRemapStart("_OutlineZOffsetMaskRemapStart", Range(0,1)) = 0 _OutlineZOffsetMaskRemapEnd("_OutlineZOffsetMaskRemapEnd", Range(0,1)) = 1 } SubShader { Tags { // SRP introduced a new "RenderPipeline" tag in Subshader. This allows you to create shaders // that can match multiple render pipelines. If a RenderPipeline tag is not set it will match // any render pipeline. In case you want your subshader to only run in URP, set the tag to // "UniversalPipeline" // here "UniversalPipeline" tag is required, because we only want this shader to run in URP. // If Universal render pipeline is not set in the graphics settings, this Subshader will fail. // One can add a subshader below or fallback to Standard built-in to make this // material work with both Universal Render Pipeline and Builtin Unity Pipeline // the tag value is "UniversalPipeline", not "UniversalRenderPipeline", be careful! // https://github.com/Unity-Technologies/Graphics/pull/1431/ "RenderPipeline" = "UniversalPipeline" // explict SubShader tag to avoid confusion "RenderType"="Opaque" "UniversalMaterialType" = "Lit" "Queue"="Geometry" } // We can extract duplicated hlsl code from all passes into this HLSLINCLUDE section. Less duplicated code = Less error HLSLINCLUDE // all Passes will need this keyword #pragma shader_feature_local_fragment _UseAlphaClipping ENDHLSL // [#0 Pass - ForwardLit] // Shades GI, all lights, emission and fog in a single pass. // Compared to Builtin pipeline forward renderer, URP forward renderer will // render a scene with multiple lights with less drawcalls and less overdraw. Pass { Name "ForwardLit" Tags { // "Lightmode" matches the "ShaderPassName" set in UniversalRenderPipeline.cs. // SRPDefaultUnlit and passes with no LightMode tag are also rendered by Universal Render Pipeline // "Lightmode" tag must be "UniversalForward" in order to render lit objects in URP. "LightMode" = "UniversalForward" } // explict render state to avoid confusion // you can expose these render state to material inspector if needed (see URP's Lit.shader) Cull Back ZTest LEqual ZWrite On Blend One Zero HLSLPROGRAM // --------------------------------------------------------------------------------------------- // Universal Render Pipeline keywords (you can always copy this section from URP's Lit.shader) // When doing custom shaders you most often want to copy and paste these #pragmas // These multi_compile variants are stripped from the build depending on: // 1) Settings in the URP Asset assigned in the GraphicsSettings at build time // e.g If you disabled AdditionalLights in the asset then all _ADDITIONA_LIGHTS variants // will be stripped from build // 2) Invalid combinations are stripped. e.g variants with _MAIN_LIGHT_SHADOWS_CASCADE // but not _MAIN_LIGHT_SHADOWS are invalid and therefore stripped. #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 _ _ADDITIONAL_LIGHT_SHADOWS #pragma multi_compile_fragment _ _SHADOWS_SOFT // --------------------------------------------------------------------------------------------- // Unity defined keywords #pragma multi_compile_fog // --------------------------------------------------------------------------------------------- #pragma vertex VertexShaderWork #pragma fragment ShadeFinalColor // because this pass is just a ForwardLit pass, no need any special #define // (no special #define) // all shader logic written inside this .hlsl, remember to write all #define BEFORE writing #include #include "SimpleURPToonLitOutlineExample_Shared.hlsl" ENDHLSL } // [#1 Pass - Outline] // Same as the above "ForwardLit" pass, but // -vertex position are pushed out a bit base on normal direction // -also color is tinted // -Cull Front instead of Cull Back because Cull Front is a must for all extra pass outline method Pass { Name "Outline" Tags { // IMPORTANT: don't write this line for any custom pass! else this outline pass will not be rendered by URP! //"LightMode" = "UniversalForward" // [Important CPU performance note] // If you need to add a custom pass to your shader (outline pass, planar shadow pass, XRay pass when blocked....), // (0) Add a new Pass{} to your shader // (1) Write "LightMode" = "YourCustomPassTag" inside new Pass's Tags{} // (2) Add a new custom RendererFeature(C#) to your renderer, // (3) write cmd.DrawRenderers() with ShaderPassName = "YourCustomPassTag" // (4) if done correctly, URP will render your new Pass{} for your shader, in a SRP-batcher friendly way (usually in 1 big SRP batch) // For tutorial purpose, current everything is just shader files without any C#, so this Outline pass is actually NOT SRP-batcher friendly. // If you are working on a project with lots of characters, make sure you use the above method to make Outline pass SRP-batcher friendly! } Cull Front // Cull Front is a must for extra pass outline method HLSLPROGRAM // Direct copy all keywords from "ForwardLit" pass // --------------------------------------------------------------------------------------------- #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 _ _ADDITIONAL_LIGHT_SHADOWS #pragma multi_compile_fragment _ _SHADOWS_SOFT // --------------------------------------------------------------------------------------------- #pragma multi_compile_fog // --------------------------------------------------------------------------------------------- #pragma vertex VertexShaderWork #pragma fragment ShadeFinalColor // because this is an Outline pass, define "ToonShaderIsOutline" to inject outline related code into both VertexShaderWork() and ShadeFinalColor() #define ToonShaderIsOutline // all shader logic written inside this .hlsl, remember to write all #define BEFORE writing #include #include "SimpleURPToonLitOutlineExample_Shared.hlsl" ENDHLSL } // ShadowCaster pass. Used for rendering URP's shadowmaps Pass { Name "ShadowCaster" Tags{"LightMode" = "ShadowCaster"} // more explict render state to avoid confusion ZWrite On // the only goal of this pass is to write depth! ZTest LEqual // early exit at Early-Z stage if possible ColorMask 0 // we don't care about color, we just want to write depth, ColorMask 0 will save some write bandwidth Cull Back // support Cull[_Cull] requires "flip vertex normal" using VFACE in fragment shader, which is maybe beyond the scope of a simple tutorial shader HLSLPROGRAM // the only keywords we need in this pass = _UseAlphaClipping, which is already defined inside the HLSLINCLUDE block // (so no need to write any multi_compile or shader_feature in this pass) #pragma vertex VertexShaderWork #pragma fragment BaseColorAlphaClipTest // we only need to do Clip(), no need shading // because it is a ShadowCaster pass, define "ToonShaderApplyShadowBiasFix" to inject "remove shadow mapping artifact" code into VertexShaderWork() #define ToonShaderApplyShadowBiasFix // all shader logic written inside this .hlsl, remember to write all #define BEFORE writing #include #include "SimpleURPToonLitOutlineExample_Shared.hlsl" ENDHLSL } // DepthOnly pass. Used for rendering URP's offscreen depth prepass (you can search DepthOnlyPass.cs in URP package) // For example, when depth texture is on, we need to perform this offscreen depth prepass for this toon shader. Pass { Name "DepthOnly" Tags{"LightMode" = "DepthOnly"} // more explict render state to avoid confusion ZWrite On // the only goal of this pass is to write depth! ZTest LEqual // early exit at Early-Z stage if possible ColorMask 0 // we don't care about color, we just want to write depth, ColorMask 0 will save some write bandwidth Cull Back // support Cull[_Cull] requires "flip vertex normal" using VFACE in fragment shader, which is maybe beyond the scope of a simple tutorial shader HLSLPROGRAM // the only keywords we need in this pass = _UseAlphaClipping, which is already defined inside the HLSLINCLUDE block // (so no need to write any multi_compile or shader_feature in this pass) #pragma vertex VertexShaderWork #pragma fragment BaseColorAlphaClipTest // we only need to do Clip(), no need color shading // because Outline area should write to depth also, define "ToonShaderIsOutline" to inject outline related code into VertexShaderWork() #define ToonShaderIsOutline // all shader logic written inside this .hlsl, remember to write all #define BEFORE writing #include #include "SimpleURPToonLitOutlineExample_Shared.hlsl" ENDHLSL } // Starting from version 10.0.x, URP can generate a normal texture called _CameraNormalsTexture. // To render to this texture in your custom shader, add a Pass with the name DepthNormals. // For example, see the implementation in Lit.shader. // TODO: DepthNormals pass (see URP's Lit.shader) /* Pass { Name "DepthNormals" Tags{"LightMode" = "DepthNormals"} //... } */ } FallBack "Hidden/Universal Render Pipeline/FallbackError" }