glsl: distance function signed by Raymarching that results in holes in the surface: required step size?

I have been learning about the functions of raymarching and sign distance, and I implemented a shader of raymarching fragments in Unity. It works well for geometric shapes, like cubes and spheres, but when I started trying to build terrains with it, I started having problems.

I think I have reduced the problem to my signed distance function and not to the implementation of raymarching. To simplify things, I have reproduced the problem using a sin wave, which seems to have the same result as I see with noise functions.

The first problem is that I have strange holes in the surface, at least I think they are holes. At many points it is rendering within the volume instead of the surface:
Dark / black holes

Here is a modified color output in which I color each pixel according to the value of the signed distance.

color = 0 - signedDistance

  • Red: surface not hit
  • Black: at or very close to the surface
  • White: Inside the volume.

The white areas that indicate the point that is being represented are within the volume.

I hope I do not see any white at all in this image. The results must be black (surface found) or red (surface not found, lightning loop stopped).

The other problem, which I think is closely related to the first, is that I'm getting normal inaccurate surface values ​​at relatively large distances that result in a wavy appearance. However, I am pretty sure that this is due to the normal calculation using the same signed distance function that is returning within the volume (which would provide incorrect distance results, probably reversed), so I am first focusing on that problem.

I have reproduced the theme that I have in Shadertoy. While it is not exactly the same code, the basic algorithm is the same in both the GLSL Shadertoy sample and in my Unity HLSL shader.

First, here is the most basic signed distance function that I have used successfully. It simply represents a plane in y = 0:

floating map (position float3) {
float sdist = position.y;
back sdist;
}

Now I add a sine wave on this plane:

floating map (position float3) {
float sdist = position.y + sin (x);
back sdist;
}

What I hope is to see the plane move up and down as a sine wave along the x axis. I see it, but I also see those strange holes (in the images above) across the surface. It is as if the part of the ray march passed through the surface and then stopped within the volume.

Somehow I can mitigate this by taking steps in steps much smaller than the calculated calculated distance. Here is an example with the step size at 0.05:

The size of the step is 0.05

But this decreases the performance since each ray / gear transmission step now traverses much smaller distances.

Is the distance shorter than the result of the calculated distance function the way to solve it?

Just to complete, here is a simplified version of the ray-light function:

// March advance multiplier
float stepSize = 1.0;
// maximum number of times to advance
float maxRayCasts = 128;
// The minimum distance considered as the surface.
floating precision = 0.01;

vec3 castRay (in vec3 ro, in vec3 rd) {
float tmin = 0.01;
float tmax = 600.0;

vec2 res;

float t = tmin;
float m = -1.0;
for (int i = 0; i <maxRayCasts; i ++) {
res = map (ro + rd * t);
yes (res.xtmax) break;
t + = res.x * stepSize;
m = res.y;
}

if (t> tmax) m = -1.0;
returns vec3 (t, m, res.x);
}