Do vertex indexes in shader follow same sequence as in mesh? (dotted line shader)

TLDR version:
if I’m rendering from an ofVboMesh, using a vertex shader. Are the vertex index numbers in the shader (which I can get from gl_VertexID) identical to their indexes within the mesh (or are they at least in the same order)?

Longer (may be useful to people trying to draw dotted lines).
I’m trying to draw dotted lines.
This is fairly easy for straight, single segment lines, I can use a cosine function in a fragment shader and change color based on distance from the start of the vertex. Like this: (adapted from here http://korkd.com/2012/02/15/dashed-lines/)

#version 150

in vec4 vertex_color;
in vec4 vertex_pos;
flat in vec4 vertex_start;

out vec4 Out_Color;
uniform int dotted;

void main()
{

	if(dotted!=0){
		if (cos(0.4*abs(distance(vertex_start.xy, vertex_pos.xy))) - 0.5 > 0.0) {
			Out_Color = vec4(0,0,0,0);
			return;
		}
	}
	Out_Color = vertex_color;

}

But for multi-segment lines: e.g. a circle, this doesn’t work so well - the distance starts again at each new segment, so the on/off pattern begins again, rather than being consistent across the joins, and the dotted pattern is not consistent.

In this case, segment length is always consistent within a single mesh - I’m drawing circles, arcs or straight lines only) so my first thought is to pass length of segment as a uniform, and multiply that by the current vertex index number from gl_VertexID to get a running_distance and add that to the distance used in the cosine function - as below.

#version 150

in vec4 vertex_color;
in vec4 vertex_pos;
flat in vec4 vertex_start;

out vec4 Out_Color;
uniform int dotted;
uniform float segmentLength;

void main()
{
    float runningDist = segmentLength * gl_VertexID;
	if(dotted!=0){
		if (cos(0.4*abs(runningDist  + distance(vertex_start.xy, vertex_pos.xy))) - 0.5 > 0.0) {
			Out_Color = vec4(0,0,0,0);
			return;
		}
	}
	Out_Color = vertex_color;

}

I think should work quite nicely, assuming that the vertex index numbers in the shader are either identical to the vertex’s numbers within the mesh, or at least follow the same sequence. Obviously I could test this and see if it works for a few instances, but that might just show that it works on my machine, but turns out to be dependent on compiler, or environment, or whatever else.

So can I rely on this ordering?

1 Like

Actually I’m not sure if I can access gl_VertexID inside the fragment shader - I’m assuming not in fact. But I could get it in the vertex shader and pass it along…

I think this confirms that vertex index numbers in the shader will be the same as their index numbers in the VBO:

https://www.opengl.org/wiki/Built-in_Variable_(GLSL)

gl_VertexID​the index of the vertex currently being processed. When using non-indexed rendering, it is the effective index of the current vertex (the number of vertices processed + the first​ value). For indexed rendering, it is the index used to fetch this vertex from the buffer.

The last bit in particular - I assume if I’m rendering from a ofVboMesh, to which I have added vertices in order, then the indices of those vertices in the shader will be the same as in the VBO?

yes the vertex id is the same as the vertex index and same thing if there’s no indices and you are drawing lines or triangles, with triangle strip or triangle fan you will get more than one vertex id for the same vertex of course

Thanks Arturo - again much appreciated.
And I’ve realised instance_id should cover ordering of primitives for the triangle strip/fan instance, should I find myself needing that.

PS - for anyone on the line-drawing tip - I have the solution above working, with the slight tweak that instead of passing line length as a uniform I derive the line length by passing (flat) value for line_end as well as line_start from the geometry shader . This means more redundant calculations of line length, but it handles the case where vertex positions are altered by the vertex shader. Obviously this will only work for regular shapes where line length is consistent.

Hi Daniel if you’re willing to share I’d be interested in seeing your solution…I’m looking for a thick dashed or dotted line shader a la http://jcgt.org/published/0002/02/08/ but maybe you found a simpler way?

No problem - I have thick dashed lines, there are limitations, and it might not be the best way to do it, but it works for my purposes. Geometry shader nicked from somewhere and adapted very slightly. I can’t find the source now

– vertex shader

#version 150`

in vec4 in_pos;
in vec4 vertex_color;

flat out int vertex_id;

out Vertex{
	vec4 pos;
	vec4 color;
} vertex;


void main(){
	vertex.pos = in_pos;
	vertex.color =  vertex_color;
	vertex_id = gl_VertexID;
}

– geometry shader

#version 150
layout(lines_adjacency) in;
layout(triangle_strip, max_vertices=9) out;

uniform mat4 modelViewProjectionMatrix;
uniform float thickness;

in Vertex{
	vec4 pos;
	vec4 color;
} vertex[];

flat in int vertex_id[];

out vec4 vertex_color;
out vec4 vertex_pos;
flat out int vertex_id_g;
flat out vec4 vertex_start;
flat out vec4 vertex_end;


#define PI 3.1415926535897932384626433832795
#define TWO_PI 6.283185307179586476925286766559

