Godot Version
Godot 4.6.3
Question
Good evening all,
I’m building procedural planets in Godot, all in-shader — no heightmaps. The terrain is 3D noise sampled in object space (so no UV seams at the poles), and I get the lit “3D” look by deriving a surface normal from the gradient of the height field via finite differences, then letting the directional sun shade it.
Honestly it’s not bad overall, but I’ve got some artifacts I can’t shake: diagonal streaking, faint grid/contour-ish lines, and thin slivers in the relief — mostly when you zoom in (screenshots attached).
Here’s the relevant code:
// --- noise ---
float hash13(vec3 p) {
p = fract(p * 0.1031);
p += dot(p, p.zyx + 31.32);
return fract((p.x + p.y) * p.z);
}
float vnoise(vec3 x) { // value noise, cubic fade
vec3 i = floor(x);
vec3 f = fract(x);
f = f * f * (3.0 - 2.0 * f);
float c000 = hash13(i + vec3(0,0,0));
float c100 = hash13(i + vec3(1,0,0));
float c010 = hash13(i + vec3(0,1,0));
float c110 = hash13(i + vec3(1,1,0));
float c001 = hash13(i + vec3(0,0,1));
float c101 = hash13(i + vec3(1,0,1));
float c011 = hash13(i + vec3(0,1,1));
float c111 = hash13(i + vec3(1,1,1));
return mix(mix(mix(c000,c100,f.x), mix(c010,c110,f.x), f.y),
mix(mix(c001,c101,f.x), mix(c011,c111,f.x), f.y), f.z);
}
float fbm(vec3 p) { // 5 octaves
float sum = 0.0, amp = 0.5;
for (int i = 0; i < 5; i++) { sum += amp * vnoise(p); p *= 2.02; amp *= 0.5; }
return sum;
}
float ridged_fbm(vec3 p) { // mountains
float sum = 0.0, amp = 0.5, norm = 0.0;
for (int i = 0; i < 5; i++) {
float n = vnoise(p) * 2.0 - 1.0;
n = 1.0 - abs(n); // fold -> ridge lines
n = n * n;
sum += amp * n; norm += amp; p *= 2.02; amp *= 0.5;
}
return sum / norm;
}
// --- height field: warped continents + ridged mountains gated to land ---
float terrain_height(vec3 dir) {
vec3 base = dir * continent_scale + vec3(seed * 3.7);
float warp = fbm(base * 1.7) - 0.5;
vec3 wb = base + warp_amount * vec3(warp); // domain warp
float cont = fbm(wb);
float ranges = smoothstep(mountain_coverage, mountain_coverage + 0.18,
fbm(base * 0.6 + vec3(31.0)));
float mtn = ridged_fbm(wb * 2.4 + vec3(17.0));
float land_gate = smoothstep(sea_level - 0.05, sea_level + 0.05, cont);
return cont + mtn * ranges * mountain_height * land_gate;
}
// --- relief normal from the height-field gradient (finite difference) ---
// (this is where the artifacts show up)
vec3 nrm = normalize(v_local);
float h = terrain_height(nrm);
vec3 up_ref = abs(nrm.y) < 0.99 ? vec3(0,1,0) : vec3(1,0,0);
vec3 t1 = normalize(cross(nrm, up_ref));
vec3 t2 = cross(nrm, t1);
float eps = relief_eps;
float hu = terrain_height(normalize(nrm + t1 * eps));
float hv = terrain_height(normalize(nrm + t2 * eps));
vec3 grad = (hu - h) * t1 + (hv - h) * t2; // forward difference
vec3 tilt = relief_strength * grad;
float tl = length(tilt);
if (tl > relief_max_tilt) tilt *= relief_max_tilt / tl; // clamp so it can't flip past the horizon
vec3 relief_n = normalize(nrm - tilt); // -> NORMAL, shaded by the sun
Has anyone hit this before? I’m trying to work out whether the streaking and the faint grid are coming from the value noise, the finite-difference normal, or something in how I’m warping the domain. Any pointers appreciated — thanks!
screen shots:
Some of the work that has come out better if your curious
still need to make the storm lines wider and less fine but I like it.










