0

I want to model a parametric surface out of a texture3D using a GLSL shader. The texture3D is basically white everywhere (this is a simplification). From that TEXTURE3D I'm select those voxels that lie on a cylinder with parabolic base:

Vertex Shader:

 varying vec3 texture_coordinate;
 uniform float thickness;
 uniform float curvature;
 varying vec4 pvertex;
 void main()
 {
    vec4 v=gl_Vertex;
    texture_coordinate= v.xyz;
    gl_Position = gl_ModelViewProjectionMatrix*v;
    pvertex = v;
 }

Fragment Shader:

varying vec3 texture_coordinate;
uniform sampler3D my_color_texture;
varying vec4 pvertex;
uniform float thickness;
uniform float curvature;
void main()
{
    // Filter out the vertices outside a given surface normal
    float parametricSurfaceEquation = (pvertex.x*pvertex.x)/curvature;
    if ( distance(pvertex.xz,vec2(pvertex.x,parametricSurfaceEquation)) <= thickness )
    {
        gl_FragColor = vec4(1.0);
    }
    else
    {
        // color the vertices outside that small volume around the surface to black
        gl_FragColor = vec4(0.0,0.0,0.0,1.0);
    }
    if ( ((pvertex.x*pvertex.x) + (pvertex.z*pvertex.z)) <= curvature*0.5f )
        gl_FragColor = vec4(0.0);
}

but the result is that, while this works for areas where the curvature is low, on highly curved areas the surface is thicker than in lowly curved areas as shown in the following images. This is the desired situation for me:

Thin surface

but if I increase the thickness parameter the center is much thicker than the peripheries.

thick surface

Is it possible to avoid this problem in some way? I'm basically coloring white the voxels that are inside a small radius around the surface but I would like to specify the thickness for every area of to be the same, directed with the normal of that point.

SOLUTION Changed the shader to:

varying vec3 texture_coordinate;
uniform sampler3D my_color_texture;
varying vec4 pvertex;
uniform float thickness;
uniform float curvature;
void main()
{
    // Filter out the vertices outside a given surface normal
    float parametricSurfaceEquation = (pvertex.x*pvertex.x)/curvature;
    float normalLength = sqrt(1.0+(2.0*pvertex.x/curvature)*(2.0*pvertex.x/curvature));
    if ( abs((pvertex.z - parametricSurfaceEquation)/normalLength) <= thickness)
    {
        gl_FragColor = vec4(1.0);
    }
    else
    {
        // color the vertices outside that small volume around the surface to black
        gl_FragColor = vec4(0.0,0.0,0.0,1.0);
    }
    if ( ((pvertex.x*pvertex.x) + (pvertex.z*pvertex.z)) <= curvature*0.5f )
        gl_FragColor = vec4(0.0);
}

and the result is the following:

Constant width shape

linello
  • 8,451
  • 18
  • 63
  • 109
  • I cannot figure out what, if anything, this has to do with 3D textures or textures in general. Did you include the wrong shader code? – Andon M. Coleman May 13 '14 at 23:44
  • I am also very confused by this question. Can you try to explain from the beginning what you are trying to do? You are also not very clear about terms. A parametric (iso) surface can be encoded in a 3d texture. Is this a sampling problem? Curvature and normals are related but not the same thing at all. In trying to be helpful, google "signed distance field". I _think_ that's what you are really trying to do. – starmole May 14 '14 at 06:29
  • You can imagine that the final color fragment is given by: `vec4 finalColor = uniformColor*texture3D(my_color_texture, texture_coordinate);` – linello May 14 '14 at 08:18
  • 1
    You might find the following page useful: http://iquilezles.org/www/articles/distance/distance.htm – GuyRT May 14 '14 at 08:48
  • I think your suggestion is what I need! Constant width shapes! Definitely! – linello May 14 '14 at 09:16
  • I've changed the condition in the shader now to: `float normalLength = sqrt(1.0+(2.0*pvertex.x/curvature)*(2.0*pvertex.x/curvature)); if ( abs((pvertex.z - parametricSurfaceEquation)/normalLength) <= thickness)` – linello May 14 '14 at 09:26

1 Answers1

1

I figure you simplified the shader code for illustration? In the form you posted, it's not using a 3D texture at all.

In any case, the way I understand your code, what you get is expected based on the math you do. Picture a parabola in it's common y = x * x form, and a thickness t. Also using your curvature parameter c, I believe you're rendering the area between the following two curves:

y1 = x * x / c - t
y2 = x * x / c + t

While y2 - y1 is always 2 * t, the thickness of the curve will not be uniform. To measure perceived thickness of the curve, you would measure the length of a line segments formed by intersecting a line orthogonal to the curve with your area. To make it look uniform, you would want all these line segments to be the same length. With the equations above, line segments in the y-direction are all the same length, but not line segments orthogonal to the curve.

To do what I believe you want, you have to calculate the distance of your fragment to the curve. The math got a little more complicated than I had hoped when I tried to solve it quickly. You should be able to find some direction with search terms like "point parabola distance". E.g. here's one search result on this site: How to find the distance between a point and a parabola in code.

Community
  • 1
  • 1
Reto Koradi
  • 53,228
  • 8
  • 93
  • 133
  • Great job making sense of a confusing question! With your answer looking at the code again I think he is doing the right thing - but his parametricSurfaceEquation is just plain wrong and not computing linear distance at all. x*y/c should probably be sqrt(x*x+y*y)/x. – starmole May 14 '14 at 06:38
  • Ah, no I was reading the code wrong. The mistake is that distance(p,warpedWithParabola(x)) != distanceParabola because the warp (here parametricSurfaceEquation) is not linear. – starmole May 14 '14 at 06:44