diff options
| -rw-r--r-- | aperiodic_tiling.cginc | 177 |
1 files changed, 92 insertions, 85 deletions
diff --git a/aperiodic_tiling.cginc b/aperiodic_tiling.cginc index 142a3de..1907c69 100644 --- a/aperiodic_tiling.cginc +++ b/aperiodic_tiling.cginc @@ -51,9 +51,20 @@ float2 tile5_barycentric(float4 p03, float p44, float2x2 m, float2 s, return mul(m, proj5(p03, p44)) - s; } -float tile5_distance_from_barycentric(float2 barycentric) { - float2 f = abs(barycentric); - return 0.5 - max(f.x, f.y); +float interval_box_coverage(float center, float half_extent, float radius) { + if (half_extent <= 1e-5) { + return abs(center) <= radius ? 1.0 : 0.0; + } + + float lo = center - half_extent; + float hi = center + half_extent; + float overlap = max(min(hi, radius) - max(lo, -radius), 0.0); + return overlap / (2.0 * half_extent); +} + +float square_box_coverage(float2 barycentric, float2 half_extents, float radius) { + return interval_box_coverage(barycentric.x, half_extents.x, radius) * + interval_box_coverage(barycentric.y, half_extents.y, radius); } #if defined(_APERIODIC_TILING_NORMALS) @@ -70,104 +81,102 @@ float3 aperiodic_tiling_normal(float2 barycentric) { } #endif // _APERIODIC_TILING_NORMALS -void choose_aperiodic_candidate(float2 barycentric, inout float distance_to_edge, - inout float2 best_barycentric) { - float candidate_distance = tile5_distance_from_barycentric(barycentric); - bool better = (candidate_distance > distance_to_edge); - distance_to_edge = better ? candidate_distance : distance_to_edge; - best_barycentric = better ? barycentric : best_barycentric; -} +void accumulate_filtered_candidate( + float2 barycentric, float2 footprint_half_extents, float edge_width, float3 face_color, + inout float3 color_sum, inout float outer_sum, inout float2 normal_xy_sum) { + float outer = square_box_coverage(barycentric, footprint_half_extents, 0.5); + float inner_radius = max(0.5 - edge_width, 0.0); + float inner = square_box_coverage(barycentric, footprint_half_extents, inner_radius); + float edge = outer - inner; -struct AperiodicTileSample { - float distance_to_edge; - float2 barycentric; - uint face_id; -}; + color_sum += face_color * inner + _Aperiodic_Tiling_Edge_Color.rgb * edge; + outer_sum += outer; -AperiodicTileSample sample_lattice( - float4 p03, float p44, float4 a03, float a44, float4 b03, float b44) { +#if defined(_APERIODIC_TILING_NORMALS) + float3 tile_normal = aperiodic_tiling_normal(barycentric); + normal_xy_sum += tile_normal.xy * outer; +#endif +} + +void accumulate_filtered_lattice( + float4 p03, float p44, float2 uv_ddx, float2 uv_ddy, + float4 a03, float a44, float4 b03, float b44, float edge_width, float3 face_color, + inout float3 color_sum, inout float outer_sum, inout float2 normal_xy_sum) { float2 pa = proj5(a03, a44); float2 pb = proj5(b03, b44); float2x2 m = inv2x2(float2x2(pa.x, pb.x, pa.y, pb.y)); float2 r = round(float2(dot5(p03, p44, a03, a44), dot5(p03, p44, b03, b44))); float2 s = float2(0.5, -0.5); - float2 best_barycentric = tile5_barycentric(p03, p44, m, r + s.xx, a03, a44, b03, b44); - float distance_to_edge = tile5_distance_from_barycentric(best_barycentric); - choose_aperiodic_candidate( + float2 dbdx = mul(m, uv_ddx); + float2 dbdy = mul(m, uv_ddy); + float2 footprint_half_extents = 0.5 * (abs(dbdx) + abs(dbdy)); + + accumulate_filtered_candidate( + tile5_barycentric(p03, p44, m, r + s.xx, a03, a44, b03, b44), + footprint_half_extents, edge_width, face_color, color_sum, outer_sum, normal_xy_sum); + accumulate_filtered_candidate( tile5_barycentric(p03, p44, m, r + s.xy, a03, a44, b03, b44), - distance_to_edge, best_barycentric); - choose_aperiodic_candidate( + footprint_half_extents, edge_width, face_color, color_sum, outer_sum, normal_xy_sum); + accumulate_filtered_candidate( tile5_barycentric(p03, p44, m, r + s.yx, a03, a44, b03, b44), - distance_to_edge, best_barycentric); - choose_aperiodic_candidate( + footprint_half_extents, edge_width, face_color, color_sum, outer_sum, normal_xy_sum); + accumulate_filtered_candidate( tile5_barycentric(p03, p44, m, r + s.yy, a03, a44, b03, b44), - distance_to_edge, best_barycentric); - - AperiodicTileSample sample; - sample.distance_to_edge = distance_to_edge; - sample.barycentric = best_barycentric; - return sample; -} - -void accumulate_best(AperiodicTileSample candidate, uint face_id, - inout AperiodicTileSample best) { - bool better = (candidate.distance_to_edge > best.distance_to_edge); - best.distance_to_edge = better ? candidate.distance_to_edge : best.distance_to_edge; - best.barycentric = better ? candidate.barycentric : best.barycentric; - best.face_id = better ? face_id : best.face_id; + footprint_half_extents, edge_width, face_color, color_sum, outer_sum, normal_xy_sum); } -void sample_aperiodic_tiling(float2 uv, out float3 albedo - , out float3 tiling_normal_tangent - ) { +void sample_aperiodic_tiling(float2 uv, inout float3 albedo, out float3 tiling_normal_tangent) { 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); float2 u = float2(0, 1); - // Accumulate best sample across all 10 face orientations = C(5,2) pairs - AperiodicTileSample best; - best.distance_to_edge = -1e10; - best.barycentric = 0; - best.face_id = 0; - - accumulate_best(sample_lattice(p03, p44, u.yxxx, u.x, u.xyxx, u.x), 0, best); - accumulate_best(sample_lattice(p03, p44, u.yxxx, u.x, u.xxyx, u.x), 1, best); - accumulate_best(sample_lattice(p03, p44, u.yxxx, u.x, u.xxxy, u.x), 2, best); - accumulate_best(sample_lattice(p03, p44, u.yxxx, u.x, u.xxxx, u.y), 3, best); - accumulate_best(sample_lattice(p03, p44, u.xyxx, u.x, u.xxyx, u.x), 4, best); - accumulate_best(sample_lattice(p03, p44, u.xyxx, u.x, u.xxxy, u.x), 5, best); - accumulate_best(sample_lattice(p03, p44, u.xyxx, u.x, u.xxxx, u.y), 6, best); - accumulate_best(sample_lattice(p03, p44, u.xxyx, u.x, u.xxxy, u.x), 7, best); - accumulate_best(sample_lattice(p03, p44, u.xxyx, u.x, u.xxxx, u.y), 8, best); - accumulate_best(sample_lattice(p03, p44, u.xxxy, u.x, u.xxxx, u.y), 9, best); - - // Compute edge coverage from distance - float edge_width = saturate(_Aperiodic_Tiling_Edge_Thickness * 2.0) * 0.5; - float edge_sd = best.distance_to_edge - edge_width; - float edge_sd_aa = max(abs(fwidth(edge_sd)), 1e-4); - float edge_mask = smoothstep(-edge_sd_aa * 0.5, edge_sd_aa * 0.5, edge_sd); - - // Select face color and composite - float3 face_colors[10]; - face_colors[0] = _Aperiodic_Tiling_Color_0.rgb; - face_colors[1] = _Aperiodic_Tiling_Color_1.rgb; - face_colors[2] = _Aperiodic_Tiling_Color_2.rgb; - face_colors[3] = _Aperiodic_Tiling_Color_3.rgb; - face_colors[4] = _Aperiodic_Tiling_Color_4.rgb; - face_colors[5] = _Aperiodic_Tiling_Color_5.rgb; - face_colors[6] = _Aperiodic_Tiling_Color_6.rgb; - face_colors[7] = _Aperiodic_Tiling_Color_7.rgb; - face_colors[8] = _Aperiodic_Tiling_Color_8.rgb; - face_colors[9] = _Aperiodic_Tiling_Color_9.rgb; - - float3 edge = _Aperiodic_Tiling_Edge_Color.rgb; - albedo = lerp(edge, face_colors[best.face_id], edge_mask); + // Analytically filter the existing local tile set in each orientation's + // barycentric space using a separable box footprint. + float edge_width = min(_Aperiodic_Tiling_Edge_Thickness, 0.5); + float3 color_sum = 0.0; + float outer_sum = 0.0; + float2 normal_xy_sum = 0.0; + + accumulate_filtered_lattice( + p03, p44, uv_ddx, uv_ddy, u.yxxx, u.x, u.xyxx, u.x, edge_width, + _Aperiodic_Tiling_Color_0.rgb, color_sum, outer_sum, normal_xy_sum); + accumulate_filtered_lattice( + p03, p44, uv_ddx, uv_ddy, u.yxxx, u.x, u.xxyx, u.x, edge_width, + _Aperiodic_Tiling_Color_1.rgb, color_sum, outer_sum, normal_xy_sum); + accumulate_filtered_lattice( + p03, p44, uv_ddx, uv_ddy, u.yxxx, u.x, u.xxxy, u.x, edge_width, + _Aperiodic_Tiling_Color_2.rgb, color_sum, outer_sum, normal_xy_sum); + accumulate_filtered_lattice( + p03, p44, uv_ddx, uv_ddy, u.yxxx, u.x, u.xxxx, u.y, edge_width, + _Aperiodic_Tiling_Color_3.rgb, color_sum, outer_sum, normal_xy_sum); + accumulate_filtered_lattice( + p03, p44, uv_ddx, uv_ddy, u.xyxx, u.x, u.xxyx, u.x, edge_width, + _Aperiodic_Tiling_Color_4.rgb, color_sum, outer_sum, normal_xy_sum); + accumulate_filtered_lattice( + p03, p44, uv_ddx, uv_ddy, u.xyxx, u.x, u.xxxy, u.x, edge_width, + _Aperiodic_Tiling_Color_5.rgb, color_sum, outer_sum, normal_xy_sum); + accumulate_filtered_lattice( + p03, p44, uv_ddx, uv_ddy, u.xyxx, u.x, u.xxxx, u.y, edge_width, + _Aperiodic_Tiling_Color_6.rgb, color_sum, outer_sum, normal_xy_sum); + accumulate_filtered_lattice( + p03, p44, uv_ddx, uv_ddy, u.xxyx, u.x, u.xxxy, u.x, edge_width, + _Aperiodic_Tiling_Color_7.rgb, color_sum, outer_sum, normal_xy_sum); + accumulate_filtered_lattice( + p03, p44, uv_ddx, uv_ddy, u.xxyx, u.x, u.xxxx, u.y, edge_width, + _Aperiodic_Tiling_Color_8.rgb, color_sum, outer_sum, normal_xy_sum); + accumulate_filtered_lattice( + p03, p44, uv_ddx, uv_ddy, u.xxxy, u.x, u.xxxx, u.y, edge_width, + _Aperiodic_Tiling_Color_9.rgb, color_sum, outer_sum, normal_xy_sum); + + float normalization = max(outer_sum, 1e-4); + albedo *= color_sum / normalization; #if defined(_APERIODIC_TILING_NORMALS) - float3 tile_normal = aperiodic_tiling_normal(best.barycentric); - tiling_normal_tangent = normalize(float3(tile_normal.xy * edge_mask, 1.0)); + tiling_normal_tangent = normalize(float3(normal_xy_sum / normalization, 1.0)); #else tiling_normal_tangent = 0; #endif @@ -178,11 +187,9 @@ void apply_aperiodic_tiling(float2 uv, inout float3 albedo, inout float3 normal_ #if defined(_APERIODIC_TILING) uv *= _Aperiodic_Tiling_Scale; float3 tiling_normal_tangent; - sample_aperiodic_tiling(uv, albedo - , tiling_normal_tangent - ); + sample_aperiodic_tiling(uv, albedo, tiling_normal_tangent); #if defined(_APERIODIC_TILING_NORMALS) - normal_tangent = blendNormalsHill12(normal_tangent, tiling_normal_tangent); + normal_tangent = tiling_normal_tangent; #endif #endif // _APERIODIC_TILING } |
