summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoryum <yum.food.vr@gmail.com>2026-03-17 15:49:21 -0700
committeryum <yum.food.vr@gmail.com>2026-03-17 15:49:25 -0700
commit1784064c7a39a69203e8975167addf1915f940bd (patch)
tree4adc272435efcf54ac7ed8399aa33acbf422a959
parent019c24186c87fd747aae1512abf4d4690e3aca07 (diff)
Add faster 3-in 1-out hasher for domain warping
Goes from ~1.7 ms/frame to ~1.1 ms/frame in 10-octave microbenchmark.
-rwxr-xr-x3ner.shader3
-rw-r--r--Scripts/Editor/GenerateNoise.cs9
-rwxr-xr-xfeatures.cginc1
-rwxr-xr-xglobals.cginc4
-rwxr-xr-xmath.cginc67
-rwxr-xr-xpbr.cginc16
6 files changed, 70 insertions, 30 deletions
diff --git a/3ner.shader b/3ner.shader
index 569ddc5..6af7ec8 100755
--- a/3ner.shader
+++ b/3ner.shader
@@ -39,7 +39,7 @@ Shader "yum_food/3ner"
_Glossiness("Smoothness", Range(0, 1)) = 0.5
[HideInInspector] m_start_IBL("Image-based lighting", Float) = 0
- _Exposure_Occlusion("Exposure Occlusion", Range(0.001, 10)) = 0.2
+ _Exposure_Occlusion("Exposure Occlusion", Range(0.001, 10)) = 1
//ifex _Bent_Normals_Enabled==0
[HideInInspector] m_start_Bent_Normals("Bent Normals", Float) = 0
@@ -1013,7 +1013,6 @@ Shader "yum_food/3ner"
//ifex _Kintsugi_Enabled==0
[HideInInspector] m_start_Kintsugi("Kintsugi", Float) = 0
[ThryToggle(_KINTSUGI)] _Kintsugi_Enabled("Enable", Float) = 0
- _Kintsugi_Noise("Noise", 3D) = "gray" {}
[ThryToggle(_KINTSUGI_PROCEDURAL)] _Kintsugi_Procedural("Procedural noise", Float) = 0
[ThryToggle(_KINTSUGI_NOISE_INVERT)] _Kintsugi_Noise_Inverted("Invert noise", Float) = 0
_Kintsugi_Scale("Scale", Vector) = (1, 1, 1, 0)
diff --git a/Scripts/Editor/GenerateNoise.cs b/Scripts/Editor/GenerateNoise.cs
index 73a5e20..472d5ff 100644
--- a/Scripts/Editor/GenerateNoise.cs
+++ b/Scripts/Editor/GenerateNoise.cs
@@ -47,11 +47,12 @@ public class GenerateNoise : EditorWindow
int res = _type == NoiseType.WhiteNoise ? _resolution : _resolution * _texelsPerCell;
Texture3D tex;
+ bool makeMipMaps = true;
switch (_type)
{
case NoiseType.WorleyNoise:
{
- tex = new Texture3D(res, res, res, TextureFormat.R8, false);
+ tex = new Texture3D(res, res, res, TextureFormat.R8, makeMipMaps);
GenerateWorleyField(res, _texelsPerCell, _metric, out var f1, out _);
var pixels = new byte[res * res * res];
for (int i = 0; i < pixels.Length; i++)
@@ -61,7 +62,7 @@ public class GenerateNoise : EditorWindow
}
case NoiseType.WorleyEdgeSDF:
{
- tex = new Texture3D(res, res, res, TextureFormat.R8, false);
+ tex = new Texture3D(res, res, res, TextureFormat.R8, makeMipMaps);
GenerateWorleyField(res, _texelsPerCell, _metric, out var f1, out var f2);
var pixels = new byte[res * res * res];
GenerateEdgeSDF(pixels, f2, _sdfRadius);
@@ -70,7 +71,7 @@ public class GenerateNoise : EditorWindow
}
default:
{
- tex = new Texture3D(res, res, res, TextureFormat.RGBA32, false);
+ tex = new Texture3D(res, res, res, TextureFormat.RGB24, makeMipMaps);
var pixels = new Color32[res * res * res];
GenerateWhiteNoise(pixels);
tex.SetPixels32(pixels);
@@ -79,7 +80,7 @@ public class GenerateNoise : EditorWindow
}
tex.wrapMode = TextureWrapMode.Repeat;
- tex.filterMode = FilterMode.Bilinear;
+ tex.filterMode = FilterMode.Trilinear;
tex.Apply();
string name;
diff --git a/features.cginc b/features.cginc
index 79c5001..c94f7ab 100755
--- a/features.cginc
+++ b/features.cginc
@@ -16,7 +16,6 @@
//ifex _Kintsugi_Enabled==0
#pragma shader_feature_local _KINTSUGI
-#pragma shader_feature_local _KINTSUGI_PROCEDURAL
#pragma shader_feature_local _KINTSUGI_NOISE_INVERT
#pragma shader_feature_local _KINTSUGI_DOMAIN_WARPING
//endex
diff --git a/globals.cginc b/globals.cginc
index d32feeb..4873108 100755
--- a/globals.cginc
+++ b/globals.cginc
@@ -84,6 +84,7 @@ float _Bent_Normals_Strength;
#endif // _BENT_NORMALS
#if defined(_MARBLE)
+Texture3D _Marble_Noise;
texture2D _Marble_Post_Ramp;
float3 _Marble_Scale;
float _Marble_Octaves;
@@ -102,7 +103,6 @@ float3 _Marble_Offset;
#endif // _MARBLE_OFFSET
#if defined(_KINTSUGI)
-texture3D _Kintsugi_Noise;
float3 _Kintsugi_Scale;
float _Kintsugi_Threshold;
float _Kintsugi_Width;
@@ -112,7 +112,7 @@ float _Kintsugi_Metallic;
#endif // _KINTSUGI
#if defined(_KINTSUGI_DOMAIN_WARPING)
-texture3D _Kintsugi_Domain_Warping_Noise;
+Texture3D _Kintsugi_Domain_Warping_Noise;
float3 _Kintsugi_Domain_Warping_Scale;
float _Kintsugi_Domain_Warping_Strength;
float _Kintsugi_Domain_Warping_Octaves;
diff --git a/math.cginc b/math.cginc
index 7c6db21..5803be5 100755
--- a/math.cginc
+++ b/math.cginc
@@ -19,6 +19,9 @@
#define F1_TO_F3(x) float3((x), (x), (x))
+// Remaps [0, INT_MAX] to [0, 1]
+#define UINT_TO_UNIT (1.0 / 4294967296.0)
+
float sin_noise_3d(float3 uvw) {
return sin(uvw[0]) * sin(uvw[1]) * sin(uvw[2]);
}
@@ -111,24 +114,48 @@ float4 alpha_blend(float4 front, float4 back) {
return float4(front.rgb * front.a + back.rgb * (1 - front.a), front.a + back.a * (1 - front.a));
}
-// Cheap procedural 3D hash -> [0,1]^3. Based on the "hashwithoutsine" family.
+// 3 in 3 out hash. Based on the "hashwithoutsine" family.
float3 hash33_fast(float3 p) {
p = frac(p * float3(0.1031, 0.1030, 0.0973));
p += dot(p, p.yxz + 33.33);
return frac((p.xxy + p.yxx) * p.zyx);
}
+// O'Neill-style PCG 32-bit output permutation (RXS-M-XS).
+uint pcg32(uint input) {
+ uint state = input * 747796405u + 2891336453u;
+ uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
+ return (word >> 22u) ^ word;
+}
+
+// 3 in 1 out hash. Mix three lanes in integer space, then PCG-finalize once.
+uint hash31_u32(uint3 p) {
+ uint seed = p.x * 0x2c1b3c6du;
+ seed ^= p.y * 0x297a2d39u;
+ seed ^= p.z * 0x1b56c4e9u;
+ return pcg32(seed);
+}
+
+// Float wrapper for arbitrary inputs.
+float hash31_ff(float3 p) {
+ return hash31_u32(asuint(p)) * UINT_TO_UNIT;
+}
+
+float hash31_if(int3 p) {
+ return hash31_u32(p) * UINT_TO_UNIT;
+}
+
// Procedural value noise in [0,1]^3 — trilinear interpolation of hashed corners.
float3 value_noise3(float3 p) {
- float3 i = floor(p);
+ int3 i = (int3)floor(p);
float3 f = frac(p);
float3 u = f * f * (3.0 - 2.0 * f);
return lerp(
- lerp(lerp(hash33_fast(i + float3(0, 0, 0)), hash33_fast(i + float3(1, 0, 0)), u.x),
- lerp(hash33_fast(i + float3(0, 1, 0)), hash33_fast(i + float3(1, 1, 0)), u.x), u.y),
- lerp(lerp(hash33_fast(i + float3(0, 0, 1)), hash33_fast(i + float3(1, 0, 1)), u.x),
- lerp(hash33_fast(i + float3(0, 1, 1)), hash33_fast(i + float3(1, 1, 1)), u.x), u.y),
+ lerp(lerp(hash31_if(i + int3(0, 0, 0)), hash31_if(i + int3(1, 0, 0)), u.x),
+ lerp(hash31_if(i + int3(0, 1, 0)), hash31_if(i + int3(1, 1, 0)), u.x), u.y),
+ lerp(lerp(hash31_if(i + int3(0, 0, 1)), hash31_if(i + int3(1, 0, 1)), u.x),
+ lerp(hash31_if(i + int3(0, 1, 1)), hash31_if(i + int3(1, 1, 1)), u.x), u.y),
u.z);
}
@@ -149,22 +176,31 @@ float3 domain_warp_procedural(float3 uvw, float strength,
return noise;
}
+float3 value_noise_3d_tex(Texture3D tex, SamplerState s, float3 p) {
+ float w, h, d;
+ tex.GetDimensions(w, h, d);
+ float3 res = float3(w, h, d);
+
+ p *= res;
+ float3 i = floor(p);
+ float3 f = frac(p);
+ float3 u = f * f * (3.0 - 2.0 * f);
+
+ return tex.Sample(s, (i + 0.5 + u) / res).rgb;
+}
+
// Domain warping using a 3D noise texture. Texture should have an EV of
-// 0.5.
-float3 domain_warp_3d_tex(texture3D noise_tex, float3 uvw, float strength,
- uint octaves, float lacunarity, float gain) {
+// 0.5. Uses cubic interpolation between lattice points (same semantics as
+// domain_warp_procedural / value_noise3).
+float3 domain_warp_3d_tex(Texture3D noise_tex, SamplerState s, float3 uvw,
+ float strength, uint octaves, float lacunarity, float gain) {
float3 noise = 0;
float g = 1;
- float3 uvw_dx = ddx(uvw);
- float3 uvw_dy = ddy(uvw);
-
for (uint ii = 0; ii < octaves; ++ii) {
- noise += noise_tex.SampleGrad(aniso4_trilinear_repeat_s, uvw + noise * strength, uvw_dx, uvw_dy).rgb * g;
+ noise += value_noise_3d_tex(noise_tex, s, uvw + noise * strength) * g;
uvw *= lacunarity;
g *= gain;
- uvw_dx *= lacunarity;
- uvw_dy *= lacunarity;
}
// Normalize: geometric series 1 + r + ... + r^{n-1} = (1 - r^n) / (1 - r)
@@ -204,4 +240,3 @@ float voronoi_edge_distance(float3 x) {
}
#endif // __MATH_INC
-
diff --git a/pbr.cginc b/pbr.cginc
index e0cf64f..dac9e1f 100755
--- a/pbr.cginc
+++ b/pbr.cginc
@@ -132,8 +132,19 @@ void apply_marble(float3 world_pos, inout float3 albedo) {
uvw += offset;
+#if 1
float3 noise = domain_warp_procedural(uvw, _Marble_Strength,
_Marble_Octaves, _Marble_Lacunarity, _Marble_Gain);
+#elif 1
+ float3 noise = hash31_fast(uvw*32);
+#elif 0
+ float3 noise = value_noise3(uvw);
+#elif 0
+ float3 noise = value_noise_3d_tex(_Marble_Noise, trilinear_repeat_s, uvw);
+#else
+ float3 noise = domain_warp_3d_tex(_Marble_Noise, trilinear_repeat_s, uvw,
+ _Marble_Strength, _Marble_Octaves, _Marble_Lacunarity, _Marble_Gain);
+#endif
noise = _Marble_Post_Ramp.Sample(linear_clamp_s, float2(noise.x, 0));
@@ -153,12 +164,7 @@ void apply_kintsugi(float3 world_pos, inout float3 albedo, inout float smoothnes
uvw += warp;
#endif
-#if defined(_KINTSUGI_PROCEDURAL)
float mask = voronoi_edge_distance(uvw) + 0.5f;
-#else
- float mask = _Kintsugi_Noise.Sample(aniso4_trilinear_repeat_s, uvw);
-#endif
-
float width = max(fwidth(mask) * 0.5f, _Kintsugi_Width);
float threshold = _Kintsugi_Threshold;
mask = smoothstep(threshold - width, threshold + width, mask);