OpenglES2.0 for Android:来画个立方体吧

OpenglES2.0 for Android:来画个立方体吧

前言:

前面一直在说OpenglES2.0二维图形的绘制,接下来我们步入三维的世界 ,三维世界远比二维要有趣的多,与此同时复杂性也要高得多,在unity3D中我们可以很容易的就创建

一个立方体,而在OpenglES2.0中这个过程要复杂得多,但是更加有趣 。先来看下我们的整个流程:

摄像机的设置:

想想你的摄像头,它的位置不同,朝向不同,对同一个事物拍摄得到的画面肯定是不同的,Opengl中的摄像头和我们日常生活中的摄像头是一样的道理

(图一)

在Opengl中摄像头包含三部分的信息:

1.  摄像头的位置  ,在三维空间中用 x  y  z 表示

2.  摄像头的镜头的指向,这里即观察的物体的坐标,一般选取物体的center坐标(通过摄像头的位置与观察的物体的坐标可以确定一个向量,这个向量就可以决定观察的方向)

3.  摄像头的UP方向,摄像机顶端的指向

下面的人眼观察物体的图示更容易帮助我们理解:

(图二 )

可以看出摄像机的位置,朝向,UP方向有很多不同的组合,对于不同的组合观察同一物体会得到不同的结果

为了更好地理解,这里给出国外大牛做的一个demo :

我们先做只需要看gluLookAt , eye 就是我们所说的摄像机的位置,center即摄像头的镜头的指向

我们可以改变这些值,来观察一下右上方的图像的变化。

下载地址:http://download.csdn.net/detail/cassiepython/9541794

透视投影

我们观察物体,会有近大远小的效果,透视投影即为了产生这种效果,和美术中 的透视是一个概念

(图三)

其中,视点指摄像机的位置,近平面指距离视点较近的垂直于观察方向的平面,视景体又叫做视锥体为椎台形区域。

透视投影的投影线互不平行,相较于视点,因此,对于同样尺寸的物体,在近处投影出来大,在远处投影出来小,由此产生近大远小的效果。

(图四)

大家可以继续结合上面的小软件修改参数试下来了解透视投影。

立方体顶点坐标

OK,现在让我们开始着手具体的编程,首先来定义立方体的顶点坐标,此时我们肯定不能只是X ,Y了 ,还要加入一个新的参数 Z ,Z和X, Y的范围是一样的,

也是从 -1 到 1 ,我们看向自己的手机屏幕,想象我们的视线穿过两个平面,前面的为 1  后面的为 -1 。

我们接着上一节的工程来做,在shape包下新建一个类 ——Cube.java ,然后创建顶点数据 ,此时 Cube.java 代码如下:

package com.cumt.shape;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import android.content.Context;

public class Cube {

	private FloatBuffer vertexBuffer;
	private Context context;
	//float类型的字节数
	private static final int BYTES_PER_FLOAT = 4;

	static float vertices[] = {
		//前面
    	0,0,1.0f,
    	1.0f,1.0f,1.0f,
    	-1.0f,1.0f,1.0f,
    	0,0,1.0f,
    	-1.0f,1.0f,1.0f,
    	-1.0f,-1.0f,1.0f,
    	0,0,1.0f,
    	-1.0f,-1.0f,1.0f,
    	1.0f,-1.0f,1.0f,
    	0,0,1.0f,
    	1.0f,-1.0f,1.0f,
    	1.0f,1.0f,1.0f,
    	//后面
    	0,0,-1.0f,
    	1.0f,1.0f,-1.0f,
    	1.0f,-1.0f,-1.0f,
    	0,0,-1.0f,
    	1.0f,-1.0f,-1.0f,
    	-1.0f,-1.0f,-1.0f,
    	0,0,-1.0f,
    	-1.0f,-1.0f,-1.0f,
    	-1.0f,1.0f,-1.0f,
    	0,0,-1.0f,
    	-1.0f,1.0f,-1.0f,
    	1.0f,1.0f,-1.0f,
    	//左面
    	-1.0f,0,0,
    	-1.0f,1.0f,1.0f,
    	-1.0f,1.0f,-1.0f,
    	-1.0f,0,0,
    	-1.0f,1.0f,-1.0f,
    	-1.0f,-1.0f,-1.0f,
    	-1.0f,0,0,
    	-1.0f,-1.0f,-1.0f,
    	-1.0f,-1.0f,1.0f,
    	-1.0f,0,0,
    	-1.0f,-1.0f,1.0f,
    	-1.0f,1.0f,1.0f,
    	//右面
    	1.0f,0,0,
    	1.0f,1.0f,1.0f,
    	1.0f,-1.0f,1.0f,
    	1.0f,0,0,
    	1.0f,-1.0f,1.0f,
    	1.0f,-1.0f,-1.0f,
    	1.0f,0,0,
    	1.0f,-1.0f,-1.0f,
    	1.0f,1.0f,-1.0f,
    	1.0f,0,0,
    	1.0f,1.0f,-1.0f,
    	1.0f,1.0f,1.0f,
    	//上面
    	0,1.0f,0,
    	1.0f,1.0f,1.0f,
    	1.0f,1.0f,-1.0f,
    	0,1.0f,0,
    	1.0f,1.0f,-1.0f,
    	-1.0f,1.0f,-1.0f,
    	0,1.0f,0,
    	-1.0f,1.0f,-1.0f,
    	-1.0f,1.0f,1.0f,
    	0,1.0f,0,
    	-1.0f,1.0f,1.0f,
    	1.0f,1.0f,1.0f,
    	//下面
    	0,-1.0f,0,
    	1.0f,-1.0f,1.0f,
    	-1.0f,-1.0f,1.0f,
    	0,-1.0f,0,
    	-1.0f,-1.0f,1.0f,
    	-1.0f,-1.0f,-1.0f,
    	0,-1.0f,0,
    	-1.0f,-1.0f,-1.0f,
    	1.0f,-1.0f,-1.0f,
    	0,-1.0f,0,
    	1.0f,-1.0f,-1.0f,
    	1.0f,-1.0f,1.0f
	};

