Vertex shader particle billboarding question
I've implemented billboarding in my vertex shader and it works.
Now here's the issue: I've been experimenting with individual perparticle rotation in my vertex shader. So each particle has a vertex attribute "Rotation" which is the amount to rotate that particle in radians. The effect looks awesome... smoke appears to sort of "billow" and flames "roil" and so on.
That being said, my implementation only sortof works. The particles do rotate, but they lose billboarding as they rotate and the camera moves about.
To give you an idea, here are two screenshots. In the first, the camera's looking pretty much straight down +y ( I use zup ) and the rotating particles look great:
In the second, I'm looking diagonally, and you can see some particles are sheared:
The approach I'm taking is to get "right" and "up" vectors from the modelview matrix and to then rotate them by the particle's rotation about the camera look vector. Then I do the standard pushing of gl_Vertex in the right direction for that vertex.
Note: If I remove the rotation math, or set the rotation to zero, it looks great.
Now, my guess is that one or both of two things could be happening:
1) My principle is fundamentally flawed! I should be doing it some nonboneheaded way. If this is the case, please let me know!
2) My math for making a matrix that rotates a vector about some other vector is flawed. This strikes me as being a likely culprit. I got this matrix after googling... I've never used it before, since I usually use quaternions for this kind of rotation.
Here's the relevant bits of my vertex shader. The fragment shader is irrelevant here.
Any help is greatly appreciated. That being said, I'm going to see if implementing a quaternion in GLSL isn't too slow for real time stuff.
Now here's the issue: I've been experimenting with individual perparticle rotation in my vertex shader. So each particle has a vertex attribute "Rotation" which is the amount to rotate that particle in radians. The effect looks awesome... smoke appears to sort of "billow" and flames "roil" and so on.
That being said, my implementation only sortof works. The particles do rotate, but they lose billboarding as they rotate and the camera moves about.
To give you an idea, here are two screenshots. In the first, the camera's looking pretty much straight down +y ( I use zup ) and the rotating particles look great:
In the second, I'm looking diagonally, and you can see some particles are sheared:
The approach I'm taking is to get "right" and "up" vectors from the modelview matrix and to then rotate them by the particle's rotation about the camera look vector. Then I do the standard pushing of gl_Vertex in the right direction for that vertex.
Note: If I remove the rotation math, or set the rotation to zero, it looks great.
Now, my guess is that one or both of two things could be happening:
1) My principle is fundamentally flawed! I should be doing it some nonboneheaded way. If this is the case, please let me know!
2) My math for making a matrix that rotates a vector about some other vector is flawed. This strikes me as being a likely culprit. I got this matrix after googling... I've never used it before, since I usually use quaternions for this kind of rotation.
Here's the relevant bits of my vertex shader. The fragment shader is irrelevant here.
Code:
mat4 rotationAboutAxis( in vec3 axis, in float rads )
{
mat4 R;
float s = sin( rads );
float c = cos( rads );
float rc = 1.0  c;
// [column][row]
R[0][0] = c + rc * axis.x;
R[0][1] = rc * axis.x*axis.y + s*axis.z;
R[0][2] = rc * axis.x*axis.z  s*axis.y;
R[0][3] = 0.0;
R[1][0] = rc * axis.y*axis.x  s*axis.z;
R[1][1] = c + rc * (axis.y * axis.y);
R[1][2] = rc * axis.y*axis.z + s*axis.x;
R[1][3] = 0.0;
R[2][0] = rc * axis.z*axis.x + s*axis.y;
R[2][1] = rc * axis.z*axis.z  s*axis.x;
R[2][2] = c + rc * (axis.z * axis.z);
R[2][3] = 0.0;
R[3][0] = 0.0;
R[3][1] = 0.0;
R[3][2] = 0.0;
R[3][3] = 1.0;
return R;
}
attribute float Rotation;
attribute vec3 VertexSize;
void main()
{
// snip: color, texture, other junk
//
// Get our billboard vectors from modelview, and then rotate them
//
vec4 right = vec4( gl_ModelViewMatrix[0][0],
gl_ModelViewMatrix[1][0],
gl_ModelViewMatrix[2][0],
0.0 );
vec4 up = vec4( gl_ModelViewMatrix[0][1],
gl_ModelViewMatrix[1][1],
gl_ModelViewMatrix[2][1],
0.0 );
vec3 look = vec3( gl_ModelViewMatrix[0][2],
gl_ModelViewMatrix[1][2],
gl_ModelViewMatrix[2][2] );
mat4 R = rotationAboutAxis( look, Rotation );
right = R * right;
up = R * up;
//
// Construct vertex direction
//
vec3 vR = VertexSize.xxx * right.xyz;
vec3 vU = VertexSize.yyy * up.xyz;
vec4 dir = vec4( vR + vU, 0.0 );
gl_Position = gl_ModelViewProjectionMatrix * (gl_Vertex + dir );
}
Any help is greatly appreciated. That being said, I'm going to see if implementing a quaternion in GLSL isn't too slow for real time stuff.
OK, I fixed it by implementing it with a quaternion. Here's the new rotationAboutAxis() method:
It works! And I'm still getting 60fps.
Code:
mat4 rotationAboutAxis( in vec3 axis, in float rads )
{
//
// Construct a quaternion (x,y,z,w), and then convert it to a matrix
// Note: We're not error checking for a nonnormalized axis
//
rads = rads * 0.5;
float s = sin( rads );
float x = axis.x * s,
y = axis.y * s,
z = axis.z * s,
w = cos( rads );
//
// Construct the matrix
//
float x2,y2,z2,xx,yy,zz,xy,yz,xz,wx,wy,wz;
x2 = x + x;
y2 = y + y;
z2 = z + z;
xx = x * x2;
yy = y * y2;
zz = z * z2;
xy = x * y2;
yz = y * z2;
xz = z * x2;
wx = w * x2;
wy = w * y2;
wz = w * z2;
mat4 R;
// [column][row]
R[0][0] = 1.0  (yy + zz); R[1][0] = xy  wz; R[2][0] = xz + wy; R[3][0] = 0.0;
R[0][1] = xy + wz; R[1][1] = 1.0  (xx + zz); R[2][1] = yz  wx; R[3][1] = 0.0;
R[0][2] = xz  wy; R[1][2] = yz + wx; R[2][2] = 1.0  (xx + yy); R[3][2] = 0.0;
R[0][3] = 0.0; R[1][3] = 0.0; R[2][3] = 0.0; R[3][3] = 1.0;
return R;
}
It works! And I'm still getting 60fps.
Just a note: When doing something for which there is already an OpenGL function, then it's always a good idea to look at the code of Mesa3D. The license is liberal enough. By the way, their implementation of glRotatef is nearly identical to your solution.
You know, that's funny. It's been so long since I used a direct call to glRotate that I forgot it takes an axis and produces a matrix representing a rotation about it. Also, I agree with using Mesa code for reference... I used it to figure out gluUnproject and friends a while back.
Possibly Related Threads...
Thread:  Author  Replies:  Views:  Last Post  
ios/mac shader  shared glsl source  OptimisticMonkey  2  7,017 
Jun 17, 2011 08:59 AM Last Post: OptimisticMonkey 

passing values from vertex to fragment shader  Sumaleth  5  15,733 
Feb 18, 2011 01:54 AM Last Post: Holmes 

Changing Uniform Variables for a Single Shader  reapz  3  7,816 
Jul 15, 2010 01:29 AM Last Post: dazza 

Particle Vertex Array  bonanza  9  6,915 
Jun 27, 2008 07:46 AM Last Post: bonanza 

GLSL Shader getting corrupted  Weebull  4  6,823 
Jun 23, 2008 03:43 PM Last Post: Weebull 