0
votes

I am making a model of the Earth using OpenGL ES 2.0 for Android. I draw the sphere, and each time, in the vertex shader, I want to rotate it. Each time it's drawn, I set a uniform representing the rotation angle. Here is how I calculate the new points:

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

I rotate each individual point around the center of the sphere, using the rotate() method in the vertex shader, and this just results in a distorted earth, made smaller on the X and Z axes, but not the Y (so imagine a very thin version of the Earth). What's even more confusing is that even though I pass in a new value for the rotation angle each time, I still get the same still image. Even if it is distorted, It should still look different each time in some way, since I'm using a different angle each time. Here's how I set the uniforms:

  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
It would be more usual to calculate your transformation matrix once outside of the shader, and pass it in. Saves recalculating it for every vertex.Alan
I haven't studied the whole thing, but it looks like you apply u_Matrix to the original coordinates, and then replace two of the resulting coordinates with coordinates that were only processed with your axis rotation. If I understand what you're trying to do, you need to first apply your axis rotation, and then apply u_Matrix to the result.Reto Koradi

1 Answers

2
votes

I believe there is a problem with how you combine the rotation around the axis with your global rotation contained in 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;

Since point only contains the rotation around the axis, and you replace x and z of gl_Position with the values from point, the resulting x and z do not have u_Matrix applied to them.

You need to first apply your axis rotation, and then apply u_Matrix to this transformed point:

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

To complete this task, it would generally be much more efficient to calculate the rotation matrix once in your Java code, set it as a uniform, and then only apply a matrix multiplication in your vertex shader. With the code you have now, you will calculate a cos() and sin() value for each vertex in your shader code, unless the GLSL compiler is really smart about it. If you don't want to pass in a full matrix, I would at least pass the cosine and sine values into the shader instead of the angle.