OpenGL ES 3. HelloWorld 三角形

Riva ·
更新时间:2024-11-13
· 632 次阅读

大家好,接下来将为大家介绍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)脚本加载进显卡并进行编译。

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接口。

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

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渲染器即可。

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 。


作者:SunnyLiaoSu



helloworld opengl

需要 登录 后方可回复, 如果你还没有账号请 注册新账号