Last active
October 6, 2025 03:54
-
-
Save Vavassor/a46b8e3e3ed1599b47a78a6ab4a1dfe4 to your computer and use it in GitHub Desktop.
Lit shader template for Unity game engine's built-in render pipeline
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // This is a lit shader example that doesn't use surface shaders. | |
| // It supports forward rendering in the Built-in render pipeline. (BiRP) | |
| // | |
| // Annoyingly, it's not possible to support baked emission without a custom inspector, | |
| // because there's no other way to set globalIlluminationFlags. So if baked emissions are required, | |
| // switch the inspector to debug mode and change the flags manually. | |
| Shader "Orchid Seal/Lit Shader Template" | |
| { | |
| Properties | |
| { | |
| _Color("Color", Color) = (1,1,1,1) | |
| _MainTex("Texture", 2D) = "white" {} | |
| _Roughness("Roughness", Range(0.0, 1.0)) = 0.5 | |
| [Gamma] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0 | |
| [Toggle(ALPHA_TEST_ON)] _AlphaTestOn("Alpha Test", Integer) = 0 | |
| _Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5 | |
| [Header(Rendering)] | |
| [Enum(UnityEngine.Rendering.BlendMode)] _BlendSrc("Source Blend", Float) = 1 // "One" | |
| [Enum(UnityEngine.Rendering.BlendMode)] _BlendDst("Destination Blend", Float) = 10 // "OneMinusSrcAlpha" | |
| [Enum(Add,0,Sub,1,RevSub,2,Min,3,Max,4)] _BlendOp("Blend Operation", Float) = 0 // "Add" | |
| [Toggle(FADE_TRANSPARENCY_ON)] _FadeTransparencyOn("Fade Transparency", Integer) = 0 | |
| [Toggle(PREMULTIPLY_ALPHA_ON)] _PremultiplyAlphaOn("Premultiply Alpha", Integer) = 1 | |
| [Header(Lighting)] | |
| [KeywordEnum(None, Probes)] Reflection_Mode("Mode", Integer) = 1 | |
| [ToggleOff] _SpecularHighlights("Specular Highlights", Integer) = 1 | |
| [Header(Culling and Depth Test)] | |
| [Enum(UnityEngine.Rendering.CullMode)] _CullMode("Cull Mode", Int) = 2 // "Back" | |
| [Enum(UnityEngine.Rendering.CompareFunction)] _ZTest("ZTest", Float) = 4 // "LessEqual" | |
| [Enum(Off,0,On,1)] _ZWrite("ZWrite", Float) = 1.0 // "On" | |
| [Header(Mask Map)] | |
| [Toggle(MASK_MAP_ON)] _MaskMapOn("Enabled", Integer) = 0 | |
| [NoScaleOffset] _MaskMap("Mask Map (ARM)", 2D) = "white" {} | |
| _OcclusionScale("Ambient Occlusion Scale", Range(0.0, 1.0)) = 1.0 | |
| _RoughnessScale("Roughness Scale", Range(0.0, 1.0)) = 1.0 | |
| _MetallicScale("Metallic Scale", Range(0.0, 1.0)) = 1.0 | |
| [Header(Normal)] | |
| [Toggle(NORMAL_MAP_ON)] _NormalMapOn("Enabled", Integer) = 0 | |
| _BumpScale("Normal Scale", Float) = 1.0 | |
| [Normal] [NoScaleOffset] _BumpMap("Normal Map", 2D) = "bump" {} | |
| [Header(Parallax)] | |
| [Toggle(PARALLAX_MAP_ON)] _ParallaxMapOn("Enabled", Integer) = 0 | |
| _Parallax("Height Scale", Range(0.005, 0.08)) = 0.02 | |
| [NoScaleOffset] _ParallaxMap("Parallax Map", 2D) = "black" {} | |
| [Header(Emission)] | |
| [Toggle(EMISSION_MAP_ON)] _EmissionMapOn("Enabled", Integer) = 0 | |
| _EmissionColor("Color", Color) = (0,0,0) | |
| [NoScaleOffset] _EmissionMap("Emission", 2D) = "white" {} | |
| [Header(Detail)] | |
| [Toggle(DETAIL_MAP_ON)] _DetailMapOn("Enabled", Integer) = 0 | |
| [Enum(Multiply X2, 0, Multiply, 1, Add, 2, Lerp, 3)] _DetailBlendMode("Blend Mode", Integer) = 0 | |
| _DetailAlbedoMap("Detail Color", 2D) = "grey" {} | |
| _DetailNormalScale("Normal Scale", Float) = 1.0 | |
| [Normal] [NoScaleOffset] _DetailNormalMap("Normal Map", 2D) = "bump" {} | |
| [Enum(UV0, 0, UV1, 1, UV2, 2, UV3, 3)] _DetailUvSet("UV Set", Integer) = 0 | |
| } | |
| HLSLINCLUDE | |
| #include "UnityCG.cginc" | |
| #include "AutoLight.cginc" | |
| #include "UnityPBSLighting.cginc" | |
| #if UNITY_PASS_META | |
| #include "UnityMetaPass.cginc" | |
| #endif | |
| // Shadow Receiving................................................................................ | |
| // Macros from AutoLight.cginc assume variables have specific names which differ from ours. | |
| // So redefine our own here. | |
| #if defined(SHADOWS_SCREEN) | |
| #if defined(UNITY_NO_SCREENSPACE_SHADOWS) | |
| #define OSP_TRANSFER_SHADOW_INTERNAL(a, positionCs, positionWs, uv1) a._ShadowCoord = mul(unity_WorldToShadow[0], positionWs); | |
| #else // UNITY_NO_SCREENSPACE_SHADOWS | |
| #define OSP_TRANSFER_SHADOW_INTERNAL(a, positionCs, positionWs, uv1) a._ShadowCoord = ComputeScreenPos(positionCs); | |
| #endif | |
| #define OSP_SHADOW_COORDS_INTERNAL(idx1) unityShadowCoord4 _ShadowCoord : TEXCOORD##idx1; | |
| #define OSP_SHADOW_ATTENUATION_INTERNAL(a) unitySampleShadow(a._ShadowCoord) | |
| #define OSP_OFF_SURFACE_SHADOW_ATTENUATION(a, positionWs, positionSs) UnityComputeForwardShadows(a._ShadowCoord.xy, positionWs, positionSs) | |
| #endif // SHADOWS_SCREEN | |
| #if defined(HANDLE_SHADOWS_BLENDING_IN_GI) // handles shadows in the depths of the GI function for performance reasons | |
| #define OSP_SHADOW_COORDS(idx1) OSP_SHADOW_COORDS_INTERNAL(idx1) | |
| #define OSP_TRANSFER_SHADOW(a, positionCs, positionWs, uv1) OSP_TRANSFER_SHADOW_INTERNAL(a, positionCs, positionWs, uv1) | |
| #define OSP_SHADOW_ATTENUATION(a, positionCs, positionWs) OSP_SHADOW_ATTENUATION_INTERNAL(a) | |
| #elif defined(SHADOWS_SCREEN) && !defined(LIGHTMAP_ON) && !defined(UNITY_NO_SCREENSPACE_SHADOWS) // no lightmap uv thus store screenPos instead | |
| // can happen if we have two directional lights. main light gets handled in GI code, but 2nd dir light can have shadow screen and mask. | |
| // - Disabled on ES2 because WebGL 1.0 seems to have junk in .w (even though it shouldn't) | |
| #if defined(SHADOWS_SHADOWMASK) && !defined(SHADER_API_GLES) | |
| #define OSP_SHADOW_COORDS(idx1) unityShadowCoord4 _ShadowCoord : TEXCOORD##idx1; | |
| #define OSP_TRANSFER_SHADOW(a, positionCs, positionWs, uv1) {a._ShadowCoord.xy = uv1 * unity_LightmapST.xy + unity_LightmapST.zw; a._ShadowCoord.zw = ComputeScreenPos(positionCs).xy;} | |
| #define OSP_SHADOW_ATTENUATION(a, positionCs, positionWs) UnityComputeForwardShadows(a._ShadowCoord.xy, positionWs, float4(a._ShadowCoord.zw, 0.0, UNITY_SHADOW_W(positionCs.w))); | |
| #else | |
| #define OSP_SHADOW_COORDS(idx1) OSP_SHADOW_COORDS_INTERNAL(idx1) | |
| #define OSP_TRANSFER_SHADOW(a, positionCs, positionWs, uv1) OSP_TRANSFER_SHADOW_INTERNAL(a, positionCs, positionWs, uv1) | |
| #define OSP_SHADOW_ATTENUATION(a, positionCs, positionWs) UnityComputeForwardShadows(0, positionWs, a._ShadowCoord) | |
| #endif | |
| #else | |
| #define OSP_SHADOW_COORDS(idx1) unityShadowCoord4 _ShadowCoord : TEXCOORD##idx1; | |
| #if defined(SHADOWS_SHADOWMASK) | |
| #define OSP_TRANSFER_SHADOW(a, positionCs, positionWs, uv1) a._ShadowCoord.xy = uv1.xy * unity_LightmapST.xy + unity_LightmapST.zw; | |
| #if (defined(SHADOWS_DEPTH) || defined(SHADOWS_SCREEN) || defined(SHADOWS_CUBE) || UNITY_LIGHT_PROBE_PROXY_VOLUME) | |
| #define OSP_SHADOW_ATTENUATION(a, positionCs, positionWs) UnityComputeForwardShadows(a._ShadowCoord.xy, positionWs, UNITY_READ_SHADOW_COORDS(a)) | |
| #else | |
| #define OSP_SHADOW_ATTENUATION(a, positionCs, positionWs) UnityComputeForwardShadows(a._ShadowCoord.xy, 0, 0) | |
| #endif | |
| #else | |
| #if !defined(UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS) | |
| #define OSP_TRANSFER_SHADOW(a, positionCs, positionWs, uv1) | |
| #else | |
| #define OSP_TRANSFER_SHADOW(a, positionCs, positionWs, uv1) OSP_TRANSFER_SHADOW_INTERNAL(a, positionCs, positionWs, uv1) | |
| #endif | |
| #if (defined(SHADOWS_DEPTH) || defined(SHADOWS_SCREEN) || defined(SHADOWS_CUBE)) | |
| #define OSP_SHADOW_ATTENUATION(a, positionCs, positionWs) UnityComputeForwardShadows(0, positionWs, UNITY_READ_SHADOW_COORDS(a)) | |
| #else | |
| #if UNITY_LIGHT_PROBE_PROXY_VOLUME | |
| #define OSP_SHADOW_ATTENUATION(a, positionCs, positionWs) UnityComputeForwardShadows(0, positionWs, UNITY_READ_SHADOW_COORDS(a)) | |
| #else | |
| #define OSP_SHADOW_ATTENUATION(a, positionCs, positionWs) UnityComputeForwardShadows(0, 0, 0) | |
| #endif | |
| #endif | |
| #endif | |
| #endif | |
| #if defined(SHADOWS_DEPTH) && defined(SPOT) | |
| #define OSP_TRANSFER_SHADOW_INTERNAL(a, positionCs, positionWs, uv1) a._ShadowCoord = mul(unity_WorldToShadow[0], positionWs) | |
| #define OSP_OFF_SURFACE_SHADOW_ATTENUATION(a, positionWs, positionSs) UnityComputeForwardShadows(a._ShadowCoord.xy, positionWs, positionSs) | |
| #endif | |
| #if defined(SHADOWS_CUBE) | |
| #define OSP_TRANSFER_SHADOW_INTERNAL(a, positionCs, positionWs, uv1) a._ShadowCoord.xyz = positionWs.xyz - _LightPositionRange.xyz | |
| #define OSP_OFF_SURFACE_SHADOW_ATTENUATION(a, positionWs, positionSs) UnityComputeForwardShadows(a._ShadowCoord.xy, positionWs, positionSs) | |
| #endif | |
| #if !defined(SHADOWS_SCREEN) && !(defined(SHADOWS_DEPTH) && defined(SPOT)) && !defined(SHADOWS_CUBE) | |
| #define OSP_TRANSFER_SHADOW_INTERNAL(a, positionCs, positionWs, uv1) | |
| #define OSP_OFF_SURFACE_SHADOW_ATTENUATION(a, positionWs, positionSs) 1.0 | |
| #endif | |
| // Light coordinates............................................................................... | |
| #ifdef POINT | |
| #define OSP_DECLARE_LIGHT_COORDS(idx) unityShadowCoord3 _LightCoord : TEXCOORD##idx; | |
| #define OSP_COMPUTE_LIGHT_COORDS(a, positionWs) a._LightCoord = mul(unity_WorldToLight, positionWs).xyz; | |
| #define OSP_LIGHT_ATTENUATION(destName, input, positionCs, positionWs) \ | |
| unityShadowCoord3 lightCoord = mul(unity_WorldToLight, unityShadowCoord4(positionWs, 1)).xyz; \ | |
| fixed shadow = OSP_SHADOW_ATTENUATION(input, positionCs, positionWs); \ | |
| fixed destName = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).r * shadow; | |
| #endif | |
| #ifdef SPOT | |
| #define OSP_DECLARE_LIGHT_COORDS(idx) unityShadowCoord4 _LightCoord : TEXCOORD##idx; | |
| #define OSP_COMPUTE_LIGHT_COORDS(a, positionWs) a._LightCoord = mul(unity_WorldToLight, positionWs); | |
| #define OSP_LIGHT_ATTENUATION(destName, input, positionCs, positionWs) \ | |
| DECLARE_LIGHT_COORD(input, positionWs); \ | |
| fixed shadow = OSP_SHADOW_ATTENUATION(input, positionCs, positionWs); \ | |
| fixed destName = (lightCoord.z > 0) * UnitySpotCookie(lightCoord) * UnitySpotAttenuate(lightCoord.xyz) * shadow; | |
| #endif | |
| #ifdef DIRECTIONAL | |
| #define OSP_DECLARE_LIGHT_COORDS(idx) | |
| #define OSP_COMPUTE_LIGHT_COORDS(a, positionWs) | |
| #define OSP_LIGHT_ATTENUATION(destName, input, positionCs, positionWs) fixed destName = OSP_SHADOW_ATTENUATION(input, positionCs, positionWs); | |
| #endif | |
| #ifdef POINT_COOKIE | |
| #define OSP_DECLARE_LIGHT_COORDS(idx) unityShadowCoord3 _LightCoord : TEXCOORD##idx; | |
| #define OSP_COMPUTE_LIGHT_COORDS(a, positionWs) a._LightCoord = mul(unity_WorldToLight, positionWs).xyz; | |
| #define OSP_LIGHT_ATTENUATION(destName, input, positionCs, positionWs) \ | |
| DECLARE_LIGHT_COORD(input, positionWs); \ | |
| fixed shadow = OSP_SHADOW_ATTENUATION(input, positionCs, positionWs); \ | |
| fixed destName = tex2D(_LightTextureB0, dot(lightCoord, lightCoord).rr).r * texCUBE(_LightTexture0, lightCoord).w * shadow; | |
| #endif | |
| #ifdef DIRECTIONAL_COOKIE | |
| #define OSP_DECLARE_LIGHT_COORDS(idx) unityShadowCoord2 _LightCoord : TEXCOORD##idx; | |
| #define OSP_COMPUTE_LIGHT_COORDS(a, positionWs) a._LightCoord = mul(unity_WorldToLight, positionWs).xy; | |
| #define OSP_LIGHT_ATTENUATION(destName, input, positionCs, positionWs) \ | |
| DECLARE_LIGHT_COORD(input, positionWs); \ | |
| fixed shadow = OSP_SHADOW_ATTENUATION(input, positionCs, positionWs); \ | |
| fixed destName = tex2D(_LightTexture0, lightCoord).w * shadow; | |
| #endif | |
| // Shadow Casting.............................................................................. | |
| #if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX) | |
| #define OSP_SHADOW_CASTER_NOPOS(idx1) float3 vec : TEXCOORD##idx1; | |
| #define OSP_TRANSFER_SHADOW_CASTER_NOPOS(o, positionCs, positionWs, positionOs, normalOs) o.vec = positionWs - _LightPositionRange.xyz; (positionCs) = UnityObjectToClipPos(positionOs); | |
| #define OSP_SHADOW_CASTER_FRAGMENT(i) UnityEncodeCubeShadowDepth((length(i.vec) + unity_LightShadowBias.x) * _LightPositionRange.w) | |
| #else | |
| #define OSP_SHADOW_CASTER_NOPOS(idx1) | |
| #define OSP_TRANSFER_SHADOW_CASTER_NOPOS(o, positionCs, positionWs, positionOs, normalWs) (positionCs) = UnityApplyLinearShadowBias(OspClipSpaceShadowCasterPos(positionWs, normalWs)); | |
| #define OSP_SHADOW_CASTER_FRAGMENT(i) 0.0 | |
| #endif | |
| float4 OspClipSpaceShadowCasterPos(float4 positionWs, float3 normalWs) | |
| { | |
| if (unity_LightShadowBias.z != 0.0) | |
| { | |
| float3 wLight = normalize(UnityWorldSpaceLightDir(positionWs.xyz)); | |
| // apply normal offset bias (inset position along the normal) | |
| // bias needs to be scaled by sine between normal and light direction | |
| // (http://the-witness.net/news/2013/09/shadow-mapping-summary-part-1/) | |
| // | |
| // unity_LightShadowBias.z contains user-specified normal offset amount | |
| // scaled by world space texel size. | |
| float shadowCos = dot(normalWs, wLight); | |
| float shadowSine = sqrt(1-shadowCos*shadowCos); | |
| float normalBias = unity_LightShadowBias.z * shadowSine; | |
| positionWs.xyz -= normalWs * normalBias; | |
| } | |
| return mul(UNITY_MATRIX_VP, positionWs); | |
| } | |
| // Fog......................................................................................... | |
| #if defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2) | |
| #if (SHADER_TARGET < 30) || defined(SHADER_API_MOBILE) | |
| // mobile or SM2.0: calculate fog factor per-vertex | |
| #define OSP_TRANSFER_FOG_COMBINED_WITH_WORLD_POS(positionWs, outpos) UNITY_CALC_FOG_FACTOR((outpos).z); (positionWs).w = unityFogFactor | |
| #else | |
| // SM3.0 and PC/console: calculate fog distance per-vertex, and fog factor per-pixel | |
| #define OSP_TRANSFER_FOG_COMBINED_WITH_WORLD_POS(positionWs, outpos) (positionWs).w = (outpos).z | |
| #endif | |
| #define OSP_EXTRACT_FOG_FROM_WORLD_POS(positionWs) float _unity_fogCoord = (positionWs).w | |
| #else | |
| #define OSP_EXTRACT_FOG_FROM_WORLD_POS(positionWs) | |
| #define OSP_TRANSFER_FOG_COMBINED_WITH_WORLD_POS(positionWs, outpos) | |
| #endif | |
| // Indirect Specular........................................................................... | |
| half3 GlossyEnvironment(UNITY_ARGS_TEXCUBE(tex), half4 hdr, half perceptualRoughness, half3 reflUvw) | |
| { | |
| perceptualRoughness = perceptualRoughness * (1.7 - 0.7 * perceptualRoughness); | |
| half mip = perceptualRoughnessToMipmapLevel(perceptualRoughness); | |
| half4 rgbm = UNITY_SAMPLE_TEXCUBE_LOD(tex, reflUvw, mip); | |
| return DecodeHDR(rgbm, hdr); | |
| } | |
| half3 GetProbeIndirectSpecular(half3 reflUvw, float3 positionWs, half perceptualRoughness) | |
| { | |
| #ifdef UNITY_SPECCUBE_BOX_PROJECTION | |
| // we will tweak reflUVW in glossIn directly (as we pass it to GlossyEnvironment twice for probe0 and probe1), so keep original to pass into BoxProjectedCubemapDirection | |
| half3 originalReflUvw = reflUvw; | |
| reflUvw = BoxProjectedCubemapDirection(originalReflUvw, positionWs, unity_SpecCube0_ProbePosition, unity_SpecCube0_BoxMin, unity_SpecCube0_BoxMax); | |
| #endif | |
| half3 env0 = GlossyEnvironment(UNITY_PASS_TEXCUBE(unity_SpecCube0), unity_SpecCube0_HDR, perceptualRoughness, reflUvw); | |
| #ifdef UNITY_SPECCUBE_BLENDING | |
| const float kBlendFactor = 0.99999; | |
| float blendLerp = unity_SpecCube0_BoxMin.w; | |
| UNITY_BRANCH | |
| if (blendLerp < kBlendFactor) | |
| { | |
| #ifdef UNITY_SPECCUBE_BOX_PROJECTION | |
| reflUvw = BoxProjectedCubemapDirection(originalReflUvw, positionWs, unity_SpecCube1_ProbePosition, unity_SpecCube1_BoxMin, unity_SpecCube1_BoxMax); | |
| #endif | |
| half3 env1 = GlossyEnvironment(UNITY_PASS_TEXCUBE_SAMPLER(unity_SpecCube1, unity_SpecCube0), unity_SpecCube1_HDR, perceptualRoughness, reflUvw); | |
| return lerp(env1, env0, blendLerp); | |
| } | |
| else | |
| { | |
| return env0; | |
| } | |
| #else | |
| return env0; | |
| #endif | |
| } | |
| // Inputs...................................................................................... | |
| struct VertexInput | |
| { | |
| float4 positionOs : POSITION; | |
| float3 normal : NORMAL; | |
| float4 tangent : TANGENT; | |
| float2 uv0 : TEXCOORD0; | |
| float2 uv1 : TEXCOORD1; | |
| float2 uv2 : TEXCOORD2; | |
| float2 uv3 : TEXCOORD3; | |
| UNITY_VERTEX_INPUT_INSTANCE_ID | |
| }; | |
| struct FragmentInput | |
| { | |
| float4 positionCs : SV_POSITION; | |
| float4 uv0 : TEXCOORD0; | |
| float4 positionWs : TEXCOORD1; // [xyz : positionWs | w : fogCoord] | |
| float4 tangentToWorldAndPackedData[3] : TEXCOORD2; // [3x3 : tangentToWorld | 1x3:viewDirForParallax] | |
| half4 ambientOrLightmapUv : TEXCOORD5; | |
| OSP_DECLARE_LIGHT_COORDS(6) | |
| OSP_SHADOW_COORDS(7) | |
| float3 lightDirectionWs : TEXCOORD8; | |
| OSP_SHADOW_CASTER_NOPOS(9) | |
| #ifdef EDITOR_VISUALIZATION | |
| float2 vizUV : TEXCOORD10; | |
| float4 lightCoord : TEXCOORD11; | |
| #endif | |
| UNITY_VERTEX_INPUT_INSTANCE_ID | |
| UNITY_VERTEX_OUTPUT_STEREO | |
| }; | |
| // UV Sets..................................................................................... | |
| float2 SelectUvSet(VertexInput v, half set) | |
| { | |
| switch (set) | |
| { | |
| default: | |
| case 0: return v.uv0; | |
| case 1: return v.uv1; | |
| case 2: return v.uv2; | |
| case 3: return v.uv3; | |
| } | |
| } | |
| // Properties.................................................................................. | |
| UNITY_DECLARE_TEX2D(_MainTex); | |
| UNITY_DECLARE_TEX2D(_MaskMap); | |
| UNITY_DECLARE_TEX2D(_BumpMap); | |
| UNITY_DECLARE_TEX2D(_EmissionMap); | |
| UNITY_DECLARE_TEX2D(_ParallaxMap); | |
| UNITY_DECLARE_TEX2D(_DetailAlbedoMap); | |
| UNITY_DECLARE_TEX2D(_DetailNormalMap); | |
| CBUFFER_START(UnityPerMaterial) | |
| half4 _Color; | |
| float4 _MainTex_ST; | |
| half _Cutoff; | |
| half _Metallic; | |
| half _MetallicScale; | |
| float _Roughness; | |
| float _RoughnessScale; | |
| half _OcclusionScale; | |
| half _BumpScale; | |
| half4 _EmissionColor; | |
| half _Parallax; | |
| float4 _DetailAlbedoMap_ST; | |
| half _DetailUvSet; | |
| half _DetailNormalScale; | |
| half _DetailBlendMode; | |
| CBUFFER_END | |
| FragmentInput VertexProgram(VertexInput v) | |
| { | |
| FragmentInput o; | |
| UNITY_SETUP_INSTANCE_ID(v); | |
| UNITY_INITIALIZE_OUTPUT(FragmentInput, o); | |
| UNITY_TRANSFER_INSTANCE_ID(v, o); | |
| UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); | |
| #if UNITY_PASS_META | |
| o.positionCs = UnityMetaVertexPosition(v.positionOs, v.uv1.xy, v.uv2.xy, unity_LightmapST, unity_DynamicLightmapST); | |
| #elif UNITY_PASS_SHADOWCASTER | |
| OSP_TRANSFER_SHADOW_CASTER_NOPOS(o, o.positionCs, mul(unity_ObjectToWorld, v.positionOs), v.positionOs, UnityObjectToWorldNormal(v.normal)) | |
| #else | |
| o.positionCs = UnityObjectToClipPos(v.positionOs); | |
| #endif | |
| o.uv0.xy = TRANSFORM_TEX(v.uv0, _MainTex); | |
| #if UNITY_PASS_FORWARDBASE || UNITY_PASS_FORWARDADD | |
| float4 positionWs = mul(unity_ObjectToWorld, v.positionOs); | |
| o.positionWs.xyz = positionWs.xyz; | |
| o.uv0.zw = TRANSFORM_TEX(SelectUvSet(v, _DetailUvSet), _DetailAlbedoMap); | |
| float3 normalWs = UnityObjectToWorldNormal(v.normal); | |
| #ifdef NORMAL_MAP_ON | |
| float4 tangentWs = float4(UnityObjectToWorldDir(v.tangent.xyz), v.tangent.w); | |
| float3x3 tangentToWorld = CreateTangentToWorldPerVertex(normalWs, tangentWs.xyz, tangentWs.w); | |
| o.tangentToWorldAndPackedData[0].xyz = tangentToWorld[0]; | |
| o.tangentToWorldAndPackedData[1].xyz = tangentToWorld[1]; | |
| o.tangentToWorldAndPackedData[2].xyz = tangentToWorld[2]; | |
| #else | |
| o.tangentToWorldAndPackedData[0].xyz = 0; | |
| o.tangentToWorldAndPackedData[1].xyz = 0; | |
| o.tangentToWorldAndPackedData[2].xyz = normalWs; | |
| #endif | |
| OSP_COMPUTE_LIGHT_COORDS(o, positionWs); | |
| OSP_TRANSFER_SHADOW(o, o.positionCs, positionWs, v.uv1); | |
| #endif | |
| #if UNITY_PASS_FORWARDBASE | |
| half4 ambientOrLightmapUV = 0; | |
| // Static lightmaps | |
| #ifdef LIGHTMAP_ON | |
| ambientOrLightmapUV.xy = v.uv1.xy * unity_LightmapST.xy + unity_LightmapST.zw; | |
| ambientOrLightmapUV.zw = 0; | |
| // Sample light probe for Dynamic objects only (no static or dynamic lightmaps) | |
| #elif UNITY_SHOULD_SAMPLE_SH | |
| #ifdef VERTEXLIGHT_ON | |
| // Approximated illumination from non-important point lights | |
| ambientOrLightmapUV.rgb = Shade4PointLights( | |
| unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0, | |
| unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb, | |
| unity_4LightAtten0, positionWs.xyz, normalWs); | |
| #endif | |
| ambientOrLightmapUV.rgb = ShadeSHPerVertex(normalWs, ambientOrLightmapUV.rgb); | |
| #endif | |
| #ifdef DYNAMICLIGHTMAP_ON | |
| ambientOrLightmapUV.zw = v.uv2.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw; | |
| #endif | |
| o.ambientOrLightmapUv = ambientOrLightmapUV; | |
| #endif | |
| #ifdef UNITY_PASS_FORWARDADD | |
| o.lightDirectionWs = _WorldSpaceLightPos0.xyz - positionWs.xyz * _WorldSpaceLightPos0.w; | |
| #endif | |
| #if PARALLAX_MAP_ON | |
| float3 bitangent = cross(normalize(v.normal), normalize(v.tangent.xyz)) * v.tangent.w; | |
| float3x3 rotation = float3x3(v.tangent.xyz, bitangent, v.normal); | |
| half3 viewDirForParallax = mul(rotation, ObjSpaceViewDir(v.positionOs)); | |
| o.tangentToWorldAndPackedData[0].w = viewDirForParallax.x; | |
| o.tangentToWorldAndPackedData[1].w = viewDirForParallax.y; | |
| o.tangentToWorldAndPackedData[2].w = viewDirForParallax.z; | |
| #endif | |
| #ifdef EDITOR_VISUALIZATION | |
| o.vizUV = 0; | |
| o.lightCoord = 0; | |
| if (unity_VisualizationMode == EDITORVIZ_TEXTURE) | |
| { | |
| o.vizUV = UnityMetaVizUV(unity_EditorViz_UVIndex, v.uv0.xy, v.uv1.xy, v.uv2.xy, unity_EditorViz_Texture_ST); | |
| } | |
| else if (unity_VisualizationMode == EDITORVIZ_SHOWLIGHTMASK) | |
| { | |
| o.vizUV = v.uv1.xy * unity_LightmapST.xy + unity_LightmapST.zw; | |
| o.lightCoord = mul(unity_EditorViz_WorldToLight, mul(unity_ObjectToWorld, float4(v.positionOs.xyz, 1))); | |
| } | |
| #endif | |
| OSP_TRANSFER_FOG_COMBINED_WITH_WORLD_POS(o.positionWs, o.positionCs); | |
| return o; | |
| } | |
| half4 FragmentProgram(FragmentInput i) : SV_Target | |
| { | |
| UNITY_APPLY_DITHER_CROSSFADE(i.positionCs.xy); | |
| // Transparent cutout | |
| float4 uv0 = i.uv0; | |
| #if PARALLAX_MAP_ON | |
| half3 parallaxViewDirection = normalize(half3( | |
| i.tangentToWorldAndPackedData[0].w, | |
| i.tangentToWorldAndPackedData[1].w, | |
| i.tangentToWorldAndPackedData[2].w)); | |
| half h = UNITY_SAMPLE_TEX2D(_ParallaxMap, uv0.xy).g; | |
| float2 offset = ParallaxOffset1Step(h, _Parallax, parallaxViewDirection); | |
| uv0 = float4(uv0.xy + offset, uv0.zw + offset); | |
| #endif | |
| fixed4 baseColor = _Color * UNITY_SAMPLE_TEX2D(_MainTex, uv0.xy); | |
| #if ALPHA_TEST_ON | |
| clip(baseColor.a - _Cutoff); | |
| #endif | |
| UNITY_SETUP_INSTANCE_ID(i); | |
| UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i); | |
| #if UNITY_PASS_FORWARDBASE || UNITY_PASS_FORWARDADD || UNITY_PASS_META | |
| // Get material properties | |
| #if MASK_MAP_ON | |
| fixed4 masks = UNITY_SAMPLE_TEX2D(_MaskMap, uv0.xy); | |
| half occlusion = _OcclusionScale * masks.r; | |
| half perceptualRoughness = _RoughnessScale * masks.g; | |
| half metallic = _MetallicScale * masks.b; | |
| half detailMask = masks.a; | |
| #else | |
| half occlusion = 1.0; | |
| half perceptualRoughness = _Roughness; | |
| half metallic = _Metallic; | |
| half detailMask = 1.0; | |
| #endif | |
| half3 albedo = baseColor.rgb; | |
| #if DETAIL_MAP_ON | |
| fixed4 detailBaseColor = UNITY_SAMPLE_TEX2D(_DetailAlbedoMap, uv0.zw); | |
| switch (_DetailBlendMode) | |
| { | |
| default: | |
| case 0: | |
| albedo.rgb *= LerpWhiteTo(unity_ColorSpaceDouble.rgb * detailBaseColor.rgb, detailMask); | |
| break; | |
| case 1: | |
| albedo.rgb *= LerpWhiteTo(detailBaseColor.rgb, detailMask); | |
| break; | |
| case 2: | |
| albedo.rgb += detailMask * detailBaseColor.rgb; | |
| break; | |
| case 3: | |
| albedo.rgb = lerp(albedo.rgb, detailBaseColor.rgb, detailMask); | |
| break; | |
| } | |
| #endif | |
| half oneMinusReflectivity = OneMinusReflectivityFromMetallic(metallic); | |
| half3 diffuseColor = albedo * oneMinusReflectivity; | |
| half3 specularColor = lerp(unity_ColorSpaceDielectricSpec.rgb, albedo, metallic); | |
| #if PREMULTIPLY_ALPHA_ON | |
| diffuseColor *= baseColor.a; | |
| #endif | |
| #endif | |
| #if EMISSION_MAP_ON | |
| half4 emissionSample = UNITY_SAMPLE_TEX2D(_EmissionMap, i.uv0.xy); | |
| half3 emission = _EmissionColor.rgb * emissionSample.rgb; | |
| #endif | |
| #if UNITY_PASS_FORWARDBASE || UNITY_PASS_FORWARDADD | |
| // Get geometric properties | |
| float3 positionWs = i.positionWs.xyz; | |
| float3 viewDirectionWs = positionWs.xyz - _WorldSpaceCameraPos; | |
| float3 unitViewDirectionWs = normalize(viewDirectionWs); | |
| #if NORMAL_MAP_ON | |
| half3 tangent = i.tangentToWorldAndPackedData[0].xyz; | |
| half3 bitangent = i.tangentToWorldAndPackedData[1].xyz; | |
| half3 normal = i.tangentToWorldAndPackedData[2].xyz; | |
| half3 normalTs = UnpackScaleNormal(UNITY_SAMPLE_TEX2D(_BumpMap, uv0.xy), _BumpScale); | |
| #if DETAIL_MAP_ON | |
| half3 detailNormalTs = UnpackScaleNormal(UNITY_SAMPLE_TEX2D(_DetailNormalMap, uv0.xy), _DetailNormalScale); | |
| switch (_DetailBlendMode) | |
| { | |
| default: | |
| normalTs = lerp(normalTs, BlendNormals(normalTs, detailNormalTs), detailMask); | |
| break; | |
| case 3: | |
| normalTs = lerp(normalTs, detailNormalTs, detailMask); | |
| break; | |
| } | |
| #endif | |
| float3 normalWs = normalize(tangent * normalTs.x + bitangent * normalTs.y + normal * normalTs.z); | |
| #else | |
| float3 normalWs = normalize(i.tangentToWorldAndPackedData[2].xyz); | |
| #endif | |
| // Get direct lighting | |
| UnityLight directLight; | |
| directLight.ndotl = 0; | |
| directLight.color = _LightColor0.rgb; | |
| #if UNITY_PASS_FORWARDBASE | |
| directLight.dir = _WorldSpaceLightPos0.xyz; | |
| #else | |
| directLight.dir = i.lightDirectionWs; | |
| #endif | |
| #ifndef USING_DIRECTIONAL_LIGHT | |
| directLight.dir = normalize(directLight.dir); | |
| #endif | |
| OSP_LIGHT_ATTENUATION(attenuation, i, i.positionCs, positionWs); | |
| // Get indirect lighting (Global Illumination) | |
| UnityIndirect indirectLight; | |
| indirectLight.diffuse = 0; | |
| indirectLight.specular = 0; | |
| #ifdef UNITY_PASS_FORWARDBASE | |
| // Indirect diffuse | |
| #if UNITY_SHOULD_SAMPLE_SH | |
| // Light Probes | |
| indirectLight.diffuse = ShadeSHPerPixel(normalWs, i.ambientOrLightmapUv, positionWs); | |
| #endif | |
| #if defined(HANDLE_SHADOWS_BLENDING_IN_GI) | |
| half bakedAtten = UnitySampleBakedOcclusion(i.ambientOrLightmapUv.xy, positionWs); | |
| float zDist = dot(_WorldSpaceCameraPos - positionWs, UNITY_MATRIX_V[2].xyz); | |
| float fadeDist = UnityComputeShadowFadeDistance(positionWs, zDist); | |
| attenuation = UnityMixRealtimeAndBakedShadows(attenuation, bakedAtten, UnityComputeShadowFade(fadeDist)); | |
| #endif | |
| #if defined(LIGHTMAP_ON) | |
| // Baked lightmaps | |
| half4 bakedColorTex = UNITY_SAMPLE_TEX2D(unity_Lightmap, i.ambientOrLightmapUv.xy); | |
| half3 bakedColor = DecodeLightmap(bakedColorTex); | |
| #ifdef DIRLIGHTMAP_COMBINED | |
| fixed4 bakedDirTex = UNITY_SAMPLE_TEX2D_SAMPLER(unity_LightmapInd, unity_Lightmap, i.ambientOrLightmapUv.xy); | |
| indirectLight.diffuse += DecodeDirectionalLightmap(bakedColor, bakedDirTex, normalWs); | |
| #if defined(LIGHTMAP_SHADOW_MIXING) && !defined(SHADOWS_SHADOWMASK) && defined(SHADOWS_SCREEN) | |
| ResetUnityLight(directLight); | |
| indirectLight.diffuse = SubtractMainLightWithRealtimeAttenuationFromLightmap(indirectLight.diffuse, attenuation, bakedColorTex, normalWs); | |
| #endif | |
| #else | |
| indirectLight.diffuse += bakedColor; | |
| #if defined(LIGHTMAP_SHADOW_MIXING) && !defined(SHADOWS_SHADOWMASK) && defined(SHADOWS_SCREEN) | |
| ResetUnityLight(directLight); | |
| indirectLight.diffuse = SubtractMainLightWithRealtimeAttenuationFromLightmap(indirectLight.diffuse, attenuation, bakedColorTex, normalWs); | |
| #endif | |
| #endif | |
| #endif | |
| #ifdef DYNAMICLIGHTMAP_ON | |
| // Dynamic lightmaps | |
| fixed4 realtimeColorTex = UNITY_SAMPLE_TEX2D(unity_DynamicLightmap, i.ambientOrLightmapUv.zw); | |
| half3 realtimeColor = DecodeRealtimeLightmap(realtimeColorTex); | |
| #ifdef DIRLIGHTMAP_COMBINED | |
| half4 realtimeDirTex = UNITY_SAMPLE_TEX2D_SAMPLER(unity_DynamicDirectionality, unity_DynamicLightmap, i.ambientOrLightmapUv.zw); | |
| indirectLight.diffuse += DecodeDirectionalLightmap(realtimeColor, realtimeDirTex, normalWs); | |
| #else | |
| indirectLight.diffuse += realtimeColor; | |
| #endif | |
| #endif | |
| indirectLight.diffuse *= occlusion; | |
| // Indirect specular | |
| #if REFLECTION_MODE_NONE | |
| indirectLight.specular = unity_IndirectSpecColor.rgb; | |
| #elif REFLECTION_MODE_PROBES | |
| // Reflection Probes | |
| indirectLight.specular = GetProbeIndirectSpecular(reflect(unitViewDirectionWs, normalWs), positionWs, perceptualRoughness); | |
| #endif | |
| indirectLight.specular *= occlusion; | |
| #endif | |
| directLight.color *= attenuation; | |
| // Lighting | |
| half4 color = UNITY_BRDF_PBS(diffuseColor, specularColor, oneMinusReflectivity, 1.0 - perceptualRoughness, normalWs, -unitViewDirectionWs, directLight, indirectLight); | |
| #if EMISSION_MAP_ON | |
| color.rgb += emission.rgb; | |
| #endif | |
| // Effects | |
| OSP_EXTRACT_FOG_FROM_WORLD_POS(i.positionWs); | |
| UNITY_APPLY_FOG(_unity_fogCoord, color.rgb); | |
| // Final transparency | |
| #if FADE_TRANSPARENCY_ON | |
| color.a = baseColor.a; | |
| #else | |
| color.a = 1.0 - oneMinusReflectivity + baseColor.a * oneMinusReflectivity; | |
| #endif | |
| return color; | |
| #endif | |
| // Cast shadows. Also, how the object appears in the camera depth texture. | |
| #if UNITY_PASS_SHADOWCASTER | |
| return OSP_SHADOW_CASTER_FRAGMENT(i); | |
| #endif | |
| // Lightmapping | |
| #if UNITY_PASS_META | |
| UnityMetaInput o; | |
| UNITY_INITIALIZE_OUTPUT(UnityMetaInput, o); | |
| #ifdef EDITOR_VISUALIZATION | |
| o.Albedo = diffuseColor; | |
| o.VizUV = i.vizUV; | |
| o.LightCoord = i.lightCoord; | |
| #else | |
| o.Albedo = diffuseColor + 0.5 * perceptualRoughness * specularColor; | |
| #endif | |
| o.SpecularColor = specularColor; | |
| #if EMISSION_MAP_ON | |
| o.Emission = emission; | |
| #endif | |
| return UnityMetaFragment(o); | |
| #endif | |
| return 0; | |
| } | |
| ENDHLSL | |
| SubShader | |
| { | |
| Tags | |
| { | |
| "PerformanceChecks" = "False" | |
| "Queue" = "Geometry" | |
| "RenderType" = "Opaque" | |
| } | |
| Cull [_CullMode] | |
| ZTest [_ZTest] | |
| ZWrite [_ZWrite] | |
| Pass | |
| { | |
| Tags | |
| { | |
| "LightMode" = "ForwardBase" | |
| } | |
| Blend [_BlendSrc] [_BlendDst] | |
| BlendOp [_BlendOp] | |
| HLSLPROGRAM | |
| #pragma vertex VertexProgram | |
| #pragma fragment FragmentProgram | |
| #pragma target 3.0 | |
| #pragma multi_compile_fog | |
| #pragma multi_compile_fwdbase | |
| #pragma multi_compile_instancing | |
| #pragma shader_feature_local ALPHA_TEST_ON | |
| #pragma shader_feature_local DETAIL_MAP_ON | |
| #pragma shader_feature_local EMISSION_MAP_ON | |
| #pragma shader_feature_local FADE_TRANSPARENCY_ON | |
| #pragma shader_feature_local MASK_MAP_ON | |
| #pragma shader_feature_local NORMAL_MAP_ON | |
| #pragma shader_feature_local PARALLAX_MAP_ON | |
| #pragma shader_feature_local PREMULTIPLY_ALPHA_ON | |
| #pragma shader_feature_local REFLECTION_MODE_NONE REFLECTION_MODE_PROBES | |
| #pragma shader_feature_local _SPECULARHIGHLIGHTS_OFF | |
| ENDHLSL | |
| } | |
| Pass | |
| { | |
| Tags | |
| { | |
| "LightMode" = "ForwardAdd" | |
| } | |
| Blend [_BlendSrc] One | |
| Fog { Color (0,0,0,0) } | |
| ZTest LEqual | |
| ZWrite Off | |
| HLSLPROGRAM | |
| #pragma vertex VertexProgram | |
| #pragma fragment FragmentProgram | |
| #pragma target 3.0 | |
| #pragma multi_compile_fog | |
| #pragma multi_compile_fwdadd_fullshadows | |
| #pragma shader_feature_local ALPHA_TEST_ON | |
| #pragma shader_feature_local DETAIL_MAP_ON | |
| #pragma shader_feature_local EMISSION_MAP_ON | |
| #pragma shader_feature_local FADE_TRANSPARENCY_ON | |
| #pragma shader_feature_local MASK_MAP_ON | |
| #pragma shader_feature_local NORMAL_MAP_ON | |
| #pragma shader_feature_local PARALLAX_MAP_ON | |
| #pragma shader_feature_local PREMULTIPLY_ALPHA_ON | |
| #pragma shader_feature_local _SPECULARHIGHLIGHTS_OFF | |
| ENDHLSL | |
| } | |
| Pass | |
| { | |
| Tags | |
| { | |
| "LightMode" = "ShadowCaster" | |
| } | |
| ZTest LEqual | |
| ZWrite On | |
| HLSLPROGRAM | |
| #pragma vertex VertexProgram | |
| #pragma fragment FragmentProgram | |
| #pragma target 3.0 | |
| #pragma multi_compile_instancing | |
| #pragma multi_compile_shadowcaster | |
| #pragma shader_feature_local ALPHA_TEST_ON | |
| #pragma shader_feature_local PARALLAX_MAP_ON | |
| ENDHLSL | |
| } | |
| Pass | |
| { | |
| Tags | |
| { | |
| "LightMode" = "Meta" | |
| } | |
| Cull Off | |
| HLSLPROGRAM | |
| #pragma vertex VertexProgram | |
| #pragma fragment FragmentProgram | |
| #pragma target 3.0 | |
| #pragma shader_feature_local ALPHA_TEST_ON | |
| #pragma shader_feature_local DETAIL_MAP_ON | |
| #pragma shader_feature EDITOR_VISUALIZATION | |
| #pragma shader_feature_local EMISSION_MAP_ON | |
| #pragma shader_feature_local MASK_MAP_ON | |
| #pragma shader_feature_local PARALLAX_MAP_ON | |
| #pragma shader_feature_local PREMULTIPLY_ALPHA_ON | |
| ENDHLSL | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment