本节我们继续来看一下《OPENGL ES 3.0编程指南 原书第2版(中文版)》书中第8章的内容,PDF下载地址:OPENGL ES 3.0编程指南 原书第2版(中文版),代码下载地址:Opengl ES Source Code。该书中好像没有对Demo的代码进行讲解,全书的重点全部都集中在Opengl ES本身,而实现出来的实例代码讲解的很少,作为初学者,理解起来确实比较费劲,这也是我为什么推荐大家先学习2.0的原因,2.0的那本书对实现出来的Demo的代码全部进行了讲解,我们就知道要实现一个功能,需要什么样的步骤,每一步是什么意思,这样比较清晰。

还是来看一下本节最终实现的效果,效果图如下,是一个纯色的立方体,在不断的旋转:

我们对照代码来看一下,先来看一下SimpleVertexShaderRenderer类,源码如下:

public class SimpleVertexShaderRenderer implements GLSurfaceView.Renderer {private Context mContext;private final float[] projectionMatrix = new float[16];private final float[] viewMatrix = new float[16];private final float[] viewProjectionMatrix = new float[16];private FloatBuffer mMatrixFloatBuffer;///// Constructor//public SimpleVertexShaderRenderer(Context context) {mContext = context;}///// Initialize the shader and program object//public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {String vShaderStr = ESShader.readShader(mContext, "chapter8/vertexShader.vert");String fShaderStr = ESShader.readShader(mContext, "chapter8/fragmentShader.frag");// Load the shaders and get a linked program objectmProgramObject = ESShader.loadProgram(vShaderStr, fShaderStr);// Get the uniform locationsmMVPLoc = GLES30.glGetUniformLocation(mProgramObject, "u_mvpMatrix");// Generate the vertex datamCube.genCube(0.6f);// Starting rotation angle for the cubemAngle = 45.0f;GLES30.glClearColor(1.0f, 1.0f, 1.0f, 0.0f);mMatrixFloatBuffer = ByteBuffer.allocateDirect(16 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();}private void update() {if (mLastTime == 0) {mLastTime = SystemClock.uptimeMillis();}long curTime = SystemClock.uptimeMillis();long elapsedTime = curTime - mLastTime;float deltaTime = elapsedTime / 1000.0f;mLastTime = curTime;//        ESTransform perspective = new ESTransform();
//        ESTransform modelview = new ESTransform();float aspect;// Compute a rotation angle based on time to rotate the cubemAngle += (deltaTime * 40.0f);if (mAngle >= 360.0f) {mAngle -= 360.0f;}// Compute the window aspect ratioaspect = (float) mWidth / (float) mHeight;// Generate a perspective matrix with a 60 degree FOV
//        perspective.matrixLoadIdentity();
//        Matrix.setIdentityM(viewMatrix, 0);
//        perspective.perspective(60.0f, aspect, 1.0f, 20.0f);Matrix.perspectiveM(projectionMatrix, 0, 60.0f, aspect, 1.0f, 20.0f);// Generate a model view matrix to rotate/translate the cube
//        modelview.matrixLoadIdentity();Matrix.setIdentityM(viewMatrix, 0);// Translate away from the viewer
//        modelview.translate(0.0f, 0.0f, -2.0f);Matrix.translateM(viewMatrix, 0, 0.0f, 0.0f, -2.0f);// Rotate the cube
//        modelview.rotate(mAngle, 1.0f, 0.0f, 1.0f);Matrix.rotateM(viewMatrix, 0, mAngle, 1.0f, 0.0f, 1.0f);// Compute the final MVP by multiplying the// modevleiw and perspective matrices together
//        mMVPMatrix.matrixMultiply(modelview.get(), perspective.get());Matrix.multiplyMM(viewProjectionMatrix, 0, projectionMatrix, 0, viewMatrix, 0);mMatrixFloatBuffer.clear();mMatrixFloatBuffer.put(viewProjectionMatrix).position(0);}///// Draw a triangle using the shader pair created in onSurfaceCreated()//public void onDrawFrame(GL10 glUnused) {update();// Set the viewportGLES30.glViewport(0, 0, mWidth, mHeight);// Clear the color bufferGLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);// Use the program objectGLES30.glUseProgram(mProgramObject);// Load the vertex dataGLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false,0, mCube.getVertices());GLES30.glEnableVertexAttribArray(0);// Set the vertex color to redGLES30.glVertexAttrib4f(1, 1.0f, 0.0f, 0.0f, 1.0f);// Load the MVP matrixGLES30.glUniformMatrix4fv(mMVPLoc, 1, false,mMatrixFloatBuffer);// Draw the cubeGLES30.glDrawElements(GLES30.GL_TRIANGLES, mCube.getNumIndices(),GLES30.GL_UNSIGNED_SHORT, mCube.getIndices());}///// Handle surface changes//public void onSurfaceChanged(GL10 glUnused, int width, int height) {mWidth = width;mHeight = height;}// Handle to a program objectprivate int mProgramObject;// Uniform locationsprivate int mMVPLoc;// Vertex data
//    private ESShapes mCube = new ESShapes();private Cube mCube = new Cube();// Rotation angleprivate float mAngle;// MVP matrixprivate ESTransform mMVPMatrix = new ESTransform();// Additional Member variablesprivate int mWidth;private int mHeight;private long mLastTime = 0;
}

