Android OpenGL的简单使用(6):图图形变换

Posted by alonealice on 2020-12-26

OpenGL中的图像变换主要用四种:

1
2
3
4
5
6
7
1. 视角(Viewing)变换

2. 模型(Modeling)变换

3. 投影(Projection)变换

4. 视窗(Viewport)变换

视角变换

视角变换相当于人拿着一台照相机在观察物体,照相机在移动,从不同的位置来观察。具体的有以下三种变换:

1
2
3
- Translate    平移变换
- Rotate 旋转变换
- Scale 缩放变换

平移变换

在之前绘制的图形的代码的基础上,添加以下代码:

1
2
3
//设置为单位矩阵
gl.glLoadIdentity();
gl.glTranslatef(0.5f, 0f, 0f);

在平移变换之前,需调用glLoadIdentity才能进行变换,不加时图像不会显示。同时,在两句代码在onDrawFrame的任何位置,包括在清屏前都是没有影响的。

旋转变换

调用glRotatef(float angle, float x, float y, float z)传入4个参数,角度和x、y、z坐标。此时一定要注意旋转的角度,角度为正表示逆时针。

1
2
gl.glLoadIdentity();
gl.glRotatef(40,1,0,0);

这里的x,y和z不是旋转的坐标点,而是向量点,即以(0,0,0)和(1,0,0)为一条线作为旋转的轴,轴的方向向(1,0,0),图像按这个轴进行旋转。旋转的方法遵循安培定则,即右手螺旋定则。用右手大拇指指向轴的正方向,其余手指卷曲的方向为旋转的方向。

glLoadIdentity在旋转中也不能少,不能会每次刷新时都会一直旋转,导致图像一直在移动。

缩放变换

调用glScalef(float x, float y, float z)进行缩放,三个参数为缩放比例,缩放之前的坐标乘以缩放比例即可。

1
2
gl.glLoadIdentity();
gl.glScalef(0.5f,0.5f, 0.5f);

glLoadIdentity的规则跟平移时一样。

组合变换

1
2
3
gl.glLoadIdentity();
gl.glScalef(0.5f, 0.5f, 0f);
gl.glTranslatef(0.5f, 0.5f, 0);

这边有个很关键的点,先平移后缩放,跟先缩放后平移效果是不一样的,而且实际变换的顺序跟逻辑执行的顺序刚好相反

重置矩阵

如果想重置矩阵为没有任何变换之前,调用glLoadIdentity()

1
gl.glLoadIdentity();

保存、恢复矩阵

调用glPushMatrix保存当前矩阵,调用glPopMatrix恢复当前矩阵。

1
2
3
4
5
6
7
//保存当前矩阵
gl.glPushMatrix();
//重置矩阵
gl.glLoadIdentity();

//恢复矩阵
gl.glPopMatrix();

保留矩阵相当于把当前的矩阵记录了下来,在保留矩阵之后做的任何变换,在恢复矩阵时都是无效的,依旧会是保留时的矩阵数据。

模型变换

模型变换相当于是相机不动,而观察的物体在做移动,产生的效果跟视角变换是一样的。

投影变换

投影变换相当于观察物体是照相机的远近具体在发生变换。

投影变换会定义一个视锥(viewing volume),视锥有两个作用:

1
2
- 物体如何投影到屏幕(透视投影或者正侧投影)
- 裁剪场景的区域大小

OpenGL ES可以使用两种不同的投影变换:透视投影(Perspective Projection)和正侧投影(Orthographic Projection)。

透视投影

透视投影 ,就像我们眼睛看一个物体,近大远小。 如下图:

从照相机(View Point)望去,由left、top、bottom、right四条线组成的面(也就是near面)和最远处的面(也就是far面)组成了一个椎体,这个椎体叫View volumn。

裁剪就是把Viewing Volume之外的都裁剪掉,这样可以提高绘图性能。

public void glFrustumf(float left,float right,float bottom,float top,float near,float far)

也可以用GLU.gluPerspective(fovy, aspect, zNear, zFar)方法,fovy是竖直方向也就是Y方向的夹角,aspect是near面的宽高比,即top的长度除以left长度。

正侧投影

正侧投影 ,View Volumn椎体不变,一直是一个长方体,无论距离怎么改变,投影都不变。相当于透视投影,眼睛是一个点,而正侧投影,眼睛是一个面。

定义正侧投影的方法如下,传入6个参数

public void glOrthof(float left, float right,float bottom,float top,float near,float far)

视窗变换

视窗变换相当于物体和相机都没有变换,但是出来的效果之后,显示这个的载体发生了变化。

把像素按照比例转化后显示到屏幕上,这就是视窗(Viewport)变换,分两步

1
2
3
- 设置窗口的位置和大小————glViewport(int x, int y, int width, int height)
- 设置观察的位置和角度————GLU.gluLookAt(gl, eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ)
123

第一种就是onSurfaceChanged

1
2
3
4
5
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// 设置窗口大小
gl.glViewport(0, 0, width , height);
}

设置观察的位置和角度

GLU.gluLookAt(gl, eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ)

1
2
3
- eye理解为照相机或者你的眼睛,默认位置在原点(0,0,0)
- center是观察物体的坐标,从eye到center的向量就是观察的方向
- up默认为Y轴正方向,可以理解为眼睛到头顶的方向,比如改成(0,-1,0)那就是倒立着观察了

默认情况下,一个物体的观察坐标是:

1
GLU.gluLookAt(gl, 0f, 0f, 0f,  0f, 0f, -1f,  0f, 1f, 0f);

即眼睛和物体都在x和y的原点,z轴上,眼睛在0,物体在-1,而y轴也是正着的。

如果改为:

1
GLU.gluLookAt(gl, 0f, 0f, 0f, 0f, 0f, -1f, 1f, 0f, 0f)

就相当于侧着头看,而且是沿着x轴的正方向看。

实际的效果是,如果在屏幕的左上角绘制一个图像,那么侧着看的话,图像就在屏幕的左下方了。