diff options
| author | yum <yum.food.vr@gmail.com> | 2026-03-30 13:11:29 -0700 |
|---|---|---|
| committer | yum <yum.food.vr@gmail.com> | 2026-03-30 13:11:29 -0700 |
| commit | 146b1e287e606b6ce3ebc4f60a1719f43c755916 (patch) | |
| tree | 914758325caffc2a1e2408119c9fd81e48f4520e | |
| parent | b5197bed4cad2a8452bcbfa8e116497760edf1ba (diff) | |
Glitter: use micro normal for IBL
| -rwxr-xr-x | 3ner.shader | 1 | ||||
| -rwxr-xr-x | brdf.cginc | 78 | ||||
| -rw-r--r-- | data.cginc | 4 | ||||
| -rw-r--r-- | glitter.cginc | 113 | ||||
| -rwxr-xr-x | globals.cginc | 1 | ||||
| -rwxr-xr-x | lighting.cginc | 14 |
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 @@ -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; @@ -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); |
