VR Grid floor with falloff based on XRCamera position.
A simple grid shader that calculates the falloff from the center of the XRCamera.
Needs the below script to be attached to a MeshInstance3D with a PlaneMesh.
If you got any pointers on how to improve it, let me know!
extends MeshInstance3D
@onready var plane = self
@onready var camera_global_position = %XRCamera3D.global_transform.origin
@onready var camera_local_position = plane.to_local(camera_global_position)
@onready var projected_position = Vector2(camera_local_position.x, camera_local_position.z)
@onready var plane_size = mesh.size
func _process(delta):
camera_global_position = %XRCamera3D.global_transform.origin
camera_local_position = plane.to_local(camera_global_position)
projected_position = Vector2(camera_local_position.x, camera_local_position.z)
var uv_coordinates = Vector2(
0.5 + camera_local_position.x / plane_size.x,
0.5 + camera_local_position.z / plane_size.y
)
var shader_material = mesh.surface_get_material(0) # Assuming the mesh is the first surface
if shader_material and shader_material is ShaderMaterial:
shader_material.set_shader_parameter("camera_uv", uv_coordinates)
Shader code
// Inspired and heavyily copied code from this excellent post:
// https://bgolus.medium.com/the-best-darn-grid-shader-yet-727f9278b9d8
shader_type spatial;
render_mode blend_mix;
uniform vec2 camera_uv;
void vertex() {
// Called for every vertex the material is visible on.
}
float pristineGrid(vec2 uv, vec2 lineWidth) {
vec2 ddx = dFdx(uv);
vec2 ddy = dFdy(uv);
vec2 uvDeriv = vec2(length(vec2(ddx.x, ddy.x)), length(vec2(ddx.y, ddy.y)));
bvec2 invertLine = bvec2(lineWidth.x > 0.5, lineWidth.y > 0.5);
vec2 targetWidth = vec2(
invertLine.x ? .0 - lineWidth.x : lineWidth.x,
invertLine.y ? 1.0 - lineWidth.y : lineWidth.y
);
vec2 drawWidth = clamp(targetWidth, uvDeriv, vec2(0.5));
vec2 lineAA = uvDeriv * 1.5;
vec2 gridUV = abs(fract(uv) * 2.0 - 1.0);
gridUV.x = invertLine.x ? gridUV.x : 1.0 - gridUV.x;
gridUV.y = invertLine.y ? gridUV.y : 1.0 - gridUV.y;
vec2 grid2 = smoothstep(drawWidth + lineAA, drawWidth - lineAA, gridUV);
grid2 *= clamp(targetWidth / drawWidth, 0.0, 1.0);
grid2 = mix(grid2, targetWidth, clamp(uvDeriv * 2.0 - 1.0, 0.0, 1.0));
grid2.x = invertLine.x ? 1.0 - grid2.x : grid2.x;
grid2.y = invertLine.y ? 1.0 - grid2.y : grid2.y;
return mix(grid2.x, 1.0, grid2.y);
}
void fragment() {
// Adjust this to control the number of grid lines
float scaleFactor = 500.0;
vec2 scaledUV = UV * scaleFactor;
// Adjust line width as needed
vec2 lineWidth = vec2(0.025, 0.025);
// Generate the grid pattern
float gridValue = pristineGrid(scaledUV, lineWidth);
// Calculate distance falloff
float centerDistance = length(UV - camera_uv);
float falloffStart = 0.0; // Start of the falloff
float falloffEnd = 0.5; // End of the falloff, after which the grid is completely faded out
float falloff = 0.25 - smoothstep(falloffStart, falloffEnd, centerDistance);
falloff = clamp(falloff, 0.0, 1.0); // Ensure falloff doesn't go below 0
// Apply the distance falloff to the grid value
float finalGridValue = gridValue * falloff;
ALBEDO = vec3(finalGridValue);
ALPHA = gridValue * falloff;
}
You can just use the global CAMERA_POSITION_WORLD.
Also you prob should make uniforms
uniform float grid_scale = 50.0;
uniform float line_width = 0.01;
Ohh, didn’t know that. Will try it out and update the shader, thanks!
Yeah I should make the uniforms 😉