OpenGL ES 3. HelloWorld 三角形

大家好,接下来将为大家介绍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 。

发布了31 篇原创文章 · 获赞 42 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/u010281924/article/details/105324166