基于Android的OpenGL—在 GLSurfaceView上绘制三角形和使用投影和相机视图

时间:2022-06-13 14:39:06

作者:张秀龙


定义三角形

OpenGL 允许我们使用三维坐标来定义物体。在绘制三角形前,我们需要定义它各个点的坐标。我们一般使用数组来存储各个顶点的坐标。

OpenGL ES 默认 [0,0,0] (X,Y,Z) 在GLSurfaceView的中心,[1,1,0]在右上角,[-1,-1,0]在左下角。

 

绘制三角形

在绘制三角形之前,我们必须告诉OpenGL我们正在使用顶点数组。然后我们才使用绘制函数画出三角形。

实验步骤:

1.      添加新的类Triangle

代码如下:

public class Triangle {

public Triangle()
{
float triangleCoords[] = {
// X, Y, Z 这是一个等边三角形
-0.5f, -0.25f, 0,
0.5f, -0.25f, 0,
0.0f, 0.559016994f, 0
};

// 初始化三角形的顶点缓存
ByteBuffer vbb = ByteBuffer.allocateDirect(
// (# of coordinate values * 4 bytes per float)
triangleCoords.length * 4);
vbb.order(ByteOrder.nativeOrder());// 使用设备硬件本身的字节序
triangleVB = vbb.asFloatBuffer(); // 从ByteBuffer中创建一个浮点缓存
triangleVB.put(triangleCoords); // 向浮点缓存中添加顶点坐标
triangleVB.position(0); // 使缓存读第一个坐标
}

public void draw(GL10 gl)
{
gl.glColor4f(0.63671875f, 0.76953125f, 0.22265625f, 0.0f); //设置当前颜色
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, triangleVB);//设置顶点
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);//绘制三角形
}

private FloatBuffer triangleVB;
}


2.      在myGLRenderer类中添加成员privateTriangle mTriangle并在构造函数中初始化。

代码如下:


public myGLRenderer()
{
mTriangle = new Triangle();
}


3.      在myGLRenderer类的onSurfaceCreated()函数最后添加glEnableClientState()方法来启用顶点数组。

代码如下:


@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// TODO Auto-generated method stub
gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
}


4.      在myGLRenderer类的onDrawFrame()函数最后添加三角形绘制方法。

代码如下:


@Override
public void onDrawFrame(GL10 gl) {
// TODO Auto-generated method stub
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
mTriangle.draw(gl);
}


这样,我们便完成了一个平面三角形的绘制。但我们发现这个三角形并不像我们定义的那样是一个等边三角形,这是由于OpenGL总假设我们的屏幕是一个正方形,这样在绘制的时候最终图形会随着屏幕长宽比例的不同而被拉伸。为了得到正确的显示,我们需要将图形投影到正确的位置。这一功能我们在下一节进行实现。


Android设备屏幕通常不是正方形的,而OpenGL总是默认地将正方形坐标系投影到这一设备上,这就导致图形无法按真实比例显示。要解决这一问题,我们可以使用OpenGL 的投影模式和相机视图将图形的坐标进行转换以适应不同的设备显示。

 

实验步骤:

1.      修改myGLRenderer类的onSurfaceCreated()函数来启用GL10.GL_PROJECTION模式,计算屏幕的长宽比并使用这一比例来转换物体的坐标。

代码如下:

@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// TODO Auto-generated method stub
gl.glViewport(0, 0, width, height);

float ratio = (float) width / height;
gl.glMatrixMode(GL10.GL_PROJECTION); // 设置当前矩阵为投影矩阵
gl.glLoadIdentity(); // 重置矩阵为初始值
gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7); // 根据长宽比设置投影矩阵
}


2.      修改myGLRenderer的onDrawFrame()方法,启用MODELVIEW模式,并使用GLU.gluLookAt()来设置视点。

代码如下:

@Override
public void onDrawFrame(GL10 gl) {
// TODO Auto-generated method stub
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

// 设置当前矩阵为模型视图模式
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity(); // reset the matrix to its default state

// 设置视点
GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);

mTriangle.draw(gl);
}

这样,我们绘制的图形比例就总是正确的,不再受设备的影响而被拉伸变形了。