0
votes

I'm drawing a quad using Geometry Shader, but can't figure out how to rotate it with angle.

void main(void)
{
float scaleX = 2.0f / u_resolution.x;
float scaleY = 2.0f / u_resolution.y;

float nx = (u_position.x * scaleX) - 1.0f;
float ny = -(u_position.y * scaleY) + 1.0f;

float nw = u_size.x * scaleX;
float nh = u_size.y * scaleY;


gl_Position = vec4( nx+nw, ny, 0.0, 1.0 );
texcoord = vec2( 1.0, 0.0 );
EmitVertex();

gl_Position = vec4(nx, ny, 0.0, 1.0 );
texcoord = vec2( 0.0, 0.0 ); 
EmitVertex();

gl_Position = vec4( nx+nw, ny-nh, 0.0, 1.0 );
texcoord = vec2( 1.0, 1.0 ); 
EmitVertex();

gl_Position = vec4(nx, ny-nh, 0.0, 1.0 );
texcoord = vec2( 0.0, 1.0 ); 
EmitVertex();


EndPrimitive(); 
}

Should I use a rotation matrix or sin and cos? I'm not too great at math.

1

1 Answers

1
votes

You don't have to use matrices, but you need to use those sine functions. Here is a way to rotate a 3D position about some arbitrary axis by some angle specified in degrees:

// This is the 3D position that we want to rotate:
vec3 p = position.xyz;

// Specify the axis to rotate about:
float x = 0.0;
float y = 0.0;
float z = 1.0;

// Specify the angle in radians:
float angle = 90.0 * 3.14 / 180.0; // 90 degrees, CCW

vec3 q;
q.x = p.x * (x*x * (1.0 - cos(angle)) + cos(angle))
    + p.y * (x*y * (1.0 - cos(angle)) + z * sin(angle))
    + p.z * (x*z * (1.0 - cos(angle)) - y * sin(angle));

q.y = p.x * (y*x * (1.0 - cos(angle)) - z * sin(angle))
    + p.y * (y*y * (1.0 - cos(angle)) + cos(angle))
    + p.z * (y*z * (1.0 - cos(angle)) + x * sin(angle));

q.z = p.x * (z*x * (1.0 - cos(angle)) + y * sin(angle))
    + p.y * (z*y * (1.0 - cos(angle)) - x * sin(angle))
    + p.z * (z*z * (1.0 - cos(angle)) + cos(angle));

gl_Position = vec4(q, 1.0);

If you know that you are rotating about some standard x-, y-, or z-axis, you can simplify the "algorithm" a lot by defining it explicitly for that standard axis.

Notice how we rotate about the z-axis in the above code. For example, rotation about the x-axis would be (x,y,z) = (1,0,0). You could set the variables to anything, but the values should result in the axis being a unit vector (if that even matters.)

Then again, you might as well use matrices:

vec3 n = vec3(0.0, 0.0, 1.0); // the axis to rotate about

// Specify the rotation transformation matrix:
mat3 m = mat3(
  n.x*n.x * (1.0f - cos(angle)) + cos(angle),       // column 1 of row 1
  n.x*n.y * (1.0f - cos(angle)) + n.z * sin(angle), // column 2 of row 1
  n.x*n.z * (1.0f - cos(angle)) - n.y * sin(angle), // column 3 of row 1

  n.y*n.x * (1.0f - cos(angle)) - n.z * sin(angle), // column 1 of row 2
  n.y*n.y * (1.0f - cos(angle)) + cos(angle),       // ...
  n.y*n.z * (1.0f - cos(angle)) + n.x * sin(angle), // ...

  n.z*n.x * (1.0f - cos(angle)) + n.y * sin(angle), // column 1 of row 3
  n.z*n.y * (1.0f - cos(angle)) - n.x * sin(angle), // ...
  n.z*n.z * (1.0f - cos(angle)) + cos(angle)        // ...
);

// Apply the rotation to our 3D position:
vec3 q = m * p;
gl_Position = vec4(q, 1.0);

Notice how the elements of the matrix are laid out such that we first complete the first column, and then the second, and so on; the matrix is in column-major order. This matters when you try to transfer a matrix written in mathematical notation into a data type in your code. You basically need to transpose it (to flip the elements diagonally) in order to use it in your code. Also, we are essentially multiplying a matrix on left with a column vector on right.

If you need a 4-by-4 homogeneous matrix, then you would simply add an extra column and a row into the above matrix, such that both the new rightmost column and bottommost row would consist of [0 0 0 1]:

vec4 p = position.xyzw; // new dimension
vec3 n = ...; // same

mat4 m = mat4( // new dimension
  ...
  0.0,

  ...
  0.0,

  ...
  0.0,

  0.0,
  0.0,
  0.0,
  1.0
);

vec4 q = m * p;
gl_Position = q;

Again, notice the order of the multiplication when applying the rotation, it is important because it affects the end result. What happens in the multiplication is basically that a new vector is formed by calculating the dot-product of the position vector and each column in the matrix; each coordinate in the resulting vector is the dot-product of the original vector and a single column in the matrix (see the first code example.)

The

q.x = p.x * (x*x * (1.0 - cos(angle)) + cos(angle))
    + p.y * (x*y * (1.0 - cos(angle)) + z * sin(angle))
    + p.z * (x*z * (1.0 - cos(angle)) - y * sin(angle));

Is same as:

q.x = dot(p, m[0]);

One could even compose the matrix with itself: m = m*m; which would result in a 180-degree counterclockwise rotation matrix, depending on the angle used.