summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--aperiodic_tiling.cginc140
-rwxr-xr-xpbr.cginc2
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
}
diff --git a/pbr.cginc b/pbr.cginc
index 2d01472..91c22f4 100755
--- a/pbr.cginc
+++ b/pbr.cginc
@@ -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);