7
\$\begingroup\$

I'm trying to create a particle system in OpenGL, and as such I need to use billboards.

I need these billboards to face the camera at all times.

The tutorials I've been following only rotated the billboard on 1 axis, such as billboards being used as grass or plants, but I need a method of rotating the quad to always face the camera, even when looking down on it.


The Original Scene: Prior to rotation

Desired Scene: Desired Scene

While searching for an answer, I only really found the technique of turning the mat3 component of the camera's view/model matrix back to an identity matrix. This isn't quite what I need, as the point's location in space never change then.

Incorrect Scene: Incorrect Rotation


How can I calculate a new rotation matrix that will successfully rotate a quad always towards the camera, or modify an existing matrix?

In the geometry shader I have access to the camera's position, the perspective matrix, and the view/model matrix.

I didn't include any shader code because it is really trivial; I create vertices that are an offset of the desired point's position based on the desired size. So if the quad should be 50 x 50, the vertex is the position offset by 25 on X and Y, multiplied by the Camera's Perspective x View matrix.


Solution:

mat4 VP = pMatrix * vMatrix;       
vec3 CameraRight = vec3(vMatrix[0][0], vMatrix[1][0], vMatrix[2][0]);
vec3 CameraUp = vec3(vMatrix[0][1], vMatrix[1][1], vMatrix[2][1]);

vec3 Pos = gl_in[0].gl_Position.xyz;  // The desired point for the billboard

vec4 v1 = VP * vec4(Pos + CameraRight * 0.5 * Size + CameraUp * -0.5 * Size, 1.0);  
vec4 v2 = VP * vec4(Pos + CameraRight * 0.5 * Size + CameraUp * 0.5 * Size, 1.0);     
vec4 v3 = VP * vec4(Pos + CameraRight * -0.5 * Size + CameraUp * -0.5 * Size, 1.0); 
vec4 v4 = VP * vec4(Pos + CameraRight * -0.5 * Size + CameraUp * 0.5 * Size, 1.0);  

Finished

\$\endgroup\$
  • \$\begingroup\$ Also, I have found similar questions posted before pertaining to other engines such as Unity, however I'm using my own engine and further this needs to be done in GLSL, so I don't have all the fancy lookat functions that would simplify matters. \$\endgroup\$ – Yattabyte Dec 16 '15 at 19:50
  • \$\begingroup\$ I seem to recall that Minecraft's player nametags are rendered with pure OpenGL calls, and those always face the camera (and are placed in perspective space). I'm away from my modding environment however, so I won't be able to dig around it in until...eesh, probably Saturday. That said, you might be able to find it on your own from that description. \$\endgroup\$ – Draco18s Dec 16 '15 at 19:57
2
\$\begingroup\$

I think this article is what you are looking for.

Article from opengl tutorials

Basically, you'll have to find the matrix camera rotation and apply a derived form to your mesh.

Hope it helps!

\$\endgroup\$
  • \$\begingroup\$ Yes! This was exactly what I needed. Thank you very very much! \$\endgroup\$ – Yattabyte Dec 16 '15 at 23:05
1
\$\begingroup\$

I'm just leaving this here for a more general solution to the problem. What you want to do is retain the "global" translate/rotate/scale of the object/billboard so it is in the correct world position, but locally rotate it back so it faces the camera. That is, you want the parent model-view matrix, but apply the inverse of its rotation part to the object/billboard. This is what I did (pseudo-code for Qt math classes, but you'll get the idea):

// Calculate object->world tranform with inverse parent->world rotation
QMatrix4x4 objectTransform;
objectTransform.setToIdentity();
objectTransform.translate(objectPosition);
// Get length of first row as scale factor. See: https://stackoverflow.com/questions/27655885/get-position-rotation-and-scale-from-matrix-in-opengl
float scaleFactor = parentModelView.row(0).toVector3D().length();
// get upper left 3x3 sub-matrix and unscale
QMatrix3x3 parentRotation = parentModelView.toGenericMatrix<3,3>();
parentRotation *= 1.0f / scaleFactor;
// convert to quaternion and invert
QQuaternion inverseParentRotation = QQuaternion::fromRotationMatrix(parentRotation).inverted();
// set as rotation
objectTransform.rotate(inverseParentRotation);
objectTransform.scale(objectScaleFactor); 

// Now for drawing calculate the combined object->world transform:
QMatrix4x4 objectModelView = parentModelView * objectTransform;

Caveat: Works only for uniform scaling and without shear transforms, as stated here.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.