Skip to content

Instantly share code, notes, and snippets.

@NedMakesGames
Last active May 30, 2023 02:36
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save NedMakesGames/0fc3cc299443d7efe81f25486c7178ec to your computer and use it in GitHub Desktop.
Save NedMakesGames/0fc3cc299443d7efe81f25486c7178ec to your computer and use it in GitHub Desktop.
NedMakesGames URP Shaders Part 02
// MIT License
// Copyright (c) 2023 NedMakesGames
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
Shader "NedMakesGames/MyLit" {
// Properties are options set per material, exposed by the material inspector
Properties{
[Header(Surface options)] // Creates a text header
// [MainTexture] and [MainColor] allow Material.mainTexture and Material.color to use the correct properties
[MainTexture] _ColorMap("Color", 2D) = "white" {}
[MainColor] _ColorTint("Tint", Color) = (1, 1, 1, 1)
_Smoothness("Smoothness", Float) = 0
}
// Subshaders allow for different behaviour and options for different pipelines and platforms
SubShader {
// These tags are shared by all passes in this sub shader
Tags {"RenderPipeline" = "UniversalPipeline"}
// Shaders can have several passes which are used to render different data about the material
// Each pass has it's own vertex and fragment function and shader variant keywords
Pass {
Name "ForwardLit" // For debugging
Tags{"LightMode" = "UniversalForward"} // Pass specific tags.
// "UniversalForward" tells Unity this is the main lighting pass of this shader
HLSLPROGRAM // Begin HLSL code
#define _SPECULAR_COLOR
// Shader variant keywords
// Unity automatically discards unused variants created using "shader_feature" from your final game build,
// however it keeps all variants created using "multi_compile"
// For this reason, multi_compile is good for global keywords or keywords that can change at runtime
// while shader_feature is good for keywords set per material which will not change at runtime
// Global URP keywords
#if UNITY_VERSION >= 202120
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE
#else
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
#endif
#pragma multi_compile_fragment _ _SHADOWS_SOFT
// Register our programmable stage functions
#pragma vertex Vertex
#pragma fragment Fragment
// Include our code file
#include "MyLitForwardLitPass.hlsl"
ENDHLSL
}
Pass {
// The shadow caster pass, which draws to shadow maps
Name "ShadowCaster"
Tags{"LightMode" = "ShadowCaster"}
ColorMask 0 // No color output, only depth
HLSLPROGRAM
#pragma vertex Vertex
#pragma fragment Fragment
#include "MyLitShadowCasterPass.hlsl"
ENDHLSL
}
}
}
// MIT License
// Copyright (c) 2023 NedMakesGames
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// This file contains the vertex and fragment functions for the forward lit pass
// This is the shader pass that computes visible colors for a material
// by reading material, light, shadow, etc. data
// Pull in URP library functions and our own common functions
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
// Textures
TEXTURE2D(_ColorMap); SAMPLER(sampler_ColorMap); // RGB = albedo, A = alpha
float4 _ColorMap_ST; // This is automatically set by Unity. Used in TRANSFORM_TEX to apply UV tiling
float4 _ColorTint;
float _Smoothness;
// This attributes struct receives data about the mesh we're currently rendering
// Data is automatically placed in fields according to their semantic
struct Attributes {
float3 positionOS : POSITION; // Position in object space
float3 normalOS : NORMAL; // Normal in object space
float2 uv : TEXCOORD0; // Material texture UVs
};
// This struct is output by the vertex function and input to the fragment function.
// Note that fields will be transformed by the intermediary rasterization stage
struct Interpolators {
// This value should contain the position in clip space (which is similar to a position on screen)
// when output from the vertex function. It will be transformed into pixel position of the current
// fragment on the screen when read from the fragment function
float4 positionCS : SV_POSITION;
// The following variables will retain their values from the vertex stage, except the
// rasterizer will interpolate them between vertices
float2 uv : TEXCOORD0; // Material texture UVs
float3 positionWS : TEXCOORD1; // Position in world space
float3 normalWS : TEXCOORD2; // Normal in world space
};
// The vertex function. This runs for each vertex on the mesh.
// It must output the position on the screen each vertex should appear at,
// as well as any data the fragment function will need
Interpolators Vertex(Attributes input) {
Interpolators output;
// These helper functions, found in URP/ShaderLib/ShaderVariablesFunctions.hlsl
// transform object space values into world and clip space
VertexPositionInputs posnInputs = GetVertexPositionInputs(input.positionOS);
VertexNormalInputs normInputs = GetVertexNormalInputs(input.normalOS);
// Pass position and orientation data to the fragment function
output.positionCS = posnInputs.positionCS;
output.uv = TRANSFORM_TEX(input.uv, _ColorMap);
output.normalWS = normInputs.normalWS;
output.positionWS = posnInputs.positionWS;
return output;
}
// The fragment function. This runs once per fragment, which you can think of as a pixel on the screen
// It must output the final color of this pixel
float4 Fragment(Interpolators input) : SV_TARGET{
float2 uv = input.uv;
// Sample the color map
float4 colorSample = SAMPLE_TEXTURE2D(_ColorMap, sampler_ColorMap, uv);
// For lighting, create the InputData struct, which contains position and orientation data
InputData lightingInput = (InputData)0; // Found in URP/ShaderLib/Input.hlsl
lightingInput.positionWS = input.positionWS;
lightingInput.normalWS = normalize(input.normalWS);
lightingInput.viewDirectionWS = GetWorldSpaceNormalizeViewDir(input.positionWS); // In ShaderVariablesFunctions.hlsl
lightingInput.shadowCoord = TransformWorldToShadowCoord(input.positionWS); // In Shadows.hlsl
// Calculate the surface data struct, which contains data from the material textures
SurfaceData surfaceInput = (SurfaceData)0;
surfaceInput.albedo = colorSample.rgb * _ColorTint.rgb;
surfaceInput.alpha = colorSample.a * _ColorTint.a;
surfaceInput.specular = 1;
surfaceInput.smoothness = _Smoothness;
#if UNITY_VERSION >= 202120
return UniversalFragmentBlinnPhong(lightingInput, surfaceInput);
#else
return UniversalFragmentBlinnPhong(lightingInput, surfaceInput.albedo, float4(surfaceInput.specular, 1), surfaceInput.smoothness, surfaceInput.emission, surfaceInput.alpha);
#endif
}
// MIT License
// Copyright (c) 2023 NedMakesGames
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// The shadow caster pass calculates the shadow silhouette of this mesh.
// It's much like the depth only pass, but has a few tweaks to take shadow
// bias offsets into account
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
struct Attributes {
float3 positionOS : POSITION;
float3 normalOS : NORMAL;
};
struct Interpolators {
float4 positionCS : SV_POSITION;
};
// These are set by Unity for the light currently "rendering" this shadow caster pass
float3 _LightDirection;
// This function offsets the clip space position by the depth and normal shadow biases
float4 GetShadowCasterPositionCS(float3 positionWS, float3 normalWS) {
float3 lightDirectionWS = _LightDirection;
// From URP's ShadowCasterPass.hlsl
float4 positionCS = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, lightDirectionWS));
// We have to make sure that the shadow bias didn't push the shadow out of
// the camera's view area. This is slightly different depending on the graphics API
#if UNITY_REVERSED_Z
positionCS.z = min(positionCS.z, UNITY_NEAR_CLIP_VALUE);
#else
positionCS.z = max(positionCS.z, UNITY_NEAR_CLIP_VALUE);
#endif
return positionCS;
}
Interpolators Vertex(Attributes input) {
Interpolators output;
VertexPositionInputs posnInputs = GetVertexPositionInputs(input.positionOS); // Found in URP/ShaderLib/ShaderVariablesFunctions.hlsl
VertexNormalInputs normInputs = GetVertexNormalInputs(input.normalOS); // Found in URP/ShaderLib/ShaderVariablesFunctions.hlsl
output.positionCS = GetShadowCasterPositionCS(posnInputs.positionWS, normInputs.normalWS);
return output;
}
float4 Fragment(Interpolators input) : SV_TARGET {
return 0;
}
@alansley
Copy link

alansley commented Oct 13, 2022

Terrific tutorial series - incredibly well put together & really enjoying!

I had a quick query though - should line 56 of MyLit.shader read:

#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE

rather than:

#pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE

The reason being, you say that in Unity 2021.2 onwards _MAIN_LIGHT_SHADOWS_CASCADE implies (i.e., auto-enables) _MAIN_LIGHT_SHADOWS. As such, just having "without" (_) and the _CASCADE version would seem to do the job and decrease the variant count.

Thanks again for the great series & apols for wasting your time if I've misunderstood!

@NedMakesGames
Copy link
Author

NedMakesGames commented Oct 15, 2022 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment