在OpenGL着色器中旋转球体

时间:2014-11-30 17:08:22

标签: android opengl-es shader

我使用OpenGL ES 2.0 for Android制作地球模型。我绘制球体,每次在顶点着色器中,我想旋转它。每次绘制时,我都会设置一个表示旋转角度的制服。以下是我计算新点的方法:

Vertex Shader

uniform mat4 u_Matrix;
uniform vec3 u_VectorToLight;
uniform vec3 u_Center;
uniform float u_RotationAngle;

attribute vec4 a_Position;
attribute vec2 a_TextureCoordinates;
attribute vec3 a_Normal;

varying vec2 v_TextureCoordinates;
varying vec3 v_VectorToLight;
varying vec3 v_Normal;

vec2 rotate(vec2 c, vec2 p, float a);

void main() {
  v_TextureCoordinates = a_TextureCoordinates;
  v_VectorToLight = u_VectorToLight;
  v_Normal = a_Normal;
  vec2 point = rotate(u_Center.xz, a_Position.xz, radians(u_RotationAngle));
  gl_Position = a_Position;
  gl_Position *= u_Matrix;
  gl_Position.x = point.x;
  gl_Position.z = point.y;
}

vec2 rotate(vec2 c, vec2 p, float a) {
  p.x -= c.x;
  p.y -= c.y;

  float x1 = p.x * cos(a) - p.y * sin(a);
  float y1 = p.x * sin(a) + p.y * cos(a);

  p.x = c.x + x1;
  p.y = c.y + y1;

  return p;
}

Fragment Shader

precision mediump float;

uniform sampler2D u_TextureUnit;

varying vec2 v_TextureCoordinates;
varying vec3 v_VectorToLight;
varying vec3 v_Normal;

void main() {
  gl_FragColor = texture2D(u_TextureUnit, v_TextureCoordinates);
  vec4 color = gl_FragColor.rgba;
  vec3 scaledNormal = v_Normal;
  scaledNormal = normalize(scaledNormal);
  float diffuse = max(dot(scaledNormal, v_VectorToLight), 0.0);
  gl_FragColor.rgb *= diffuse;
  float ambient = 0.2;
  gl_FragColor.rgb += ambient * color;
}

我使用顶点着色器中的rotate()方法围绕球体中心旋转每个单独的点,这只会导致扭曲的地球,在X和Z轴上变小,但不是Y (想象一下地球的一个非常薄的版本)。更令人困惑的是,即使我每次传递一个新的旋转角度值,我仍然得到相同的静止图像。即使它是扭曲的,每次都应该以某种方式看起来不同,因为我每次都使用不同的角度。这是我如何设置制服:

  public void setUniforms(float[] matrix, Vector vectorToLight, int texture, Point center, float angle) {
    glUniformMatrix4fv(uMatrixLocation, 1, false, matrix, 0);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);
    glUniform1i(uTextureUnitLocation, 0);
    glUniform3f(uRadiusLocation, center.x, center.y, center.z);
    glUniform3f(uVectorToLightLocation, vectorToLight.x, vectorToLight.y, vectorToLight.z);
    glUniform1f(uRotationAngleLocation, angle);  // <-- I set the rotation angle
  }

1 个答案:

答案 0 :(得分:2)

我认为围绕轴的旋转与u_Matrix中包含的全局旋转的组合方式存在问题:

vec2 point = rotate(u_Center.xz, a_Position.xz, radians(u_RotationAngle));
gl_Position = a_Position;
gl_Position *= u_Matrix;
gl_Position.x = point.x;
gl_Position.z = point.y;

由于point仅包含围绕轴的轮播,并且您使用x中的值替换z的{​​{1}}和gl_Position,因此生成{ {1}}和point没有应用x

您需要首先应用轴旋转,然后将z应用于此变换点:

u_Matrix

要完成此任务,在Java代码中计算一次旋转矩阵,将其设置为均匀,然后仅在顶点着色器中应用矩阵乘法通常会更有效。使用现在的代码,您将为着色器代码中的每个顶点计算u_Matrixvec2 point = rotate(u_Center.xz, a_Position.xz, radians(u_RotationAngle)); vec4 rotPoint = a_Position; rotPoint.x = point.x; rotPoint.z = point.y; gl_Position = rotPoint; gl_Position *= u_Matrix; 值,除非GLSL编译器真的很聪明。如果你不想传入一个完整的矩阵,我至少会将余弦和正弦值传递给着色器而不是角度。