Skip to content

Commit 15b5de3

Browse files
committed
(PR #2) Adds specular lighting based on the directionality of the baked lightmap lighting.
(PR #2) Derives the correct, but un-normalized light direction vector from the lightmap. (PR #2) Adds extra debug functions, and remapping functions. (PR #2) Decreases smoothness of materials in the non-directional light, to simulate specular scattering. (PR #2) Add warning about extra texture fetch. (PR #3) Fix up shadergraph lit shader to populate the lightmap direction vector.
1 parent 6b6a61f commit 15b5de3

File tree

9 files changed

+162
-10
lines changed

9 files changed

+162
-10
lines changed

com.unity.render-pipelines.core/ShaderLibrary/Debug.hlsl

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,4 +276,104 @@ real3 GetColorCodeFunction(real value, real4 threshold)
276276
return outColor;
277277
}
278278

279+
// Maps a value within the range to the range of colors in a heatmap.
280+
// 3 color bands: Red, Black, Green
281+
half4 HeatMapColorRedBlackGreen(half value, half minValue, half maxValue)
282+
{
283+
#define HEATMAP_COLORS_COUNT 3
284+
half4 colors[HEATMAP_COLORS_COUNT] =
285+
{
286+
// min value of the range
287+
//half4(0.32, 0.00, 0.32, 1.00),
288+
//half4(0.00, 0.00, 1.00, 1.00),
289+
//half4(0.00, 1.00, 0.00, 1.00),
290+
//half4(1.00, 1.00, 0.00, 1.00),
291+
//half4(1.00, 0.60, 0.00, 1.00),
292+
//half4(1.00, 0.00, 0.00, 1.00),
293+
half4(1, 0, 0, 1),
294+
half4(0, 0, 0, 1),
295+
half4(0, 1, 0, 1)
296+
// max value of the range
297+
};
298+
half ratio=(HEATMAP_COLORS_COUNT-1.0)*saturate((value-minValue)/(maxValue-minValue));
299+
half indexMin=floor(ratio);
300+
half indexMax=min(indexMin+1,HEATMAP_COLORS_COUNT-1);
301+
302+
return lerp(colors[indexMin], colors[indexMax], ratio-indexMin);
303+
}
304+
305+
// Maps a value within the range to the range of colors in a heatmap.
306+
// 3 color bands: Red, Black, White
307+
half4 HeatMapColorRedBlackWhite(half value, half minValue, half maxValue)
308+
{
309+
#define HEATMAP_COLORS_COUNT 3
310+
half4 colors[HEATMAP_COLORS_COUNT] =
311+
{
312+
// min value of the range
313+
//half4(0.32, 0.00, 0.32, 1.00),
314+
//half4(0.00, 0.00, 1.00, 1.00),
315+
//half4(0.00, 1.00, 0.00, 1.00),
316+
//half4(1.00, 1.00, 0.00, 1.00),
317+
//half4(1.00, 0.60, 0.00, 1.00),
318+
//half4(1.00, 0.00, 0.00, 1.00),
319+
half4(1, 0, 0, 1),
320+
half4(0, 0, 0, 1),
321+
half4(1, 1, 1, 1)
322+
// max value of the range
323+
};
324+
half ratio=(HEATMAP_COLORS_COUNT-1.0)*saturate((value-minValue)/(maxValue-minValue));
325+
half indexMin=floor(ratio);
326+
half indexMax=min(indexMin+1,HEATMAP_COLORS_COUNT-1);
327+
328+
return lerp(colors[indexMin], colors[indexMax], ratio-indexMin);
329+
}
330+
331+
// Maps a value within the range to the range of colors in a heatmap.
332+
// 2 color bands: Red, Black, White
333+
half4 HeatMapColorBlackWhite(half value, half minValue, half maxValue)
334+
{
335+
#define HEATMAP_COLORS_COUNT 2
336+
half4 colors[HEATMAP_COLORS_COUNT] =
337+
{
338+
// min value of the range
339+
//half4(0.32, 0.00, 0.32, 1.00),
340+
//half4(0.00, 0.00, 1.00, 1.00),
341+
//half4(0.00, 1.00, 0.00, 1.00),
342+
//half4(1.00, 1.00, 0.00, 1.00),
343+
//half4(1.00, 0.60, 0.00, 1.00),
344+
//half4(1.00, 0.00, 0.00, 1.00),
345+
half4(0, 0, 0, 1),
346+
half4(1, 1, 1, 1)
347+
// max value of the range
348+
};
349+
half ratio=(HEATMAP_COLORS_COUNT-1.0)*saturate((value-minValue)/(maxValue-minValue));
350+
half indexMin=floor(ratio);
351+
half indexMax=min(indexMin+1,HEATMAP_COLORS_COUNT-1);
352+
353+
return lerp(colors[indexMin], colors[indexMax], ratio-indexMin);
354+
}
355+
356+
// Maps a value within the range to the range of colors in a heatmap.
357+
// 6 color bands
358+
half4 HeatMapColorMulticolor(half value, half minValue, half maxValue)
359+
{
360+
#define HEATMAP_COLORS_COUNT 6
361+
half4 colors[HEATMAP_COLORS_COUNT] =
362+
{
363+
// min value of the range
364+
half4(0.32, 0.00, 0.32, 1.00),
365+
half4(0.00, 0.00, 1.00, 1.00),
366+
half4(0.00, 1.00, 0.00, 1.00),
367+
half4(1.00, 1.00, 0.00, 1.00),
368+
half4(1.00, 0.60, 0.00, 1.00),
369+
half4(1.00, 0.00, 0.00, 1.00),
370+
// max value of the range
371+
};
372+
half ratio=(HEATMAP_COLORS_COUNT-1.0)*saturate((value-minValue)/(maxValue-minValue));
373+
half indexMin=floor(ratio);
374+
half indexMax=min(indexMin+1,HEATMAP_COLORS_COUNT-1);
375+
376+
return lerp(colors[indexMin], colors[indexMax], ratio-indexMin);
377+
}
378+
279379
#endif // UNITY_DEBUG_INCLUDED

com.unity.render-pipelines.universal/Editor/ShaderGraph/Includes/PBRForwardPass.hlsl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,22 @@ void BuildInputData(Varyings input, float3 normal, out InputData inputData)
4747
inputData.fogCoord = input.fogFactorAndVertexLight.x;
4848
inputData.vertexLighting = input.fogFactorAndVertexLight.yzw;
4949
inputData.bakedGI = SAMPLE_GI(input.lightmapUV, input.sh, inputData.normalWS);
50+
51+
#ifdef LIGHTMAP_ON
52+
half2 uv = input.lightmapUV;
53+
54+
// TODO(fixforship): This adds an *additional* unnecessary texture fetch to the shader. We're already sampling
55+
// the directional lightmap in the SAMPLE_GI function, so we should sample it first, and feed it
56+
// in, instead.
57+
real4 direction_raw = SAMPLE_TEXTURE2D(unity_LightmapInd, samplerunity_Lightmap, uv);
58+
half3 direction = (direction_raw.xyz - 0.5) * 2; // convert from [0,1] to [-1,1]
59+
inputData.bakedGI_directionWS = direction;
60+
61+
#else // LIGHTMAP_ON
62+
63+
inputData.bakedGI_directionWS = half3(0,0,0);
64+
65+
#endif
5066
}
5167

