Infinite Ground Grid
A proper 3D infinite ground grid, with perspective independent line thickness and correct depth. Similar to grid in Godot’s editor viewport and most 3D modeling apps.
- projected on camera facing quad or sprite, no need for ground geometry
- renders grid, checkerboard or both combined
- major and minor grid lines
- adjustable line thickness, subdivisions, colors and transparency
- adjustable distance fade zone
- adjustable ground plane elevation and center offset
Usage: Assign a shader to a 3D sprite or a quad and put it directly in front of camera so that it covers the entire view.
Shader code
shader_type spatial;
render_mode blend_mix,depth_draw_opaque, unshaded;
uniform vec4 gridColor: source_color;
uniform vec4 checkerColor: source_color;
uniform float fadeStart = 0.0;
uniform float fadeEnd = 10.0;
uniform float unitSize = 1.0;
uniform int subdivisions: hint_range(1, 10) = 5;
uniform float majorLineThickness = 2.0;
uniform float minorLineThickness = 1.0;
uniform float minorLineAlpha: hint_range(0.0, 1.0) = .3;
uniform vec3 centerOffset = vec3(0.0, 0.0, 0.0);
// calculate line mask, usning a bit of fwidth() magic to make line width not affected by perspective
float grid(vec2 pos, float unit, float thickness){
vec2 threshold = fwidth(pos) * thickness * .5 / unit;
vec2 posWrapped = pos / unit;
vec2 line = step(fract(-posWrapped), threshold) + step(fract(posWrapped), threshold);
return max(line.x, line.y);
}
// calculate checkerboard mask
float checker(vec2 pos, float unit){
float square1 = step(.5, fract(pos.x / unit *.5));
float square2 = step(.5, fract(pos.y / unit *.5));
return max(square1,square2) - square1 * square2;
}
void fragment() {
// ray from camera to fragemnt in wrold space
vec3 rayWorld = normalize(mat3(INV_VIEW_MATRIX) * VIEW) ;
// calculate fragment position in world space
vec3 posWorld;
float t = -(CAMERA_POSITION_WORLD.y - centerOffset.y) / (rayWorld.y );
posWorld.y = -centerOffset.y;
posWorld.xz = CAMERA_POSITION_WORLD.xz + t * rayWorld.xz + centerOffset.xz;
// calculate planar distance from camera to fragment (used for fading)
float distPlanar = distance(posWorld.xz, centerOffset.xz);
// grid
float line = grid(posWorld.xz, unitSize, majorLineThickness);
line += grid(posWorld.xz, unitSize / float(subdivisions), minorLineThickness) * minorLineAlpha;
line = clamp(line, 0.0, 1.0);
// checkerboard
float chec = checker(posWorld.xz, unitSize);
// distance fade factor
float fadeFactor = 1.0 - clamp((distPlanar - fadeStart) / (fadeEnd - fadeStart), 0.0, 1.0);
// write ground plane depth into z buffer
vec4 pp = (PROJECTION_MATRIX * (VIEW_MATRIX * vec4(posWorld, 1.0)));
DEPTH = pp.z / pp.w;
// final alpha
float alphaGrid = line * gridColor.a;
float alphaChec = chec * checkerColor.a;
ALPHA = clamp(alphaGrid + alphaChec, 0.0, 1.0) * fadeFactor;
// eliminate grid above the horizon
ALPHA *= step(t, 0.0);
// final color (premultiplied alpha blend)
ALBEDO = (checkerColor.rgb * alphaChec) * (1.0 - alphaGrid) + (gridColor.rgb * alphaGrid);
}
Wow it’s perfect for my project, thank you! In my project I have the ground starting from the y coordinate 0, how can I make the grid appear from the y coordinate -1 so that it can be seen below the ground?
From coordinate 1 no line is seen and it is perfect.
Hi,
Glad you find the shader useful. Per your suggestion, I now updated the shader so it has the additional elevation uniform that controls the elevation of the ground plane.
Great Shader, is there a way to use arbitary world position instead of camera position to center the grid?
The center is at world origin. You can easily offset it in xz plane by adding a vec2 to posWorld.xz in line 38
I updated the shader code. The elevation is nowreplaced with centerOffset uniform that lets you offset the grid on all 3 axes.
This effect is so cool, just what I needed, thank you
Thanks. Glad you found it useful.
experiencing a weird issue where the projection on the y axis is wrong if the camera is far from the origin. you can test by setting the camera’s z position in the demo project to something like 50 and you’ll see the grid appears much below y 0.
Fixed the shader code. Thanks for spotting this.
fixed now thank you.
do you know how i could keep the fade consistent in screen space? when the camera is far away the perceived size of the grid shrinks.
You can update the fade range paramters, depending on camera distance from the grid origin.