	public Cube(Context context){
		this.context = context;

		vertexBuffer = ByteBuffer
    			.allocateDirect(vertices.length * BYTES_PER_FLOAT)
    			.order(ByteOrder.nativeOrder())
    			.asFloatBuffer();
    	// 把坐标们加入FloatBuffer中
        vertexBuffer.put(vertices);
        // 设置buffer,从第一个坐标开始读
        vertexBuffer.position(0);
	}
}

我们每个面定义了12个顶点:

每一个面分为4个三角形,每个三角形包含3个点,共12个点(我们打算使用GL_TRIANGLES方式绘制 )

接下来看下我们的着色器代码,顶点着色器我们使用仍然使用上一节的 vertex_shader.glsl  :

uniform mat4 u_Matrix;
attribute vec4 a_Position;  

void main()
{
    gl_Position = u_Matrix * a_Position;
}   

u_Matrix用于我们传入最终的变幻矩阵 (投影矩阵 and 摄像机矩阵)。

然后我们编译链接着色器  此时Cube类代码 (Cube.java ):

package com.cumt.shape;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import com.cumt.openglestwo_test_one.R;
import com.cumt.utils.ShaderHelper;
import com.cumt.utils.TextResourceReader;
import android.content.Context;
import android.opengl.GLES20;

public class Cube {

	private FloatBuffer vertexBuffer;
	private Context context;
	//float类型的字节数
	private static final int BYTES_PER_FLOAT = 4;
	//共有72个顶点坐标,每个面包含12个顶点坐标
	private static final int POSITION_COMPONENT_COUNT = 12*6;
	// 数组中每个顶点的坐标数
    private static final int COORDS_PER_VERTEX = 3;

	private static final String A_POSITION = "a_Position";
	private static final String U_COLOR = "u_Color";
	private static final String U_MATRIX = "u_Matrix";
	private int uMatrixLocation;
	private int uColorLocation;
	private int aPositionLocation;
	private int program;

	static float vertices[] = {
		//前面
    	0,0,1.0f,
    	1.0f,1.0f,1.0f,
    	-1.0f,1.0f,1.0f,
    	0,0,1.0f,
    	-1.0f,1.0f,1.0f,
    	-1.0f,-1.0f,1.0f,
    	0,0,1.0f,
    	-1.0f,-1.0f,1.0f,
    	1.0f,-1.0f,1.0f,
    	0,0,1.0f,
    	1.0f,-1.0f,1.0f,
    	1.0f,1.0f,1.0f,
    	//后面
    	0,0,-1.0f,
    	1.0f,1.0f,-1.0f,
    	1.0f,-1.0f,-1.0f,
    	0,0,-1.0f,
    	1.0f,-1.0f,-1.0f,
    	-1.0f,-1.0f,-1.0f,
    	0,0,-1.0f,
    	-1.0f,-1.0f,-1.0f,
    	-1.0f,1.0f,-1.0f,
    	0,0,-1.0f,
    	-1.0f,1.0f,-1.0f,
    	1.0f,1.0f,-1.0f,
    	//左面
    	-1.0f,0,0,
    	-1.0f,1.0f,1.0f,
    	-1.0f,1.0f,-1.0f,
    	-1.0f,0,0,
    	-1.0f,1.0f,-1.0f,
    	-1.0f,-1.0f,-1.0f,
    	-1.0f,0,0,
    	-1.0f,-1.0f,-1.0f,
    	-1.0f,-1.0f,1.0f,
    	-1.0f,0,0,
    	-1.0f,-1.0f,1.0f,
    	-1.0f,1.0f,1.0f,
    	//右面
    	1.0f,0,0,
    	1.0f,1.0f,1.0f,
    	1.0f,-1.0f,1.0f,
    	1.0f,0,0,
    	1.0f,-1.0f,1.0f,
    	1.0f,-1.0f,-1.0f,
    	1.0f,0,0,
    	1.0f,-1.0f,-1.0f,
    	1.0f,1.0f,-1.0f,
    	1.0f,0,0,
    	1.0f,1.0f,-1.0f,
    	1.0f,1.0f,1.0f,
    	//上面
    	0,1.0f,0,
    	1.0f,1.0f,1.0f,
    	1.0f,1.0f,-1.0f,
    	0,1.0f,0,
    	1.0f,1.0f,-1.0f,
    	-1.0f,1.0f,-1.0f,
    	0,1.0f,0,
    	-1.0f,1.0f,-1.0f,
    	-1.0f,1.0f,1.0f,
    	0,1.0f,0,
    	-1.0f,1.0f,1.0f,
    	1.0f,1.0f,1.0f,
    	//下面
    	0,-1.0f,0,
    	1.0f,-1.0f,1.0f,
    	-1.0f,-1.0f,1.0f,
    	0,-1.0f,0,
    	-1.0f,-1.0f,1.0f,
    	-1.0f,-1.0f,-1.0f,
    	0,-1.0f,0,
    	-1.0f,-1.0f,-1.0f,
    	1.0f,-1.0f,-1.0f,
    	0,-1.0f,0,
    	1.0f,-1.0f,-1.0f,
    	1.0f,-1.0f,1.0f
	};

	public Cube(Context context){
		this.context = context;

		vertexBuffer = ByteBuffer
    			.allocateDirect(vertices.length * BYTES_PER_FLOAT)
    			.order(ByteOrder.nativeOrder())
    			.asFloatBuffer();
    	// 把坐标们加入FloatBuffer中
        vertexBuffer.put(vertices);
        // 设置buffer,从第一个坐标开始读
        vertexBuffer.position(0);

        getProgram();

        uColorLocation = GLES20.glGetUniformLocation(program, U_COLOR);
		aPositionLocation = GLES20.glGetAttribLocation(program, A_POSITION);
		uMatrixLocation = GLES20.glGetUniformLocation(program, U_MATRIX);

		//---------传入顶点数据数据
		GLES20.glVertexAttribPointer(aPositionLocation, COORDS_PER_VERTEX,
				GLES20.GL_FLOAT, false, 0, vertexBuffer);
		GLES20.glEnableVertexAttribArray(aPositionLocation);
	}