5268
PackedVaryings vert(Attributes input)

com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ struct InputData
2222
half fogCoord;
2323
half3 vertexLighting;
2424
half3 bakedGI;
25+
half3 bakedGI_directionWS; // XYZ: Light direction, length is 'directionality'
2526
};
2627

2728
///////////////////////////////////////////////////////////////////////////////

com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -510,15 +510,12 @@ half3 SubtractDirectMainLightFromLightmap(Light mainLight, half3 normalWS, half3
510510
return min(bakedGI, realtimeShadow);
511511
}
512512

513-
half3 GlobalIllumination(BRDFData brdfData, half3 bakedGI, half occlusion, half3 normalWS, half3 viewDirectionWS)
513+
// (ASG) Calculates the GI by treating GI as simply another light source we pass into the DirectBRDF.
514+
// This gives us nice specular contribution from the baked lights! Very helpful in VR for making an object appear grounded.
515+
half3 GlobalIllumination(BRDFData brdfData, half3 bakedGI, half3 bakedGIDirectionWS, half occlusion, half3 normalWS, half3 viewDirectionWS)
514516
{
515-
half3 reflectVector = reflect(-viewDirectionWS, normalWS);
516-
half fresnelTerm = Pow4(1.0 - saturate(dot(normalWS, viewDirectionWS)));
517-
518517
half3 indirectDiffuse = bakedGI * occlusion;
519-
half3 indirectSpecular = GlossyEnvironmentReflection(reflectVector, brdfData.perceptualRoughness, occlusion);
520-
521-
return EnvironmentBRDF(brdfData, indirectDiffuse, indirectSpecular, fresnelTerm);
518+
return DirectBDRF(brdfData, normalWS, bakedGIDirectionWS, viewDirectionWS) * indirectDiffuse;
522519
}
523520

