summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoryum <yum.food.vr@gmail.com>2026-03-30 13:11:29 -0700
committeryum <yum.food.vr@gmail.com>2026-03-30 13:11:29 -0700
commit146b1e287e606b6ce3ebc4f60a1719f43c755916 (patch)
tree914758325caffc2a1e2408119c9fd81e48f4520e
parentb5197bed4cad2a8452bcbfa8e116497760edf1ba (diff)
Glitter: use micro normal for IBL
-rwxr-xr-x3ner.shader1
-rwxr-xr-xbrdf.cginc78
-rw-r--r--data.cginc4
-rw-r--r--glitter.cginc113
-rwxr-xr-xglobals.cginc1
-rwxr-xr-xlighting.cginc14
6 files changed, 120 insertions, 91 deletions
diff --git a/3ner.shader b/3ner.shader
index 5cc10be..0910996 100755
--- a/3ner.shader
+++ b/3ner.shader
@@ -661,6 +661,7 @@ Shader "yum_food/3ner"
[ThryToggle(_GLITTER)] _Glitter_Enabled("Enable", Float) = 0
_Glitter_Amount("Amount", Range(0, 1)) = 0.5
_Glitter_Roughness("Roughness", Range(0.001, 0.1)) = 0.01
+ _Glitter_IBL_Roughness("IBL Roughness", Range(0.001, 1)) = 0.01
_Glitter_Tint("Tint", Color) = (1, 1, 1, 1)
[HideInInspector] m_end_Glitter("Glitter", Float) = 0
//endex
diff --git a/brdf.cginc b/brdf.cginc
index 92fd8bb..a76ee13 100755
--- a/brdf.cginc
+++ b/brdf.cginc
@@ -1,12 +1,12 @@
#ifndef __BRDF_INC
#define __BRDF_INC
-#include "glitter.cginc"
-#include "lighting.cginc"
+#include "LightVolumes.cginc"
#include "lysenko.cginc"
#include "math.cginc"
#include "pema99.cginc"
#include "pbr.cginc"
+#include "glitter.cginc"
float pow5(float x) {
float x2 = x * x;
@@ -101,43 +101,6 @@ float G_Estevez(float roughness, float NoL, float NoV) {
return 1.0 / ((1.0 + lambda_l + lambda_v) * 4.0 * NoL * NoV);
}
-#if defined(_GLITTER)
-struct GlitterData {
- float2 uv;
- float2x2 uv_J;
- float N;
-};
-
-struct GlitterDFG {
- float d;
- float3 f;
- float g;
-};
-
-GlitterData GetGlitterData(v2f i) {
- GlitterData data;
- data.uv = i.uv01.xy;
- data.uv_J = uv_ellipsoid(transpose(float2x2(ddx(data.uv), ddy(data.uv))));
- data.N = 8.0e5f * pow(10.0f, _Glitter_Amount * 6.0f - 2.0f);
- return data;
-}
-
-GlitterDFG GetGlitterDFG(GlitterData data,
- float3x3 world_to_tangent, float roughness, float3 H, float LoH,
- float NoL, float NoV) {
- GlitterDFG dfg;
- const float glitter_filter_size = 0.7f;
- float3 H_tangent = mul(H, world_to_tangent);
- float f0 = 0.15f;
- float f90 = 1.0f;
- dfg.f = F_Schlick(LoH, f0, f90);
- dfg.d = D_Kemppinen(H_tangent, roughness, _Glitter_Roughness,
- data.uv, data.uv_J, data.N, glitter_filter_size);
- dfg.g = G_GGXSmith(roughness, NoL, NoV);
- return dfg;
-}
-#endif
-
float4 brdf(v2f i, Pbr pbr, LightData data, out BrdfData bd) {
bd = (BrdfData)0;
float3 specular = 0;
@@ -181,11 +144,6 @@ float4 brdf(v2f i, Pbr pbr, LightData data, out BrdfData bd) {
float3 cc_energy_comp = 1.0f + cc_f0_color * (1.0f / (bd.ibl_dfg_cc.xxx + bd.ibl_dfg_cc.yyy) - 1.0f);
#endif
-#if defined(_GLITTER)
- GlitterData glitter_data = GetGlitterData(i);
- float3x3 glitter_world_to_tangent = transpose(pbr.tbn);
-#endif
-
// Direct
{
float3 remainder = 1.0f;
@@ -223,32 +181,21 @@ float4 brdf(v2f i, Pbr pbr, LightData data, out BrdfData bd) {
#endif // _CLOTH
#if defined(_GLITTER)
- GlitterDFG direct_glitter = GetGlitterDFG(glitter_data,
- glitter_world_to_tangent, pbr.roughness, data.direct.H,
- data.direct.LoH, data.direct.NoL, data.common.NoV);
- bd.direct_f = direct_glitter.f;
- bd.direct_d = direct_glitter.d;
- bd.direct_g = direct_glitter.g;
- float3 direct_specular_glitter = (direct_glitter.d * direct_glitter.g)
- * direct_glitter.f * data.direct.color * data.direct.NoL
+ float3 direct_f_glitter = F_Schlick(data.direct.LoH, 0.15f, 1.0f);
+ float direct_g_glitter = G_GGXSmith(pbr.roughness, data.direct.NoL, data.common.NoV);
+ float3 direct_specular_glitter = (data.glitter.direct_D * direct_g_glitter)
+ * direct_f_glitter * data.direct.color * data.direct.NoL
* _Glitter_Tint;
// No spec ao for glitter, please.
direct_specular_glitter *= remainder;
specular += direct_specular_glitter;
+#endif
- float direct_g = G_GGXSmith(pbr.roughness, data.direct.NoL, data.common.NoV);
- float3 direct_f = F_Schlick(data.direct.LoH, f0_color, f90);
- float direct_d = D_GGX(pbr.roughness, data.direct.NoH);
-#else
bd.direct_g = G_GGXSmith(pbr.roughness, data.direct.NoL, data.common.NoV);
bd.direct_f = F_Schlick(data.direct.LoH, f0_color, f90);
bd.direct_d = D_GGX(pbr.roughness, data.direct.NoH);
- float direct_g = bd.direct_g;
- float3 direct_f = bd.direct_f;
- float direct_d = bd.direct_d;
-#endif
- float3 direct_specular = (direct_d * direct_g) * direct_f;
+ float3 direct_specular = (bd.direct_d * bd.direct_g) * bd.direct_f;
direct_specular *= data.direct.color * data.direct.NoL;
direct_specular *= energy_comp;
direct_specular *= remainder;
@@ -286,11 +233,10 @@ float4 brdf(v2f i, Pbr pbr, LightData data, out BrdfData bd) {
diffuse += indirect_diffuse * remainder;
#else
#if defined(_GLITTER)
- GlitterDFG indirect_glitter = GetGlitterDFG(glitter_data,
- glitter_world_to_tangent, pbr.roughness, data.indirect.H,
- data.indirect.LoH, data.indirect.NoL, data.common.NoV);
- float3 indirect_specular_glitter = (indirect_glitter.d * indirect_glitter.g)
- * indirect_glitter.f * data.indirect.specular * data.indirect.NoL
+ float3 indirect_f_glitter = F_Schlick(data.glitter.indirect_LoH, 0.15f, 1.0f);
+ float indirect_g_glitter = G_GGXSmith(pbr.roughness, data.glitter.indirect_NoL, data.common.NoV);
+ float3 indirect_specular_glitter = (data.glitter.indirect_D * indirect_g_glitter)
+ * indirect_f_glitter * data.glitter.indirect_specular * data.glitter.indirect_NoL
* _Glitter_Tint;
// No spec ao for glitter, please.
specular += indirect_specular_glitter * remainder;
diff --git a/data.cginc b/data.cginc
index 0923da3..a28d25e 100644
--- a/data.cginc
+++ b/data.cginc
@@ -1,6 +1,7 @@
#ifndef __DATA_INC
#define __DATA_INC
+#include "glitter.cginc"
#include "globals.cginc"
// From filament: min roughness s.t. MIN_PERCEPTUAL_ROUGHNESS^4 > 0 in target
@@ -89,6 +90,9 @@ struct LightData {
LightCommon common;
LightDirect direct;
LightIndirect indirect;
+#if defined(_GLITTER)
+ LightGlitter glitter;
+#endif
};
struct BrdfData {
diff --git a/glitter.cginc b/glitter.cginc
index 2ed0ef4..34cd17c 100644
--- a/glitter.cginc
+++ b/glitter.cginc
@@ -13,6 +13,10 @@
* I have made changes to this code. They are:
* 1. Syntax changes required to translate GLSL to HLSL.
* 2. Stylistic preferences, like using "1" or "1.0" instead of "1.".
+ * 3. The `GetGlitterLighting` function, which populates data required for
+ * IBL. The original paper only discusses analytic lighting. For IBL, you
+ * also need the micro-normal to figure out which part of the cubemap to
+ * sample.
*
* @article{KPT:2025:Glinty,
* title = {Evaluating and Sampling Glinty NDFs in Constant Time},
@@ -150,7 +154,16 @@ float compensation(float2 x_a, float2x2 sigma_a, float res_a) {
return containing - explicitly_evaluated;
}
-float D_Kemppinen(float3 h, float alpha, float glint_alpha, float2 uv, float2x2 uv_J, float N, float filter_size) {
+float3 disk_to_ndf_ggx(float2 v_disk, float alpha) {
+ float2 p = v_disk * 2.0f - 1.0f;
+ float r2 = saturate(dot(p, p));
+ float3 hemi = float3(p * sqrt(max(1e-6f, 2.0f - r2)), 1.0f - r2);
+ float alpha2 = alpha * alpha;
+ float denom = sqrt(max(1e-6f, alpha2 * dot(hemi.xy, hemi.xy) + hemi.z * hemi.z));
+ return float3(alpha * hemi.xy, hemi.z) / denom;
+}
+
+float D_Kemppinen(float3 h, float alpha, float glint_alpha, float2 uv, float2x2 uv_J, float N, float filter_size, out float3 micro_normal) {
float res = sqrt(N);
float2 x_s = uv;
float3 x_a_and_d = ndf_to_disk_ggx(h, alpha);
@@ -160,44 +173,94 @@ float D_Kemppinen(float3 h, float alpha, float glint_alpha, float2 uv, float2x2
float lambda = QueryLod(res * uv_J, filter_size);
float D_filter = 0;
+ float best_weight = 0;
+ float2 best_g_a = x_a;
[loop]
- for (float m = 0; m < 2; m += 1) {
- float l = floor(lambda) + m;
+ for (float m = 0; m < 2; m += 1) {
+ float l = floor(lambda) + m;
- float w_lambda = 1.0 - abs(lambda - l);
- float res_s = res * pow(2, -l);
- float res_a = pow(2, l);
+ float w_lambda = 1.0 - abs(lambda - l);
+ float res_s = res * pow(2, -l);
+ float res_a = pow(2, l);
- float2x2 uv_J2 = filter_size * uv_J;
- float2x2 sigma_s = mul(uv_J2, transpose(uv_J2));
+ float2x2 uv_J2 = filter_size * uv_J;
+ float2x2 sigma_s = mul(uv_J2, transpose(uv_J2));
- float2x2 sigma_a = d * pow(glint_alpha, 2) * float2x2(1, 0, 0, 1);
+ float2x2 sigma_a = d * pow(glint_alpha, 2) * float2x2(1, 0, 0, 1);
- float2 base_i_a = clamp(round(x_a * res_a), 1, res_a-1);
- [loop]
- for (uint j_a = 0; j_a < 4; ++j_a) {
- float2 i_a = base_i_a + float2(int2(j_a, j_a/2)%2)-.5;
+ float2 base_i_a = clamp(round(x_a * res_a), 1, res_a-1);
+ [loop]
+ for (uint j_a = 0; j_a < 4; ++j_a) {
+ float2 i_a = base_i_a + float2(int2(j_a, j_a/2)%2)-.5;
- float2 base_i_s = round(x_s * res_s);
- [loop]
- for (uint j_s = 0; j_s < 4; ++j_s) {
- float2 i_s = base_i_s + float2(int2(j_s, j_s/2)%2)-.5;
+ float2 base_i_s = round(x_s * res_s);
+ [loop]
+ for (uint j_s = 0; j_s < 4; ++j_s) {
+ float2 i_s = base_i_s + float2(int2(j_s, j_s/2)%2)-.5;
- float2 g_s = (i_s + Rand2D(i_s, i_a, l, 1u) - .5) / res_s;
- float2 g_a = (i_a + Rand2D(i_s, i_a, l, 2u) - .5) / res_a;
+ float2 g_s = (i_s + Rand2D(i_s, i_a, l, 1u) - .5) / res_s;
+ float2 g_a = (i_a + Rand2D(i_s, i_a, l, 2u) - .5) / res_a;
- float r = Rand1D(i_s, i_a, l, 4u);
- float roulette = smoothstep(max(.0, r-.1), min(1.0, r+.1), w_lambda);
+ float r = Rand1D(i_s, i_a, l, 4u);
+ float roulette = smoothstep(max(.0, r-.1), min(1.0, r+.1), w_lambda);
- D_filter += roulette * normal(sigma_a, x_a - g_a) * normal(sigma_s, x_s - g_s) / N;
- }
+ float w = roulette * normal(sigma_a, x_a - g_a) * normal(sigma_s, x_s - g_s) / N;
+ D_filter += w;
+ if (w > best_weight) {
+ best_weight = w;
+ best_g_a = g_a;
}
- D_filter += w_lambda * compensation(x_a, sigma_a, res_a);
+ }
}
+ D_filter += w_lambda * compensation(x_a, sigma_a, res_a);
+ }
+ micro_normal = normalize(disk_to_ndf_ggx(best_g_a, alpha));
return D_filter * d / PI;
}
-#endif // __GLITTER_INC
+#if defined(_GLITTER)
+struct LightGlitter {
+ float direct_D;
+
+ float indirect_D;
+ float3 indirect_specular;
+ float3 indirect_dir;
+ float3 indirect_H;
+ float indirect_NoL;
+ float indirect_LoH;
+};
+
+// Glitter data getter to be run from lighting code.
+LightGlitter GetGlitterLighting(
+ float glitter_amount, float glitter_roughness,
+ float2 uv, float3x3 tbn, float roughness,
+ float3 normal, float3 V, float3 direct_H, float3 indirect_H) {
+ LightGlitter g;
+ const float glitter_filter_size = 0.7f;
+ float2x2 uv_J = uv_ellipsoid(transpose(float2x2(ddx(uv), ddy(uv))));
+ float N = 8.0e5f * pow(10.0f, glitter_amount * 6.0f - 2.0f);
+
+ // Direct
+ float3 direct_H_tangent = mul(direct_H, transpose(tbn));
+ float3 direct_micro_normal; // unused
+ g.direct_D = D_Kemppinen(direct_H_tangent, roughness, glitter_roughness,
+ uv, uv_J, N, glitter_filter_size, direct_micro_normal);
+
+ // Indirect
+ float3 indirect_H_tangent = mul(indirect_H, transpose(tbn));
+ float3 indirect_micro_normal; // used to sample cubemap
+ g.indirect_D = D_Kemppinen(indirect_H_tangent, roughness, glitter_roughness,
+ uv, uv_J, N, glitter_filter_size, indirect_micro_normal);
+ // Normal vector is the halfway vector in IBL.
+ g.indirect_H = normalize(mul(indirect_micro_normal, tbn));
+ g.indirect_dir = reflect(-V, g.indirect_H);
+ g.indirect_NoL = max(1e-4, dot(normal, g.indirect_dir));
+ g.indirect_LoH = max(1e-4, dot(g.indirect_dir, g.indirect_H));
+
+ return g;
+}
+#endif
+#endif // __GLITTER_INC
diff --git a/globals.cginc b/globals.cginc
index 88fb663..2b9c163 100755
--- a/globals.cginc
+++ b/globals.cginc
@@ -155,6 +155,7 @@ int _Details_UV_Channel;
#if defined(_GLITTER)
float _Glitter_Amount;
float _Glitter_Roughness;
+float _Glitter_IBL_Roughness;
float3 _Glitter_Tint;
#endif // _GLITTER
diff --git a/lighting.cginc b/lighting.cginc
index 8b6693c..52c9cac 100755
--- a/lighting.cginc
+++ b/lighting.cginc
@@ -12,6 +12,7 @@
#include "interpolators.cginc"
#include "LightVolumes.cginc"
#include "pbr.cginc"
+#include "glitter.cginc"
#include "poi.cginc"
float3 getDirectLightDirection(v2f i) {
@@ -264,6 +265,13 @@ void GetLighting(v2f i, Pbr pbr, out LightData data) {
data.indirect.diffuse = getIndirectDiffuse(i, pbr, data);
data.indirect.specular = getIndirectSpecular(i, pbr.roughness_perceptual, view_dir, data.indirect.dir, data.indirect.diffuse);
+#if defined(_GLITTER)
+ data.glitter = GetGlitterLighting(_Glitter_Amount, _Glitter_Roughness,
+ i.uv01.xy, pbr.tbn, pbr.roughness,
+ pbr.normal, data.common.V, data.direct.H, data.indirect.H);
+ data.glitter.indirect_specular = getIndirectSpecular(i, _Glitter_IBL_Roughness,
+ view_dir, data.glitter.indirect_dir, data.indirect.diffuse);
+#endif
data.common.ao = getAO(i);
data.common.spec_ao = getSpecularAO(i, pbr, data, reflect_dir);
@@ -290,6 +298,12 @@ void GetLighting(v2f i, Pbr pbr, out LightData data) {
tmpHSV[2] = clamp(tmpHSV[2], 0, _Brightness_Clamp_Max);
data.indirect.specular = HSVtoRGB(tmpHSV);
+#if defined(_GLITTER)
+ tmpHSV = RGBtoHSV(data.glitter.indirect_specular);
+ tmpHSV[2] = clamp(tmpHSV[2], 0, _Brightness_Clamp_Max);
+ data.glitter.indirect_specular = HSVtoRGB(tmpHSV);
+#endif
+
#if defined(_CLEARCOAT)
tmpHSV = RGBtoHSV(data.indirect.specular_cc);
tmpHSV[2] = clamp(tmpHSV[2], 0, _Brightness_Clamp_Max);