summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-x3ner.shader47
-rwxr-xr-xScripts/gaussianize61
-rw-r--r--data.cginc3
-rw-r--r--debug.cginc6
-rwxr-xr-xfeatures.cginc6
-rwxr-xr-xglobals.cginc16
-rwxr-xr-xpbr.cginc352
7 files changed, 317 insertions, 174 deletions
diff --git a/3ner.shader b/3ner.shader
index b656b26..63e8118 100755
--- a/3ner.shader
+++ b/3ner.shader
@@ -665,6 +665,28 @@ Shader "yum_food/3ner"
[HideInInspector] m_end_Glitter("Glitter", Float) = 0
//endex
+ //ifex _Parallax_Heightmap_Enabled==0
+ [HideInInspector] m_start_Parallax_Heightmap("Parallax Heightmap", Float) = 0
+ [ThryToggle(_PARALLAX_HEIGHTMAP)] _Parallax_Heightmap_Enabled("Enable", Float) = 0
+ _Parallax_Heightmap_Scale("Scale", Float) = 1
+ _Parallax_Heightmap_Bias("Neutral point", Float) = 0.5
+
+ //ifex _Parallax_Heightmap_Texture_Enabled==0
+ [HideInInspector] m_start_Parallax_Heightmap_Texture("Heightmap", Float) = 0
+ [ThryToggle(_PARALLAX_HEIGHTMAP_TEXTURE)] _Parallax_Heightmap_Texture_Enabled("Enable", Float) = 0
+ _Parallax_Heightmap("Texture", 2D) = "gray" {}
+ [HideInInspector] m_end_Parallax_Heightmap_Texture("Heightmap", Float) = 0
+ //endex
+
+ //ifex _Parallax_Heightmap_Ray_Marching_Enabled==0
+ [HideInInspector] m_start_Parallax_Heightmap_Ray_Marching("Parallax Heightmap Ray Marching", Float) = 0
+ [ThryToggle(_PARALLAX_HEIGHTMAP_RAY_MARCHING)] _Parallax_Heightmap_Ray_Marching_Enabled("Enable", Float) = 0
+ [IntRange] _Parallax_Heightmap_Ray_Marching_Steps("Steps", Range(1, 10)) = 5
+ [HideInInspector] m_end_Parallax_Heightmap_Ray_Marching("Parallax Heightmap Ray Marching", Float) = 0
+ //endex
+ [HideInInspector] m_end_Parallax_Heightmap("Parallax Heightmap", Float) = 0
+ //endex
+
//ifex _Burley_Tiling_Enabled==0
[HideInInspector] m_start_Burley_Tiling("Burley Tiling", Float) = 0
[ThryToggle(_BURLEY_TILING)] _Burley_Tiling_Enabled("Enable", Float) = 0
@@ -697,6 +719,14 @@ Shader "yum_food/3ner"
[HideInInspector] m_end_Burley_Tiling_Normal("Normal", Float) = 0
//endex
+ //ifex _Burley_Tiling_Heightmap_Enabled==0
+ [HideInInspector] m_start_Burley_Tiling_Heightmap("Heightmap", Float) = 0
+ [ThryToggle(_BURLEY_TILING_HEIGHTMAP)] _Burley_Tiling_Heightmap_Enabled("Enable", Float) = 0
+ _Burley_Tiling_Heightmap("Texture", 2D) = "gray" {}
+ _Burley_Tiling_Heightmap_LUT("LUT", 2D) = "white" {}
+ [HideInInspector] m_end_Burley_Tiling_Heightmap("Heightmap", Float) = 0
+ //endex
+
[HideInInspector] m_end_Burley_Tiling("Burley Tiling", Float) = 0
//endex
@@ -1150,6 +1180,7 @@ Shader "yum_food/3ner"
[ThryToggle(_DEBUG_VIEW_WORLD_SPACE_NORMALS)] _Debug_View_World_Space_Normals("World space normals", Float) = 0
[ThryToggle(_DEBUG_VIEW_OBJECT_SPACE_NORMALS)] _Debug_View_Object_Space_Normals("Object space normals", Float) = 0
[ThryToggle(_DEBUG_VIEW_METALLIC_GLOSS)] _Debug_View_Metallic_Gloss("Metallic gloss", Float) = 0
+ [ThryToggle(_DEBUG_VIEW_HEIGHT)] _Debug_View_Height("Height", Float) = 0
[ThryToggle(_DEBUG_VIEW_DEPTH)] _Debug_View_Depth("Depth", Float) = 0
[ThryToggle(_DEBUG_VIEW_DIRECT_NOH)] _Debug_View_Direct_NoH("Direct NoH", Float) = 0
[ThryToggle(_DEBUG_VIEW_DIRECT_LOH)] _Debug_View_Direct_LoH("Direct LoH", Float) = 0
@@ -1195,22 +1226,6 @@ Shader "yum_food/3ner"
[HideInInspector] m_end_Instancing("Instancing", Float) = 0
//endex
- //ifex _Parallax_Heightmap_Enabled==0
- [HideInInspector] m_start_Parallax_Heightmap("Parallax Heightmap", Float) = 0
- [ThryToggle(_PARALLAX_HEIGHTMAP)] _Parallax_Heightmap_Enabled("Enable", Float) = 0
- _Parallax_Heightmap("Heightmap", 2D) = "gray" {}
- _Parallax_Heightmap_Scale("Scale", Float) = 1
- _Parallax_Heightmap_Bias("Neutral point", Float) = 0.5
-
- //ifex _Parallax_Heightmap_Ray_Marching_Enabled==0
- [HideInInspector] m_start_Parallax_Heightmap_Ray_Marching("Parallax Heightmap Ray Marching", Float) = 0
- [ThryToggle(_PARALLAX_HEIGHTMAP_RAY_MARCHING)] _Parallax_Heightmap_Ray_Marching_Enabled("Enable", Float) = 0
- [IntRange] _Parallax_Heightmap_Ray_Marching_Steps("Steps", Range(1, 10)) = 5
- [HideInInspector] m_end_Parallax_Heightmap_Ray_Marching("Parallax Heightmap Ray Marching", Float) = 0
- //endex
- [HideInInspector] m_end_Parallax_Heightmap("Parallax Heightmap", Float) = 0
- //endex
-
//ifex _Shadow_Caster_Enabled==0
[HideInInspector] m_start_Shadow_Caster("Shadow caster pass", Float) = 0
[ThryToggle(_SHADOW_CASTER)] _Shadow_Caster_Enabled("Enable", Float) = 1
diff --git a/Scripts/gaussianize b/Scripts/gaussianize
index 2ca915d..620e05d 100755
--- a/Scripts/gaussianize
+++ b/Scripts/gaussianize
@@ -72,6 +72,58 @@ def save_image(image: np.ndarray, output_path: Path):
raise ValueError(f"Unsupported output format '{output_path.suffix}'. Use .exr or .png.")
+def rgb_to_ycrcb(image: np.ndarray) -> np.ndarray:
+ """Convert RGB in [0, 1] to full-range YCrCb in [0, 1]."""
+ rgb = np.clip(image, 0.0, 1.0)
+ r = rgb[:, :, 0]
+ g = rgb[:, :, 1]
+ b = rgb[:, :, 2]
+
+ y = 0.299 * r + 0.587 * g + 0.114 * b
+ cb = 0.5 + (b - y) * 0.564
+ cr = 0.5 + (r - y) * 0.713
+ return np.stack((y, cr, cb), axis=-1)
+
+
+def ycrcb_to_rgb(image: np.ndarray) -> np.ndarray:
+ """Convert full-range YCrCb in [0, 1] back to RGB in [0, 1]."""
+ ycrcb = np.asarray(image, dtype=np.float64)
+ y = ycrcb[:, :, 0]
+ cr = ycrcb[:, :, 1] - 0.5
+ cb = ycrcb[:, :, 2] - 0.5
+
+ r = y + 1.402 * cr
+ g = y - 0.714136 * cr - 0.344136 * cb
+ b = y + 1.772 * cb
+ return np.clip(np.stack((r, g, b), axis=-1), 0.0, 1.0)
+
+
+def subtract_low_frequencies(channel: np.ndarray, cutoff_cycles: float = 8.0) -> np.ndarray:
+ """Subtract a smooth periodic low-frequency component from a scalar image."""
+ if cutoff_cycles <= 0.0:
+ raise ValueError(f"cutoff_cycles must be > 0, got {cutoff_cycles}")
+
+ channel = np.asarray(channel, dtype=np.float64)
+ height, width = channel.shape
+
+ fy = np.fft.fftfreq(height)
+ fx = np.fft.fftfreq(width)
+ radius = np.sqrt((fy[:, np.newaxis] * height) ** 2 + (fx[np.newaxis, :] * width) ** 2)
+
+ low_mask = np.exp(-((radius / cutoff_cycles) ** 2))
+ low_pass = np.fft.ifft2(np.fft.fft2(channel) * low_mask).real
+
+ # Remove only the spatial trend and keep the original average luminance.
+ return channel - low_pass + float(np.mean(low_pass))
+
+
+def homogenize_luminance(image: np.ndarray, cutoff_cycles: float = 8.0) -> np.ndarray:
+ """Reduce very-low-frequency luminance variation while preserving chroma."""
+ ycrcb = rgb_to_ycrcb(image)
+ ycrcb[:, :, 0] = subtract_low_frequencies(ycrcb[:, :, 0], cutoff_cycles=cutoff_cycles)
+ return ycrcb_to_rgb(ycrcb)
+
+
class TruncatedGaussian:
"""Truncated Gaussian distribution for histogram transformation."""
@@ -450,6 +502,11 @@ def main():
action="store_true",
help="Print detailed progress information"
)
+ parser.add_argument(
+ "--homo",
+ action="store_true",
+ help="Homogenize luminance before gaussianizing by removing very-low-frequency Y in YCrCb space"
+ )
args = parser.parse_args()
@@ -467,6 +524,10 @@ def main():
image = load_image(args.input)
quantization_step = 0.0 if args.input.suffix.lower() == ".exr" else (1.0 / 255.0)
+ if args.homo:
+ print("Homogenizing luminance in YCrCb space...")
+ image = homogenize_luminance(image)
+
# Gaussianize the texture
print("Applying per-channel Gaussianization...")
gaussianized, inverse_luts = gaussianize_texture(
diff --git a/data.cginc b/data.cginc
index d8c6960..370c3e6 100644
--- a/data.cginc
+++ b/data.cginc
@@ -31,6 +31,9 @@ struct Pbr {
float cc_roughness_perceptual;
float cc_strength;
#endif
+#if defined(_PARALLAX_HEIGHTMAP) || defined(_BURLEY_TILING_HEIGHTMAP)
+ float height;
+#endif
#if defined(_DEBUG_VIEW_TANGENT_SPACE_NORMALS)
float3 tangent_normal;
#endif
diff --git a/debug.cginc b/debug.cginc
index 6510fdd..f6c8f15 100644
--- a/debug.cginc
+++ b/debug.cginc
@@ -14,6 +14,12 @@ float4 apply_debug_view(v2f i, Pbr pbr, LightData light_data, BrdfData bd,
return float4((normalOS + 1.0f) * 0.5f, 1);
#elif defined(_DEBUG_VIEW_METALLIC_GLOSS)
return float4(pbr.metallic, pbr.smoothness, 0, 1);
+#elif defined(_DEBUG_VIEW_HEIGHT)
+ #if defined(_PARALLAX_HEIGHTMAP) || defined(_BURLEY_TILING_HEIGHTMAP)
+ return float4(F1_TO_F3(pbr.height), 1);
+ #else
+ return float4(0, 0, 0, 1);
+ #endif
#elif defined(_DEBUG_VIEW_DEPTH)
float2 screen_uv = i.pos.xy / _ScreenParams.xy;
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, screen_uv);
diff --git a/features.cginc b/features.cginc
index 09a2c30..312af41 100755
--- a/features.cginc
+++ b/features.cginc
@@ -82,6 +82,7 @@
#pragma shader_feature_local _DEBUG_VIEW_WORLD_SPACE_NORMALS
#pragma shader_feature_local _DEBUG_VIEW_OBJECT_SPACE_NORMALS
#pragma shader_feature_local _DEBUG_VIEW_METALLIC_GLOSS
+#pragma shader_feature_local _DEBUG_VIEW_HEIGHT
#pragma shader_feature_local _DEBUG_VIEW_DEPTH
#pragma shader_feature_local _DEBUG_VIEW_DIRECT_NOH
#pragma shader_feature_local _DEBUG_VIEW_DIRECT_LOH
@@ -111,9 +112,7 @@
//ifex _Parallax_Heightmap_Enabled==0
#pragma shader_feature_local _PARALLAX_HEIGHTMAP
-//endex
-
-//ifex _Parallax_Heightmap_Ray_Marching_Enabled==0
+#pragma shader_feature_local _PARALLAX_HEIGHTMAP_TEXTURE
#pragma shader_feature_local _PARALLAX_HEIGHTMAP_RAY_MARCHING
//endex
@@ -243,6 +242,7 @@
//ifex _Burley_Tiling_Enabled==0
#pragma shader_feature_local _BURLEY_TILING
+#pragma shader_feature_local _BURLEY_TILING_HEIGHTMAP
#pragma shader_feature_local _BURLEY_TILING_SMOOTHNESS
#pragma shader_feature_local _BURLEY_TILING_SMOOTHNESS_INVERT
#pragma shader_feature_local _BURLEY_TILING_NORMAL
diff --git a/globals.cginc b/globals.cginc
index 88fb663..a475083 100755
--- a/globals.cginc
+++ b/globals.cginc
@@ -273,16 +273,21 @@ UNITY_INSTANCING_BUFFER_END(InstanceProps)
#endif // _INSTANCE_TEXTURE_OFFSET && UNITY_INSTANCING_ENABLED
#if defined(_PARALLAX_HEIGHTMAP)
-texture2D _Parallax_Heightmap;
-float4 _Parallax_Heightmap_ST;
float _Parallax_Heightmap_Scale;
float _Parallax_Heightmap_Bias;
-#endif // _PARALLAX_HEIGHTMAP
+
+#if defined(_PARALLAX_HEIGHTMAP_TEXTURE)
+texture2D _Parallax_Heightmap;
+float4 _Parallax_Heightmap_ST;
+#endif // _PARALLAX_HEIGHTMAP_TEXTURE
#if defined(_PARALLAX_HEIGHTMAP_RAY_MARCHING)
float _Parallax_Heightmap_Ray_Marching_Steps;
#endif // _PARALLAX_HEIGHTMAP_RAY_MARCHING
+#endif // _PARALLAX_HEIGHTMAP
+
+
#define DECAL_UV_MODE_REPEAT 0
#define DECAL_UV_MODE_MIRROR 1
#define DECAL_UV_MODE_CLAMP 2
@@ -636,4 +641,9 @@ texture2D _Burley_Tiling_Normal_Map_LUT;
float _Burley_Tiling_Normal_Strength;
#endif // _BURLEY_TILING_NORMAL
+#if defined(_BURLEY_TILING_HEIGHTMAP)
+texture2D _Burley_Tiling_Heightmap;
+texture2D _Burley_Tiling_Heightmap_LUT;
+#endif // _BURLEY_TILING_HEIGHTMAP
+
#endif // __GLOBALS_INC
diff --git a/pbr.cginc b/pbr.cginc
index 337d967..500eef0 100755
--- a/pbr.cginc
+++ b/pbr.cginc
@@ -9,6 +9,178 @@
#include "letter_grid.cginc"
#include "texture_utils.cginc"
+#if defined(_BURLEY_TILING)
+float2 burley_tri_to_cart(float2 tri_coord) {
+ return float2(
+ tri_coord.x + tri_coord.y * 0.5f,
+ tri_coord.y * SQRT_3_OVER_2);
+}
+
+float3 burley_apply_blend_gamma(float3 weights, float gamma) {
+ weights = pow(weights, gamma);
+ return weights / (weights.x + weights.y + weights.z);
+}
+
+// Equation 4 (first half).
+float3 burley_soft_clipping_lower_half(float3 x_hat, float w_hat) {
+ float linear_start = 0.25f * (2.0f - w_hat);
+ float3 linear_value = (x_hat - 0.5f) / w_hat + 0.5f;
+ float3 linear_mask = step(float3(linear_start, linear_start, linear_start), x_hat);
+
+ if (w_hat >= TWO_OVER_THREE) {
+ float3 t = x_hat / (2.0f - w_hat);
+ float3 quadratic = 8.0f * (1.0f / w_hat - 1.0f) * t * t + (3.0f - 2.0f / w_hat) * t;
+ return lerp(quadratic, linear_value, linear_mask);
+ }
+
+ float quadratic_start = 0.25f * (2.0f - 3.0f * w_hat);
+ float3 d = (x_hat - quadratic_start) / w_hat;
+ float3 quadratic = d * d;
+ float3 quadratic_mask = step(float3(quadratic_start, quadratic_start, quadratic_start), x_hat);
+ float3 result = quadratic * quadratic_mask;
+ return lerp(result, linear_value, linear_mask);
+}
+
+// Equation 4.
+float3 burley_soft_clipping_contrast(float3 x_hat, float w_hat) {
+ float3 upper_mask = step(0.5f, x_hat);
+ float3 lower_x = min(x_hat, 1.0f - x_hat);
+ float3 lower_y = burley_soft_clipping_lower_half(lower_x, w_hat);
+ return lerp(lower_y, 1.0f - lower_y, upper_mask);
+}
+
+float3 burley_apply_soft_clipping(float3 gaussian_color, float3 weights) {
+ float w_hat = sqrt(dot(weights, weights));
+ return burley_soft_clipping_contrast(gaussian_color, w_hat);
+}
+
+float3 burley_degaussianize(texture2D lut, float3 gaussian_color, bool decode_srgb = true) {
+ float2 uv_r = float2(gaussian_color.r, 0.5f);
+ float2 uv_g = float2(gaussian_color.g, 0.5f);
+ float2 uv_b = float2(gaussian_color.b, 0.5f);
+ float lut_r = lut.Sample(linear_clamp_s, uv_r).r;
+ float lut_g = lut.Sample(linear_clamp_s, uv_g).g;
+ float lut_b = lut.Sample(linear_clamp_s, uv_b).b;
+ float3 restored = float3(lut_r, lut_g, lut_b);
+ return decode_srgb ? srgb_to_linear(restored) : restored;
+}
+
+struct BurleyPatchTransform {
+ float2 uv;
+ float2 dx;
+ float2 dy;
+ float2x2 uv_to_patch;
+};
+
+BurleyPatchTransform burley_make_patch_transform(float2 uv, float2 uv_dx, float2 uv_dy,
+ float2 tri_vertex, float input_scale) {
+ float3 cube_id = float3(tri_vertex.x, tri_vertex.y, -tri_vertex.x - tri_vertex.y);
+ float3 tile_rand3 = hash33_fast(cube_id);
+ float2 vertex_uv = burley_tri_to_cart(tri_vertex);
+ // Map the unit-radius hex support to the unit square so arbitrary rotation
+ // stays within bounds.
+ float2 local_uv = (uv - vertex_uv) * 0.5f;
+ // Apply input scaling.
+ local_uv *= input_scale;
+ float2 sample_dx = uv_dx * (0.5f * input_scale);
+ float2 sample_dy = uv_dy * (0.5f * input_scale);
+ // Rotate.
+ float theta = hash31_ff(tile_rand3) * TAU;
+ float2x2 rot = float2x2(cos(theta), -sin(theta), sin(theta), cos(theta));
+ local_uv = mul(rot, local_uv);
+ sample_dx = mul(rot, sample_dx);
+ sample_dy = mul(rot, sample_dy);
+ // Apply randomized offset, staying within bounds.
+ // The scaled-and-rotated footprint is bounded by [-Input_Scale / 2, Input_Scale / 2],
+ // so we can offset by [(1 - Input_Scale) / 2].
+ float2 random_offset = (tile_rand3.yz * 2.0f - 1.0f) * (0.5f * (1.0f - input_scale));
+ local_uv += random_offset;
+ // Finally, remap onto [0, 1].
+ local_uv += 0.5f;
+
+ BurleyPatchTransform patch;
+ patch.uv = local_uv;
+ patch.dx = sample_dx;
+ patch.dy = sample_dy;
+ patch.uv_to_patch = rot * (0.5f * input_scale);
+ return patch;
+}
+
+float4 burley_sample_patch(texture2D tex, BurleyPatchTransform patch) {
+ return tex.SampleGrad(
+ aniso4_trilinear_repeat_s, patch.uv, patch.dx, patch.dy);
+}
+
+struct BurleyTilingContext {
+ BurleyPatchTransform patch_0;
+ BurleyPatchTransform patch_1;
+ BurleyPatchTransform patch_2;
+ float3 weights;
+ float2 base_uv;
+ float uv_scale;
+};
+
+static BurleyTilingContext _burley_ctx;
+
+void burley_tiling_setup(float2 base_uv) {
+ _burley_ctx.base_uv = base_uv;
+ float2 uv = base_uv - 0.5;
+ // Scale so that any rotation remains within [0, 1] bounds.
+ uv *= TWO_OVER_SQRT_3;
+ uv /= _Burley_Tiling_Output_Scale;
+ _burley_ctx.uv_scale = TWO_OVER_SQRT_3 / _Burley_Tiling_Output_Scale;
+ float3 hex_coord = cart_to_hex(uv);
+ float2 tri_coord = hex_coord.yz;
+ float2 tri_cell = floor(tri_coord);
+ float2 tri_frac = tri_coord - tri_cell;
+ float2 vertex_0;
+ float2 vertex_1;
+ float2 vertex_2;
+ float3 baryc;
+ if (tri_frac.x + tri_frac.y < 1.0f) {
+ vertex_0 = tri_cell;
+ vertex_1 = tri_cell + float2(1.0f, 0.0f);
+ vertex_2 = tri_cell + float2(0.0f, 1.0f);
+ baryc = float3(1.0f - (tri_frac.x + tri_frac.y), tri_frac.x, tri_frac.y);
+ } else {
+ vertex_0 = tri_cell + 1.0f;
+ vertex_1 = tri_cell + float2(0.0f, 1.0f);
+ vertex_2 = tri_cell + float2(1.0f, 0.0f);
+ baryc = float3(tri_frac.x + tri_frac.y - 1.0f, 1.0f - tri_frac.x, 1.0f - tri_frac.y);
+ }
+
+ float input_scale = _Burley_Tiling_Input_Scale;
+ _burley_ctx.weights = burley_apply_blend_gamma(baryc, _Burley_Tiling_Blend_Gamma);
+ float2 uv_dx = ddx(uv);
+ float2 uv_dy = ddy(uv);
+ _burley_ctx.patch_0 = burley_make_patch_transform(uv, uv_dx, uv_dy, vertex_0, input_scale);
+ _burley_ctx.patch_1 = burley_make_patch_transform(uv, uv_dx, uv_dy, vertex_1, input_scale);
+ _burley_ctx.patch_2 = burley_make_patch_transform(uv, uv_dx, uv_dy, vertex_2, input_scale);
+}
+#endif // _BURLEY_TILING
+
+#if defined(_PARALLAX_HEIGHTMAP_TEXTURE) || defined(_BURLEY_TILING_HEIGHTMAP)
+float heightmap_sample(float2 uv) {
+ #if defined(_BURLEY_TILING_HEIGHTMAP)
+ float2 delta = (uv - _burley_ctx.base_uv) * _burley_ctx.uv_scale;
+ float4 s0 = _Burley_Tiling_Heightmap.SampleGrad(aniso4_trilinear_repeat_s,
+ _burley_ctx.patch_0.uv + mul(_burley_ctx.patch_0.uv_to_patch, delta),
+ _burley_ctx.patch_0.dx, _burley_ctx.patch_0.dy);
+ float4 s1 = _Burley_Tiling_Heightmap.SampleGrad(aniso4_trilinear_repeat_s,
+ _burley_ctx.patch_1.uv + mul(_burley_ctx.patch_1.uv_to_patch, delta),
+ _burley_ctx.patch_1.dx, _burley_ctx.patch_1.dy);
+ float4 s2 = _Burley_Tiling_Heightmap.SampleGrad(aniso4_trilinear_repeat_s,
+ _burley_ctx.patch_2.uv + mul(_burley_ctx.patch_2.uv_to_patch, delta),
+ _burley_ctx.patch_2.dx, _burley_ctx.patch_2.dy);
+ float4 blend = s0 * _burley_ctx.weights.x + s1 * _burley_ctx.weights.y + s2 * _burley_ctx.weights.z;
+ return burley_degaussianize(_Burley_Tiling_Heightmap_LUT,
+ burley_apply_soft_clipping(blend.rgb, _burley_ctx.weights), false).r;
+ #elif defined(_PARALLAX_HEIGHTMAP_TEXTURE)
+ return _Parallax_Heightmap.Sample(linear_repeat_s, uv * _Parallax_Heightmap_ST.xy + _Parallax_Heightmap_ST.zw).r;
+ #endif
+}
+#endif // _PARALLAX_HEIGHTMAP_TEXTURE || _BURLEY_TILING_HEIGHTMAP
+
#if defined(_PARALLAX_HEIGHTMAP)
float2 parallax_offset(float2 uv, float3 view_dir_world, float3x3 tbn) {
float3 view_dir_tangent = mul(tbn, view_dir_world);
@@ -28,8 +200,7 @@ float2 parallax_offset(float2 uv, float3 view_dir_world, float3x3 tbn) {
float2 cur_uv = uv;
float2 prev_uv = uv;
float cur_depth = 0.0;
- float cur_height = 1.0 - _Parallax_Heightmap.Sample(linear_repeat_s,
- cur_uv * _Parallax_Heightmap_ST.xy + _Parallax_Heightmap_ST.zw).r;
+ float cur_height = 1.0 - heightmap_sample(cur_uv);
cur_height = (cur_height - (1.0 - _Parallax_Heightmap_Bias));
// If starting inside geometry, march backwards
@@ -53,8 +224,7 @@ float2 parallax_offset(float2 uv, float3 view_dir_world, float3x3 tbn) {
cur_uv += delta_uv;
cur_depth += delta_depth;
- cur_height = 1.0 - _Parallax_Heightmap.Sample(linear_repeat_s,
- cur_uv * _Parallax_Heightmap_ST.xy + _Parallax_Heightmap_ST.zw).r;
+ cur_height = 1.0 - heightmap_sample(cur_uv);
cur_height = (cur_height - (1.0 - _Parallax_Heightmap_Bias));
}
@@ -68,8 +238,7 @@ float2 parallax_offset(float2 uv, float3 view_dir_world, float3x3 tbn) {
[unroll(2)]
for (int j = 0; j < 2; j++) {
- float mid_height = 1.0 - _Parallax_Heightmap.Sample(linear_repeat_s,
- 0.5 * (low_uv + high_uv) * _Parallax_Heightmap_ST.xy + _Parallax_Heightmap_ST.zw).r;
+ float mid_height = 1.0 - heightmap_sample(0.5 * (low_uv + high_uv));
mid_height = (mid_height - (1.0 - _Parallax_Heightmap_Bias));
float mid_depth = 0.5 * (low_depth + high_depth);
float mid_sign = mid_height - mid_depth;
@@ -86,8 +255,7 @@ float2 parallax_offset(float2 uv, float3 view_dir_world, float3x3 tbn) {
float2 refine_uv = 0.5 * (low_uv + high_uv);
return refine_uv - uv;
#else
- float2 heightmap_uv = uv * _Parallax_Heightmap_ST.xy + _Parallax_Heightmap_ST.zw;
- float height = _Parallax_Heightmap.Sample(linear_repeat_s, heightmap_uv).r;
+ float height = heightmap_sample(uv);
height = saturate(height - _Parallax_Heightmap_Bias);
return uv_step * height;
@@ -182,152 +350,20 @@ void apply_letter_grid(v2f i, inout Pbr pbr) {
#endif
}
+void apply_burley_tiling(inout Pbr pbr, inout float3 normal_tangent) {
#if defined(_BURLEY_TILING)
-float2 burley_tri_to_cart(float2 tri_coord) {
- return float2(
- tri_coord.x + tri_coord.y * 0.5f,
- tri_coord.y * SQRT_3_OVER_2);
-}
-
-float3 burley_apply_blend_gamma(float3 weights, float gamma) {
- weights = pow(weights, gamma);
- return weights / (weights.x + weights.y + weights.z);
-}
-
-// Equation 4 (first half).
-float3 burley_soft_clipping_lower_half(float3 x_hat, float w_hat) {
- float linear_start = 0.25f * (2.0f - w_hat);
- float3 linear_value = (x_hat - 0.5f) / w_hat + 0.5f;
- float3 linear_mask = step(float3(linear_start, linear_start, linear_start), x_hat);
-
- if (w_hat >= TWO_OVER_THREE) {
- float3 t = x_hat / (2.0f - w_hat);
- float3 quadratic = 8.0f * (1.0f / w_hat - 1.0f) * t * t + (3.0f - 2.0f / w_hat) * t;
- return lerp(quadratic, linear_value, linear_mask);
- }
-
- float quadratic_start = 0.25f * (2.0f - 3.0f * w_hat);
- float3 d = (x_hat - quadratic_start) / w_hat;
- float3 quadratic = d * d;
- float3 quadratic_mask = step(float3(quadratic_start, quadratic_start, quadratic_start), x_hat);
- float3 result = quadratic * quadratic_mask;
- return lerp(result, linear_value, linear_mask);
-}
-
-// Equation 4.
-float3 burley_soft_clipping_contrast(float3 x_hat, float w_hat) {
- float3 upper_mask = step(0.5f, x_hat);
- float3 lower_x = min(x_hat, 1.0f - x_hat);
- float3 lower_y = burley_soft_clipping_lower_half(lower_x, w_hat);
- return lerp(lower_y, 1.0f - lower_y, upper_mask);
-}
-
-float3 burley_apply_soft_clipping(float3 gaussian_color, float3 weights) {
- float w_hat = sqrt(dot(weights, weights));
- return burley_soft_clipping_contrast(gaussian_color, w_hat);
-}
-
-float3 burley_degaussianize(texture2D lut, float3 gaussian_color, bool decode_srgb = true) {
- float2 uv_r = float2(gaussian_color.r, 0.5f);
- float2 uv_g = float2(gaussian_color.g, 0.5f);
- float2 uv_b = float2(gaussian_color.b, 0.5f);
- float lut_r = lut.Sample(linear_clamp_s, uv_r).r;
- float lut_g = lut.Sample(linear_clamp_s, uv_g).g;
- float lut_b = lut.Sample(linear_clamp_s, uv_b).b;
- float3 restored = float3(lut_r, lut_g, lut_b);
- return decode_srgb ? srgb_to_linear(restored) : restored;
-}
-
-struct BurleyPatchTransform {
- float2 uv;
- float2 dx;
- float2 dy;
-};
-
-BurleyPatchTransform burley_make_patch_transform(float2 uv, float2 uv_dx, float2 uv_dy,
- float2 tri_vertex, float input_scale) {
- float3 cube_id = float3(tri_vertex.x, tri_vertex.y, -tri_vertex.x - tri_vertex.y);
- float3 tile_rand3 = hash33_fast(cube_id);
- float2 vertex_uv = burley_tri_to_cart(tri_vertex);
- // Map the unit-radius hex support to the unit square so arbitrary rotation
- // stays within bounds.
- float2 local_uv = (uv - vertex_uv) * 0.5f;
- // Apply input scaling.
- local_uv *= input_scale;
- float2 sample_dx = uv_dx * (0.5f * input_scale);
- float2 sample_dy = uv_dy * (0.5f * input_scale);
- // Rotate.
- float theta = hash31_ff(tile_rand3) * TAU;
- float2x2 rot = float2x2(cos(theta), -sin(theta), sin(theta), cos(theta));
- local_uv = mul(rot, local_uv);
- sample_dx = mul(rot, sample_dx);
- sample_dy = mul(rot, sample_dy);
- // Apply randomized offset, staying within bounds.
- // The scaled-and-rotated footprint is bounded by [-Input_Scale / 2, Input_Scale / 2],
- // so we can offset by [(1 - Input_Scale) / 2].
- float2 random_offset = (tile_rand3.yz * 2.0f - 1.0f) * (0.5f * (1.0f - input_scale));
- local_uv += random_offset;
- // Finally, remap onto [0, 1].
- local_uv += 0.5f;
+ float3 weights = _burley_ctx.weights;
- BurleyPatchTransform patch;
- patch.uv = local_uv;
- patch.dx = sample_dx;
- patch.dy = sample_dy;
- return patch;
-}
-
-float4 burley_sample_patch(texture2D tex, BurleyPatchTransform patch) {
- return tex.SampleGrad(
- aniso4_trilinear_repeat_s, patch.uv, patch.dx, patch.dy);
-}
-#endif // _BURLEY_TILING
-
-void apply_burley_tiling(v2f i, inout Pbr pbr, inout float3 normal_tangent) {
-#if defined(_BURLEY_TILING)
- // Center at 0.
- float2 uv = i.uv01.xy - 0.5;
- // Scale so that any rotation remains within [0, 1] bounds.
- uv *= TWO_OVER_SQRT_3;
- uv /= _Burley_Tiling_Output_Scale;
- float3 hex_coord = cart_to_hex(uv);
- float2 tri_coord = hex_coord.yz;
- float2 tri_cell = floor(tri_coord);
- float2 tri_frac = tri_coord - tri_cell;
- float2 vertex_0;
- float2 vertex_1;
- float2 vertex_2;
- float3 baryc;
- if (tri_frac.x + tri_frac.y < 1.0f) {
- vertex_0 = tri_cell;
- vertex_1 = tri_cell + float2(1.0f, 0.0f);
- vertex_2 = tri_cell + float2(0.0f, 1.0f);
- baryc = float3(1.0f - (tri_frac.x + tri_frac.y), tri_frac.x, tri_frac.y);
- } else {
- vertex_0 = tri_cell + 1.0f;
- vertex_1 = tri_cell + float2(0.0f, 1.0f);
- vertex_2 = tri_cell + float2(1.0f, 0.0f);
- baryc = float3(tri_frac.x + tri_frac.y - 1.0f, 1.0f - tri_frac.x, 1.0f - tri_frac.y);
- }
-
- float input_scale = _Burley_Tiling_Input_Scale;
- float3 weights = burley_apply_blend_gamma(baryc, _Burley_Tiling_Blend_Gamma);
- float2 uv_dx = ddx(uv);
- float2 uv_dy = ddy(uv);
- BurleyPatchTransform patch_0_transform = burley_make_patch_transform(uv, uv_dx, uv_dy, vertex_0, input_scale);
- BurleyPatchTransform patch_1_transform = burley_make_patch_transform(uv, uv_dx, uv_dy, vertex_1, input_scale);
- BurleyPatchTransform patch_2_transform = burley_make_patch_transform(uv, uv_dx, uv_dy, vertex_2, input_scale);
-
- float4 patch_0 = burley_sample_patch(_Burley_Tiling_Maintex, patch_0_transform);
- float4 patch_1 = burley_sample_patch(_Burley_Tiling_Maintex, patch_1_transform);
- float4 patch_2 = burley_sample_patch(_Burley_Tiling_Maintex, patch_2_transform);
+ float4 patch_0 = burley_sample_patch(_Burley_Tiling_Maintex, _burley_ctx.patch_0);
+ float4 patch_1 = burley_sample_patch(_Burley_Tiling_Maintex, _burley_ctx.patch_1);
+ float4 patch_2 = burley_sample_patch(_Burley_Tiling_Maintex, _burley_ctx.patch_2);
float4 gaussian_blend = patch_0 * weights.x + patch_1 * weights.y + patch_2 * weights.z;
pbr.albedo.xyz = burley_degaussianize(_Burley_Tiling_Maintex_LUT, burley_apply_soft_clipping(gaussian_blend.rgb, weights));
#if defined(_BURLEY_TILING_SMOOTHNESS)
- float4 patch_0_smoothness = burley_sample_patch(_Burley_Tiling_Smoothness_Map, patch_0_transform);
- float4 patch_1_smoothness = burley_sample_patch(_Burley_Tiling_Smoothness_Map, patch_1_transform);
- float4 patch_2_smoothness = burley_sample_patch(_Burley_Tiling_Smoothness_Map, patch_2_transform);
+ float4 patch_0_smoothness = burley_sample_patch(_Burley_Tiling_Smoothness_Map, _burley_ctx.patch_0);
+ float4 patch_1_smoothness = burley_sample_patch(_Burley_Tiling_Smoothness_Map, _burley_ctx.patch_1);
+ float4 patch_2_smoothness = burley_sample_patch(_Burley_Tiling_Smoothness_Map, _burley_ctx.patch_2);
float4 smoothness_blend = patch_0_smoothness * weights.x + patch_1_smoothness * weights.y + patch_2_smoothness * weights.z;
pbr.smoothness = burley_degaussianize(_Burley_Tiling_Smoothness_Map_LUT, burley_apply_soft_clipping(smoothness_blend, weights)).r;
#if defined(_BURLEY_TILING_SMOOTHNESS_INVERT)
@@ -337,9 +373,9 @@ void apply_burley_tiling(v2f i, inout Pbr pbr, inout float3 normal_tangent) {
#if defined(_BURLEY_TILING_NORMAL)
// TODO whiteout blending?
- float4 patch_0_normal = burley_sample_patch(_Burley_Tiling_Normal_Map, patch_0_transform);
- float4 patch_1_normal = burley_sample_patch(_Burley_Tiling_Normal_Map, patch_1_transform);
- float4 patch_2_normal = burley_sample_patch(_Burley_Tiling_Normal_Map, patch_2_transform);
+ float4 patch_0_normal = burley_sample_patch(_Burley_Tiling_Normal_Map, _burley_ctx.patch_0);
+ float4 patch_1_normal = burley_sample_patch(_Burley_Tiling_Normal_Map, _burley_ctx.patch_1);
+ float4 patch_2_normal = burley_sample_patch(_Burley_Tiling_Normal_Map, _burley_ctx.patch_2);
float4 normal_blend = patch_0_normal * weights.x + patch_1_normal * weights.y + patch_2_normal * weights.z;
normal_tangent = burley_degaussianize(_Burley_Tiling_Normal_Map_LUT, burley_apply_soft_clipping(normal_blend.rgb, weights), false);
normal_tangent.xy = normal_tangent.xy * 2 - 1;
@@ -364,12 +400,24 @@ Pbr getPbr(v2f i) {
i.uv01.xy += getTime() * _UV_Scroll_Speed;
#endif // _UV_SCROLL
+#if defined(_BURLEY_TILING)
+ burley_tiling_setup(i.uv01.xy);
+#endif
+
#if defined(_PARALLAX_HEIGHTMAP)
float2 uv_parallax = i.uv01.xy + parallax_offset(i.uv01.xy, normalize(i.eyeVec.xyz), pbr.tbn);
#else
float2 uv_parallax = i.uv01.xy;
#endif // _PARALLAX_HEIGHTMAP
+#if defined(_PARALLAX_HEIGHTMAP) || defined(_BURLEY_TILING_HEIGHTMAP)
+ pbr.height = heightmap_sample(i.uv01.xy);
+#endif
+
+#if defined(_BURLEY_TILING)
+ burley_tiling_setup(uv_parallax);
+#endif
+
#if defined(OUTLINES_PASS) && defined(_OUTLINES)
pbr.albedo = _Outlines_Color;
#else
@@ -396,7 +444,7 @@ Pbr getPbr(v2f i) {
apply_marble(i.worldPos, pbr.albedo.xyz);
apply_kintsugi(i.worldPos, pbr.albedo.xyz, pbr.smoothness, pbr.metallic);
apply_letter_grid(i, pbr);
- apply_burley_tiling(i, pbr, normal_tangent);
+ apply_burley_tiling(pbr, normal_tangent);
applyDecals(i, pbr, normal_tangent);