524521
void MixRealtimeAndBakedGI(inout Light light, half3 normalWS, inout half3 bakedGI, half4 shadowMask)
@@ -582,13 +579,28 @@ half3 VertexLighting(float3 positionWS, half3 normalWS)
582579
half4 UniversalFragmentPBR(InputData inputData, half3 albedo, half metallic, half3 specular,
583580
half smoothness, half occlusion, half3 emission, half alpha)
584581
{
582+
// Decreases smoothness as the directionality of the light decreases. This approximates the specular highlight
583+
// spreading and scattering, as the light becomes less directional. Without this, even shadowed areas look shiny.
584+
// This is recommended by: https://media.contentapi.ea.com/content/dam/eacom/frostbite/files/gdc2018-precomputedgiobalilluminationinfrostbite.pdf
585+
// Although, here we do not apply the sqrt to the falloff. Instead we square it, which seems to produce a closer image to the blender groundtruth.
586+
// The range remap makes sure that surfaces in perfectly direct light (directionality > .9) remain their true specular (directionality rarely reaches a perfect 1).
587+
// See this Github discussion for more information: https://github.com/AStrangerGravity/Graphics/pull/2#discussion_r459731172
588+
589+
// Worth optimizing more if we become ALU bound. Just make sure to do a before/after.
590+
// This smoothness falloff has been carefully tested to look good compared to the Blender render.
591+
half physicalSmoothness = 1 - PerceptualSmoothnessToRoughness(smoothness);
592+
half directionality_squared = dot(inputData.bakedGI_directionWS, inputData.bakedGI_directionWS); // The directionality is encoded as the length of the GI direction vector.
593+
half adjustedSmoothness = physicalSmoothness * RangeRemap(0.0, .9 * .9, directionality_squared);
594+
half perceptualAdjustedSmoothness = 1 - RoughnessToPerceptualRoughness(1 - adjustedSmoothness);
595+
585596
BRDFData brdfData;
586-
InitializeBRDFData(albedo, metallic, specular, smoothness, alpha, brdfData);
587-
597+
InitializeBRDFData(albedo, metallic, specular, perceptualAdjustedSmoothness, alpha, brdfData);
598+
588599
Light mainLight = GetMainLight(inputData.shadowCoord);
589600
MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI, half4(0, 0, 0, 0));
590601

591-
half3 color = GlobalIllumination(brdfData, inputData.bakedGI, occlusion, inputData.normalWS, inputData.viewDirectionWS);
602+
half3 giDirectionWS = SafeNormalize(inputData.bakedGI_directionWS);
603+
half3 color = GlobalIllumination(brdfData, inputData.bakedGI, giDirectionWS, occlusion, inputData.normalWS, inputData.viewDirectionWS);
592604
color += LightingPhysicallyBased(brdfData, mainLight, inputData.normalWS, inputData.viewDirectionWS);
593605

594606
#ifdef _ADDITIONAL_LIGHTS

