The grid worked out for each pixel (as in Blender): one square on its plane, every set of lines showing everywhere their lines are far enough apart on the screen (no patch round the middle, no pieces smearing or fading as you move), a pixel or so wide and smooth, the axes in the colour of the world's axis they run along; in every renderer (drawGrid: a sprite marked by its colour's alpha)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Nexoniarz 2026-10-21 17:18:13 +02:00
commit b2fc0437bc
6 changed files with 101 additions and 70 deletions

View file

@ -9,8 +9,44 @@ PUSH_BLOCK {
vec4 up;
} pc;
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec3 v_world;
// A grid (a sprite with colour.a < 0), worked out for each pixel where it lies on its plane:
// four sets of lines each `first` × 10 apart, each set showing by how far apart its lines are on
// the screen (none where they crowd together, stronger where they're far apart), lines about a
// pixel wide, smooth; the axes through the middle in the colour of the world's axis they run along.
// look: x the lines' shade (0 dark, 1 light), y how faint, z how strong; reach: it fades out there.
vec3 gridAxisColor(vec3 d) {
d = abs(d);
return d.x > 0.5 ? vec3(0.9, 0.25, 0.25) : d.y > 0.5 ? vec3(0.3, 0.75, 0.25) : vec3(0.25, 0.45, 0.95);
}
vec4 gridColor(vec3 w, vec3 centre, vec3 u, vec3 v, float first, vec3 look, float reach) {
vec2 c = vec2(dot(w, u), dot(w, v));
vec2 fw = max(fwidth(c), vec2(1e-9));
float lines = 0.0;
for (int k = 0; k < 4; ++k) {
float s = first * pow(10.0, float(k));
vec2 apart = s / fw; // (pixels between this set's lines, each way)
vec2 off = abs(fract(c / s + 0.5) - 0.5) * apart; // (pixels to the nearest of them)
vec2 strong = smoothstep(20.0, 160.0, apart);
vec2 cover = clamp(0.5 * (1.0 + 0.6 * strong) + 0.5 - off, 0.0, 1.0);
vec2 a = cover * smoothstep(5.0, 20.0, apart) * mix(vec2(look.y), vec2(look.z), strong);
lines = max(lines, max(a.x, a.y));
}
vec2 axis = clamp(1.25 - abs(c) / fw, 0.0, 1.0) * 0.9; // (x: the line along v, y: along u)
float fade = smoothstep(reach, reach * 0.6, length(w - centre));
vec3 col = vec3(look.x) * lines;
float a = lines;
col = mix(col, gridAxisColor(v), axis.x), a = mix(a, 1.0, axis.x);
col = mix(col, gridAxisColor(u), axis.y), a = mix(a, 1.0, axis.y);
return vec4(col / max(a, 1e-6), a * fade);
}
layout(location = 0) out vec4 o_color;
void main() {
if (pc.color.a < 0.0) { // a grid: right.w its finest step, color.rgb its look, posSize.w how far it reaches
o_color = gridColor(v_world, pc.posSize.xyz, normalize(pc.right.xyz), normalize(pc.up.xyz), pc.right.w, pc.color.rgb, pc.posSize.w);
return;
}
vec4 t = sampleTex(floatBitsToUint(pc.up.w), v_uv);
// Block compression leaves a little alpha where there should be none; bright sprites
// would show it as a square: cut it.

View file

@ -9,17 +9,20 @@ PUSH_BLOCK {
vec4 up; // camera up, w = texture index (bits)
} pc;
layout(location = 0) out vec2 v_uv;
layout(location = 1) out vec3 v_world;
const vec2 kQuad[6] = vec2[](vec2(-1,-1), vec2(1,-1), vec2(1,1), vec2(-1,-1), vec2(1,1), vec2(-1,1));
void main() {
vec2 q = kQuad[VERTEX_ID];
float c = cos(pc.right.w), s = sin(pc.right.w);
vec2 r = vec2(c * q.x - s * q.y, s * q.x + c * q.y);
vec3 world = pc.posSize.xyz + (pc.right.xyz * r.x + pc.up.xyz * r.y) * pc.posSize.w;
if (pc.posSize.w < 0.0) { // a streak: right.xyz the eye, w its half width at the far end; each end's side faces the eye
if (pc.color.a < 0.0) world = pc.posSize.xyz + (pc.right.xyz * q.x + pc.up.xyz * q.y) * pc.posSize.w; // a grid: a square on its plane
else if (pc.posSize.w < 0.0) { // a streak: right.xyz the eye, w its half width at the far end; each end's side faces the eye
vec3 end = pc.posSize.xyz + pc.up.xyz * q.y, side = cross(pc.up.xyz, pc.right.xyz - end);
world = end + (length(side) > 1e-9 ? normalize(side) : vec3(0.0, 0.0, 1.0)) * q.x * (q.y < 0.0 ? -pc.posSize.w : pc.right.w);
}
v_uv = q * 0.5 + 0.5;
v_uv.y = 1.0 - v_uv.y;
v_world = world;
gl_Position = pc.viewProj * vec4(world, 1.0);
}