0

Following this turorial here

I have managed to create a cylindrical billboard (it utilizes a geometry shader which takes points and produces quads). The problem is that when i move the camera so that it's higher than the billboard (using gluLookat) the billboard does not rotate to truly face the camera (as if it was a cylindrical billboard).

How do I make it into spherical?

if anyone interested, here is slightly modified geometry shader code:

#version 330
//based on a great tutorial at http://ogldev.atspace.co.uk/www/tutorial27/tutorial27.html

layout (points) in;
layout (triangle_strip) out;
layout (max_vertices = 4) out;

uniform mat4 mvp;
uniform vec3 cameraPos;

out vec2 texCoord;

void main(){
    vec3 pos = gl_in[0].gl_Position.xyz;
    pos /= gl_in[0].gl_Position.w; //normalized device coordinates
    vec3 toCamera = normalize(cameraPos - pos);
    vec3 up = vec3(0,1,0);
    vec3 right = normalize(cross(up, toCamera)); //right-handed coordinate system
    //vec3 right = cross(toCamera, up); //left-handed coordinate system

    pos -= (right*0.5);
    gl_Position = mvp*vec4(pos,1.0);
    texCoord = vec2(0,0);
    EmitVertex();

    pos.y += 1.0;   
    gl_Position = mvp*vec4(pos,1.0);
    texCoord = vec2(0,1);
    EmitVertex();

    pos.y -= 1.0;   
    pos += right;
    gl_Position = mvp*vec4(pos,1.0);
    texCoord = vec2(1,0);
    EmitVertex();

    pos.y += 1.0;       
    gl_Position = mvp*vec4(pos,1.0);
    texCoord = vec2(1,1);
    EmitVertex();
}

EDIT: As I said before, I have tried the approach of setting the 3,3-submatrix to identity. I might have explained the behaviour wrong, but this gif should do it better: This is what I mean by "incorrect moving"

In the picture above, the camera is rotated with the billboard (red) using identity submatrix approach. The billboard, however, should not move through the surface (white), it should maintain it's position correctly and always be on one side of the surface, which does not happen.

Timur Nuriyasov
  • 359
  • 2
  • 16
  • what about rotating each billboard individually without the geometry shader? – Quonux Apr 10 '13 at 11:55
  • @Quonux: even then, I would need some way to rotate it correctly. It doesn't matter where the rotation happens IMHO. I use GS to improve bandwidth - passing points instead of quads is better – Timur Nuriyasov Apr 10 '13 at 12:05

3 Answers3

1

Just reset the top left 3×3 subpart of the modelview matrix to identity, leaving the 4th column and row as it is, i.e.:

1 0 0 …
0 1 0 …
0 0 1 …
… … … …

UPDATE World space axis following billboards

The key insight into efficiently implementing aligned billboards is to realize how they work in view space. By definition the normal vector of a billboard in view space is Z = (0, 0, 1). This leaves only one free parameter, namely the rotation of the billboard around this axis. In a view aligned billboard the billboard right and up axes are merely forced to be view X and Y. This is what setting the upper left 3×3 of the modelview matrix does.

Now when we want the billboard be aligned to a certain axis within the scene yet still face the viewer, the only parameter we can vary is the billboards rotation. For this we do the following:

In world space we choose an axis that should be the up axis of the billboard. Note that if the viewing axis is parallel to the billboard up axis the following steps become singular, i.e. the rotation of the billboard is undefined. You have to deal with this in some way, that I leave undefined here.

This chosen axis we bring into view space. Now an axis is the same kind of thing like a normal, i.e. a direction, so we transform it the same way as we do with normals. We transform it by the inverse transpose of the modelview matrix as you to with normals; note that since we defined the axis in world space, we need to actually use the inverse transpose of the world to view transformation matrix then.

The transformed major axis of the billboard is now in view space. Next step is to orthogonalize it to the viewing direction. For this you use the Gram-Schmidt method. Now we got the Z and the Y column of the billboard transform. Remains the X column, which we get by taking the cross product of the Z with the Y column.

datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • This will effectively make the billboard not move when the camera is moving around the scene. I have tried this approach and I assume you have read the lighthouse3d tutorial?:) – Timur Nuriyasov Apr 10 '13 at 11:51
  • @TimurNuriyasov: If you mean by "not move around" that it keeps it's orientation fixed to the camera plane, then, well yes. This is exactly what a spherical billboard does. Of course if you want to tie a certain axis into a general direction, then you've to do a bit more. No I didn't read that tutorial. In fact I think lighthose3d might have gotten this from a tutorial I wrote in the previous millenium ;) – datenwolf Apr 10 '13 at 11:55
  • @TimurNuriyasov: I know what you're after, but this is a bit more complicated. Let me write this down and update the answer. I'll be back online in about 2 hours. – datenwolf Apr 10 '13 at 11:59
  • by not moving i don't mean not rotating but not moving (sorry for the awkwardness). Say the billboard is not located at (0,0,0) but at (1,0,0). If you rotate the camera now, given that you have modified your mv matrix, the object will face the camera (I admit) but the position of it will not be the correct one. It will simply stay where it was. But I only want the bilboard to rotate to the camera, not to completely deny it's existence (what this "cheating" approach does) – Timur Nuriyasov Apr 10 '13 at 12:01
  • @TimurNuriyasov: The position is entirely determined by the 4th column of the modelview matrix. Setting the upper left 3×3 to identity only affects the rotation (and scaling) but not the position. If the position get's influenced by this, something is off in the way you deal with your matrices. – datenwolf Apr 10 '13 at 14:32
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/27955/discussion-between-timur-nuriyasov-and-datenwolf) – Timur Nuriyasov Apr 10 '13 at 15:00
1