该类的代码我已经修改过了,修改的原因就是原来的实现过于复杂,理解起来不方便,而修改后的则非常清晰。修改点有两处:1、去掉了ESShapes mCube成员变量,自己新增加了一个Cube类,对应的成员变量是Cube mCube,该类的作用就是用来存储立方体的顶点和索引数据的,后面分析到时,我们就可以看到;2、去掉了update方法中所有矩阵的操作,代替使用系统Matrix类提供的API来进行矩阵计算。

先来看一下该类中的成员变量:mProgramObject表示编译链接成功的program的ID;mMVPLoc表示着色器程序中定义的投影矩阵变量;mAngle表示立方体旋转的角度;mLastTime表示最后一次的更新时间戳;mWidth、mHeight表示视窗口的宽度和高度;最后来看一下mCube和mMVPMatrix。原来的mCube是ESShapes类型,在当前的Render渲染类中最主要就是调用mCube.genCube(0.6f)生成Cube立方体的顶点数据,ESShapes类的源码如下:

public class ESShapes {public ESShapes() {}public int genSphere(int numSlices, float radius) {int i;int j;int numParallels = numSlices;int numVertices = (numParallels + 1) * (numSlices + 1);int numIndices = numParallels * numSlices * 6;float angleStep = ((2.0f * (float) Math.PI) / numSlices);// Allocate memory for buffersmVertices = ByteBuffer.allocateDirect(numVertices * 3 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();mNormals = ByteBuffer.allocateDirect(numVertices * 3 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();mTexCoords = ByteBuffer.allocateDirect(numVertices * 2 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();mIndices = ByteBuffer.allocateDirect(numIndices * 2).order(ByteOrder.nativeOrder()).asShortBuffer();for (i = 0; i < numParallels + 1; i++) {for (j = 0; j < numSlices + 1; j++) {int vertex = (i * (numSlices + 1) + j) * 3;mVertices.put(vertex + 0,(float) (radius* Math.sin(angleStep * (float) i) * Math.sin(angleStep * (float) j)));mVertices.put(vertex + 1,(float) (radius * Math.cos(angleStep * (float) i)));mVertices.put(vertex + 2,(float) (radius* Math.sin(angleStep * (float) i) * Math.cos(angleStep * (float) j)));mNormals.put(vertex + 0, mVertices.get(vertex + 0) / radius);mNormals.put(vertex + 1, mVertices.get(vertex + 1) / radius);mNormals.put(vertex + 2, mVertices.get(vertex + 2) / radius);int texIndex = (i * (numSlices + 1) + j) * 2;mTexCoords.put(texIndex + 0, (float) j / (float) numSlices);mTexCoords.put(texIndex + 1, (1.0f - (float) i)/ (float) (numParallels - 1));}}int index = 0;for (i = 0; i < numParallels; i++) {for (j = 0; j < numSlices; j++) {mIndices.put(index++, (short) (i * (numSlices + 1) + j));mIndices.put(index++, (short) ((i + 1) * (numSlices + 1) + j));mIndices.put(index++,(short) ((i + 1) * (numSlices + 1) + (j + 1)));mIndices.put(index++, (short) (i * (numSlices + 1) + j));mIndices.put(index++,(short) ((i + 1) * (numSlices + 1) + (j + 1)));mIndices.put(index++, (short) (i * (numSlices + 1) + (j + 1)));}}mNumIndices = numIndices;return numIndices;}public int genCube(float scale) {int i;int numVertices = 24;int numIndices = 36;float[] cubeVerts = {-0.5f, -0.5f, -0.5f,-0.5f, -0.5f, 0.5f,0.5f, -0.5f, 0.5f,0.5f, -0.5f, -0.5f,-0.5f, 0.5f, -0.5f,-0.5f, 0.5f, 0.5f,0.5f, 0.5f, 0.5f,0.5f, 0.5f, -0.5f,-0.5f, -0.5f, -0.5f,-0.5f, 0.5f, -0.5f,0.5f, 0.5f, -0.5f,0.5f, -0.5f, -0.5f,-0.5f, -0.5f, 0.5f,-0.5f, 0.5f, 0.5f,0.5f, 0.5f, 0.5f,0.5f, -0.5f, 0.5f,-0.5f, -0.5f, -0.5f,-0.5f, -0.5f, 0.5f,-0.5f, 0.5f, 0.5f,-0.5f, 0.5f, -0.5f,0.5f, -0.5f, -0.5f,0.5f, -0.5f, 0.5f,0.5f, 0.5f, 0.5f,0.5f, 0.5f, -0.5f,};float[] cubeNormals = {0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,-1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f,0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f,0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,-1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,0.0f, 0.0f, 1.0f, 0.0f, 0.0f,};//        float[] cubeTex = {0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
//                1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
//                0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
//                1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
//                1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
//        };// Allocate memory for buffersmVertices = ByteBuffer.allocateDirect(numVertices * 3 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
//        mNormals = ByteBuffer.allocateDirect(numVertices * 3 * 4)
//                .order(ByteOrder.nativeOrder()).asFloatBuffer();
//        mTexCoords = ByteBuffer.allocateDirect(numVertices * 2 * 4)
//                .order(ByteOrder.nativeOrder()).asFloatBuffer();mIndices = ByteBuffer.allocateDirect(numIndices * 2).order(ByteOrder.nativeOrder()).asShortBuffer();mVertices.put(cubeVerts).position(0);for (i = 0; i < numVertices * 3; i++) {mVertices.put(i, mVertices.get(i) * scale);}//        mNormals.put(cubeNormals).position(0);
//        mTexCoords.put(cubeTex).position(0);short[] cubeIndices = {0, 2, 1, 0, 3, 2, 4, 5, 6, 4, 6, 7, 8, 9, 10,8, 10, 11, 12, 15, 14, 12, 14, 13, 16, 17, 18, 16, 18, 19, 20,23, 22, 20, 22, 21};mIndices.put(cubeIndices).position(0);mNumIndices = numIndices;return numIndices;}public FloatBuffer getVertices() {return mVertices;}public FloatBuffer getNormals() {return mNormals;}public FloatBuffer getTexCoords() {return mTexCoords;}public ShortBuffer getIndices() {return mIndices;}public int getNumIndices() {return mNumIndices;}// Member variablesprivate FloatBuffer mVertices;private FloatBuffer mNormals;private FloatBuffer mTexCoords;private ShortBuffer mIndices;private int mNumIndices;
}

构造方法是我自己加入的,来看一下它的成员变量:mVertices表示立方体的顶点数据;mNormals、mTexCoords在当前的立方体实例中没有用到,先不管;mIndices表示索引数据;mNumIndices表示索引个数;我们主要来看一下genCube方法,cubeVerts局部变量指定了所有顶点的位置,负值都是0.5个单位,正值也是0.5个单位,所以立方体的边长是1个单位,而对每个顶点位置和scale相乘,就可以把立方体缩放;该方法中和mNormals、mTexCoords相关的逻辑全部无用,我都注释掉了,cubeIndices描述了所有索引数据的值,它和顶点数据配合一起,就可以表示出我们的立方体,但是大家看到,顶点数据太多了,我自己开始也想尝试把这些顶点全部画出来,以理解原来的程序如何用这么多顶点表示一个立方体,但是大脑堆栈严重崩溃,根本无法想象这么多顶点到底是怎么样分布了,而理解清楚了这个类所有的作用,我们要实现一个立方体,根本不需要这么多顶点,所以我自己写了一个Cube类,源码如下:

public class Cube {private final float[] cubeVerts = {-0.5f, 0.5f, 0.5f,     // (0) Top-left near0.5f, 0.5f, 0.5f,     // (1) Top-right near-0.5f, -0.5f, 0.5f,     // (2) Bottom-left near0.5f, -0.5f, 0.5f,     // (3) Bottom-right near-0.5f, 0.5f, -0.5f,     // (4) Top-left far0.5f, 0.5f, -0.5f,     // (5) Top-right far-0.5f, -0.5f, -0.5f,     // (6) Bottom-left far0.5f, -0.5f, -0.5f,      // (7) Bottom-right far};private final short[] cubeIndices = new short[]{// Front0, 2, 1,1, 2, 3,// Back4, 7, 5,5, 7, 6,// Left0, 2, 4,4, 2, 6,// Right5, 7, 1,1, 7, 3,// Top5, 1, 4,4, 1, 0,// Bottom6, 2, 7,7, 2, 3};public Cube() {}public int genCube(float scale) {// Allocate memory for buffersmVertices = ByteBuffer.allocateDirect(cubeVerts.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();mIndices = ByteBuffer.allocateDirect(cubeIndices.length * 2).order(ByteOrder.nativeOrder()).asShortBuffer();mVertices.put(cubeVerts).position(0);for (int i = 0; i < cubeVerts.length; i++) {mVertices.put(i, mVertices.get(i) * scale);}mIndices.put(cubeIndices).position(0);mNumIndices = cubeIndices.length;return mNumIndices;}public FloatBuffer getVertices() {return mVertices;}public ShortBuffer getIndices() {return mIndices;}public int getNumIndices() {return mNumIndices;}// Member variablesprivate FloatBuffer mVertices;private ShortBuffer mIndices;private int mNumIndices;
}

很简单,我们需要把一个立方体描述清楚,只需要八个顶点就可以了,然后按照卷曲三角形逆时针的顺序指定它们的索引值,这样就足以画出来这个立方体了,所有索引数据和Opengl ES系列学习--增加天空盒一节中讲的完全相同,如果大家对这些顶点索引值还有什么疑问,请先回头搞清楚。该类中的genCube方法也很清晰,我们就不分析了。

回到SimpleVertexShaderRenderer类当中,onSurfaceCreated方法中,调用mCube.genCube(0.6f)逻辑来生成一个缩放0.6倍的立方体,所有顶点位置数据都存储在mVertices成员变量中,所有顶点索引数据存储在mIndices成员变量中;最后给成员变量mMatrixFloatBuffer赋值并分配存储空间,因为它存储我们对矩阵运算完的结果,矩阵都是4 * 4的,所以有16位分量,每个分量都是float类型,所以分配空间时需要乘以4。

再来看update方法,原来的逻辑全部都是通过perspective、modelview两个ESTransform类型的局部变量进行运算来实现矩阵操作的,ESTransform源码如下:

public class ESTransform {public ESTransform() {mMatrixFloatBuffer = ByteBuffer.allocateDirect(16 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();}public void scale(float sx, float sy, float sz) {mMatrix[0 * 4 + 0] *= sx;mMatrix[0 * 4 + 1] *= sx;mMatrix[0 * 4 + 2] *= sx;mMatrix[0 * 4 + 3] *= sx;mMatrix[1 * 4 + 0] *= sy;mMatrix[1 * 4 + 1] *= sy;mMatrix[1 * 4 + 2] *= sy;mMatrix[1 * 4 + 3] *= sy;mMatrix[2 * 4 + 0] *= sz;mMatrix[2 * 4 + 1] *= sz;mMatrix[2 * 4 + 2] *= sz;mMatrix[2 * 4 + 3] *= sz;}public void translate(float tx, float ty, float tz) {mMatrix[3 * 4 + 0] += (mMatrix[0 * 4 + 0] * tx + mMatrix[1 * 4 + 0]* ty + mMatrix[2 * 4 + 0] * tz);mMatrix[3 * 4 + 1] += (mMatrix[0 * 4 + 1] * tx + mMatrix[1 * 4 + 1]* ty + mMatrix[2 * 4 + 1] * tz);mMatrix[3 * 4 + 2] += (mMatrix[0 * 4 + 2] * tx + mMatrix[1 * 4 + 2]* ty + mMatrix[2 * 4 + 2] * tz);mMatrix[3 * 4 + 3] += (mMatrix[0 * 4 + 3] * tx + mMatrix[1 * 4 + 3]* ty + mMatrix[2 * 4 + 3] * tz);}public void rotate(float angle, float x, float y, float z) {float sinAngle, cosAngle;float mag = (float) Math.sqrt((double) (x * x + y * y + z * z));sinAngle = (float) Math.sin((double) (angle * Math.PI / 180.0));cosAngle = (float) Math.cos((double) (angle * Math.PI / 180.0));if (mag > 0.0f) {float xx, yy, zz, xy, yz, zx, xs, ys, zs;float oneMinusCos;float[] rotMat = new float[16];x /= mag;y /= mag;z /= mag;xx = x * x;yy = y * y;zz = z * z;xy = x * y;yz = y * z;zx = z * x;xs = x * sinAngle;ys = y * sinAngle;zs = z * sinAngle;oneMinusCos = 1.0f - cosAngle;rotMat[0 * 4 + 0] = (oneMinusCos * xx) + cosAngle;rotMat[0 * 4 + 1] = (oneMinusCos * xy) - zs;rotMat[0 * 4 + 2] = (oneMinusCos * zx) + ys;rotMat[0 * 4 + 3] = 0.0F;rotMat[1 * 4 + 0] = (oneMinusCos * xy) + zs;rotMat[1 * 4 + 1] = (oneMinusCos * yy) + cosAngle;rotMat[1 * 4 + 2] = (oneMinusCos * yz) - xs;rotMat[1 * 4 + 3] = 0.0F;rotMat[2 * 4 + 0] = (oneMinusCos * zx) - ys;rotMat[2 * 4 + 1] = (oneMinusCos * yz) + xs;rotMat[2 * 4 + 2] = (oneMinusCos * zz) + cosAngle;rotMat[2 * 4 + 3] = 0.0F;rotMat[3 * 4 + 0] = 0.0F;rotMat[3 * 4 + 1] = 0.0F;rotMat[3 * 4 + 2] = 0.0F;rotMat[3 * 4 + 3] = 1.0F;matrixMultiply(rotMat, mMatrix);}}public void frustum(float left, float right, float bottom, float top,float nearZ, float farZ) {float deltaX = right - left;float deltaY = top - bottom;float deltaZ = farZ - nearZ;float[] frust = new float[16];if ((nearZ <= 0.0f) || (farZ <= 0.0f) || (deltaX <= 0.0f)|| (deltaY <= 0.0f) || (deltaZ <= 0.0f)) {return;}frust[0 * 4 + 0] = 2.0f * nearZ / deltaX;frust[0 * 4 + 1] = frust[0 * 4 + 2] = frust[0 * 4 + 3] = 0.0f;frust[1 * 4 + 1] = 2.0f * nearZ / deltaY;frust[1 * 4 + 0] = frust[1 * 4 + 2] = frust[1 * 4 + 3] = 0.0f;frust[2 * 4 + 0] = (right + left) / deltaX;frust[2 * 4 + 1] = (top + bottom) / deltaY;frust[2 * 4 + 2] = -(nearZ + farZ) / deltaZ;frust[2 * 4 + 3] = -1.0f;frust[3 * 4 + 2] = -2.0f * nearZ * farZ / deltaZ;frust[3 * 4 + 0] = frust[3 * 4 + 1] = frust[3 * 4 + 3] = 0.0f;matrixMultiply(frust, mMatrix);}public void perspective(float fovy, float aspect, float nearZ, float farZ) {float frustumW, frustumH;frustumH = (float) Math.tan(fovy / 360.0 * Math.PI) * nearZ;frustumW = frustumH * aspect;frustum(-frustumW, frustumW, -frustumH, frustumH, nearZ, farZ);}public void ortho(float left, float right, float bottom, float top,float nearZ, float farZ) {float deltaX = right - left;float deltaY = top - bottom;float deltaZ = farZ - nearZ;float[] orthoMat = makeIdentityMatrix();if ((deltaX == 0.0f) || (deltaY == 0.0f) || (deltaZ == 0.0f)) {return;}orthoMat[0 * 4 + 0] = 2.0f / deltaX;orthoMat[3 * 4 + 0] = -(right + left) / deltaX;orthoMat[1 * 4 + 1] = 2.0f / deltaY;orthoMat[3 * 4 + 1] = -(top + bottom) / deltaY;orthoMat[2 * 4 + 2] = -2.0f / deltaZ;orthoMat[3 * 4 + 2] = -(nearZ + farZ) / deltaZ;matrixMultiply(orthoMat, mMatrix);}public void matrixMultiply(float[] srcA, float[] srcB) {float[] tmp = new float[16];int i;for (i = 0; i < 4; i++) {tmp[i * 4 + 0] = (srcA[i * 4 + 0] * srcB[0 * 4 + 0])+ (srcA[i * 4 + 1] * srcB[1 * 4 + 0])+ (srcA[i * 4 + 2] * srcB[2 * 4 + 0])+ (srcA[i * 4 + 3] * srcB[3 * 4 + 0]);tmp[i * 4 + 1] = (srcA[i * 4 + 0] * srcB[0 * 4 + 1])+ (srcA[i * 4 + 1] * srcB[1 * 4 + 1])+ (srcA[i * 4 + 2] * srcB[2 * 4 + 1])+ (srcA[i * 4 + 3] * srcB[3 * 4 + 1]);tmp[i * 4 + 2] = (srcA[i * 4 + 0] * srcB[0 * 4 + 2])+ (srcA[i * 4 + 1] * srcB[1 * 4 + 2])+ (srcA[i * 4 + 2] * srcB[2 * 4 + 2])+ (srcA[i * 4 + 3] * srcB[3 * 4 + 2]);tmp[i * 4 + 3] = (srcA[i * 4 + 0] * srcB[0 * 4 + 3])+ (srcA[i * 4 + 1] * srcB[1 * 4 + 3])+ (srcA[i * 4 + 2] * srcB[2 * 4 + 3])+ (srcA[i * 4 + 3] * srcB[3 * 4 + 3]);}mMatrix = tmp;}public void matrixLoadIdentity() {for (int i = 0; i < 16; i++) {mMatrix[i] = 0.0f;}mMatrix[0 * 4 + 0] = 1.0f;mMatrix[1 * 4 + 1] = 1.0f;mMatrix[2 * 4 + 2] = 1.0f;mMatrix[3 * 4 + 3] = 1.0f;}private float[] makeIdentityMatrix() {float[] result = new float[16];for (int i = 0; i < 16; i++) {result[i] = 0.0f;}result[0 * 4 + 0] = 1.0f;result[1 * 4 + 1] = 1.0f;result[2 * 4 + 2] = 1.0f;result[3 * 4 + 3] = 1.0f;return result;}public FloatBuffer getAsFloatBuffer() {mMatrixFloatBuffer.put(mMatrix).position(0);return mMatrixFloatBuffer;}public float[] get() {return mMatrix;}private float[] mMatrix = new float[16];private FloatBuffer mMatrixFloatBuffer;}

该类中的方法都是基本的矩阵运算操作,从这里也可以看到,作者的功底还是比较厚的,一般我们都只是会使用这些而已,从来没有关心底层如何实现,而作者能手动写出这些运算逻辑,那真不是盖的!!我对这些底层操作也不理解,也懒,所以就把这些操作全部注释掉了,Matrix系统类中提供了所有矩阵操作的API,所以update方法中,我就直接使用系统API来进行矩阵运算,以达到我们的目的了。

先根据curTime - mLastTime的差值计算出耗时时间,再乘以40.0f增大旋转角度。如果if (mAngle >= 360.0f) 角度大于360度,则将它减去-360度,相当于完整一圈。接着计算窗口纵横比,这个值我们在之前也都已经讲过了,使用投影矩阵时,需要使用,而且必须将mWidth、mHeight全部强转为float进行计算,否则得到的结果将会是0。接着调用Matrix.perspectiveM(projectionMatrix, 0, 60.0f, aspect, 1.0f, 20.0f)生成投影矩阵,然后分别调用Matrix.setIdentityM(viewMatrix, 0)生成单位矩阵、Matrix.translateM(viewMatrix, 0, 0.0f, 0.0f, -2.0f)对单位矩阵进行平移、Matrix.rotateM(viewMatrix, 0, mAngle, 1.0f, 0.0f, 1.0f)对平移后的结果进行旋转,然后Matrix.multiplyMM(viewProjectionMatrix, 0, projectionMatrix, 0, viewMatrix, 0)把投影矩阵和viewMatrix结果相乘,得到的结果存储在viewProjectionMatrix矩阵当中,最后将矩阵运算得到的数据存储在mMatrixFloatBuffer当中,后面我们只需要通过API将矩阵数据赋值给着色器中的u_mvpMatrix变量就可以了。

再来看onDrawFrame方法,先调用update对旋转角度进行计算,同时将矩阵运算结果更新到mMatrixFloatBuffer成员变量当中,然后调用GLES30.glViewport指定视窗口大小,调用GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 0, mCube.getVertices())来给传递顶点数据,它在onSurfaceCreated方法当中通过调用genCube成员方法已经生成好顶点位置数据和索引数据了,这里只需要获取就可以了,GLES30.glVertexAttrib4f(1, 1.0f, 0.0f, 0.0f, 1.0f)给通过location指定的ID值为1的变量赋值,也就是颜色,所以我们看到的是一个红色的立方体;GLES30.glUniformMatrix4fv(mMVPLoc, 1, false, mMatrixFloatBuffer)给矩阵变量赋值,矩阵变量已经在onDrawFrame方法开始时计算好了,最后调用GLES30.glDrawElements系统API以三角形GLES30.GL_TRIANGLES的方式传入mCube.getIndices()所有索引数据来绘制三角形,我们的立方体就显示出来了。

最后来看一下app\src\main\assets\chapter8目录下的vertexShader.vert顶点着色器,源码如下:

#version 300 es
uniform mat4 u_mvpMatrix;
layout(location = 0) in vec4 a_position;
layout(location = 1) in vec4 a_color;
out vec4 v_color;
void main()
{v_color = a_color;gl_Position = u_mvpMatrix * a_position;
}

因为这里通过location关键字指定了0和1两个变量,所以在onDrawFrame方法中我们通过API给着色器变量赋值时,才可以传入0和1的ID,矩阵变量u_mvpMatrix还是传统的方式,先通过API获取到它的ID,也就是mMVPLoc成员变量,然后再调用GLES30.glUniformMatrix4fv来给它赋值,最后在main函数中执行u_mvpMatrix * a_position将它运用在顶点属性上,赋值给内建变量gl_Position,着色器程序就结束了。

片段着色器和之前的基本一样,我们就不展开了,大家可以自己阅读。

好了,继续开车吧!!!

Opengl ES系列学习--顶点着色器相关推荐

