summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoryum <yum.food.vr@gmail.com>2026-07-20 17:39:35 -0700
committeryum <yum.food.vr@gmail.com>2026-07-20 17:39:35 -0700
commitb7d6ad400c41fcc0608e2dc15ec45d3692d82f16 (patch)
tree44492271c9ea186eeda984424ee360a4d2aff760
parent0425eadf188ebac3c370942dd55f9ba8e0f5afa9 (diff)
More clanker adjustments to glitter
-rwxr-xr-x3ner.shader2
-rw-r--r--glitter.cginc45
2 files changed, 38 insertions, 9 deletions
diff --git a/3ner.shader b/3ner.shader
index 1369c18..213eb72 100755
--- a/3ner.shader
+++ b/3ner.shader
@@ -708,7 +708,7 @@ Shader "yum_food/3ner"
//ifex _Glitter_Enabled==0
[HideInInspector] m_start_Glitter("Glitter", Float) = 0
[ThryToggle(_GLITTER)] _Glitter_Enabled("Enable", Float) = 0
- _Glitter_Amount("Amount", Range(0, 1)) = 0.5
+ _Glitter_Amount("Amount", Range(0, 4)) = 0.5
_Glitter_Roughness("Roughness", Range(0.001, 0.1)) = 0.01
[IntRange] _Glitter_Angular_Cells("Angular Cells", Range(1, 4)) = 4
_Glitter_Filter_Size("Filter Size", Range(0.1, 2.0)) = 0.7
diff --git a/glitter.cginc b/glitter.cginc
index 512be96..5475ec7 100644
--- a/glitter.cginc
+++ b/glitter.cginc
@@ -35,6 +35,12 @@
// Remaps [0, UINT_MAX] to [0, 1]
#define UINT_TO_UNIT (1.0 / 4294967296.0)
+#define GLITTER_AMOUNT_MAX 64.0
+#define GLITTER_REFERENCE_AMOUNT 0.5
+#define GLITTER_REFERENCE_N 8.0e6
+#define GLITTER_POPULATION_SCALE \
+ (GLITTER_AMOUNT_MAX / GLITTER_REFERENCE_AMOUNT)
+
// Lambert azimuthal equal area projection
float2 lambert(float3 v) {
return v.xy / sqrt(1 + v.z);
@@ -210,7 +216,7 @@ float3 disk_to_ndf_ggx(float2 v_disk, float alpha) {
// Algorithm 1 from Kemppinen et. al.
float D_Kemppinen(float3 h, float alpha, float glint_alpha, int angular_cells,
- float2 uv, float2x2 uv_J, float N, float filter_size,
+ float2 uv, float2x2 uv_J, float N, float amount, float filter_size,
out float3 micro_normal) {
float res = sqrt(N);
float2 x_s = uv;
@@ -219,6 +225,21 @@ float D_Kemppinen(float3 h, float alpha, float glint_alpha, int angular_cells,
float d = x_a_and_d.z;
int angular_sample_count = clamp(angular_cells, 1, 4);
+ // The paper normalizes both Gaussian kernels and the point population, so
+ // narrower kernels and smaller populations have taller individual peaks.
+ // That is desirable for an energy-preserving NDF, but makes roughness and
+ // density alter the apparent size of a glint.
+ // Preserve the peaks at the original defaults instead: roughness controls
+ // angular width, filter_size controls spatial width, and amount controls only
+ // how many flakes are active.
+ float angular_peak_scale = pow(glint_alpha / 0.01, 2.0);
+ float spatial_peak_scale = pow(filter_size / 0.7, 2.0);
+ // Scaling the maximum population and each point's weight together keeps the
+ // reference amount's population, peak, and average energy unchanged.
+ float profile_scale = GLITTER_POPULATION_SCALE
+ * angular_peak_scale * spatial_peak_scale;
+ float amount_fraction = saturate(amount / GLITTER_AMOUNT_MAX);
+
// Both the spatial and angular neighborhoods require at least a 2x2 grid.
float max_lod = floor(log2(res)) - 1.0;
float lambda = clamp(QueryLod(res * uv_J, filter_size), 1.0,
@@ -293,9 +314,12 @@ float D_Kemppinen(float3 h, float alpha, float glint_alpha, int angular_cells,
float r = Rand1D(i_s_neighbor, i_a_neighbor, l, 4u);
float roulette = smoothstep(max(.0, r-.1), min(1.0, r+.1), w_lambda);
+ float amount_r = Rand1D(i_s_neighbor, i_a_neighbor, l, 8u);
+ float active = smoothstep(max(0.0, amount_r - 0.02),
+ min(1.0, amount_r + 0.02), amount_fraction);
- float w = roulette * normal(sigma_a, x_a - g_a)
- * normal(sigma_s, x_s - g_s) / N;
+ float w = active * roulette * normal(sigma_a, x_a - g_a)
+ * normal(sigma_s, x_s - g_s) * profile_scale / N;
D_filter += w;
if (w > best_weight) {
best_weight = w;
@@ -303,8 +327,9 @@ float D_Kemppinen(float3 h, float alpha, float glint_alpha, int angular_cells,
}
}
}
- D_filter += w_lambda * compensation(x_a, sigma_a, res_a, i_a,
- angular_step, angular_sample_count);
+ D_filter += amount_fraction * w_lambda * profile_scale
+ * compensation(x_a, sigma_a, res_a, i_a, angular_step,
+ angular_sample_count);
}
micro_normal = normalize(disk_to_ndf_ggx(best_g_a, alpha));
@@ -327,13 +352,16 @@ LightGlitter GetGlitterLighting(
float3 normal, float3 V, float3 direct_H, float3 indirect_dir) {
LightGlitter g;
float2x2 uv_J = uv_ellipsoid(transpose(float2x2(ddx(uv), ddy(uv))));
- float N = 8.0e5f * pow(10.0f, glitter_amount * 6.0f - 2.0f);
+ // Keep the procedural population fixed. `glitter_amount` independently
+ // controls the fraction of that population which is active in D_Kemppinen.
+ float N = GLITTER_REFERENCE_N * GLITTER_POPULATION_SCALE;
// 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,
- glitter_angular_cells, uv, uv_J, N, glitter_filter_size,
+ glitter_angular_cells, uv, uv_J, N, glitter_amount,
+ glitter_filter_size,
direct_micro_normal);
// Indirect
@@ -341,7 +369,8 @@ LightGlitter GetGlitterLighting(
float3 indirect_H_tangent = mul(indirect_H, transpose(tbn));
float3 indirect_micro_normal; // unused, but required by D_Kemppinen
g.indirect_D = D_Kemppinen(indirect_H_tangent, roughness, glitter_roughness,
- glitter_angular_cells, uv, uv_J, N, glitter_filter_size,
+ glitter_angular_cells, uv, uv_J, N, glitter_amount,
+ glitter_filter_size,
indirect_micro_normal);
g.indirect_NoL = max(1e-4, dot(normal, indirect_dir));
g.indirect_LoH = max(1e-4, dot(indirect_dir, indirect_H));