com.unity.render-pipelines.universal/Shaders/LitForwardPass.hlsl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,26 @@ void InitializeInputData(Varyings input, half3 normalTS, out InputData inputData
7474

7575
inputData.fogCoord = input.fogFactorAndVertexLight.x;
7676
inputData.vertexLighting = input.fogFactorAndVertexLight.yzw;
77+
7778
inputData.bakedGI = SAMPLE_GI(input.lightmapUV, input.vertexSH, inputData.normalWS);
79+
80+
// Get the direction of the lightmap lighting (scale is equal to the 'directionality')
81+
#ifdef LIGHTMAP_ON
82+
83+
half2 uv = input.lightmapUV;
84+
85+
// TODO(fixforship): This adds an *additional* unnecessary texture fetch to the shader. We're already sampling
86+
// the directional lightmap in the SAMPLE_GI function, so we should sample it first, and feed it
87+
// in, instead.
88+
real4 direction_raw = SAMPLE_TEXTURE2D(unity_LightmapInd, samplerunity_Lightmap, uv);
89+
half3 direction = (direction_raw.xyz - 0.5) * 2; // convert from [0,1] to [-1,1]
90+
inputData.bakedGI_directionWS = direction;
91+
92+
#else // LIGHTMAP_ON
93+
94+
inputData.bakedGI_directionWS = half3(0,0,0);
95+
96+
#endif
7897
}
7998

8099
///////////////////////////////////////////////////////////////////////////////

com.unity.render-pipelines.universal/Shaders/Nature/SpeedTree7CommonPasses.hlsl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ void InitializeInputData(SpeedTreeVertexOutput input, half3 normalTS, out InputD
8686
inputData.fogCoord = input.fogFactorAndVertexLight.x;
8787
inputData.vertexLighting = input.fogFactorAndVertexLight.yzw;
8888
inputData.bakedGI = half3(0, 0, 0); // No GI currently.
89+
inputData.bakedGI_directionWS = half3(0,0,0);
8990
}
9091

9192
half4 SpeedTree7Frag(SpeedTreeVertexOutput input) : SV_Target

com.unity.render-pipelines.universal/Shaders/Nature/SpeedTree8Passes.hlsl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ void InitializeInputData(SpeedTreeFragmentInput input, half3 normalTS, out Input
295295
inputData.fogCoord = input.interpolated.fogFactorAndVertexLight.x;
296296
inputData.vertexLighting = input.interpolated.fogFactorAndVertexLight.yzw;
297297
inputData.bakedGI = half3(0, 0, 0); // No GI currently.
298+
inputData.bakedGI_directionWS = half3(0,0,0);
298299
}
299300

300301
half4 SpeedTree8Frag(SpeedTreeFragmentInput input) : SV_Target

com.unity.render-pipelines.universal/Shaders/SimpleLitForwardPass.hlsl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ void InitializeInputData(Varyings input, half3 normalTS, out InputData inputData
6969
inputData.fogCoord = input.fogFactorAndVertexLight.x;
7070
inputData.vertexLighting = input.fogFactorAndVertexLight.yzw;
7171
inputData.bakedGI = SAMPLE_GI(input.lightmapUV, input.vertexSH, inputData.normalWS);
72+
inputData.bakedGI_directionWS = half3(0,0,0);
7273
}
7374

7475
///////////////////////////////////////////////////////////////////////////////

com.unity.render-pipelines.universal/Shaders/Terrain/WavingGrassPasses.hlsl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ void InitializeInputData(GrassVertexOutput input, out InputData inputData)
5959
inputData.fogCoord = input.fogFactorAndVertexLight.x;
6060
inputData.vertexLighting = input.fogFactorAndVertexLight.yzw;
6161
inputData.bakedGI = SAMPLE_GI(input.lightmapUV, input.vertexSH, inputData.normalWS);
62+
inputData.bakedGI_directionWS = half3(0,0,0);
6263
}
6364

6465
void InitializeVertData(GrassVertexInput input, inout GrassVertexOutput vertData)

0 commit comments

Comments
 (0)