Reputation: 2025
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
}
Upvotes: 0
Views: 1884
Reputation: 54592
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.
Upvotes: 2