diff options
| author | yum <yum.food.vr@gmail.com> | 2026-03-16 14:11:40 -0700 |
|---|---|---|
| committer | yum <yum.food.vr@gmail.com> | 2026-03-16 14:11:40 -0700 |
| commit | e641d53cc28ca8b77c237aef89f4968c8114b614 (patch) | |
| tree | d079d46ad94711665022d821279787068973bd81 | |
| parent | 4bf004c7e9c6eb2edf497b075ee01375ae878ff1 (diff) | |
Add kintsugi domain warping
| -rwxr-xr-x | 3ner.shader | 11 | ||||
| -rwxr-xr-x | features.cginc | 1 | ||||
| -rwxr-xr-x | globals.cginc | 9 | ||||
| -rwxr-xr-x | math.cginc | 92 | ||||
| -rwxr-xr-x | pbr.cginc | 59 |
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; @@ -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 @@ -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 |