	//获取program
    private void getProgram(){
    	//获取顶点着色器文本
    	String vertexShaderSource = TextResourceReader
				.readTextFileFromResource(context, R.raw.vertex_shader);
    	//获取片段着色器文本
		String fragmentShaderSource = TextResourceReader
				.readTextFileFromResource(context, R.raw.simple_fragment_shader);
		//获取program的id
		program = ShaderHelper.buildProgram(vertexShaderSource, fragmentShaderSource);
		GLES20.glUseProgram(program);
    }
}

接下来我们应该定义实现draw方法,在此之前先让我们解决投影矩阵的问题

设置相机和投影

我们来定义一个工具类来获得最终的变幻矩阵 ,我们需要使用两个函数 :

这两个函数的参数的含义与上面我们讲解相机和投影时的那些参数是对应的,大家可以回到前面再看下,这里不再重复。

我们在utils包中定义工具类——MatrixState  然后看我的工具类的代码 (MatrixState.java):

package com.cumt.utils;

import android.opengl.Matrix;

//存储系统矩阵状态的类
public class MatrixState {
	private static float[] mProjMatrix = new float[16];// 4x4矩阵 存储投影矩阵
	private static float[] mVMatrix = new float[16];// 摄像机位置朝向9参数矩阵

	// 设置摄像机
	public static void setCamera(float cx, // 摄像机位置x
			float cy, // 摄像机位置y
			float cz, // 摄像机位置z
			float tx, // 摄像机目标点x
			float ty, // 摄像机目标点y
			float tz, // 摄像机目标点z
			float upx, // 摄像机UP向量X分量
			float upy, // 摄像机UP向量Y分量
			float upz // 摄像机UP向量Z分量
	) {
		Matrix.setLookAtM(mVMatrix, 0, cx, cy, cz, tx, ty, tz, upx, upy, upz);
	}

	// 设置透视投影参数
	public static void setProjectFrustum(float left, // near面的left
			float right, // near面的right
			float bottom, // near面的bottom
			float top, // near面的top
			float near, // near面距离
			float far // far面距离
	) {
		Matrix.frustumM(mProjMatrix, 0, left, right, bottom, top, near, far);
	}

	// 获取具体物体的总变换矩阵
	static float[] mMVPMatrix = new float[16];

	public static float[] getFinalMatrix() {
		Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
		return mMVPMatrix;
	}
}

这个工具类中有三个方法,我们在渲染类 (MyRender.java)中的 onSurfaceChanged中调用前两个方法设置摄像机和投影矩阵,在Cube的draw方法中就可以通过第三个方法

getFinalMatrix 来获取最终的变换矩阵 然后传入着色器程序。 (我们注意multiplyMM函数,这个函数工作是将我们的投影矩阵和摄像机矩阵进行矩阵的乘法运算,存储在

mMVPMatrix中,这个mMVPMatrix即为我们最终的变换矩阵,即我们需要传入着色器程序的矩阵。

OK,看下完整的 Cube.java 类 和 MyRender.java  :

package com.cumt.shape;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import com.cumt.openglestwo_test_one.R;
import com.cumt.utils.MatrixState;
import com.cumt.utils.ShaderHelper;
import com.cumt.utils.TextResourceReader;
import android.content.Context;
import android.opengl.GLES20;

public class Cube {

	private FloatBuffer vertexBuffer;
	private Context context;
	//float类型的字节数
	private static final int BYTES_PER_FLOAT = 4;
	//共有72个顶点坐标,每个面包含12个顶点坐标
	private static final int POSITION_COMPONENT_COUNT = 12*6;
	// 数组中每个顶点的坐标数
    private static final int COORDS_PER_VERTEX = 3;

	private static final String A_POSITION = "a_Position";
	private static final String U_COLOR = "u_Color";
	private static final String U_MATRIX = "u_Matrix";
	private int uMatrixLocation;
	private int uColorLocation;
	private int aPositionLocation;
	private int program;

	static float vertices[] = {
		//前面
    	0,0,1.0f,
    	1.0f,1.0f,1.0f,
    	-1.0f,1.0f,1.0f,
    	0,0,1.0f,
    	-1.0f,1.0f,1.0f,
    	-1.0f,-1.0f,1.0f,
    	0,0,1.0f,
    	-1.0f,-1.0f,1.0f,
    	1.0f,-1.0f,1.0f,
    	0,0,1.0f,
    	1.0f,-1.0f,1.0f,
    	1.0f,1.0f,1.0f,
    	//后面
    	0,0,-1.0f,
    	1.0f,1.0f,-1.0f,
    	1.0f,-1.0f,-1.0f,
    	0,0,-1.0f,
    	1.0f,-1.0f,-1.0f,
    	-1.0f,-1.0f,-1.0f,
    	0,0,-1.0f,
    	-1.0f,-1.0f,-1.0f,
    	-1.0f,1.0f,-1.0f,
    	0,0,-1.0f,
    	-1.0f,1.0f,-1.0f,
    	1.0f,1.0f,-1.0f,
    	//左面
    	-1.0f,0,0,
    	-1.0f,1.0f,1.0f,
    	-1.0f,1.0f,-1.0f,
    	-1.0f,0,0,
    	-1.0f,1.0f,-1.0f,
    	-1.0f,-1.0f,-1.0f,
    	-1.0f,0,0,
    	-1.0f,-1.0f,-1.0f,
    	-1.0f,-1.0f,1.0f,
    	-1.0f,0,0,
    	-1.0f,-1.0f,1.0f,
    	-1.0f,1.0f,1.0f,
    	//右面
    	1.0f,0,0,
    	1.0f,1.0f,1.0f,
    	1.0f,-1.0f,1.0f,
    	1.0f,0,0,
    	1.0f,-1.0f,1.0f,
    	1.0f,-1.0f,-1.0f,
    	1.0f,0,0,
    	1.0f,-1.0f,-1.0f,
    	1.0f,1.0f,-1.0f,
    	1.0f,0,0,
    	1.0f,1.0f,-1.0f,
    	1.0f,1.0f,1.0f,
    	//上面
    	0,1.0f,0,
    	1.0f,1.0f,1.0f,
    	1.0f,1.0f,-1.0f,
    	0,1.0f,0,
    	1.0f,1.0f,-1.0f,
    	-1.0f,1.0f,-1.0f,
    	0,1.0f,0,
    	-1.0f,1.0f,-1.0f,
    	-1.0f,1.0f,1.0f,
    	0,1.0f,0,
    	-1.0f,1.0f,1.0f,
    	1.0f,1.0f,1.0f,
    	//下面
    	0,-1.0f,0,
    	1.0f,-1.0f,1.0f,
    	-1.0f,-1.0f,1.0f,
    	0,-1.0f,0,
    	-1.0f,-1.0f,1.0f,
    	-1.0f,-1.0f,-1.0f,
    	0,-1.0f,0,
    	-1.0f,-1.0f,-1.0f,
    	1.0f,-1.0f,-1.0f,
    	0,-1.0f,0,
    	1.0f,-1.0f,-1.0f,
    	1.0f,-1.0f,1.0f
	};

	public Cube(Context context){
		this.context = context;

		vertexBuffer = ByteBuffer
    			.allocateDirect(vertices.length * BYTES_PER_FLOAT)
    			.order(ByteOrder.nativeOrder())
    			.asFloatBuffer();
    	// 把坐标们加入FloatBuffer中
        vertexBuffer.put(vertices);
        // 设置buffer,从第一个坐标开始读
        vertexBuffer.position(0);

        getProgram();

        uColorLocation = GLES20.glGetUniformLocation(program, U_COLOR);
		aPositionLocation = GLES20.glGetAttribLocation(program, A_POSITION);
		uMatrixLocation = GLES20.glGetUniformLocation(program, U_MATRIX);

		//---------传入顶点数据数据
		GLES20.glVertexAttribPointer(aPositionLocation, COORDS_PER_VERTEX,
				GLES20.GL_FLOAT, false, 0, vertexBuffer);
		GLES20.glEnableVertexAttribArray(aPositionLocation);
	}

	//获取program
    private void getProgram(){
    	//获取顶点着色器文本
    	String vertexShaderSource = TextResourceReader
				.readTextFileFromResource(context, R.raw.vertex_shader);
    	//获取片段着色器文本
		String fragmentShaderSource = TextResourceReader
				.readTextFileFromResource(context, R.raw.simple_fragment_shader);
		//获取program的id
		program = ShaderHelper.buildProgram(vertexShaderSource, fragmentShaderSource);
		GLES20.glUseProgram(program);
    }

    public void draw(){
		GLES20.glUniform4f(uColorLocation, 1.0f, 0.0f, 0.0f, 0.0f);
		GLES20.glUniformMatrix4fv(uMatrixLocation, 1, false, MatrixState.getFinalMatrix(),0);
		GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, POSITION_COMPONENT_COUNT);
    }
}
package com.cumt.render;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import com.cumt.utils.MatrixState;
import com.cumt.shape.Cube;
import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView.Renderer;
import android.util.Log;
import static android.opengl.GLES20.glClear;
import static android.opengl.GLES20.glClearColor;
import static android.opengl.GLES20.glViewport;

