OpenGLES3D编程:矩阵、变换与层次系统
立即解锁
发布时间: 2025-08-24 00:53:57 阅读量: 1 订阅数: 13 

### OpenGL ES 3D 编程:矩阵、变换与层次系统
在 3D 图形编程中,矩阵和变换是核心概念,它们帮助我们在虚拟空间中定位、旋转和缩放物体。本文将深入探讨 OpenGL ES 中的矩阵操作,以及如何利用矩阵栈构建层次系统。
#### 矩阵与变换回顾
矩阵在 3D 图形中扮演着重要角色,它可以实现点(或顶点)的平移、缩放和旋转。以下是矩阵的一些基本性质:
- **平移**:通过矩阵与点的位置相乘,将点移动到新的位置。
- **缩放**:矩阵可以对每个坐标轴上的点进行缩放,即每个坐标乘以一个常数。
- **旋转**:矩阵可以绕某个轴旋转一个点。
- **单位矩阵**:与单位矩阵相乘不会改变点的位置,矩阵与单位矩阵相乘也不会改变矩阵本身。
- **矩阵相乘**:两个矩阵相乘得到一个新的矩阵,将点与这个新矩阵相乘会同时应用原始矩阵中的变换。
OpenGL ES 提供了三种类型的矩阵:
- **投影矩阵**:用于设置视锥体的形状和大小,决定投影类型和可见世界的范围。
- **模型视图矩阵**:用于在模型空间中变换模型,并将模型放置在世界空间中。
- **纹理矩阵**:用于动态操作纹理坐标,但在某些设备上此功能可能存在问题,本文不使用。
在 3D 编程中,我们有更多的操作选项,例如可以绕任意轴旋转模型,而不仅仅是 z 轴。实际上,在之前的渲染中我们已经在 3D 空间中工作,只是忽略了 z 轴。
#### 矩阵栈的使用
在 OpenGL ES 中,我们通常这样使用矩阵:
```java
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glOrthof(−1, 1, -1, 1, -10, 10);
```
第一行代码设置当前活动矩阵,后续的矩阵操作将在该矩阵上执行。这里我们将活动矩阵设置为单位矩阵,然后乘以正交投影矩阵。
对于模型视图矩阵,我们也进行类似的操作:
```java
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glTranslatef(0, 0, -10);
gl.glRotate(45, 0, 1, 0);
```
这段代码首先加载单位矩阵,清除之前的内容,然后乘以平移矩阵和旋转矩阵。需要注意的是,矩阵乘法的顺序很重要,最后指定的变换将首先应用到顶点上。在上述例子中,顶点先绕 y 轴旋转 45 度,然后沿 z 轴移动 -10 个单位。
实际上,每种矩阵类型都有一个矩阵栈可供使用。目前,我们只使用栈顶(TOS)的矩阵,它是 OpenGL ES 实际用于变换顶点的矩阵。栈中 TOS 以下的矩阵处于闲置状态,等待成为新的 TOS。
OpenGL ES 提供了两个方法来操作矩阵栈:
- `GL10.glPushMatrix()`:复制当前 TOS 并将其压入栈中。
- `GL10.glPopMatrix()`:弹出当前 TOS,使栈中其下方的元素成为新的 TOS。
下面是一个简单的示例:
```java
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glTranslate(0,0,-10);
gl.glPushMatrix();
gl.glRotatef(45, 0, 1, 0);
gl.glScalef(1, 2, 1);
gl.glPopMatrix();
```
在这个示例中,我们首先在模型视图矩阵栈中创建一个平移矩阵,然后使用 `glPushMatrix()` 复制该矩阵并压入栈中。接着,我们对栈顶矩阵进行旋转和缩放操作。最后,使用 `glPopMatrix()` 弹出栈顶矩阵,恢复到原始的平移矩阵。
矩阵栈的一个重要用途是记录需要应用到所有对象的变换。例如,我们希望世界中的所有对象在每个坐标轴上偏移 10 个单位,可以这样做:
```java
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glTranslatef(10, 10, 10);
for( MyObject obj: myObjects) {
gl.glPushMatrix();
gl.glTranslatef(obj.x, obj.y, obj.z);
gl.glRotatef(obj.angle, 0, 1, 0);
// render model of object given in model space, e.g., the cube
gl.glPopMatrix();
}
```
#### 矩阵栈操作流程图
```mermaid
graph TD;
A[设置模型视图矩阵为单位矩阵] --> B[平移矩阵操作];
B --> C[复制栈顶矩阵并压入栈中];
C --> D[旋转和缩放操作];
D --> E[弹出栈顶矩阵];
```
#### 利用矩阵栈构建层次系统
层次系统是一种具有父子关系的结构,例如太阳系。太阳是行星的父对象,行星是卫星的父对象。每个子对象的位置相对于其父对象,而不是世界原点。
我们可以使用矩阵栈来构建这样的层次系统。例如,太阳的位置和自转可以通过 `glTranslatef()` 和 `glRotatef()` 来实现。行星绕太阳旋转,同时自身也在自转,卫星绕行星旋转,也有自身的自转。
#### 简单的板条箱太阳系示例
我们创建一个简单的板条箱太阳系示例。在世界坐标系中,中心有一个位于 (0,0,5) 的“太阳”板条箱,周围有一个距离为 3 单位的“行星”板条箱,行星板条箱周围有一个距离为 1 单位的“卫星”板条箱。行星和卫星板条箱分别缩小到 0.2 和 0.1 单位,它们在 x-z 平面绕各自的父对象旋转,并且所有对象绕自身的 y 轴旋转。
#### HierarchicalObject 类的实现
为了表示太阳系中的对象,我们定义了 `HierarchicalObject` 类:
```java
package com.badlogic.androidgames.gl3d;
import java.util.ArrayList;
import java.util.List;
import javax.microedition.khronos.opengles.GL10;
import com.badlogic.androidgames.framework.gl.Vertices3;
public class HierarchicalObject {
public float x, y, z;
public float scale = 1;
public float rotationY, rotationParent;
public boolean hasParent;
public final List<HierarchicalObject> children = new ArrayList<HierarchicalObject>();
public final Vertices3 mesh;
public HierarchicalObject(Vertices3 mesh, boolean hasParent) {
this.mesh = mesh;
this.hasParent = hasParent;
}
public void update(float deltaTime) {
rotationY += 45 * deltaTime;
rotationParent += 20 * deltaTime;
i
```
0
0
复制全文
相关推荐









