Abstract 3D
This is a Volumetric Raymarcher shader adapted for Godot’s CanvasItem that creates a highly fluid, abstract 3D environment using Simplex Noise (snoise). The effect simulates flying through a continuous volume of density, often resulting in a cloud tunnel, wormhole, or liquid space effect.
The shader works by:
-
Generating Noise: Utilizing a full implementation of Simplex Noise to calculate a smooth, turbulent density value at any point in 3D space (
p). -
Raymarching: Stepping a virtual camera (
cam) through the volume along the ray direction (rd). -
Volume Visualization: The color and intensity of the scene are determined by the distance (
c) traveled until the ray hits a density threshold (d > 0.5).
This Godot adaptation exposes key parameters to control the scene’s appearance and motion:
Shader code
shader_type canvas_item;
uniform float movement_speed : hint_range(0.0, 1.0) = 0.1;
uniform float movement_scale : hint_range(0.0, 5.0) = 2.0;
uniform vec4 color_1 : source_color = vec4(0.0, 0.63, 0.49, 1);
uniform vec4 color_2 : source_color = vec4(0.16, 0.16, 0.16,1);
vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec4 permute(vec4 x) { return mod289(((x * 34.0) + 1.0) * x); }
vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }
float snoise(vec3 v) {
const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0);
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
vec3 i = floor(v + dot(v, C.yyy));
vec3 x0 = v - i + dot(i, C.xxx);
vec3 g = step(x0.yzx, x0.xyz);
vec3 l = 1.0 - g;
vec3 i1 = min(g.xyz, l.zxy);
vec3 i2 = max(g.xyz, l.zxy);
vec3 x1 = x0 - i1 + C.xxx;
vec3 x2 = x0 - i2 + C.yyy;
vec3 x3 = x0 - D.yyy;
i = mod289(i);
vec4 p = permute(permute(permute(i.z + vec4(0.0, i1.z, i2.z, 1.0)) + i.y + vec4(0.0, i1.y, i2.y, 1.0)) + i.x + vec4(0.0, i1.x, i2.x, 1.0));
float n_ = 0.142857142857;
vec3 ns = n_ * D.wyz - D.xzx;
vec4 j = p - 49.0 * floor(p * ns.z * ns.z);
vec4 x_ = floor(j * ns.z);
vec4 y_ = floor(j - 7.0 * x_);
vec4 x = x_ * ns.x + ns.yyyy;
vec4 y = y_ * ns.x + ns.yyyy;
vec4 h = 1.0 - abs(x) - abs(y);
vec4 b0 = vec4(x.xy, y.xy);
vec4 b1 = vec4(x.zw, y.zw);
vec4 s0 = floor(b0) * 2.0 + 1.0;
vec4 s1 = floor(b1) * 2.0 + 1.0;
vec4 sh = -step(h, vec4(0.0));
vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;
vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww;
vec3 p0 = vec3(a0.xy, h.x);
vec3 p1 = vec3(a0.zw, h.y);
vec3 p2 = vec3(a1.xy, h.z);
vec3 p3 = vec3(a1.zw, h.w);
vec4 norm = taylorInvSqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));
p0 *= norm.x;
p1 *= norm.y;
p2 *= norm.z;
p3 *= norm.w;
vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);
m = m * m;
return 42.0 * dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3)));
}
void fragment() {
vec2 uv = (UV * 2.0 - 1.0);
float aspect_ratio = (1.0 / SCREEN_PIXEL_SIZE).x / (1.0 / SCREEN_PIXEL_SIZE).y;
uv.x *= aspect_ratio;
vec2 xy_plane_motion = vec2(cos(TIME * movement_speed), sin(TIME * movement_speed)) * movement_scale;
vec3 cam = vec3(xy_plane_motion, TIME * 0.08);
vec3 ro = vec3(0.0, 0.0, 0.0);
vec3 rd = normalize(vec3(uv.xy, 1.0));
float c = 4.0;
for (float step = 0.0; step < 64.0; step += 1.0) {
vec3 p = cam + ro + ((rd * 0.08) * step);
float d = snoise(p);
c = length(cam - p);
if (d > 0.5 && c > 1.0) {
break;
}
}
c = (c > 4.0) ? 0.0 : (4.0 - c) * 0.25;
COLOR = vec4(mix(color_2.rgb, color_1.rgb, c) * c, 1.0);
}