public class MyRender implements Renderer {

	private Context context;

	public MyRender(Context context){
		this.context = context;
	}

//	Circle circle;
	Cube cube;

	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		Log.w("MyRender","onSurfaceCreated");
		//设置屏幕背景色RGBA
        glClearColor(0.5f,0.5f,0.5f, 1.0f);
//		//打开深度检测
        GLES20.glEnable(GLES20.GL_DEPTH_TEST);
//        //打开背面剪裁
        GLES20.glEnable(GLES20.GL_CULL_FACE);
//		circle = new Circle(context);
		cube = new Cube(context);
	}

	public void onSurfaceChanged(GL10 gl, int width, int height) {
		glViewport(0,0,width,height);
		float ratio = (float) width / height;
		//设置投影矩阵
//		circle.projectionMatrix(width, height);
		// 调用此方法计算产生透视投影矩阵
        MatrixState.setProjectFrustum(-ratio,ratio, -1, 1, 20, 100);
		// 调用此方法产生摄像机9参数位置矩阵
		MatrixState.setCamera(-16f, 8f, 45, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
	}

	public void onDrawFrame(GL10 gl) {
//		glClear(GL_COLOR_BUFFER_BIT);
		//清除深度缓冲与颜色缓冲
        glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
//      circle.draw();
		cube.draw();
	}
}

然后我们回到MainActivity 设置屏幕显示方式为竖屏,全屏 ,此时MainActivity代码如下 (MainActivity.java):

package com.cumt.openglestwo_test_one;

import com.cumt.render.MyRender;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;

public class MainActivity extends Activity {

	private GLSurfaceView glSurfaceView;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		// 设置为全屏
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
				WindowManager.LayoutParams.FLAG_FULLSCREEN);
		// 设置为横屏模式
		setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
		glSurfaceView = new GLSurfaceView(this);
		//OpenGL ES 2.0
		glSurfaceView.setEGLContextClientVersion(2);
		glSurfaceView.setRenderer(new MyRender(this));
		// 设置渲染模式为主动渲染
		glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
		setContentView(glSurfaceView);
	}

	@Override
	protected void onPause() {
		// TODO Auto-generated method stub
		super.onPause();
		glSurfaceView.onPause();
	}

	@Override
	protected void onResume() {
		// TODO Auto-generated method stub
		super.onResume();
		glSurfaceView.onResume();
	}
}

快来运行一下 :

一个全红的立方体诞生了~~(大家还可以尝试用线段的绘制方式来绘制一个  " 空心 "  的立方体),可是这样看起来根本看不出来它的一些边 ,我们来想办法给它美化一下,在这节先不考虑纹理贴图,我们把每个面设置成不同的颜色。

OK,先来看下我们接下来的过程 :

