在几何着色器中旋转四边形

Rotate quad made in geometry shader

我正在使用几何着色器绘制四边形,但不知道如何旋转它的角度。

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(); 
}

我应该使用旋转矩阵还是 sin 和 cos?我数学不太好。

您不必使用矩阵,但您需要使用那些正弦函数。这是一种将 3D 位置绕任意轴旋转一定角度(以度为单位指定)的方法:

// 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);

如果您知道您正在围绕某个标准的 x、y 或 z 轴旋转,则可以通过为该标准轴显式定义它来大大简化 "algorithm"。

注意我们在上面的代码中是如何绕 z 轴旋转的。例如,绕 x 轴的旋转将为 (x,y,z) = (1,0,0)。您可以将变量设置为任何值,但这些值应该导致轴成为单位向量(如果这很重要的话。)

再说一次,你不妨使用矩阵:

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);

注意矩阵元素的布局方式,我们首先完成第一列,然后是第二列,依此类推;矩阵按列优先顺序排列。当您尝试将以数学符号编写的矩阵转换为代码中的数据类型时,这一点很重要。您基本上需要转置它(以对角线翻转元素)以便在您的代码中使用它。此外,我们实质上是将左侧的矩阵与右侧的列向量相乘。

如果你需要一个 4×4 齐次矩阵,那么你只需在上面的矩阵中添加一个额外的列和一行,这样新的最右边的列和最下面的行都将包含 [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;

再次注意应用旋转时的乘法顺序,这很重要,因为它会影响最终结果。乘法基本上是通过计算位置向量与矩阵中每一列的点积形成一个新向量;结果向量中的每个坐标都是原始向量与矩阵中单个列的点积(参见第一个代码示例。)

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.x = dot(p, m[0]);

甚至可以将矩阵与自身组合:m = m*m; 这将导致逆时针旋转 180 度的矩阵,具体取决于所使用的 angle