1

I have a function f defined on a triangle mesh, i.e. for each vertex, I have an associated number. For example, f could be the geodesic distance on the mesh, i.e. f(x)=d(a,x) computed for each point in the mesh, from a specific fixed point a. Clearly, it is easy to "plot" this function on the surface, simply by normalizing the values of f and mapping them to colors on the mesh nodes.

But what if I want to draw isolines (also called contour lines or isoclines) on the surface?

How would I go about this? This does not seem doable easily by simply coloring the mesh as above. Especially since the meshes may have sparse areas with few vertices (i.e. large faces). This suggests (perhaps) that I should compute the isoclines directly as 3D curves on the mesh and use e.g. GL-lines. But computing the isocline curves is not so obvious since the naive approach is to fix a set of target values, and then I will have to check every triangle for whether it reaches the target at some point(s) on the triangle via interpolation, which does not seem ideal. Could anyone point me to any resources that address this?

For reference, I am using python OpenGL.

user3658307
  • 761
  • 1
  • 7
  • 23
  • Render surface to image as height map to frame buffer. Then using opencv convert height map to texture with isolines, then render mesh again with isoline texture. – Hihikomori Jul 16 '23 at 11:07

3 Answers3

1

if the value is float and you know its range You can try the coloring as you described with fragment shader on top of it that would render only fragment very near your rendered value.

The easiest is to use single rendering pass use something like this in fragment:

float x;
x = (value/10.0)+0.5; // 10 is distance of value you want the lines to render
x = x-floor(x);   // x = value mod distance
if (abs(x-0.5)>0.001) discard; // do not render fragments too far from your line

the code simply decides if you are very near to every distance=10 of the value and throw away any fragments that are not. However the thickness of the lines/curves can vary depending on the value density and triangle size distribution. You must set the constants correctly (distance=10,threshold=0.001) to make this work.

This GLSL animated mesh surface highlight is using very similar technique apart the discard as the stuff there is continuous.

More accurate option is to use the same principle in 2 pass render. Where you render your value as color into some texture in the first pass. And then in second for each fragment you scan the texture around and find the closest position of renderable value and than based on the distance to it either render or not. This provides accurate thickness of the line no matter what. But for that the value must contain specific renderable values. This How to implement 2D raycasting light effect in GLSL might help as it also scans the texture in all the directions (but for different purpose).

If none of the above suits you then only other option that comes into my mind is geometrical approach where you create polylines from your mesh based on value intersection with your mesh (like cross section with plane).

Spektre
  • 49,595
  • 11
  • 110
  • 380
1

Let's say you want to draw contours (isolines) whose Z-coordinate is a multiple of ci (for 'contour interval'). for example, for ci=5 valid values are 25, 30, 35, etc.
A triangle can have none, one, or several contours crossing it. I don't bother with lines. I use points, I examine each fragment in the fragment shader.

The first goal is to get back the world coordinates of the fragment in the fragment shader.
The fragment shader receives gl_FragCoord which has the {x,y,z} in screen coordinates. Or in the vertex shader you can pass a varying with the z. Read this q&a to learn how this transform-back can be done.

Now you have a z in world coordinates, it's time to tell if it belongs to a contour or not:

float maxErr = 0.0001;
float dif = abs(z - (ci * floor(z/ci)));
if ( dif > maxErr )
    color = triangleColor;
else
    color = contourColor;

Because a fragment is not the same as a pixel you may get a not-width-constant line.

Ripi2
  • 7,031
  • 1
  • 17
  • 33
0

Clearly, it is easy to "plot" this function on the surface, simply by normalizing the values of f and mapping them to colors on the mesh nodes... This does not seem doable easily by simply coloring the mesh as above.

Actually, drawing visible lines as boundaries between same color regions is similarly easy. One just creates an OpenGL texture with desired palette of colors, and asking OpenGL to sample it without interpolation (GL_NEAREST mode):

glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );

and specifies one of uv texture coordinates in each vertex proportional to the value at this vertex.

Here how it looks like with with a texture having only 4 different elements:

Drawing Isolines using texture

The alternative approach is to construct isolines composed of line segments and draw them as such. Given some iso-value, every mesh triangle can contribute either no or single line segment to the resulting polyline. The latter case happens when vertex values in three triangle vertices are both below and above selected iso-value. In that case two edges joining the vertices with below and above values are crossed by the iso-line (and the remaining third edge is not crossed). This can be seen as a variation of marching cubes algorithm.

Same iso-lines on the sphere:

Direct drawing of iso-lines

In this short video, both methods are presented together. Following the links there, an open-source C++ library is found. I think it is rather straightforward to port the algorithms in python.

Fedor
  • 17,146
  • 13
  • 40
  • 131