第零步:加载表示物体的obj文件,获得所需的顶点坐标、法向量,将其写入缓冲区。
//创建顶点坐标数据缓冲
//vertices.length*4是因为一个整数四个字节
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
vbb.order(ByteOrder.nativeOrder());//设置字节顺LITTLE_ENDIAN or BIG_ENDIAN.
rodVertexBuffer = vbb.asFloatBuffer();//转换为Float型缓冲
rodVertexBuffer.put(vertices);//向缓冲区中放入顶点坐标数据
rodVertexBuffer.position(0);//设置缓冲区起始位置
//特别提示:由于不同平台字节顺序不同数据单元不是字节的一定要经过ByteBuffer
//转换,关键是要通过ByteOrder设置nativeOrder(),否则有可能会出问题
//顶点坐标数据的初始化================end============================
//顶点法向量数据的初始化================begin============================
ByteBuffer cbb = ByteBuffer.allocateDirect(normals.length*4);
cbb.order(ByteOrder.nativeOrder());//设置字节顺序
rodNormalBuffer = cbb.asFloatBuffer();//转换为Float型缓冲
rodNormalBuffer.put(normals);//向缓冲区中放入顶点法向量数据
rodNormalBuffer.position(0);//设置缓冲区起始位置
//特别提示:由于不同平台字节顺序不同数据单元不是字节的一定要经过ByteBuffer
//转换,关键是要通过ByteOrder设置nativeOrder(),否则有可能会出问题
//顶点着色数据的初始化================end============================
为什么要将顶点数据和法向量数据进行缓存?
答:省去了数据IO的麻烦
第一步:编写顶点着色器、片元着色器
得到String vertexSource, String fragmentSource
第二步:编译顶点着色器、片元着色器
int shader = GLES20.glCreateShader(shaderType);
//加载shader的源代码
GLES20.glShaderSource(shader, source);
//编译shader
GLES20.glCompileShader(shader);
//存放编译成功shader数量的数组
int[] compiled = new int[1];
//获取Shader的编译情况
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
得到vertexShader、pixelShader
第三步:创建程序,向其中加入顶点着色器、片元着色器
int program = GLES20.glCreateProgram();
//向程序中加入顶点着色器
GLES20.glAttachShader(program, vertexShader);
checkGlError("glAttachShader");
//向程序中加入片元着色器
GLES20.glAttachShader(program, pixelShader);
//链接程序
GLES20.glLinkProgram(program);
//存放链接成功program数量的数组
int[] linkStatus = new int[1];
//获取program的链接情况
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
得到program
第四步:从着色器程序中获得各属性、矩阵的引用
//获取程序中顶点位置属性引用
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
//获取程序中顶点颜色属性引用
maNormalHandle= GLES20.glGetAttribLocation(mProgram, "aNormal");
//获取程序中总变换矩阵引用
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
//获取位置、旋转变换矩阵引用
muMMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMMatrix");
//获取程序中光源位置引用
maLightLocationHandle=GLES20.glGetUniformLocation(mProgram, "uLightLocation");
//获取程序中摄像机位置引用
maCameraHandle=GLES20.glGetUniformLocation(mProgram, "uCamera");
第五步:使用着色器程序,向所获得的各属性、矩阵引用中传入数据,启用顶点、法向量数据,绘制被加载的物体。
//制定使用某套着色器程序
GLES20.glUseProgram(mProgram);
//将最终变换矩阵传入着色器程序
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, MatrixState.getFinalMatrix(), 0);
//将位置、旋转变换矩阵传入着色器程序
GLES20.glUniformMatrix4fv(muMMatrixHandle, 1, false, MatrixState.getMMatrix(), 0);
//将光源位置传入着色器程序
GLES20.glUniform3fv(maLightLocationHandle, 1, MatrixState.lightPositionFB);
//将摄像机位置传入着色器程序
GLES20.glUniform3fv(maCameraHandle, 1, MatrixState.cameraFB);
// 将顶点位置数据传入渲染管线
GLES20.glVertexAttribPointer
(
maPositionHandle,
3,
GLES20.GL_FLOAT,
false,
3*4,
tunnelVertexBuffer
);
//将顶点法向量数据传入渲染管线
GLES20.glVertexAttribPointer
(
maNormalHandle,
3,
GLES20.GL_FLOAT,
false,
3*4,
tunnelNormalBuffer
);
//启用顶点位置、法向量数据
GLES20.glEnableVertexAttribArray(maPositionHandle);
GLES20.glEnableVertexAttribArray(maNormalHandle);
//绘制被加载的物体
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, tunnelCount);
GLES20相关API的解释文档在https://www.khronos.org/opengles/sdk/docs/man/