# Billboarding a line

## Recommended Posts

Let me preface this by acknowledging that I suck at math.

I'm trying to billboard a volumetric spline ribbon with the option to keep it constant width. I don't want to project the end result, because I still want it to be depth tested. Here's the relevant bit from my geometry shader with my own comments on what I think the math does. I'm pretty sure I'm wrong, though:

	// seg has two points, a and b, whose position is in world space

// convert the end points to view space
vec4 vsp1 = gd_matModelView * vec4(seg.a.position.xyz, 1);
vec4 vsp2 = gd_matModelView * vec4(seg.b.position.xyz, 1);

// direction is an interpolated normal along the spline at knots (seg.a.position and seg.b.position);
// convert these to view space as well
vec4 vsn1 = gd_matModelView * vec4(seg.dir1, 1);
vec4 vsn2 = gd_matModelView * vec4(seg.dir2, 1);

// project and normalize
dir1 = vec3(normalize(vsn1.xy), 0);
dir2 = vec3(normalize(vsn2.xy), 0);

// this part is definitely wrong, but I'm not sure what it should be; the idea is to avoid
// segments whose end points change orientation from becoming twists (see below screenshot).
// I'm basically trying to smooth out the discontinuity that arises at oblique angles.
if(dot(dir1, dir2) < 0)
dir2 = -dir2;

// calculate the right vectors in view space
vec3 n1 = vec3(-dir1.y, dir1.x, 0);
vec3 n2 = vec3(-dir2.y, dir2.x, 0);

....

// the segments are issued as:
// 	vsn1 + n1 * thickness1
//	vsn1 - n1 * thickness1
// 	vsn2 + n2 * thickness2
// 	vsn2 - n2 * thickness2

....

// and finally, vertices are emitted after converting to clip space:
gl_Position	= gd_matProjection * position;


There are several issues here, most of which are hilighted in the screenshot below. For this example I set the line thickness to something constant. The thickness is not adjusted for by distance.

From most directions the ribbon actually seems fine, but when the camera points increasingly down the direction of the spline:

1) the ribbon generates a twist

2) projection does not seem to work the way my little brain assumes it should

What am I doing wrong?

##### Share on other sites

I'm not totally sure what your after, but it looks like the line thickness is the exact same throughout the entire thing, it appears to be 1 pixel thick all the way through.

If you don't want it to look like its smaller the further away it gets, could you try an orthographic projection matrix instead of a perspective projection?

##### Share on other sites
6 minutes ago, iedoc said:

I'm not totally sure what your after, but it looks like the line thickness is the exact same throughout the entire thing, it appears to be 1 pixel thick all the way through.

If you don't want it to look like its smaller the further away it gets, could you try an orthographic projection matrix instead of a perspective projection?

Just to clarify, by thickness I mean width :). The line has no actual volumetric thickness - it's just a flat ribbon.

##### Share on other sites

Ah OK, what about that twist? You don't want the twist?

##### Share on other sites
5 minutes ago, iedoc said:

Ah OK, what about that twist? You don't want the twist?

No, I want all segments to be billboarded (facing the camera) so that the line is viewable from any direction and the desired width/thickness is maintained at all times. Right now my code fails to billboard the segments properly and also generates an undesirable twist.

##### Share on other sites

this won't help the twist issue, but if you change gd_matProjection to be an orthographic projection, everything will appear the same size no matter how far away from the camera it is

EDIT: I just realized what you are talking about with the twists. You want the direction to be reversed once it's facing away from the camera, so once it reaches the point where its about to face away from the camera, the direction is reversed

##### Share on other sites
1 minute ago, iedoc said:

this won't help the twist issue, but if you change gd_matProjection to be an orthographic projection, everything will appear the same size no matter how far away from the camera it is

Like I said in my original post, I want to keep the line in 3D as I want it to be depth tested. Orthographic projection doesn't help me here.

##### Share on other sites

I'm probably still not understanding 100%, but orthographic projection would still get you depth. the only difference between orthographic projection and perspective projection is that in perspective projection, things further away appear smaller. depth is still written to the depth buffer in both of them.

##### Share on other sites
1 hour ago, iedoc said:

I'm probably still not understanding 100%, but orthographic projection would still get you depth. the only difference between orthographic projection and perspective projection is that in perspective projection, things further away appear smaller. depth is still written to the depth buffer in both of them.

Okay - that would be an alternative to just scaling things based on distance in the shader. I prefer scaling things manually anyway, because that does not entail changing the projection mode on the client side. That being said, getting things to appear constant size is secondary right now as my primary concern is that my math for calculating the billboarded segments isn't working in the first place.

If you could tell me what is still unclear about my initial query, perhaps I could elaborate?

##### Share on other sites

I'm not sure exactly what your doing in your geometry shader to get the final position, but it looks to me like your not taking the camera position into account. For the dot product, you should dot the normal of your segments with the camera-to-segment direction.

something like this maybe:

dot(segmentNormal, camPosition - segmentPosition) > 0) // reverse segment direction because segment is now facing away from camera

##### Share on other sites
8 minutes ago, iedoc said:

I'm not sure exactly what your doing in your geometry shader to get the final position, but it looks to me like your not taking the camera position into account. For the dot product, you should dot the normal of your segments with the camera-to-segment direction.

something like this maybe:


dot(segmentNormal, camPosition - segmentPosition) > 0) // reverse segment direction because segment is now facing away from camera

Trying not to sound dumb here, but doesn't transforming the WS position with the modelview matrix get me to view space, which is already camera-relative? If I then discard Z, then my coordinate should effectively be in screen space. Insofar as I follow the math, I'm transforming the WS end points to views space, then I project the spline directions at either endpoint to screen space and compute the 2D normals (right vectors)*, which are implicitly camera-aligned orthogonal vectors to the view space point. Right?

Finally, transforming by the projection matrix gets me to clip space.

* this would akin to calculating normalize(cross(endNormal, camPosition - endPosition))

##### Share on other sites

oh yeah, your right, that does actually make sense. But since your z is bigger the further away from the camera, when you multiply by the projection matrix the width of your ribbon will get smaller the further away it is, if your using the same width all the way through.

I'll have to think about your code a little longer to figure out why it appears to be twisting and turning so much. are seg.a and seg.b the left and right side of the ribbon?, and seg.dir1 and seg.dir2 are the direction the ribbon is flowing from the two points?

##### Share on other sites

can you try this? (if it works probably want to pull out the dot so you don't have to do it twice)

if(dir1.z - dir2.z < 0 && dot(dir1, dir2) < 0)
dir2 = -dir2;
else if(dir2.z - dir1.z < 0 && dot(dir1, dir2) < 0)
dir1 = -dir1;

##### Share on other sites
4 minutes ago, iedoc said:

are seg.a and seg.b the left and right side of the ribbon?, and seg.dir1 and seg.dir2 are the direction the ribbon is flowing from the two points?

No - these are midpoints at either end of a spline segment.

Given a 3-point spline { p0, p1, p2 }, the two segments that are drawn by the above shader are:

1)

seg.a.position = p0
seg.b.position = p1

dir1 = normalize(p1 - p0)
dir2 = normalize(lerp(p1 - p0, p2 - p1, 0.5))

2)

seg.a.position = p1
seg.b.position = p2

dir1 = normalize(lerp(p1 - p0, p2 - p1, 0.5))
dir2 = normalize(p2 - p1)

(actually the directions for the first and last points are derived from adjacency information, but that's irrelevant here)

##### Share on other sites
15 minutes ago, iedoc said:

can you try this? (if it works probably want to pull out the dot so you don't have to do it twice)


if(dir1.z - dir2.z < 0 && dot(dir1, dir2) < 0)
dir2 = -dir2;
else if(dir2.z - dir1.z < 0 && dot(dir1, dir2) < 0)
dir1 = -dir1;

This makes no difference. And I'm not sure why it should... It doesn't matter which normal I flip - either should do the trick.

Frankly, it doesn't even make sense to me that the twist happens over the the course of several segments (this implies that the code is wrong on a more fundamental level). It should be a discontinuity, which occurs at a single point, which is dependent on the position of the camera.

## Create an account or sign in to comment

You need to be a member in order to leave a comment

## Create an account

Sign up for a new account in our community. It's easy!

Register a new account