void emitV( vec2 v, vec4 color, vec4 pos, vec4 start, vec4 end){
	gl_Position = modelViewProjectionMatrix * vec4(v,0,1);
	vertex_color = color;
	vertex_pos = pos;
	vertex_start = start;
	vertex_end = end;
	EmitVertex();
}

// keep an angle between -pi and +pi
// otherwise taking half the angle becomes awkward
float goodRad( float rad ){
	return mod(mod(rad+PI,TWO_PI)+TWO_PI,TWO_PI)-PI;
}

void main(){
	vertex_id_g = vertex_id[2];

	vec2 a = vertex[0].pos.xy;
	vec2 b = vertex[1].pos.xy;
	vec2 c = vertex[2].pos.xy;
	vec2 d = vertex[3].pos.xy;

	vec2 ba = b - a;
	vec2 cb = c - b;
	vec2 cd = d - c;
	
	float width = thickness;
	float len = length(cb);
	
	if( len > 0 ){
		// do things for a,b,c
		float alpha1 = 0.5 * goodRad(atan(ba.y, ba.x) - atan(cb.y, cb.x));
		
		float sa = sin(alpha1);
		float ca = cos(alpha1);

		// rotation matrix + compute normal
		mat2 rot = mat2(ca, sa, -sa, ca);
		vec2 b1 = b + rot * vec2(-cb.y, cb.x) * width / len;
		vec2 b2 = b + rot * vec2(cb.y, -cb.x) * width / len;
		
		// and for b,c,d
		float alpha2 = 0.5 * goodRad(atan(cd.y, cd.x) - atan(cb.y, cb.x));

		sa = sin(alpha2);
		ca = cos(alpha2);

		// rotation matrix + compute normal
		rot = mat2(ca, sa, -sa, ca);
		vec2 c1 = c + rot * vec2(-cb.y, cb.x) * width / len;
		vec2 c2 = c + rot * vec2(cb.y, -cb.x) * width / len;

		emitV( b1, vertex[1].color, vertex[1].pos, vertex[1].pos, vertex[2].pos);
		emitV( c1, vertex[2].color, vertex[2].pos, vertex[1].pos, vertex[2].pos);
		emitV( b2, vertex[1].color, vertex[1].pos, vertex[1].pos, vertex[2].pos);
		emitV( c2, vertex[2].color, vertex[2].pos, vertex[1].pos, vertex[2].pos);

	}
}

– frag shader

#version 150


in vec4 vertex_color;
in vec4 vertex_pos;
flat in int vertex_id_g;
flat in vec4 vertex_start;
flat in vec4 vertex_end;

out vec4 Out_Color;
uniform int dotted;

void main()
{

	if(dotted!=0){
		float cumDist = distance(vertex_start.xy, vertex_end.xy) * vertex_id_g;

		if (cos(0.4*
				(abs(cumDist) + abs(distance(vertex_start.xy, vertex_pos.xy)))
				) - 0.5 > 0.0) {
			Out_Color = vec4(0,0,0,0);
			return;
		}
	}
	Out_Color = vertex_color;

}

– drawing function

_lineShader.begin();
_lineShader.setUniform1i("dotted", 1);
for (auto &obj : _dottedObjects) {
    _lineShader.setUniform1f("thickness", fmaxf(1, obj.thickness));
    obj.mesh.draw();
}

_dottedObjects.clear();

_lineShader.setUniform1i("dotted", 0);
for (auto &obj : _undottedObjects) {
    _lineShader.setUniform1f("thickness", fmaxf(1, obj.thickness));
    obj.mesh.draw();
}
_undottedObjects.clear();
_lineShader.end();

PS - one major limitation worth noting here:
The only slightly tricksy bit in this process is making sure that the dash pattern is consistent along the length of an entire line made of multiple segments. The dash pattern is generated by the cosine function and if handled in a simplistic way that cosine function will start again from 0 for each new line segment, causing the dot pattern to be interrupted mid-dot where line length is not a whole number multiple of the cosine period.

In my own case I am only drawing shapes of regular side length (including circles), or straight lines, so I was able to make one big simplifying assumption: I calculate the cosine phase at the start of each vertex (cumDist) on the assumption that all previous lines have been the same length as this one. This means I can arrive at starting phase for the pattern by simply multiplying the length of this line by the index of the current vertex. This is not strictly accurate (e.g. where transitioning between shapes of different size) but no problem for my purposes since the pattern remains consistent within a single shape. Also not a problem since I am drawing my objects in the same order each time, and these shapes do not change in size between frames. This last factor means that while the phase of the pattern on starting drawing a new shape is indeterminate, it will not change between frames.

The limitation here is that using my method exactly as it is you will run into problems when you try to draw irregular shapes (e.g. Bezier curves), and I think you may get movement on the dot pattern if lines change shape between frames.

To solve this you would need to keep track of the length of lines more accurately, outside of the shader, passing this in as a uniform. That might not be difficult, but I’m not enough of a graphics programmer to know if it’s inefficient.

Another note: I was in a rush putting this together. It would have made sense to expose the parameters controlling the on/off periods of the dash pattern to the program, through a uniform. As it stands that is set with inline constants in the frag shader. It should be easy to change that though.