Skip to content

Instantly share code, notes, and snippets.

@niallobrien
Last active August 11, 2025 22:50
Show Gist options
  • Save niallobrien/230908c04bcbb0b451c415c12da993a5 to your computer and use it in GitHub Desktop.
Save niallobrien/230908c04bcbb0b451c415c12da993a5 to your computer and use it in GitHub Desktop.
Lit URP shader with vertex color support
Shader "Custom/URP/LitWithVertexColor"
{
Properties
{
_BaseMap ("Base Map", 2D) = "white" {}
_BaseColor ("Base Color", Color) = (1,1,1,1)
_Smoothness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader
{
Tags
{
"RenderPipeline" = "UniversalPipeline"
"RenderType" = "Opaque"
"Queue" = "Geometry"
}
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);
CBUFFER_START(UnityPerMaterial)
float4 _BaseColor;
float _Smoothness;
float _Metallic;
float4 _BaseMap_ST;
CBUFFER_END
ENDHLSL
Pass
{
Name "ForwardLit"
Tags { "LightMode" = "UniversalForward" }
HLSLPROGRAM
#pragma vertex Vert
#pragma fragment Frag
#pragma multi_compile_fog
#pragma multi_compile_instancing
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
#pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS
#pragma multi_compile _ _SHADOWS_SOFT
#pragma multi_compile _ REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR
#pragma multi_compile _ MAIN_LIGHT_CALCULATE_SHADOWS
struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float4 tangentOS : TANGENT;
float2 uv : TEXCOORD0;
float4 color : COLOR;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
float3 positionWS : TEXCOORD1;
float3 normalWS : TEXCOORD2;
float4 color : COLOR;
float4 fogFactorAndVertexLight : TEXCOORD3; // x: fogFactor, yzw: vertex light
#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
float4 shadowCoord : TEXCOORD4;
#endif
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
Varyings Vert(Attributes input)
{
Varyings output = (Varyings)0;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
// Transformations
VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS, input.tangentOS);
// Position outputs
output.positionCS = vertexInput.positionCS;
output.positionWS = vertexInput.positionWS;
output.normalWS = normalInput.normalWS;
// UVs and vertex color
output.uv = input.uv * _BaseMap_ST.xy + _BaseMap_ST.zw; // Manual expansion of TRANSFORM_TEX macro
output.color = input.color;
// Lighting and fog
half3 vertexLight = VertexLighting(vertexInput.positionWS, normalInput.normalWS);
half fogFactor = ComputeFogFactor(vertexInput.positionCS.z);
output.fogFactorAndVertexLight = half4(fogFactor, vertexLight);
#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
output.shadowCoord = GetShadowCoord(vertexInput);
#endif
return output;
}
half4 Frag(Varyings input) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(input);
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
// Sample texture and apply vertex color
half4 albedoAlpha = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv);
half4 baseColor = albedoAlpha * _BaseColor * input.color;
// Setup SurfaceData
SurfaceData surfaceData = (SurfaceData)0;
surfaceData.albedo = baseColor.rgb;
surfaceData.alpha = baseColor.a;
surfaceData.metallic = _Metallic;
surfaceData.smoothness = _Smoothness;
surfaceData.normalTS = float3(0, 0, 1);
surfaceData.occlusion = 1.0;
surfaceData.emission = 0;
// Setup InputData
InputData inputData = (InputData)0;
inputData.positionWS = input.positionWS;
inputData.normalWS = NormalizeNormalPerPixel(input.normalWS);
inputData.viewDirectionWS = GetWorldSpaceNormalizeViewDir(input.positionWS);
#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
inputData.shadowCoord = input.shadowCoord;
#elif defined(MAIN_LIGHT_CALCULATE_SHADOWS)
inputData.shadowCoord = TransformWorldToShadowCoord(input.positionWS);
#else
inputData.shadowCoord = float4(0, 0, 0, 0);
#endif
inputData.fogCoord = input.fogFactorAndVertexLight.x;
inputData.vertexLighting = input.fogFactorAndVertexLight.yzw;
inputData.bakedGI = SampleSH(input.normalWS); // Properly sample ambient lighting
// Calculate lighting
half4 finalColor = UniversalFragmentPBR(inputData, surfaceData);
// Apply fog
float fogFactor = input.fogFactorAndVertexLight.x;
finalColor.rgb = MixFog(finalColor.rgb, fogFactor);
return finalColor;
}
ENDHLSL
}
// Shadow Caster Pass
Pass
{
Name "ShadowCaster"
Tags {"LightMode" = "ShadowCaster"}
ZWrite On
ZTest LEqual
ColorMask 0
HLSLPROGRAM
#pragma vertex ShadowPassVertex
#pragma fragment ShadowPassFragment
#pragma multi_compile_instancing
// Shadow Caster Pass Implementation
#include "Packages/com.unity.render-pipelines.universal/Shaders/ShadowCasterPass.hlsl"
ENDHLSL
}
// Depth Only Pass
Pass
{
Name "DepthOnly"
Tags {"LightMode" = "DepthOnly"}
ZWrite On
ColorMask 0
HLSLPROGRAM
#pragma vertex DepthOnlyVertex
#pragma fragment DepthOnlyFragment
#pragma multi_compile_instancing
#include "Packages/com.unity.render-pipelines.universal/Shaders/DepthOnlyPass.hlsl"
ENDHLSL
}
}
FallBack "Universal Forward"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment