注: 参考:http://wiki.jikexueyuan.com/project/opengl-es-basics/coordinate-transformation.html
OpenGL 使用了右手坐标系统,右手坐标系判断方法:在空间直角坐标系中,让右手拇指指向x轴的正方向,食指指向y轴的正方向,如果中指能指向z轴的正方向,则称这个坐标系为右手直角坐标系。
方法 public abstract void glTranslatef (float x, float y, float z)
用于坐标平移变换。
方法public abstract void glRotatef(float angle, float x, float y, float z)
用来实现选择坐标变换,单位为角度。
比如你选择一个骰子,首先按下列顺序选择3次:
gl.glRotatef(90f, 1.0f, 0.0f, 0.0f);
gl.glRotatef(90f, 0.0f, 1.0f, 0.0f);
gl.glRotatef(90f, 0.0f, 0.0f, 1.0f);
然后打算逆向旋转回原先的初始状态,需要有如下旋转:
gl.glRotatef(90f, -1.0f, 0.0f, 0.0f);
gl.glRotatef(90f, 0.0f, -1.0f, 0.0f);
gl.glRotatef(90f, 0.0f, 0.0f, -1.0f);
或者如下旋转:
gl.glRotatef(90f, 0.0f, 0.0f, -1.0f);
gl.glRotatef(90f, 0.0f, -1.0f, 0.0f);
gl.glRotatef(90f, -1.0f, 0.0f, 0.0f);
旋转变换 glRotatef(angle, -x, -y, -z) 和 glRotatef(-angle, x, y, z) 是等价的,但选择变换的顺序直接影响最终坐标变换的结果。 角度为正时表示逆时针方向。
在对 Mesh(网格,构成三维形体的基本单位)同时进行平移和选择变换时,坐标变换的顺序也直接影响最终的结果。
比如:先平移后旋转,旋转的中心为平移后的坐标。
先选择后平移: 平移在则相对于旋转后的坐标系:
方法public abstract void glScalef (float x, float y, float z)
用于缩放变换。
比如使用 gl.glScalef(2f, 2f, 2f) 变换后的基本,相当于把每个坐标刻度值都乘以2.
同样当需要平移和缩放时,变换的顺序也会影响最终结果。
比如先平移后缩放:
gl.glTranslatef(2, 0, 0);
gl.glScalef(0.5f, 0.5f, 0.5f);
如果调换一下顺序:
gl.glScalef(0.5f, 0.5f, 0.5f);
gl.glTranslatef(2, 0, 0);
结果就有所不同:
注:以上所有操作都是针对矩阵,对于已经绘制的图形无影响
在进行平移,旋转,缩放变换时,所有的变换都是针对当前的矩阵(与当前矩阵相乘),如果需要将当前矩阵回复最初的无变换的矩阵,可以使用单位矩阵(无平移,缩放,旋转
public abstract void glLoadIdentity()。
在栈中保存当前矩阵和从栈中恢复所存矩阵,可以使用
public abstract void glPushMatrix()
和
public abstract void glPopMatrix()。
在进行坐标变换的一个好习惯是在变换前使用 glPushMatrix 保存当前矩阵,完成坐标变换操作后,再调用 glPopMatrix 恢复原先的矩阵设置。
利用上面介绍的坐标变换知识,来绘制 3 个正方形 A,B,C。进行缩放变换,使的 B 比 A 小 50%,C 比 B 小 50%。 然后以屏幕中心逆时针旋转 A,B 以 A 为中心顺时针旋转,C 以 B 为中心顺时针旋转同时以自己中心高速逆时针旋转。
本次示例使用透视投影
在onSurfaceChanged中设置
// Sets the current view port to the new size.
gl.glViewport(0, 0, width, height);
// Select the projection matrix
gl.glMatrixMode(GL10.GL_PROJECTION);
// Reset the projection matrix
gl.glLoadIdentity();// OpenGL docs.
//设置透视投影,参数作用请看之前的文章
GLU.gluPerspective(gl,45.f,(float) width/height,10f,100f);
// Select the modelview matrix
gl.glMatrixMode(GL10.GL_MODELVIEW);
// Reset the modelview matrix
gl.glLoadIdentity();
所画图形为之前的矩形(因设置透视投影,各轴刻度值相等,所画矩形为正方形),封装成drawREC绘制方法
private void drawREC(GL10 gl) {
float vertices[] = {
-0.5f, 0.5f, 0.0f, // 0, Top Left
-0.5f, -0.5f, 0.0f, // 1, Bottom Left
0.5f, -0.5f, 0.0f, // 2, Bottom Right
0.5f, 0.5f, 0.0f, // 3, Top Right
};
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
vbb.order(ByteOrder.nativeOrder());
FloatBuffer vertexBuffer = vbb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
short[] indices = { 0, 1,
2, 0,
2, 3 };
ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2);
ibb.order(ByteOrder.nativeOrder());
ShortBuffer indexBuffer = ibb.asShortBuffer();
indexBuffer.put(indices);
indexBuffer.position(0);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
gl.glDrawElements(GL10.GL_TRIANGLES, indices.length,
GL10.GL_UNSIGNED_SHORT, indexBuffer);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
}
在onDrawFrame开始绘制
float angle =0f;
@Override
public void onDrawFrame(GL10 gl) {
gl.glFrontFace(GL10.GL_CCW);
gl.glEnable(GL10.GL_CULL_FACE);
//重置视角矩阵
gl.glLoadIdentity();
gl.glTranslatef(0,0,-10f);
// 清除屏幕和深度缓存
gl.glClear(GL10.GL_COLOR_BUFFER_BIT |
GL10.GL_DEPTH_BUFFER_BIT);
//REC A
// Save the current matrix.
gl.glPushMatrix();
// Rotate square A counter-clockwise.
gl.glRotatef(angle, 0, 0, 1);
// Draw REC A.
drawREC(gl);
// Restore the last matrix.
gl.glPopMatrix();
// SQUARE B
//making it rotate around A.
gl.glRotatef(-angle, 0, 0, 1);
// Move square B.
gl.glTranslatef(2, 0, 0);
// Scale it to 50% of REC A
gl.glScalef(0.5f, 0.5f, 0.5f);
// Draw REC B.
drawREC(gl);
// REC C
// Make the rotation around B
gl.glRotatef(-angle, 0, 0, 1);
gl.glTranslatef(2, 0, 0);
// Scale it to 50% of REC B
gl.glScalef(0.5f, .5f, 0.5f);
// Rotate around it's own center.
gl.glRotatef(angle*10, 0, 0, 1);
// Draw REC C.
drawREC(gl);
// Increse the angle.
angle++;
}