  1. OpenGL ES渲染管线与着色器

    转自:http://blog.csdn.net/kesalin/article/details/8223649 [OpenGL ES 02]OpenGL ES渲染管线与着色器 罗朝辉 (http:// ...

  2. Opengl ES系列学习--顶点属性、顶点数组和缓冲区对象

    本节我们继续来看一下<OPENGL ES 3.0编程指南 原书第2版(中文版)>书中第6章的内容,PDF下载地址:OPENGL ES 3.0编程指南 原书第2版(中文版),代码下载地址:O ...

  3. [OpenGL ES 02]OpenGL ES渲染管线与着色器

    http://blog.csdn.net/kesalin/article/details/8223649 罗朝辉 (http://blog.csdn.net/kesalin) 本文遵循"署名 ...

  4. Learn OpenGL(三)——顶点着色器(Vertext Shader)

    顶点着色器是几个着色器中的一个, 它是可编程的. 现代OpenGL需要我们至少设置一个顶点着色器和一个片段着色器, 如果我们打算做渲染的话. 我们会简要介绍一下着色器以及配置两个非常简单的着色器来绘制 ...

  5. opengl之高级GLSL(1)-顶点着色器变量与片段着色器变量

    官方教程位置 openglCN GLSL的内建变量: 我们已经学会使用顶点属性.uniform和采样器来完成这一任务了.然而,除此之外,GLSL还定义了另外几个以gl_为前缀的变量,它们能提供给我们更 ...

