405 lines
16 KiB
HLSL
405 lines
16 KiB
HLSL
#ifndef CUSTOM_LIGHTING_INCLUDED
|
|
#define CUSTOM_LIGHTING_INCLUDED
|
|
|
|
// @Cyanilux | https://github.com/Cyanilux/URP_ShaderGraphCustomLighting
|
|
// Note this version of the package assumes v12+ due to usage of "Branch on Input Connection" node
|
|
// For older versions, see branches on github repo!
|
|
|
|
//------------------------------------------------------------------------------------------------------
|
|
// Main Light
|
|
//------------------------------------------------------------------------------------------------------
|
|
|
|
/*
|
|
- Obtains the Direction, Color and Distance Atten for the Main Light.
|
|
- (DistanceAtten is either 0 or 1 for directional light, depending if the light is in the culling mask or not)
|
|
- If you want shadow attenutation, see MainLightShadows_float, or use MainLightFull_float instead
|
|
*/
|
|
void MainLight_float (out float3 Direction, out float3 Color, out float DistanceAtten){
|
|
#ifdef SHADERGRAPH_PREVIEW
|
|
Direction = normalize(float3(1,1,-0.4));
|
|
Color = float4(1,1,1,1);
|
|
DistanceAtten = 1;
|
|
#else
|
|
Light mainLight = GetMainLight();
|
|
Direction = mainLight.direction;
|
|
Color = mainLight.color;
|
|
DistanceAtten = mainLight.distanceAttenuation;
|
|
#endif
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------
|
|
// Main Light Layer Test
|
|
//------------------------------------------------------------------------------------------------------
|
|
|
|
#ifndef SHADERGRAPH_PREVIEW
|
|
#if UNITY_VERSION < 202220
|
|
/*
|
|
GetMeshRenderingLayer() is only available in 2022.2+
|
|
Previous versions need to use GetMeshRenderingLightLayer()
|
|
*/
|
|
uint GetMeshRenderingLayer(){
|
|
return GetMeshRenderingLightLayer();
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
/*
|
|
- Tests whether the Main Light Layer Mask appears in the Rendering Layers from renderer
|
|
- (Used to support Light Layers, pass your shading from Main Light into this)
|
|
- To work in an Unlit Graph, requires keywords :
|
|
- Boolean Keyword, Global Multi-Compile "_LIGHT_LAYERS"
|
|
*/
|
|
void MainLightLayer_float(float3 Shading, out float3 Out){
|
|
#ifdef SHADERGRAPH_PREVIEW
|
|
Out = Shading;
|
|
#else
|
|
Out = 0;
|
|
uint meshRenderingLayers = GetMeshRenderingLayer();
|
|
#ifdef _LIGHT_LAYERS
|
|
if (IsMatchingLightLayer(GetMainLight().layerMask, meshRenderingLayers))
|
|
#endif
|
|
{
|
|
Out = Shading;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
- Obtains the Light Cookie assigned to the Main Light
|
|
- (For usage, You'd want to Multiply the result with your Light Colour)
|
|
- To work in an Unlit Graph, requires keywords :
|
|
- Boolean Keyword, Global Multi-Compile "_LIGHT_COOKIES"
|
|
*/
|
|
void MainLightCookie_float(float3 WorldPos, out float3 Cookie){
|
|
Cookie = 1;
|
|
#if defined(_LIGHT_COOKIES)
|
|
Cookie = SampleMainLightCookie(WorldPos);
|
|
#endif
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------
|
|
// Main Light Shadows
|
|
//------------------------------------------------------------------------------------------------------
|
|
|
|
/*
|
|
- This undef (un-define) is required to prevent the "invalid subscript 'shadowCoord'" error,
|
|
which occurs when _MAIN_LIGHT_SHADOWS is used with 1/No Shadow Cascades with the Unlit Graph.
|
|
- It's not required for the PBR/Lit graph, so I'm using the SHADERPASS_FORWARD to ignore it for that pass
|
|
*/
|
|
#ifndef SHADERGRAPH_PREVIEW
|
|
#include "Packages/com.unity.render-pipelines.universal/Editor/ShaderGraph/Includes/ShaderPass.hlsl"
|
|
#if (SHADERPASS != SHADERPASS_FORWARD)
|
|
#undef REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR
|
|
#endif
|
|
#endif
|
|
|
|
/*
|
|
- Samples the Shadowmap for the Main Light, based on the World Position passed in. (Position node)
|
|
- For shadows to work in the Unlit Graph, the following keywords must be defined in the blackboard :
|
|
- Enum Keyword, Global Multi-Compile "_MAIN_LIGHT", with entries :
|
|
- "SHADOWS"
|
|
- "SHADOWS_CASCADE"
|
|
- "SHADOWS_SCREEN"
|
|
- Boolean Keyword, Global Multi-Compile "_SHADOWS_SOFT"
|
|
- For a PBR/Lit Graph, these keywords are already handled for you.
|
|
*/
|
|
void MainLightShadows_float (float3 WorldPos, half4 Shadowmask, out float ShadowAtten){
|
|
#ifdef SHADERGRAPH_PREVIEW
|
|
ShadowAtten = 1;
|
|
#else
|
|
#if defined(_MAIN_LIGHT_SHADOWS_SCREEN) && !defined(_SURFACE_TYPE_TRANSPARENT)
|
|
float4 shadowCoord = ComputeScreenPos(TransformWorldToHClip(WorldPos));
|
|
#else
|
|
float4 shadowCoord = TransformWorldToShadowCoord(WorldPos);
|
|
#endif
|
|
ShadowAtten = MainLightShadow(shadowCoord, WorldPos, Shadowmask, _MainLightOcclusionProbes);
|
|
#endif
|
|
}
|
|
|
|
void MainLightShadows_float (float3 WorldPos, out float ShadowAtten){
|
|
MainLightShadows_float(WorldPos, half4(1,1,1,1), ShadowAtten);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------
|
|
// Shadowmask (v10+)
|
|
//------------------------------------------------------------------------------------------------------
|
|
|
|
/*
|
|
- Used to support "Shadowmask" mode in Lighting window.
|
|
- Should be sampled once in graph, then input into the Main Light Shadows and/or Additional Light subgraphs/functions.
|
|
- To work in an Unlit Graph, likely requires keywords :
|
|
- Boolean Keyword, Global Multi-Compile "SHADOWS_SHADOWMASK"
|
|
- Boolean Keyword, Global Multi-Compile "LIGHTMAP_SHADOW_MIXING"
|
|
- (also LIGHTMAP_ON, but I believe Shader Graph is already defining this one)
|
|
*/
|
|
void Shadowmask_half (float2 lightmapUV, out half4 Shadowmask){
|
|
#ifdef SHADERGRAPH_PREVIEW
|
|
Shadowmask = half4(1,1,1,1);
|
|
#else
|
|
OUTPUT_LIGHTMAP_UV(lightmapUV, unity_LightmapST, lightmapUV);
|
|
Shadowmask = SAMPLE_SHADOWMASK(lightmapUV);
|
|
#endif
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------
|
|
// Ambient Lighting
|
|
//------------------------------------------------------------------------------------------------------
|
|
|
|
/*
|
|
- Uses "SampleSH", the spherical harmonic stuff that ambient lighting / light probes uses.
|
|
- Will likely be used in the fragment, so will be per-pixel.
|
|
- Alternatively could use the Baked GI node, as it'll also handle this for you.
|
|
- Could also use the Ambient node, would be cheaper but the result won't automatically adapt based on the Environmental Lighting Source (Lighting tab).
|
|
*/
|
|
void AmbientSampleSH_float (float3 WorldNormal, out float3 Ambient){
|
|
#ifdef SHADERGRAPH_PREVIEW
|
|
Ambient = float3(0.1, 0.1, 0.1); // Default ambient colour for previews
|
|
#else
|
|
Ambient = SampleSH(WorldNormal);
|
|
#endif
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------
|
|
// Subtractive Baked GI
|
|
//------------------------------------------------------------------------------------------------------
|
|
/*
|
|
- Used to support "Subtractive" mode in Lighting window.
|
|
- To work in an Unlit Graph, likely requires keywords :
|
|
- Boolean Keyword, Global Multi-Compile "LIGHTMAP_SHADOW_MIXING"
|
|
- (also LIGHTMAP_ON, but I believe Shader Graph is already defining this one)
|
|
*/
|
|
void SubtractiveGI_float (float ShadowAtten, float3 normalWS, float3 bakedGI, out half3 result){
|
|
#ifdef SHADERGRAPH_PREVIEW
|
|
result = half3(1,1,1);
|
|
#else
|
|
Light mainLight = GetMainLight();
|
|
mainLight.shadowAttenuation = ShadowAtten;
|
|
MixRealtimeAndBakedGI(mainLight, normalWS, bakedGI);
|
|
result = bakedGI;
|
|
#endif
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------
|
|
// Mix Fog
|
|
//------------------------------------------------------------------------------------------------------
|
|
|
|
/*
|
|
- Adds fog to the colour, based on the Fog settings in the Lighting tab.
|
|
- Note : Not required for v12, can use Lerp instead. See "Mix Fog" SubGraph
|
|
*/
|
|
void MixFog_float (float3 Colour, float Fog, out float3 Out){
|
|
#ifdef SHADERGRAPH_PREVIEW
|
|
Out = Colour;
|
|
#else
|
|
Out = MixFog(Colour, Fog);
|
|
#endif
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------
|
|
// Default Additional Lights
|
|
//------------------------------------------------------------------------------------------------------
|
|
|
|
/*
|
|
- Handles additional lights (e.g. additional directional, point, spotlights)
|
|
- For custom lighting, you may want to duplicate this and swap the LightingLambert / LightingSpecular functions out. See Toon Example below!
|
|
- To work in the Unlit Graph, the following keywords must be defined in the blackboard :
|
|
- Boolean Keyword, Global Multi-Compile "_ADDITIONAL_LIGHT_SHADOWS"
|
|
- Boolean Keyword, Global Multi-Compile "_ADDITIONAL_LIGHTS"
|
|
- To support Forward+ path,
|
|
- Boolean Keyword, Global Multi-Compile "_FORWARD_PLUS" (2022.2+)
|
|
*/
|
|
void AdditionalLights_float(float3 SpecColor, float Smoothness, float3 WorldPosition, float3 WorldNormal, float3 WorldView, half4 Shadowmask,
|
|
out float3 Diffuse, out float3 Specular) {
|
|
float3 diffuseColor = 0;
|
|
float3 specularColor = 0;
|
|
#ifndef SHADERGRAPH_PREVIEW
|
|
Smoothness = exp2(10 * Smoothness + 1);
|
|
uint pixelLightCount = GetAdditionalLightsCount();
|
|
uint meshRenderingLayers = GetMeshRenderingLayer();
|
|
|
|
#if USE_FORWARD_PLUS
|
|
for (uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); lightIndex++) {
|
|
FORWARD_PLUS_SUBTRACTIVE_LIGHT_CHECK
|
|
Light light = GetAdditionalLight(lightIndex, WorldPosition, Shadowmask);
|
|
#ifdef _LIGHT_LAYERS
|
|
if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
|
|
#endif
|
|
{
|
|
// Blinn-Phong
|
|
float3 attenuatedLightColor = light.color * (light.distanceAttenuation * light.shadowAttenuation);
|
|
diffuseColor += LightingLambert(attenuatedLightColor, light.direction, WorldNormal);
|
|
specularColor += LightingSpecular(attenuatedLightColor, light.direction, WorldNormal, WorldView, float4(SpecColor, 0), Smoothness);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// For Foward+ the LIGHT_LOOP_BEGIN macro will use inputData.normalizedScreenSpaceUV, inputData.positionWS, so create that:
|
|
InputData inputData = (InputData)0;
|
|
float4 screenPos = ComputeScreenPos(TransformWorldToHClip(WorldPosition));
|
|
inputData.normalizedScreenSpaceUV = screenPos.xy / screenPos.w;
|
|
inputData.positionWS = WorldPosition;
|
|
|
|
LIGHT_LOOP_BEGIN(pixelLightCount)
|
|
Light light = GetAdditionalLight(lightIndex, WorldPosition, Shadowmask);
|
|
#ifdef _LIGHT_LAYERS
|
|
if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
|
|
#endif
|
|
{
|
|
// Blinn-Phong
|
|
float3 attenuatedLightColor = light.color * (light.distanceAttenuation * light.shadowAttenuation);
|
|
diffuseColor += LightingLambert(attenuatedLightColor, light.direction, WorldNormal);
|
|
specularColor += LightingSpecular(attenuatedLightColor, light.direction, WorldNormal, WorldView, float4(SpecColor, 0), Smoothness);
|
|
}
|
|
LIGHT_LOOP_END
|
|
#endif
|
|
|
|
Diffuse = diffuseColor;
|
|
Specular = specularColor;
|
|
}
|
|
|
|
// For backwards compatibility (before Shadowmask was introduced)
|
|
void AdditionalLights_float(float3 SpecColor, float Smoothness, float3 WorldPosition, float3 WorldNormal, float3 WorldView,
|
|
out float3 Diffuse, out float3 Specular) {
|
|
AdditionalLights_float(SpecColor, Smoothness, WorldPosition, WorldNormal, WorldView, half4(1,1,1,1), Diffuse, Specular);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------
|
|
// Additional Lights Toon Example
|
|
//------------------------------------------------------------------------------------------------------
|
|
|
|
/*
|
|
- Calculates light attenuation values to produce multiple bands for a toon effect. See AdditionalLightsToon function below
|
|
*/
|
|
#ifndef SHADERGRAPH_PREVIEW
|
|
float ToonAttenuation(int lightIndex, float3 positionWS, float pointBands, float spotBands){
|
|
#if !USE_FORWARD_PLUS
|
|
lightIndex = GetPerObjectLightIndex(lightIndex);
|
|
#endif
|
|
#if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA
|
|
float4 lightPositionWS = _AdditionalLightsBuffer[lightIndex].position;
|
|
half4 spotDirection = _AdditionalLightsBuffer[lightIndex].spotDirection;
|
|
half4 distanceAndSpotAttenuation = _AdditionalLightsBuffer[lightIndex].attenuation;
|
|
#else
|
|
float4 lightPositionWS = _AdditionalLightsPosition[lightIndex];
|
|
half4 spotDirection = _AdditionalLightsSpotDir[lightIndex];
|
|
half4 distanceAndSpotAttenuation = _AdditionalLightsAttenuation[lightIndex];
|
|
#endif
|
|
|
|
// Point
|
|
float3 lightVector = lightPositionWS.xyz - positionWS * lightPositionWS.w;
|
|
float distanceSqr = max(dot(lightVector, lightVector), HALF_MIN);
|
|
float range = rsqrt(distanceAndSpotAttenuation.x);
|
|
float dist = sqrt(distanceSqr) / range;
|
|
|
|
// Spot
|
|
half3 lightDirection = half3(lightVector * rsqrt(distanceSqr));
|
|
half SdotL = dot(spotDirection.xyz, lightDirection);
|
|
half spotAtten = saturate(SdotL * distanceAndSpotAttenuation.z + distanceAndSpotAttenuation.w);
|
|
spotAtten *= spotAtten;
|
|
float maskSpotToRange = step(dist, 1);
|
|
|
|
// Atten
|
|
bool isSpot = (distanceAndSpotAttenuation.z > 0);
|
|
return isSpot ?
|
|
//step(0.01, spotAtten) : // cheaper if you just want "1" band for spot lights
|
|
(floor(spotAtten * spotBands) / spotBands) * maskSpotToRange :
|
|
saturate(1.0 - floor(dist * pointBands) / pointBands);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
- Handles additional lights (e.g. point, spotlights) with banded toon effect
|
|
- For shadows to work in the Unlit Graph, the following keywords must be defined in the blackboard :
|
|
- Boolean Keyword, Global Multi-Compile "_ADDITIONAL_LIGHT_SHADOWS"
|
|
- Boolean Keyword, Global Multi-Compile "_ADDITIONAL_LIGHTS" (required to prevent the one above from being stripped from builds)
|
|
- For a PBR/Lit Graph, these keywords are already handled for you.
|
|
*/
|
|
void AdditionalLightsToon_float(float3 SpecColor, float Smoothness, float3 WorldPosition, float3 WorldNormal, float3 WorldView, half4 Shadowmask,
|
|
float PointLightBands, float SpotLightBands,
|
|
out float3 Diffuse, out float3 Specular) {
|
|
float3 diffuseColor = 0;
|
|
float3 specularColor = 0;
|
|
|
|
#ifndef SHADERGRAPH_PREVIEW
|
|
Smoothness = exp2(10 * Smoothness + 1);
|
|
uint pixelLightCount = GetAdditionalLightsCount();
|
|
uint meshRenderingLayers = GetMeshRenderingLayer();
|
|
|
|
#if USE_FORWARD_PLUS
|
|
for (uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); lightIndex++) {
|
|
FORWARD_PLUS_SUBTRACTIVE_LIGHT_CHECK
|
|
Light light = GetAdditionalLight(lightIndex, WorldPosition, Shadowmask);
|
|
#ifdef _LIGHT_LAYERS
|
|
if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
|
|
#endif
|
|
{
|
|
if (PointLightBands <= 1 && SpotLightBands <= 1){
|
|
// Solid colour lights
|
|
diffuseColor += light.color * step(0.0001, light.distanceAttenuation * light.shadowAttenuation);
|
|
}else{
|
|
// Multiple bands
|
|
diffuseColor += light.color * light.shadowAttenuation * ToonAttenuation(lightIndex, WorldPosition, PointLightBands, SpotLightBands);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// For Foward+ the LIGHT_LOOP_BEGIN macro will use inputData.normalizedScreenSpaceUV, inputData.positionWS, so create that:
|
|
InputData inputData = (InputData)0;
|
|
float4 screenPos = ComputeScreenPos(TransformWorldToHClip(WorldPosition));
|
|
inputData.normalizedScreenSpaceUV = screenPos.xy / screenPos.w;
|
|
inputData.positionWS = WorldPosition;
|
|
|
|
LIGHT_LOOP_BEGIN(pixelLightCount)
|
|
Light light = GetAdditionalLight(lightIndex, WorldPosition, Shadowmask);
|
|
#ifdef _LIGHT_LAYERS
|
|
if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
|
|
#endif
|
|
{
|
|
if (PointLightBands <= 1 && SpotLightBands <= 1){
|
|
// Solid colour lights
|
|
diffuseColor += light.color * step(0.0001, light.distanceAttenuation * light.shadowAttenuation);
|
|
}else{
|
|
// Multiple bands
|
|
diffuseColor += light.color * light.shadowAttenuation * ToonAttenuation(lightIndex, WorldPosition, PointLightBands, SpotLightBands);
|
|
}
|
|
}
|
|
LIGHT_LOOP_END
|
|
#endif
|
|
|
|
/*
|
|
#ifndef SHADERGRAPH_PREVIEW
|
|
Smoothness = exp2(10 * Smoothness + 1);
|
|
WorldNormal = normalize(WorldNormal);
|
|
WorldView = SafeNormalize(WorldView);
|
|
int pixelLightCount = GetAdditionalLightsCount();
|
|
for (int i = 0; i < pixelLightCount; ++i) {
|
|
Light light = GetAdditionalLight(i, WorldPosition, Shadowmask);
|
|
|
|
// DIFFUSE
|
|
if (PointLightBands <= 1 && SpotLightBands <= 1){
|
|
// Solid colour lights
|
|
diffuseColor += light.color * step(0.0001, light.distanceAttenuation * light.shadowAttenuation);
|
|
}else{
|
|
// Multiple bands :
|
|
diffuseColor += light.color * light.shadowAttenuation * ToonAttenuation(i, WorldPosition, PointLightBands, SpotLightBands);
|
|
}
|
|
}
|
|
#endif
|
|
*/
|
|
|
|
Diffuse = diffuseColor;
|
|
Specular = specularColor;
|
|
// Didn't really like the look of specular lighting in the toon shader here, so just keeping it at 0
|
|
}
|
|
|
|
// For backwards compatibility (before Shadowmask was introduced)
|
|
void AdditionalLightsToon_float(float3 SpecColor, float Smoothness, float3 WorldPosition, float3 WorldNormal, float3 WorldView,
|
|
float PointLightBands, float SpotLightBands,
|
|
out float3 Diffuse, out float3 Specular) {
|
|
AdditionalLightsToon_float(SpecColor, Smoothness, WorldPosition, WorldNormal, WorldView, half4(1,1,1,1),
|
|
PointLightBands, SpotLightBands,Diffuse, Specular);
|
|
}
|
|
|
|
#endif // CUSTOM_LIGHTING_INCLUDED
|