在Cube类中定义顶点坐标,并转化为FloatBuffer类型 ,此时代码如下 (Cube.java ) :

package com.cumt.shape;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import com.cumt.openglestwo_test_one.R;
import com.cumt.utils.MatrixState;
import com.cumt.utils.ShaderHelper;
import com.cumt.utils.TextResourceReader;
import android.content.Context;
import android.opengl.GLES20;

public class Cube {

	//顶点坐标
	private FloatBuffer vertexBuffer;
	//颜色坐标
	private FloatBuffer colorBuffer;
	private Context context;
	//float类型的字节数
	private static final int BYTES_PER_FLOAT = 4;
	//共有72个顶点坐标,每个面包含12个顶点坐标
	private static final int POSITION_COMPONENT_COUNT = 12*6;
	// 数组中每个顶点的坐标数
    private static final int COORDS_PER_VERTEX = 3;

	private static final String A_POSITION = "a_Position";
	private static final String U_COLOR = "u_Color";
	private static final String U_MATRIX = "u_Matrix";
	private int uMatrixLocation;
	private int uColorLocation;
	private int aPositionLocation;
	private int program;

	static float vertices[] = {
		//前面
    	0,0,1.0f,
    	1.0f,1.0f,1.0f,
    	-1.0f,1.0f,1.0f,
    	0,0,1.0f,
    	-1.0f,1.0f,1.0f,
    	-1.0f,-1.0f,1.0f,
    	0,0,1.0f,
    	-1.0f,-1.0f,1.0f,
    	1.0f,-1.0f,1.0f,
    	0,0,1.0f,
    	1.0f,-1.0f,1.0f,
    	1.0f,1.0f,1.0f,
    	//后面
    	0,0,-1.0f,
    	1.0f,1.0f,-1.0f,
    	1.0f,-1.0f,-1.0f,
    	0,0,-1.0f,
    	1.0f,-1.0f,-1.0f,
    	-1.0f,-1.0f,-1.0f,
    	0,0,-1.0f,
    	-1.0f,-1.0f,-1.0f,
    	-1.0f,1.0f,-1.0f,
    	0,0,-1.0f,
    	-1.0f,1.0f,-1.0f,
    	1.0f,1.0f,-1.0f,
    	//左面
    	-1.0f,0,0,
    	-1.0f,1.0f,1.0f,
    	-1.0f,1.0f,-1.0f,
    	-1.0f,0,0,
    	-1.0f,1.0f,-1.0f,
    	-1.0f,-1.0f,-1.0f,
    	-1.0f,0,0,
    	-1.0f,-1.0f,-1.0f,
    	-1.0f,-1.0f,1.0f,
    	-1.0f,0,0,
    	-1.0f,-1.0f,1.0f,
    	-1.0f,1.0f,1.0f,
    	//右面
    	1.0f,0,0,
    	1.0f,1.0f,1.0f,
    	1.0f,-1.0f,1.0f,
    	1.0f,0,0,
    	1.0f,-1.0f,1.0f,
    	1.0f,-1.0f,-1.0f,
    	1.0f,0,0,
    	1.0f,-1.0f,-1.0f,
    	1.0f,1.0f,-1.0f,
    	1.0f,0,0,
    	1.0f,1.0f,-1.0f,
    	1.0f,1.0f,1.0f,
    	//上面
    	0,1.0f,0,
    	1.0f,1.0f,1.0f,
    	1.0f,1.0f,-1.0f,
    	0,1.0f,0,
    	1.0f,1.0f,-1.0f,
    	-1.0f,1.0f,-1.0f,
    	0,1.0f,0,
    	-1.0f,1.0f,-1.0f,
    	-1.0f,1.0f,1.0f,
    	0,1.0f,0,
    	-1.0f,1.0f,1.0f,
    	1.0f,1.0f,1.0f,
    	//下面
    	0,-1.0f,0,
    	1.0f,-1.0f,1.0f,
    	-1.0f,-1.0f,1.0f,
    	0,-1.0f,0,
    	-1.0f,-1.0f,1.0f,
    	-1.0f,-1.0f,-1.0f,
    	0,-1.0f,0,
    	-1.0f,-1.0f,-1.0f,
    	1.0f,-1.0f,-1.0f,
    	0,-1.0f,0,
    	1.0f,-1.0f,-1.0f,
    	1.0f,-1.0f,1.0f
	};

	//顶点颜色值数组,每个顶点4个色彩值RGBA
    static float colors[]=new float[]{
    		//前面
    		1,1,1,0,//中间为白色
    		1,0,0,0,
    		1,0,0,0,
    		1,1,1,0,//中间为白色
    		1,0,0,0,
    		1,0,0,0,
    		1,1,1,0,//中间为白色
    		1,0,0,0,
    		1,0,0,0,
    		1,1,1,0,//中间为白色
    		1,0,0,0,
    		1,0,0,0,
    		//后面
    		1,1,1,0,//中间为白色
    		0,0,1,0,
    		0,0,1,0,
    		1,1,1,0,//中间为白色
    		0,0,1,0,
    		0,0,1,0,
    		1,1,1,0,//中间为白色
    		0,0,1,0,
    		0,0,1,0,
    		1,1,1,0,//中间为白色
    		0,0,1,0,
    		0,0,1,0,
    		//左面
    		1,1,1,0,//中间为白色
    		1,0,1,0,
    		1,0,1,0,
    		1,1,1,0,//中间为白色
    		1,0,1,0,
    		1,0,1,0,
    		1,1,1,0,//中间为白色
    		1,0,1,0,
    		1,0,1,0,
    		1,1,1,0,//中间为白色
    		1,0,1,0,
    		1,0,1,0,
    		//右面
    		1,1,1,0,//中间为白色
    		1,1,0,0,
    		1,1,0,0,
    		1,1,1,0,//中间为白色
    		1,1,0,0,
    		1,1,0,0,
    		1,1,1,0,//中间为白色
    		1,1,0,0,
    		1,1,0,0,
    		1,1,1,0,//中间为白色
    		1,1,0,0,
    		1,1,0,0,
    		//上面
    		1,1,1,0,//中间为白色
    		0,1,0,0,
    		0,1,0,0,
    		1,1,1,0,//中间为白色
    		0,1,0,0,
    		0,1,0,0,
    		1,1,1,0,//中间为白色
    		0,1,0,0,
    		0,1,0,0,
    		1,1,1,0,//中间为白色
    		0,1,0,0,
    		0,1,0,0,
    		//下面
    		1,1,1,0,//中间为白色
    		0,1,1,0,
    		0,1,1,0,
    		1,1,1,0,//中间为白色
    		0,1,1,0,
    		0,1,1,0,
    		1,1,1,0,//中间为白色
    		0,1,1,0,
    		0,1,1,0,
    		1,1,1,0,//中间为白色
    		0,1,1,0,
    		0,1,1,0,
    };