  6. OpenGL播放yuv数据流(着色器SHADER)-IOS(一)

    OpenGL播放yuv数据流(着色器SHADER)-IOS(一) 和windows平台的类似,只是用object-c编写的,着色器语言shader,rgb转yuv有些不同,具体看代码注释. //.h ...

  7. Opengl ES系列学习--莫比乌斯带

    一个莫比乌斯带的shader,效果如下: Java类为GlMobiusRender,完整源码如下: package com.opengl.learn.aric;import android.conte ...

  8. 【Shader特效3】旋转扭动效果顶点着色器实现

    旋转扭动效果顶点着色器实现 说在开头: 此小节基于顶点着色器开发一个旋转扭动的效果 作者:尹豆(憨豆酒),联系我yindou97@163.com,熟悉图形学,图像处理领域,本章代码: https:// ...

  9. Opengl ES系列学习--颜色

    本节我们来学习一下颜色的知识,在看完原作者讲解的颜色的知识的基础上,只要我们理解了,应该就能提取出重点,其实本节的重点就是下面这一句话. 所以我们要作的就是计算出物体颜色和光源颜色,然后把它们两个相乘 ...

最新文章

  1. 软件工程项目组Z.XML会议记录 2013/09/18
  2. 2.3.3 操作系统之实现临界区进程互斥的硬件实现方法
  3. Daily Scrum 2012/12/09
  4. .NET轻松写博客园爬虫
  5. python web应用_如何使用Python将通知发送到Web应用
  6. java中容易混淆的方法_java中容易混淆的区别
  7. flux架构浅谈:什么数据才应该放store
  8. 实现简单的List功能
  9. linux串口导致死机,Linux系统死机情况分析与处理方案介绍
  10. 数学基础(0)-- 高等数学、概率论与数理统计
  11. RTKLIB源码解析(三)、 Rinex文件读取(rinex.c)——2
  12. 一种基于A* 算法的动态多路径规划算法
  13. Matlab P文件解密
  14. python json.dumps(output) ^ SyntaxError: invalid syntax
  15. Android 取消蓝牙消息通知流程分析(一)
  16. 小程序中图片太大应该怎么处理
  17. 【数据挖掘】聚类分析实例
  18. 2015年终总结-青春如歌
  19. java设置select选中_按值设置选择选项'selected'
  20. 华为IT总监离职时给大家写了一封告别信

热门文章

  1. 计算机纸牌游戏攻略,电脑开始选单里的《空当接龙》和《纸牌游戏》怎么玩?...
  2. 2021考研 张 宇 强化班已经开课了
  3. 关于strcat的低级错误
  4. 实在智能发布会带你见证什么是真正属于你的神机百炼?
  5. kafka实战篇(二):消息消费实战
  6. 【零基础入门】看漫画学Python
  7. 直线如何用计算机表达,电脑画图软件内如何绘制直线并设置线条的粗线
  8. DHCP Lease Time - 动态 IP 使用时限
  9. jquery轮播图详解
  10. _28LeetCode代码随想录算法训练营第二十八天-贪心算法 | 122.买卖股票的最佳时机II 、55.跳跃游戏、45.跳跃游戏II