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 平滑着色 ” 这个关键字搜索一下 。这里不再讲解。