	public Cube(Context context){
		this.context = context;

		vertexBuffer = ByteBuffer
    			.allocateDirect(vertices.length * BYTES_PER_FLOAT)
    			.order(ByteOrder.nativeOrder())
    			.asFloatBuffer();
    	// 把坐标们加入FloatBuffer中
        vertexBuffer.put(vertices);
        // 设置buffer,从第一个坐标开始读
        vertexBuffer.position(0);

        //颜色buffer
        colorBuffer = ByteBuffer
    			.allocateDirect(colors.length * BYTES_PER_FLOAT)
    			.order(ByteOrder.nativeOrder())
    			.asFloatBuffer();
        colorBuffer.put(colors);
        colorBuffer.position(0);

        getProgram();

        uColorLocation = GLES20.glGetUniformLocation(program, U_COLOR);
		aPositionLocation = GLES20.glGetAttribLocation(program, A_POSITION);
		uMatrixLocation = GLES20.glGetUniformLocation(program, U_MATRIX);

		//---------传入顶点数据数据
		GLES20.glVertexAttribPointer(aPositionLocation, COORDS_PER_VERTEX,
				GLES20.GL_FLOAT, false, 0, vertexBuffer);
		GLES20.glEnableVertexAttribArray(aPositionLocation);
	}

	//获取program
    private void getProgram(){
    	//获取顶点着色器文本
    	String vertexShaderSource = TextResourceReader
				.readTextFileFromResource(context, R.raw.vertex_shader);
    	//获取片段着色器文本
		String fragmentShaderSource = TextResourceReader
				.readTextFileFromResource(context, R.raw.simple_fragment_shader);
		//获取program的id
		program = ShaderHelper.buildProgram(vertexShaderSource, fragmentShaderSource);
		GLES20.glUseProgram(program);
    }

    public void draw(){
		GLES20.glUniform4f(uColorLocation, 1.0f, 0.0f, 0.0f, 0.0f);
		GLES20.glUniformMatrix4fv(uMatrixLocation, 1, false, MatrixState.getFinalMatrix(),0);
		GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, POSITION_COMPONENT_COUNT);
    }
}

创建新的顶点着色器和片段着色器程序  (vertex_shader_cube.glsl  和 fragment_shader_cube.glsl ):

//vertex_shader_cube.glsl
uniform mat4 u_Matrix;
attribute vec4 a_Position;
attribute vec4 a_Color;    //顶点颜色
varying  vec4 v_Color;  //用于传递给片元着色器的变量

void main()
{
    gl_Position = u_Matrix * a_Position;
    v_Color = a_Color;//将接收的颜色传递给片元着色器
} 
//fragment_shader_cube.glsl
precision mediump float;
varying vec4 v_Color; //接收从顶点着色器过来的参数  							

void main()
{
    gl_FragColor = v_Color;
}

去点原来的引入颜色的相关代码,加入当前要传递的颜色相关的代码,此时Cube类如下 (Cube.java ) :

package com.cumt.shape;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import com.cumt.openglestwo_test_one.R;
import com.cumt.utils.MatrixState;
import com.cumt.utils.ShaderHelper;
import com.cumt.utils.TextResourceReader;
import android.content.Context;
import android.opengl.GLES20;

public class Cube {

	//顶点坐标
	private FloatBuffer vertexBuffer;
	//颜色坐标
	private FloatBuffer colorBuffer;
	private Context context;
	//float类型的字节数
	private static final int BYTES_PER_FLOAT = 4;
	//共有72个顶点坐标,每个面包含12个顶点坐标
	private static final int POSITION_COMPONENT_COUNT = 12*6;
	// 数组中每个顶点的坐标数
    private static final int COORDS_PER_VERTEX = 3;
    // 颜色数组中每个颜色的值数
    private static final int COORDS_PER_COLOR = 4;

	private static final String A_POSITION = "a_Position";
	private static final String A_COLOR = "a_Color";
	private static final String U_MATRIX = "u_Matrix";
	private int uMatrixLocation;
	private int aColorLocation;
	private int aPositionLocation;
	private int program;

