Chlumsky / msdfgen

Multi-channel signed distance field generator
MIT License
3.9k stars 404 forks source link

3d raymarching extruded edge artifacts #206

Closed GeorgeHastings closed 2 months ago

GeorgeHastings commented 2 months ago

I would label this as "seeking advice" rather than a library issue. I'm having trouble dealing with artifacts along the extruded edges of my msdf.

image image

It seems to improve when using smaller sdf resolutions (64x64) but this introduces "banding" artifacts instead.

Texture parameters: ./msdfgen -svg ../../us5.svg -dimensions 1024 1024 -autoframe -scale 0.15 -pxrange 400 -o output.png output

I've experimented with different source SVG sizes as well which seem to have an impact but I don't know what is optimal.

I'm using a standard raymarching function with other analytical sdfs like this:

float sphere(vec3 p, float r) {
    return length(p) - r;
}

Here is the code using the msdf:

float sdCustom(vec3 p) {
  // p is a vec3 coord from the raymarch
  p *= rotX(radians(90.0));
  p.xy = p.xy * 0.25 + 0.5;

  if (p.x < 0.0 || p.x > 1.0 || p.y < 0.0 || p.y > 1.0) {
      return 1.0; // Return 1.0 if out of bounds
  }

  vec3 msdf = texture(uCustom, p.xy).rgb; 
  float sd = median(msdf.r, msdf.g, msdf.b);
  float screenPxDistance = (sd - 0.5);

  float d = -screenPxDistance; //need to invert this since default appears to recognize the negative space as a hit

  return opExtrusion(p, d, uExtrude * 0.5);
}

When I try using screenPxDistance per your documentation, it either doesn't show up, or shows positive/negative space with lots of artifacts. It seems this is to help with antialiasing/blending with the background, but I am just trying to return the distance. Can the distance be "antialaised"?

float screenPxRange() { vec2 unitRange = vec2(400)/vec2(textureSize(uCustom, 0)); vec2 screenTexSize = vec2(1.0)/fwidth(vTextureCoord); return max(0.5*dot(unitRange, screenTexSize), 1.0); }

float sdCustom(vec3 p) {
  // p is a vec3 coord from the raymarch
  p *= rotX(radians(90.0));
  p.xy = p.xy * 0.25 + 0.5;

  if (p.x < 0.0 || p.x > 1.0 || p.y < 0.0 || p.y > 1.0) {
      return 1.0; // Return 1.0 if out of bounds
  }

  vec3 msdf = texture(uCustom, p.xy).rgb; 
  float sd = median(msdf.r, msdf.g, msdf.b);
  float screenPxDistance = screenPxRange() * (sd - 0.5);

  float d = screenPxDistance;

  return opExtrusion(p, d, uExtrude * 0.5);
}
image

I don't know how much this technique has been proven using raymarching like this but curious if you have any suggestions for improving!