diff options
| -rw-r--r-- | aperiodic_tiling.cginc | 140 | ||||
| -rwxr-xr-x | pbr.cginc | 2 |
2 files changed, 128 insertions, 14 deletions
diff --git a/aperiodic_tiling.cginc b/aperiodic_tiling.cginc index 4106530..28cfb71 100644 --- a/aperiodic_tiling.cginc +++ b/aperiodic_tiling.cginc @@ -13,7 +13,10 @@ #if defined(_APERIODIC_TILING) static const float M5 = sqrt(2.0 / 5.0); -static const float APERIODIC_FILTER_THRESHOLD = 0.1; +static const float APERIODIC_FILTER_THRESHOLD = 0.5; +static const float APERIODIC_FAR_THRESHOLD = 1.0; +static const float APERIODIC_FILTER_BLEND_WIDTH = 0.5; +static const float APERIODIC_SMOOTHSTEP_SQ_MEAN = 13.0 / 35.0; static const float4 basis_u5_03 = M5 * float4( cos(0 * TAU / 10), @@ -87,6 +90,21 @@ static const float aperiodic_face_c44[10] = { 1, 1, 1, 0, 1, 1, 0, 1, 0, 0 }; +// Area weights for the 10 face orientations. Adjacent axis pairs are the thin +// rhombs, separated pairs are the thick rhombs. These weights sum to 1. +static const float aperiodic_face_weights[10] = { + 0.0552786404500042, + 0.1447213595499958, + 0.1447213595499958, + 0.0552786404500042, + 0.0552786404500042, + 0.1447213595499958, + 0.1447213595499958, + 0.0552786404500042, + 0.1447213595499958, + 0.0552786404500042 +}; + // Precomputed per-face barycentric transforms. For each fixed face // orientation, this is inverse(float2x2(proj5(a), proj5(b))). static const float2x2 aperiodic_face_matrices[10] = { @@ -168,6 +186,11 @@ struct AperiodicPointSample { int face_id; }; +struct AperiodicFootprintRange { + float min_extent; + float max_extent; +}; + void aperiodic_accumulate_point_orientation(float2 uv, float4 p03, float p44, int face_id, inout AperiodicPointSample best) { float2x2 m = aperiodic_face_matrices[face_id]; @@ -220,19 +243,22 @@ void sample_aperiodic_tiling_point(float2 uv, float4 p03, float p44, out float3 #endif } -bool aperiodic_requires_filtering(float2 uv_ddx, float2 uv_ddy) { +AperiodicFootprintRange aperiodic_footprint_range(float2 uv_ddx, float2 uv_ddy) { + AperiodicFootprintRange range; + range.min_extent = 1e10; + range.max_extent = 0.0; + [unroll] for (int face_id = 0; face_id < 10; ++face_id) { float2x2 m = aperiodic_face_matrices[face_id]; float2 dbdx = mul(m, uv_ddx); float2 dbdy = mul(m, uv_ddy); - float2 half_extents = 0.5 * (abs(dbdx) + abs(dbdy)); - if (max(half_extents.x, half_extents.y) >= APERIODIC_FILTER_THRESHOLD) { - return true; - } + float face_extent = max(abs(dbdx.x) + abs(dbdy.x), abs(dbdx.y) + abs(dbdy.y)) * 0.5; + range.min_extent = min(range.min_extent, face_extent); + range.max_extent = max(range.max_extent, face_extent); } - return false; + return range; } void aperiodic_accumulate_filtered_orientation(float2 uv, float4 p03, float p44, @@ -295,28 +321,116 @@ void sample_aperiodic_tiling_filtered(float2 uv, float4 p03, float p44, float2 u #endif } -void sample_aperiodic_tiling(float2 uv, out float3 albedo, out float3 tiling_normal_tangent) { +float3 sample_aperiodic_tiling_far_albedo() { + float3 face_mean = 0.0; + + [unroll] + for (int face_id = 0; face_id < 10; ++face_id) { + face_mean += aperiodic_face_color(face_id) * aperiodic_face_weights[face_id]; + } + + float inner_width = saturate(1.0 - 2.0 * min(_Aperiodic_Tiling_Edge_Thickness, 0.5)); + float inner_fraction = inner_width * inner_width; + return lerp(_Aperiodic_Tiling_Edge_Color.rgb, face_mean, inner_fraction); +} + +float aperiodic_minified_normal_kernel_roughness(float minified_weight) { +#if defined(_APERIODIC_TILING_NORMALS) + float bevel_width = min(_Aperiodic_Tiling_Normal_Thickness, 0.5); + if (bevel_width <= 1e-5 || minified_weight <= 1e-5) { + return 0.0; + } + + float flat_width = saturate(1.0 - 2.0 * bevel_width); + float bevel_fraction = 1.0 - flat_width * flat_width; + float slope_variance = + _Aperiodic_Tiling_Normal_Strength * _Aperiodic_Tiling_Normal_Strength * + bevel_fraction * APERIODIC_SMOOTHSTEP_SQ_MEAN; + return minified_weight * min(2.0 * slope_variance, _Specular_AA_Threshold); +#else + return 0.0; +#endif +} + +void apply_aperiodic_smoothness(inout float smoothness, float minified_weight) { + float kernel_roughness = aperiodic_minified_normal_kernel_roughness(minified_weight); + if (kernel_roughness <= 1e-5) { + return; + } + + float perceptual_roughness = 1.0 - smoothness; + float roughness = perceptual_roughness * perceptual_roughness; + float square_roughness = saturate(roughness * roughness + kernel_roughness); + smoothness = 1.0 - saturate(sqrt(sqrt(square_roughness))); +} + +void sample_aperiodic_tiling_minified(float2 uv, float4 p03, float p44, float2 uv_ddx, + float2 uv_ddy, float far_weight, out float3 albedo, + out float3 tiling_normal_tangent) { + float3 filtered_albedo; + float3 filtered_normal_tangent; + float3 far_albedo = sample_aperiodic_tiling_far_albedo(); + + sample_aperiodic_tiling_filtered( + uv, p03, p44, uv_ddx, uv_ddy, filtered_albedo, filtered_normal_tangent); + albedo = lerp(filtered_albedo, far_albedo, far_weight); + +#if defined(_APERIODIC_TILING_NORMALS) + tiling_normal_tangent = normalize(lerp( + filtered_normal_tangent, float3(0.0, 0.0, 1.0), far_weight)); +#else + tiling_normal_tangent = 0.0; +#endif +} + +void sample_aperiodic_tiling(float2 uv, out float3 albedo, out float3 tiling_normal_tangent, + out float minified_weight) { float4 p03 = uv.x * basis_u5_03 + uv.y * basis_v5_03; float p44 = uv.x * basis_u5_44 + uv.y * basis_v5_44; float2 uv_ddx = ddx(uv); float2 uv_ddy = ddy(uv); + AperiodicFootprintRange footprint = aperiodic_footprint_range(uv_ddx, uv_ddy); + float filter_start = max(APERIODIC_FILTER_THRESHOLD - APERIODIC_FILTER_BLEND_WIDTH, 0.0); + float filter_end = APERIODIC_FILTER_THRESHOLD + APERIODIC_FILTER_BLEND_WIDTH; + float far_weight = smoothstep( + APERIODIC_FILTER_THRESHOLD, APERIODIC_FAR_THRESHOLD, footprint.min_extent); [branch] - if (aperiodic_requires_filtering(uv_ddx, uv_ddy)) { - sample_aperiodic_tiling_filtered(uv, p03, p44, uv_ddx, uv_ddy, albedo, tiling_normal_tangent); - } else { + if (footprint.max_extent <= filter_start) { sample_aperiodic_tiling_point(uv, p03, p44, albedo, tiling_normal_tangent); + minified_weight = 0.0; + } else if (footprint.max_extent >= filter_end) { + sample_aperiodic_tiling_minified( + uv, p03, p44, uv_ddx, uv_ddy, far_weight, albedo, tiling_normal_tangent); + minified_weight = 1.0; + } else { + float3 point_albedo; + float3 point_normal_tangent; + float3 minified_albedo; + float3 minified_normal_tangent; + float filter_weight = smoothstep(filter_start, filter_end, footprint.max_extent); + + sample_aperiodic_tiling_point(uv, p03, p44, point_albedo, point_normal_tangent); + sample_aperiodic_tiling_minified( + uv, p03, p44, uv_ddx, uv_ddy, far_weight, minified_albedo, minified_normal_tangent); + albedo = lerp(point_albedo, minified_albedo, filter_weight); + tiling_normal_tangent = normalize(lerp( + point_normal_tangent, minified_normal_tangent, filter_weight)); + minified_weight = filter_weight; } } #endif // defined(_APERIODIC_TILING) -void apply_aperiodic_tiling(float2 uv, inout float3 albedo, inout float3 normal_tangent) { +void apply_aperiodic_tiling(float2 uv, inout float3 albedo, inout float smoothness, + inout float3 normal_tangent) { #if defined(_APERIODIC_TILING) uv *= _Aperiodic_Tiling_Scale; float3 tiling_normal_tangent; - sample_aperiodic_tiling(uv, albedo, tiling_normal_tangent); + float minified_weight; + sample_aperiodic_tiling(uv, albedo, tiling_normal_tangent, minified_weight); #if defined(_APERIODIC_TILING_NORMALS) normal_tangent = tiling_normal_tangent; + apply_aperiodic_smoothness(smoothness, minified_weight); #endif #endif // _APERIODIC_TILING } @@ -287,7 +287,7 @@ Pbr getPbr(v2f i) { apply_burley_tiling(pbr, normal_tangent); apply_triplanar_layers(i.worldPos, i.normal, pbr, normal_tangent); apply_custom31_world(i, pbr, normal_tangent); - apply_aperiodic_tiling(i.uv01.xy, pbr.albedo.xyz, normal_tangent); + apply_aperiodic_tiling(i.uv01.xy, pbr.albedo.xyz, pbr.smoothness, normal_tangent); applyDecals(i, pbr, normal_tangent); |