A alternative to create billboards is to throw the geometry shaders away and do it manually like this:

Vector3 DiffCamera = Billboard.position - Camera.position;
Vector3 UpVector   = new Vector3(0.0f, 1.0f, 0.0f);

Vector3 CrossA     = DiffCamera.cross(UpVector).normalize(); // (Step A)
Vector3 CrossB     = DiffCamera.cross(CrossA).normalize();   // (Step B)

// now you can use CrossA and CrossB and the billboard position to calculate the positions of the edges of the billboard-rectangle

// like this
Vector3 Pos1 = Billboard.position + CrossA + CrossB;
Vector3 Pos2 = Billboard.position - CrossA + CrossB;
Vector3 Pos3 = Billboard.position + CrossA - CrossB;
Vector3 Pos4 = Billboard.position - CrossA - CrossB;

we calculate in Step A the cross-product because we want the horizontal aligned direction of the billboard.

In step B we do it for the vertical direction.

do this for every billbaord in the scene.

or better as geometry shader (just a try)

vec3 pos = gl_in[0].gl_Position.xyz;
pos /= gl_in[0].gl_Position.w; //normalized device coordinates
vec3 toCamera = normalize(cameraPos - pos);
vec3 up = vec3(0,1,0);
vec3 CrossA = normalize(cross(up, toCamera));
vec3 CrossB = normalize(cross(CrossA, toCamera));

// set coordinates of the 4 points
Quonux
  • 2,975
  • 1
  • 24
  • 32
  • 1
    If you do the math, i.e. simplyfy the operations you suggest, the result will have exactly the same effect as setting the modelview matrix upper left 3×3 submatrix to identity. Hence it boils down to what I wrote. – datenwolf Apr 10 '13 at 13:23
  • @Quonux: This does seem to work, but problems arise when toCamera vector is parallel to the up (0,1,0) vector - the billboard starts to rotate around toCamera vector. – Timur Nuriyasov Apr 10 '13 at 13:54
  • @datenwolf i really wasn't aware of that, this is the way i did it – Quonux Apr 10 '13 at 13:59
  • @Quonux: A little explanation: `DiffCamera` is the camera viewing axis in world space. Assuming the camera is upright, `UpVector` is approximately the up vector in world space, but not exactly. The following two cross products and normalization make a orthonormalized tripod of the camera alignment in world space. Now by definition transforming this tripod using the world to view space transformation must result in identity (the camera in world must transform onto itself in view). `CrossA` and `CrossB` are rence the X and Y columns of the inverse of the modelview transform. – datenwolf Apr 10 '13 at 14:45
  • @Quonux: But by definition inv(M) · M = M · inv(M) = Identity. Hence setting the upper left 3×3 of the modelview transform to identity and using the base (1, 0, 0); (0, 1, 0) for the billboard has the same effect, as using the base CrossA; CrossB and transforming it through the unmodified modelview. – datenwolf Apr 10 '13 at 14:47
0

In case anyone wonders how I solved this.
I have based my solution on Quonux's answer, the only problem with it was that the billboard would rotate very fast when the camera is right above it (when the up vector is almost parallel to the camera look vector). This strange behaviour is a result of using a cross product to find the right vector: when the camera hovers over the top of the billboard, the cross product changes it's sign, and so does the right vector's direction. That explains the rotation that happens.
So all I needed was to find a right vector using some other way. As I knew camera's rotation angles (both horizontal and vertical) I decided to use that to find a right vector:

rotatedRight = Vector4.Transform(unRotatedRight, Matrix4.CreateRotationY((-alpha)));

and the geometry shader:

...
uniform vec3 rotRight;
uniform vec3 cameraPos;

out vec2 texCoord;

void main(){

vec3 pos = gl_in[0].gl_Position.xyz;
pos /= gl_in[0].gl_Position.w; //normalized device coordinates
vec3 toCamera = normalize(cameraPos - pos);

vec3 CrossA = rotRight;
... (Continues as Quonux's code)
Timur Nuriyasov
  • 359
  • 2
  • 16