	static float vertices[] = {
		//前面
    	0,0,1.0f,
    	1.0f,1.0f,1.0f,
    	-1.0f,1.0f,1.0f,
    	0,0,1.0f,
    	-1.0f,1.0f,1.0f,
    	-1.0f,-1.0f,1.0f,
    	0,0,1.0f,
    	-1.0f,-1.0f,1.0f,
    	1.0f,-1.0f,1.0f,
    	0,0,1.0f,
    	1.0f,-1.0f,1.0f,
    	1.0f,1.0f,1.0f,
    	//后面
    	0,0,-1.0f,
    	1.0f,1.0f,-1.0f,
    	1.0f,-1.0f,-1.0f,
    	0,0,-1.0f,
    	1.0f,-1.0f,-1.0f,
    	-1.0f,-1.0f,-1.0f,
    	0,0,-1.0f,
    	-1.0f,-1.0f,-1.0f,
    	-1.0f,1.0f,-1.0f,
    	0,0,-1.0f,
    	-1.0f,1.0f,-1.0f,
    	1.0f,1.0f,-1.0f,
    	//左面
    	-1.0f,0,0,
    	-1.0f,1.0f,1.0f,
    	-1.0f,1.0f,-1.0f,
    	-1.0f,0,0,
    	-1.0f,1.0f,-1.0f,
    	-1.0f,-1.0f,-1.0f,
    	-1.0f,0,0,
    	-1.0f,-1.0f,-1.0f,
    	-1.0f,-1.0f,1.0f,
    	-1.0f,0,0,
    	-1.0f,-1.0f,1.0f,
    	-1.0f,1.0f,1.0f,
    	//右面
    	1.0f,0,0,
    	1.0f,1.0f,1.0f,
    	1.0f,-1.0f,1.0f,
    	1.0f,0,0,
    	1.0f,-1.0f,1.0f,
    	1.0f,-1.0f,-1.0f,
    	1.0f,0,0,
    	1.0f,-1.0f,-1.0f,
    	1.0f,1.0f,-1.0f,
    	1.0f,0,0,
    	1.0f,1.0f,-1.0f,
    	1.0f,1.0f,1.0f,
    	//上面
    	0,1.0f,0,
    	1.0f,1.0f,1.0f,
    	1.0f,1.0f,-1.0f,
    	0,1.0f,0,
    	1.0f,1.0f,-1.0f,
    	-1.0f,1.0f,-1.0f,
    	0,1.0f,0,
    	-1.0f,1.0f,-1.0f,
    	-1.0f,1.0f,1.0f,
    	0,1.0f,0,
    	-1.0f,1.0f,1.0f,
    	1.0f,1.0f,1.0f,
    	//下面
    	0,-1.0f,0,
    	1.0f,-1.0f,1.0f,
    	-1.0f,-1.0f,1.0f,
    	0,-1.0f,0,
    	-1.0f,-1.0f,1.0f,
    	-1.0f,-1.0f,-1.0f,
    	0,-1.0f,0,
    	-1.0f,-1.0f,-1.0f,
    	1.0f,-1.0f,-1.0f,
    	0,-1.0f,0,
    	1.0f,-1.0f,-1.0f,
    	1.0f,-1.0f,1.0f
	};

	//顶点颜色值数组,每个顶点4个色彩值RGBA
    static float colors[]=new float[]{
    		//前面
    		1,1,1,0,//中间为白色
    		1,0,0,0,
    		1,0,0,0,
    		1,1,1,0,//中间为白色
    		1,0,0,0,
    		1,0,0,0,
    		1,1,1,0,//中间为白色
    		1,0,0,0,
    		1,0,0,0,
    		1,1,1,0,//中间为白色
    		1,0,0,0,
    		1,0,0,0,
    		//后面
    		1,1,1,0,//中间为白色
    		0,0,1,0,
    		0,0,1,0,
    		1,1,1,0,//中间为白色
    		0,0,1,0,
    		0,0,1,0,
    		1,1,1,0,//中间为白色
    		0,0,1,0,
    		0,0,1,0,
    		1,1,1,0,//中间为白色
    		0,0,1,0,
    		0,0,1,0,
    		//左面
    		1,1,1,0,//中间为白色
    		1,0,1,0,
    		1,0,1,0,
    		1,1,1,0,//中间为白色
    		1,0,1,0,
    		1,0,1,0,
    		1,1,1,0,//中间为白色
    		1,0,1,0,
    		1,0,1,0,
    		1,1,1,0,//中间为白色
    		1,0,1,0,
    		1,0,1,0,
    		//右面
    		1,1,1,0,//中间为白色
    		1,1,0,0,
    		1,1,0,0,
    		1,1,1,0,//中间为白色
    		1,1,0,0,
    		1,1,0,0,
    		1,1,1,0,//中间为白色
    		1,1,0,0,
    		1,1,0,0,
    		1,1,1,0,//中间为白色
    		1,1,0,0,
    		1,1,0,0,
    		//上面
    		1,1,1,0,//中间为白色
    		0,1,0,0,
    		0,1,0,0,
    		1,1,1,0,//中间为白色
    		0,1,0,0,
    		0,1,0,0,
    		1,1,1,0,//中间为白色
    		0,1,0,0,
    		0,1,0,0,
    		1,1,1,0,//中间为白色
    		0,1,0,0,
    		0,1,0,0,
    		//下面
    		1,1,1,0,//中间为白色
    		0,1,1,0,
    		0,1,1,0,
    		1,1,1,0,//中间为白色
    		0,1,1,0,
    		0,1,1,0,
    		1,1,1,0,//中间为白色
    		0,1,1,0,
    		0,1,1,0,
    		1,1,1,0,//中间为白色
    		0,1,1,0,
    		0,1,1,0,
    };

	public Cube(Context context){
		this.context = context;

		vertexBuffer = ByteBuffer
    			.allocateDirect(vertices.length * BYTES_PER_FLOAT)
    			.order(ByteOrder.nativeOrder())
    			.asFloatBuffer();
    	// 把坐标们加入FloatBuffer中
        vertexBuffer.put(vertices);
        // 设置buffer,从第一个坐标开始读
        vertexBuffer.position(0);

        //颜色buffer
        colorBuffer = ByteBuffer
    			.allocateDirect(colors.length * BYTES_PER_FLOAT)
    			.order(ByteOrder.nativeOrder())
    			.asFloatBuffer();
        colorBuffer.put(colors);
        colorBuffer.position(0);

        getProgram();

        aColorLocation = GLES20.glGetAttribLocation(program, A_COLOR);
		aPositionLocation = GLES20.glGetAttribLocation(program, A_POSITION);
		uMatrixLocation = GLES20.glGetUniformLocation(program, U_MATRIX);

		//---------传入顶点数据数据
		GLES20.glVertexAttribPointer(aPositionLocation, COORDS_PER_VERTEX,
				GLES20.GL_FLOAT, false, 0, vertexBuffer);
		GLES20.glEnableVertexAttribArray(aPositionLocation);
		//---------传入颜色数据
		GLES20.glVertexAttribPointer(aColorLocation, COORDS_PER_COLOR,
				GLES20.GL_FLOAT, false, 0, colorBuffer);
		GLES20.glEnableVertexAttribArray(aColorLocation);
	}

