summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoryum <yum.food.vr@gmail.com>2026-03-16 14:11:40 -0700
committeryum <yum.food.vr@gmail.com>2026-03-16 14:11:40 -0700
commite641d53cc28ca8b77c237aef89f4968c8114b614 (patch)
treed079d46ad94711665022d821279787068973bd81
parent4bf004c7e9c6eb2edf497b075ee01375ae878ff1 (diff)
Add kintsugi domain warping
-rwxr-xr-x3ner.shader11
-rwxr-xr-xfeatures.cginc1
-rwxr-xr-xglobals.cginc9
-rwxr-xr-xmath.cginc92
-rwxr-xr-xpbr.cginc59
5 files changed, 124 insertions, 48 deletions
diff --git a/3ner.shader b/3ner.shader
index df3127a..6683b2d 100755
--- a/3ner.shader
+++ b/3ner.shader
@@ -1025,6 +1025,17 @@ Shader "yum_food/3ner"
[HDR] _Kintsugi_Color("Color", Color) = (1, 1, 1, 1)
_Kintsugi_Smoothness("Smoothness", Range(0, 1)) = 0.5
_Kintsugi_Metallic("Metallic", Range(0, 1)) = 0.5
+
+ //ifex _Kintsugi_Domain_Warping_Enabled==0
+ [HideInInspector] m_start_Kintsugi_Domain_Warping("Domain Warping", Float) = 0
+ [ThryToggle(_KINTSUGI_DOMAIN_WARPING)] _Kintsugi_Domain_Warping_Enabled("Enable", Float) = 0
+ _Kintsugi_Domain_Warping_Scale("Scale", Vector) = (1, 1, 1, 0)
+ _Kintsugi_Domain_Warping_Strength("Strength", Float) = 1
+ [IntRange] _Kintsugi_Domain_Warping_Octaves("Octaves", Range(1, 10)) = 3
+ _Kintsugi_Domain_Warping_Lacunarity("Lacunarity", Float) = 2
+ _Kintsugi_Domain_Warping_Gain("Gain", Float) = 0.5
+ [HideInInspector] m_end_Kintsugi_Domain_Warping("Domain Warping", Float) = 0
+ //endex
[HideInInspector] m_end_Kintsugi("Kintsugi", Float) = 0
//endex
diff --git a/features.cginc b/features.cginc
index f67a855..9a05eb2 100755
--- a/features.cginc
+++ b/features.cginc
@@ -18,6 +18,7 @@
#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
//ifex _Tessellation_Enabled==0
diff --git a/globals.cginc b/globals.cginc
index 7ad1c3f..f96a23d 100755
--- a/globals.cginc
+++ b/globals.cginc
@@ -112,6 +112,15 @@ float _Kintsugi_Smoothness;
float _Kintsugi_Metallic;
#endif // _KINTSUGI
+#if defined(_KINTSUGI_DOMAIN_WARPING)
+texture3D _Kintsugi_Domain_Warping_Noise;
+float3 _Kintsugi_Domain_Warping_Scale;
+float _Kintsugi_Domain_Warping_Strength;
+float _Kintsugi_Domain_Warping_Octaves;
+float _Kintsugi_Domain_Warping_Lacunarity;
+float _Kintsugi_Domain_Warping_Gain;
+#endif // _KINTSUGI_DOMAIN_WARPING
+
#if defined(_TESSELLATION)
float _Tessellation_Factor;
float _Tessellation_Frustum_Culling_Bias;
diff --git a/math.cginc b/math.cginc
index 9d95588..6b24a55 100755
--- a/math.cginc
+++ b/math.cginc
@@ -109,5 +109,97 @@ 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.
+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);
+}
+
+// Procedural value noise in [0,1]^3 — trilinear interpolation of hashed corners.
+float3 value_noise3(float3 p) {
+ float3 i = 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),
+ u.z);
+}
+
+// Domain warping using procedural value noise.
+float3 domain_warp_procedural(float3 uvw, float strength,
+ uint octaves, float lacunarity, float gain) {
+ float3 noise = 0;
+ float g = 1;
+
+ for (uint ii = 0; ii < octaves; ++ii) {
+ noise += value_noise3(uvw + noise * strength) * g;
+ uvw *= lacunarity;
+ g *= gain;
+ }
+
+ noise *= (1 - gain) / (1 - pow(gain, octaves));
+
+ return noise;
+}
+
+// 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) {
+ 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;
+ uvw *= lacunarity;
+ g *= gain;
+ uvw_dx *= lacunarity;
+ uvw_dy *= lacunarity;
+ }
+
+ // Normalize: geometric series 1 + r + ... + r^{n-1} = (1 - r^n) / (1 - r)
+ noise *= (1 - gain) / (1 - pow(gain, octaves));
+
+ return noise;
+}
+
+// Return distance to the nearest cell edge in a Voronoi pattern.
+float voronoi_edge_distance(float3 x) {
+ float3 p = floor(x);
+ float3 f = frac(x);
+ float d1 = 1e6;
+ float d2 = 1e6;
+ float3 p1 = 0;
+ float3 p2 = 0;
+
+ for (int k = -1; k <= 1; k++) {
+ for (int j = -1; j <= 1; j++) {
+ for (int i = -1; i <= 1; i++) {
+ float3 b = float3(i, j, k);
+ float3 r = b + hash33_fast(p + b) - f;
+ float d = dot(r, r);
+ if (d < d1) {
+ d2 = d1;
+ p2 = p1;
+ d1 = d;
+ p1 = r;
+ } else if (d < d2) {
+ d2 = d;
+ p2 = r;
+ }
+ }
+ }
+ }
+ return (d2 - d1) / (2.0 * max(1e-4, length(p2 - p1)));
+}
+
#endif // __MATH_INC
diff --git a/pbr.cginc b/pbr.cginc
index cc566b4..f6000b8 100755
--- a/pbr.cginc
+++ b/pbr.cginc
@@ -126,9 +126,6 @@ void apply_marble(float3 world_pos, inout float3 albedo) {
#if defined(_MARBLE)
float3 uvw = world_pos * _Marble_Scale;
- float3 noise = 0;
- float gain = 1;
-
float3 offset = 0;
#if defined(_MARBLE_TIME)
offset += _Time[0] * _Marble_Speed * _Marble_Direction;
@@ -139,15 +136,8 @@ void apply_marble(float3 world_pos, inout float3 albedo) {
uvw += offset;
- for (uint ii = 0; ii < _Marble_Octaves; ++ii) {
- noise += _Marble_Noise.Sample(aniso4_trilinear_repeat_s, uvw + noise * _Marble_Strength).rgb * gain;
-
- uvw *= _Marble_Lacunarity;
- gain *= _Marble_Gain;
- }
-
- // In general, the series 1 + r + ... + r^{n-1} = (1 - r^n) / (1 - r)
- noise *= (1 - _Marble_Gain) / (1 - pow(_Marble_Gain, _Marble_Octaves));
+ float3 noise = domain_warp_3d_tex(_Marble_Noise, uvw, _Marble_Strength,
+ _Marble_Octaves, _Marble_Lacunarity, _Marble_Gain);
noise = _Marble_Post_Ramp.Sample(linear_clamp_s, float2(noise.x, 0));
@@ -155,47 +145,20 @@ void apply_marble(float3 world_pos, inout float3 albedo) {
#endif
}
-float3 kintsugi_hash33(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);
-}
-
-float kintsugi_voronoi_edge(float3 x) {
- float3 p = floor(x);
- float3 f = frac(x);
- float d1 = 8.0;
- float d2 = 8.0;
- float3 p1 = 0;
- float3 p2 = 0;
-
- for (int k = -1; k <= 1; k++) {
- for (int j = -1; j <= 1; j++) {
- for (int i = -1; i <= 1; i++) {
- float3 b = float3(i, j, k);
- float3 r = b + kintsugi_hash33(p + b) - f;
- float d = dot(r, r);
- if (d < d1) {
- d2 = d1;
- p2 = p1;
- d1 = d;
- p1 = r;
- } else if (d < d2) {
- d2 = d;
- p2 = r;
- }
- }
- }
- }
- return (d2 - d1) / (2.0 * max(0.0001, length(p2 - p1)));
-}
-
void apply_kintsugi(float3 world_pos, inout float3 albedo, inout float smoothness, inout float metallic) {
#if defined(_KINTSUGI)
float3 uvw = world_pos * _Kintsugi_Scale;
+#if defined(_KINTSUGI_DOMAIN_WARPING)
+ float3 warp = domain_warp_procedural(
+ world_pos * _Kintsugi_Domain_Warping_Scale,
+ _Kintsugi_Domain_Warping_Strength, _Kintsugi_Domain_Warping_Octaves,
+ _Kintsugi_Domain_Warping_Lacunarity, _Kintsugi_Domain_Warping_Gain);
+ uvw += warp;
+#endif
+
#if defined(_KINTSUGI_PROCEDURAL)
- float mask = kintsugi_voronoi_edge(uvw) + 0.5f;
+ float mask = voronoi_edge_distance(uvw) + 0.5f;
#else
float mask = _Kintsugi_Noise.Sample(aniso4_trilinear_repeat_s, uvw);
#endif