大家好,接下来将为大家介绍OpenGL ES 3. HelloWorld 三角形。
本节仅为大家提供一个感性的认识,很多知识后续节选会进行详细介绍。
1、确定三角形的顶点坐标和顶点颜色:
三角形的顶点坐标如下:左下(-0.5,-0.25)、右下(0.5,-0.25)、上中(0,0.5)。
三角形的顶点颜色如下:左下(1,1,1,0)、右下(0,1,0,0)、上中(0,0,1,0)。
2、一个工具类ShaderUtil
对常用的加载着色器的代码进行封装,主要功能就是将着色器(Shader)脚本加载进显卡并进行编译。shader操作工具类:加载顶点Shader与片元Shader的工具类ShaderUtil,loadShader加载制定shader的方法,createProgram创建shader程序的方法,checkGlError检查每一步操作是否有错误的方法,loadFromAssetsFile从sh脚本中加载shader内容的方法。
import android.content.res.Resources;
import android.opengl.GLES30;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
/**
* 加载顶点与片元着色器的类
*/
public class ShaderUtil {
/**
* 加载指定着色器的方法
* @param shaderType 着色器的类型
* @param source 着色器的脚本字符串
*/
public static int loadShader( int shaderType , String source ){
//创建一个shader,并记录其类型
int shader = GLES30.glCreateShader(shaderType);
//若创建成功则加载着色器
if( shader != 0 ){
//加载着色器的源代码
GLES30.glShaderSource(shader,source);
//编译
GLES30.glCompileShader(shader);
int[] compiled = new int[1];
//获取Shader的编译情况
GLES30.glGetShaderiv(shader,GLES30.GL_COMPILE_STATUS,compiled,0);
//若编译失败则显示错误日志并删除此shader
if( compiled[0] == 0 ){
Log.e("ES30_ERROR","Could not compile shader " + shaderType + ":");
Log.e("ES30_ERROR",GLES30.glGetShaderInfoLog(shader));
GLES30.glDeleteShader(shader);
shader = 0 ;
}
}
return shader;
}
/**
* 创建着色器程序的方法
*/
public static int createProgram(String vertexSource,String fragmentSource){
//加载顶点着色器
int vertexShader = loadShader(GLES30.GL_VERTEX_SHADER , vertexSource);
if( vertexShader == 0){
return 0 ;
}
//加载片元着色器
int pixelShader = loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentSource);
if( pixelShader == 0 ){
return 0 ;
}
//创建程序
int program = GLES30.glCreateProgram();
//若程序创建成功则向程序中加入顶点着色器与片元着色器
if( program != 0 ){
//向程序中加入顶点着色器
GLES30.glAttachShader(program,vertexShader);
checkGlError("glAttachShader");
//向程序中加入片元着色器
GLES30.glAttachShader(program,pixelShader);
checkGlError("glAttachShader");
//链接程序
GLES30.glLinkProgram(program);
//存放链接成功program状态值的数组
int[] linkStatus = new int[1];
GLES30.glGetProgramiv(program, GLES30.GL_LINK_STATUS , linkStatus , 0 ) ;
//若链接失败则报错并删除程序
if( linkStatus[0] != GLES30.GL_TRUE){
Log.e("ES30_ERROR" , "Could not link program");
Log.e("ES30_ERROR" , GLES30.glGetProgramInfoLog(program));
//删除程序
GLES30.glDeleteProgram(program);
program = 0 ;
}
}
return program;
}
/**
* 检查每一步操作是否有错误的方法
* @param op 发生错误的方法名
*/
public static void checkGlError( String op){
int error ;
while( ( error = GLES30.glGetError()) != GLES30.GL_NO_ERROR){
//后台打印错误
Log.e("ES30_ERROR", op + ": glError " + error);
//抛出异常
throw new RuntimeException( op + ": glError " + error) ;
}
}
/**
* 从sh 脚本中加载着色器内容的方法
* @param fname 文件名
* @param r 资源文件
* @return 结果字符串
*/
public static String loadFromAssetsFile(String fname , Resources r){
String result = null ;
try {
//从assets文件夹中读取信息
InputStream in = r.getAssets().open(fname);
//定义一个int型变量
int ch = 0 ;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while( ( ch = in.read()) != -1 ){
baos.write(ch);
}
byte[] buff = baos.toByteArray();
//关闭输出流
baos.close();
in.close();
//转换为UTF-8编码
result = new String( buff, "UTF-8");
result = result.replaceAll("\\r\\n","\n");
}catch ( Exception e){
e.printStackTrace();
}
return result;
}
}
其中loadFromAssets方法从Assets文件夹下加载着色器代码脚本,checkGlError方法检查代码运行过程中每一步是否出错,loadShader方法加载指定着色器,createProgram创建渲染program对象,并返回着色器程序的id。
3、创建一个类MyGLSurfaceView,其继承GLSurfaceView,并实现GLSurfaceView.Renderer接口。创建自定义渲染器SceneRenderer,同时设置渲染模式为主动渲染:RENDERMODE_CONTINUOUSLY。
import android.content.Context;
import android.opengl.GLES30;
import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGLConfig;
public class MyGLSurfaceView extends GLSurfaceView {
//自定义渲染器的引用
SceneRenderer mRenderer ;
//构造器
public MyGLSurfaceView(Context context){
super(context);
//使用OpenGL ES 3.0 需设置该值为3
this.setEGLContextClientVersion(3);
//创建SceneRenderer类的对象
mRenderer = new SceneRenderer();
//设置渲染器
setRenderer(mRenderer);
setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
}
public class SceneRenderer implements GLSurfaceView.Renderer{
//声明Triangle类的引用
Triangle tle ;
//重写onDrawFrame方法
@Override
public void onDrawFrame(GL10 gl) {
GLES30.glClear(GLES30.GL_DEPTH_BUFFER_BIT |
GLES30.GL_COLOR_BUFFER_BIT);
tle.drawSelf();
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//设置视口
GLES30.glViewport(0,0,width,height);
//计算屏幕的宽度和高度比例
float ratio = (float) width/height;
//设置透视投影
android.opengl.Matrix.frustumM(Triangle.mProjMatrix,0,
-ratio,ratio,-1,1,1,10);
//设置摄像机
android.opengl.Matrix.setLookAtM(Triangle.mVMatrix,0,0,0,3,0f,0f,0f,0f,1.0f,0.0f);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//设置屏幕背景色
GLES30.glClearColor(0,0,0,1.0f);
//创建Triangle类的对象
tle = new Triangle(MyGLSurfaceView.this);
GLES30.glEnable(GLES30.GL_DEPTH_TEST);
}
}
}
其中,构造方法中设置了自定义的渲染器,并设置了渲染模式,onSurfaceChanged方法中设置了摄像机的位置,透视投影的参数等。
4、三角形渲染类Triangle,initVertexData方法用于初始化三角形的顶点数据,initShader方法用于创建着色器对象:
import android.opengl.GLES30;
public class Triangle {
//4x4投影矩阵
public static float[] mProjMatrix = new float[16] ;
//摄像机位置朝向的参数矩阵
public static float[] mVMatrix = new float[16] ;
//总变换矩阵
public static float[] mMVPMatrix ;
//自定义渲染管线着色器程序id
int mProgram ;
//总变换矩阵引用
int muMVPMatrixHandle ;
//顶点位置属性引用
int maPositionHandle ;
//顶点颜色属性引用
int maColorHandle ;
//顶点着色器代码脚本
String mVertexShader ;
//片元着色器代码脚本
String mFragmentShader ;
//具体物体的3D变换矩阵,包括旋转,平移,缩放
static float[] mMMatrix = new float[16];
//顶点坐标数据缓冲
FloatBuffer mVertexBuffer ;
//顶点着色数据缓冲
FloatBuffer mColorBuffer ;
//顶点数量
int vCount ;
//绕x轴旋转的角度
float xAngle = 0 ;
//构造函数
public Triangle(MyGLSurfaceView mv ){
//初始化顶点数据
initVertexData();
//初始化着色器
initShader(mv);
}
//自定义的初始化顶点数据的方法
public void initVertexData(){
//顶点数量为3
vCount = 3 ;
//顶点坐标数组
float vertices[] = new float[]{ -0.5,-0.25,0, 0,0.5,0, 0.5,-0.25,0 };
//开辟缓冲
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
//设置字节顺序为本地操作系统顺序
vbb.order(ByteOrder.nativeOrder());
//转换为float型缓冲
mVertexBuffer = vbb.asFloatBuffer();
//在缓冲区内写入数据
mVertexBuffer.put(vertices);
//设置缓冲区起始位置
mVertexBuffer.position(0);
//顶点颜色数组
float colors[] = new float[]{ 1,1,1,0, 0,0,1,0, 0,1,0,0 };
ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length*4);
cbb.order(ByteOrder.nativeOrder());
mColorBuffer = cbb.asFloatBuffer();
mColorBuffer.put(colors);
mColorBuffer.position(0);
}
//产生最终变换矩阵的方法
public static float[] getFinalMatrix( float[] spec ){
mMVPMatrix = new float[16];
Matrix.multiplyMM(mMVPMatrix,0,mVMatrix,0,spec,0);
Matrix.multiplyMM(mMVPMatrix,0,mProjMatrix,0,mMVPMatrix,0);
//返回总变换矩阵
return mMVPMatrix ;
}
public void initShader(MyGLSurfaceView mv ){
mVertexShader = ShaderUtil.loadFromAssetsFile("vertex.sh",mv.getResources());
mFragmentShader = ShaderUtil.loadFromAssetsFile("frag.sh" , mv.getResources());
mProgram = ShaderUtil.createProgram(mVertexShader,mFragmentShader);
maPositionHandle = GLES30.glGetAttribLocation(mProgram,"vPosition");
maColorHandle = GLES30.glGetAttribLocation(mProgram,"vColor");
muMVPMatrixHandle = GLES30.glGetUniformLocation(mProgram,"vMatrix");
}
public void drawSelf(){
GLES30.glUseProgram(mProgram);
//初始化变换矩阵
Matrix.setRotateM(mMMatrix,0,0,0,1,0);
//设置沿z轴正向位移
Matrix.translateM(mMMatrix,0,0,0,1);
//设置绕x轴旋转
Matrix.rotateM(mMMatrix,0,xAngle,1,0,0);
GLES30.glUniformMatrix4fv(muMVPMatrixHandle,1,false,Triangle.getFinalMatrix(mMMatrix),0);
//将顶点数据传送进渲染管线
GLES30.glVertexAttribPointer(maPositionHandle,3,GLES30.GL_FLOAT,false,3*4,mVertexBuffer);
//将顶点着色数据传送进渲染管线
GLES30.glVertexAttribPointer(maColorHandle,4,GLES30.GL_FLOAT,false,4*4,mColorBuffer);
//启用顶点位置数据
GLES30.glEnableVertexAttribArray(maPositionHandle);
//启用着色数据
GLES30.glEnableVertexAttribArray(maColorHandle);
//执行绘制
GLES30.glDrawArrays(GLES30.GL_TRIANGLES,0,vCount);
}
}
其中,initshader方法用于编译链接shader,创建渲染程序program,drawSelf方法是核心的渲染实现。
5、在MainActivity中设置显示的视图为自定义的MyGLSurfaceview,并设置Renderer渲染器即可。同时设置渲染模式为主动渲染:RENDERMODE_CONTINUOUSLY。
import android.content.pm.ActivityInfo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
MyGLSurfaceView mView ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
//创建MySurfaceView对象
mView = new MyGLSurfaceView(this);
//获取焦点
mView.requestFocus();
//设为可触控
mView.setFocusableInTouchMode(true);
setContentView(mView);
}
@Override
protected void onResume() {
super.onResume();
//调用GLSurfaceview的onResume方法
mView.onResume();
}
@Override
protected void onPause() {
super.onPause();
//调用GLSurfaceview的onPause方法
mView.onPause();
}
}
6、着色器shader
#version 300 es
in vec3 vPosition;
uniform mat4 vMatrix;
in vec4 vColor;
out vec4 mColor;
void main() {
gl_Position = vMatrix * vec4(vPosition,1);
mColor = vColor;
}
#version 300 es
precision mediump float;
in vec4 mColor;
void main() {
fragColor = mColor;
}
7、渲染结果示例
最后,欢迎大家一起交流学习:微信:liaosy666 ; QQ:2209115372 。