	//获取program
    private void getProgram(){
    	//获取顶点着色器文本
    	String vertexShaderSource = TextResourceReader
				.readTextFileFromResource(context, R.raw.vertex_shader_cube);
    	//获取片段着色器文本
		String fragmentShaderSource = TextResourceReader
				.readTextFileFromResource(context, R.raw.fragment_shader_cube);
		//获取program的id
		program = ShaderHelper.buildProgram(vertexShaderSource, fragmentShaderSource);
		GLES20.glUseProgram(program);
    }

    public void draw(){
		GLES20.glUniformMatrix4fv(uMatrixLocation, 1, false, MatrixState.getFinalMatrix(),0);
		GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, POSITION_COMPONENT_COUNT);
    }
}

运行一下看看结果 :

哈 现在我们的立方体就完成了 ,是不是很漂亮 。 关于颜色设置,这里用到了OpenglEs 的平滑着色 ,如果有不明白的

可以去按照 “ opengl es 平滑着色 ” 这个关键字搜索一下 。这里不再讲解。

时间: 2024-12-21 12:37:24

OpenglES2.0 for Android:来画个立方体吧的相关文章

OpenglES2.0 for Android:来画个球吧

OpenglES2.0 for Android:来画个球吧 理解球坐标系 首先看下球的坐标系 ,如图 : (图来自百度百科 ) 设球上有一点 A ,球心为O ,OA在 xOy上的投影与X轴夹角为 φ (范围为 0 到360 ,单位 :度), OA在与Z的夹角为 θ (范围为 0 到 180  ,单位:度 ),球的半径为r,则有 ; r * sin θ = y / sin φ    r * sinθ  = x / cos φ   z = r * cos θ 由此可得 X,Y,Z坐标 我们前面已经知

OpenglES2.0 for Android:来画个三角形吧

OpenglES2.0 for Android:来画个三角形吧 先看看我们的整个流程: 理解坐标系: 左侧是Opengl默认的坐标系,右边是典型的android设备屏幕的坐标系.左侧的瘦瘦的三角形映射到android屏幕上就变成了胖胖的三角形(屏幕横向的时候),我们可以使用 camera和投影解决这个问题,具体怎么解决这里就先不累述了.这里我们只需要知道屏幕的左上角是(-1.0,1.0)横向向右为X轴正向,纵向向下为Y轴 负向,其范围都是从 -1到 +1. 定义三角形顶点: 我们在第一个andr

OpenglES2.0 for Android:来画个矩形吧

OpenglES2.0 for Android:来画个矩形吧 上一节中我们绘制了一个三角形,我们在上一节的基础上来完成矩形的绘制 . OK,开始动手做吧,首先在上一节的项目中的shape目录下新建一个类--Square (Square.java),然后定义矩形的四个顶点的坐标,此时代码如下(Square.java): <span style="font-size:14px;">package com.cumt.shape; public class Square { //f

OpenglES2.0 for Android:再谈纹理映射

OpenglES2.0 for Android:再谈纹理映射 前言 上一节我们实现了一个简单的纹理映射的例子--一个简单的贴图,这节我们来做一些稍微复杂一点的例子,最后再给我们前面的立方体做一个纹理. 纹理拉伸 重复拉伸方式 这种是经常使用的一张纹理拉伸方式,常用于绘制一些重复的元素,比如我们在游戏绘制一幅方格式的地图时.使用重复拉伸方式使得纹理能够根据目标平 面的大小自动重复,这样既不会失去纹理图的效果,也可以节省内存.如下图所示: 实现起来很简单,我们回到上节的项目,找到我们纹理的工具类Te

OpenglES2.0 for Android:各种变换来一波

OpenglES2.0 for Android:各种变换来一波 监听屏幕事件 在进行各种变换之前,我们先来了解一下如何监听屏幕的事件.我们下面的变换都需要用立方体来演示,所以我们继续使用上一节的绘制立方体的内容 首先新建一个项目 OpengESChange ,将上一节中关于绘制立方体的代码复制过来 .在前面我们一直在使用 android.opengl.GLSurfaceView 在第一篇中我们已经知道了这个类的作用,为了监听屏幕事件,我们创建一个类继承自该类,重写其onTouchEvent方法.

OpenglES2.0 for Android:纹理映射

OpenglES2.0 for Android:纹理映射 前言 纹理映射又叫做纹理贴图,是将纹理空间中的纹理像素映射到屏幕空间中的像素的过程.就是把一幅图像贴到三维物体的表面上来增强真实感, 可以和光照计算.图像混合等技术结合起来形成许多非常漂亮的效果 (百度百科).简单来说,纹理就是一个图形或者照片,我们可以将它们 加载到Opengl中用以美化我们绘制的物体. 前期准备 我们现在准备实现这样一个功能:将一张图片贴到一个正方形中 .我们在以前画矩形的那节代码的基础上进行实现纹理贴图.这里我们新建

Android上使用OpenglES2.0遇到的一点问题

按照教程开发OpenglES2.0应用,遇到Logcat报错“Called unimplemented OpenGL ES API” 在论坛和stackoverflow上找到了答案. 1.manifest里面加上 <uses-feature android:glEsVersion="0x00020000" android:required="true" /> 2.surfaceView要设置 mGLSurfaceView.setEGLContextCli

android gridview画分割线

dongyangzhang android gridview画分割线,如图: 1.先上图: 2.具体实现代码: public class LineGridView extends GridView { public LineGridView(Context context) { super(context); // TODO Auto-generated constructor stub } public LineGridView(Context context, AttributeSet at

windows下Qt5.1.0配置android环境搭建 good

1.首先下载好需要配置的软件: 1>Qt 5.1.0 for Android (Windows 32-bit, 716 MB)(Info)下载地址: http://qt-project.org/downloads 2>android SDK tools (只下载tools即可)下载地址:http://developer.android.com/sdk/index.html android SDK 离线安装包(已是最新sdk 2013/7/26)下载地址:http://www.wuleba.co