This commit is contained in:
Nicholas Butzke
2026-08-09 01:31:44 -04:00
parent 922983429e
commit 9358746582
43 changed files with 4324 additions and 481 deletions
+64 -9
View File
@@ -36,8 +36,10 @@ render_mode unshaded, fog_disabled, depth_draw_never, depth_test_disabled, cull_
// away from the viewer) inks faintly. That is what an animator does — the
// outside line of a shape is heavy, the contact line inside it is light.
uniform sampler2D screen_tex : hint_screen_texture, filter_linear;
uniform sampler2D depth_tex : hint_depth_texture, filter_nearest;
uniform sampler2D screen_tex : hint_screen_texture, filter_linear, repeat_disable;
uniform sampler2D depth_tex : hint_depth_texture, filter_nearest, repeat_disable;
uniform sampler2D normal_roughness_tex : hint_normal_roughness_texture,
filter_nearest, repeat_disable;
group_uniforms ink;
uniform vec4 ink_color : source_color = vec4(0.224, 0.196, 0.310, 1.0);
@@ -163,8 +165,7 @@ vec3 painted_sky(vec3 dir) {
// Linear view depth, with the sky pinned to a finite value so the second
// difference across a silhouette is large but never infinite.
float linear_depth(vec2 uv, mat4 inv_proj) {
float d = texture(depth_tex, uv).r;
float linear_depth(vec2 uv, float d, mat4 inv_proj) {
// Godot 4.3+ is reverse-Z: the far plane and the sky read exactly 0.
if (d <= 0.000001) {
return ink_sky_depth;
@@ -175,6 +176,18 @@ float linear_depth(vec2 uv, mat4 inv_proj) {
}
vec3 view_position(vec2 uv, float raw_depth, mat4 inv_proj) {
vec4 ndc = vec4(uv * 2.0 - 1.0, raw_depth, 1.0);
vec4 view = inv_proj * ndc;
return view.xyz / view.w;
}
vec3 screen_normal(vec2 uv) {
return normalize(texture(normal_roughness_tex, uv).xyz * 2.0 - 1.0);
}
vec3 apply_grade(vec3 c, vec2 uv) {
if (!grade_enabled) {
return c;
@@ -226,11 +239,15 @@ void fragment() {
}
vec2 t = px * ink_thickness;
float dc = linear_depth(uv, INV_PROJECTION_MATRIX);
float dl = linear_depth(uv - vec2(t.x, 0.0), INV_PROJECTION_MATRIX);
float dr = linear_depth(uv + vec2(t.x, 0.0), INV_PROJECTION_MATRIX);
float du = linear_depth(uv + vec2(0.0, t.y), INV_PROJECTION_MATRIX);
float dd = linear_depth(uv - vec2(0.0, t.y), INV_PROJECTION_MATRIX);
float raw_l = texture(depth_tex, uv - vec2(t.x, 0.0)).r;
float raw_r = texture(depth_tex, uv + vec2(t.x, 0.0)).r;
float raw_u = texture(depth_tex, uv + vec2(0.0, t.y)).r;
float raw_d = texture(depth_tex, uv - vec2(0.0, t.y)).r;
float dc = linear_depth(uv, raw_depth, INV_PROJECTION_MATRIX);
float dl = linear_depth(uv - vec2(t.x, 0.0), raw_l, INV_PROJECTION_MATRIX);
float dr = linear_depth(uv + vec2(t.x, 0.0), raw_r, INV_PROJECTION_MATRIX);
float du = linear_depth(uv + vec2(0.0, t.y), raw_u, INV_PROJECTION_MATRIX);
float dd = linear_depth(uv - vec2(0.0, t.y), raw_d, INV_PROJECTION_MATRIX);
// Second difference of linear depth, normalised by distance so that a given
// real-world crease inks with the same weight near and far.
@@ -245,6 +262,44 @@ void fragment() {
edge = max(edge, smoothstep(
ink_concave, ink_concave * 3.4, concave) * ink_concave_amount);
// Depth precision forms a staircase on a flat floor viewed almost edge-on.
// Its second difference looks like curvature even though every tapped point
// belongs to the same plane; beyond a camera-dependent distance the old test
// therefore inked the entire road as one dark band. Confirm that curvature
// with the normal/depth prepass before drawing it. A true edge either changes
// normal, leaves the centre tangent plane, or crosses from geometry to sky.
if (edge > 0.0001) {
float sky_edge = step(raw_depth, 0.000001)
!= step(raw_l, 0.000001) || step(raw_depth, 0.000001)
!= step(raw_r, 0.000001) || step(raw_depth, 0.000001)
!= step(raw_u, 0.000001) || step(raw_depth, 0.000001)
!= step(raw_d, 0.000001) ? 1.0 : 0.0;
float geometry_evidence = sky_edge;
if (raw_depth > 0.000001 && sky_edge < 0.5) {
vec3 nc = screen_normal(uv);
vec3 nl = screen_normal(uv - vec2(t.x, 0.0));
vec3 nr = screen_normal(uv + vec2(t.x, 0.0));
vec3 nu = screen_normal(uv + vec2(0.0, t.y));
vec3 nd = screen_normal(uv - vec2(0.0, t.y));
float normal_turn = max(max(1.0 - dot(nc, nl), 1.0 - dot(nc, nr)),
max(1.0 - dot(nc, nu), 1.0 - dot(nc, nd)));
vec3 pc = view_position(uv, raw_depth, INV_PROJECTION_MATRIX);
vec3 pl = view_position(uv - vec2(t.x, 0.0), raw_l, INV_PROJECTION_MATRIX);
vec3 pr = view_position(uv + vec2(t.x, 0.0), raw_r, INV_PROJECTION_MATRIX);
vec3 pu = view_position(uv + vec2(0.0, t.y), raw_u, INV_PROJECTION_MATRIX);
vec3 pd = view_position(uv - vec2(0.0, t.y), raw_d, INV_PROJECTION_MATRIX);
float off_plane = max(max(abs(dot(nc, normalize(pl - pc))),
abs(dot(nc, normalize(pr - pc)))),
max(abs(dot(nc, normalize(pu - pc))),
abs(dot(nc, normalize(pd - pc)))));
float normal_evidence = smoothstep(0.006, 0.045, normal_turn);
float plane_evidence = smoothstep(0.012, 0.080, off_plane);
geometry_evidence = max(normal_evidence, plane_evidence);
}
edge *= geometry_evidence;
}
// Fade on the nearest real surface involved in the edge, not on dc: a sky
// pixel beside a nearby roof would otherwise be measured at 900 m and its
// half of the silhouette would fade out while the roof's half did not.