opengl es3.0游戏开发学习笔记1-绘制旋转的三角形

前段时间一直在看opengl es2.0游戏开发的知识 ,这几天买了本opengl es3.0游戏开发的书  


源代码点击打开链接

打算一边学习一边整理学习笔记,我的开发环境是Android studio 2.1.3,不过有个问题是Android studio自带的模拟器只能支持es2.0  

无法使用es3.0  所以3.0只能在真机上调试,手机必须是4.3或者是以上系统,我试过4.3的三星s3和  6.0的华为mates。下面是效果图


首先是各个类的功能


LoggerConfig这个类是控制错误日志输出的,这个其实很简单里面就一句话,在错误日志输出时判断它里面是不是true

MyActivity这个类是主活动的类,创建了视图,在这里把显示的视图设置为opengl的视窗口。

MyTDView这个类是继承自GLSurfaceView 实现opengl的窗口和 一个渲染器,我是这么理解的

shaderUtil是着色器的帮助类  实现读取着色器和编译连接

Tringle是绘制三角形

我个人感觉整体过过程是这样的


下边是LoggerConfig的代码

package com.opengl.a3_1_triangle;

/**
 * Created by admin on 2016/11/12.
 */
public class LoggerConfig {
    public static final boolean ON = true;   //在后面输出错误日志时判断是不是true
}

下边是从shaderUtil帮助类的代码

首先得实现的是把着色器代码从文本中读取出来并且进行编译,书中作者使用的是把着色器代码写在sh脚本中然后读取,Android studio中有glsl着色器的插件,安装后可以使用glsl格式文本,glsl格式额能提示关键字有个好处是防止着色器程序写错。



代码

package com.opengl.a3_1_triangle;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.opengl.GLES20;
import android.opengl.GLES30;
import android.util.Log;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * Created by admin on 2016/11/12.
 */
//加载顶点shader与片元shader的工具类
public class ShaderUtil {
    //加载定制shader的方法
    public  static int loadShader(
            int shaderType,//shader的类型 GLES30.GL_VERTEX_SHADER(顶点) GLES30.GL_FRAGMENT_SHADER(片元)
            String source //shader的脚本字符串

    ){
        //创建一个新的Shader
        int shader = GLES30.glCreateShader(shaderType);
        //创建成功则加载shader
        if(shader!=0)
        {
            //加载shader的源代码
            GLES30.glShaderSource(shader,source);
            //编译shader
            GLES30.glCompileShader(shader);
            //存放编译成功shader数量的数组
            int [] compile=new int[1];
            //获取Shader的编译情况
            GLES30.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS,compile,0);
            if(compile[0]==0){
                //若编译失败则显示错误的日志并删除shader
                Log.e("ES20_ERROR","不能编译着色器"+shaderType+":");
                Log.e("ES20_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);
            //向程序中加入片元着色器
            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("ES20_ERROR", "Could not link program: ");
                Log.e("ES20_ERROR", GLES30.glGetProgramInfoLog(program));
                GLES30.glDeleteProgram(program);
                program = 0;
            }
        }
        return program;
        }
    //检查每一步操作是否有错误的方法
    @SuppressLint("NewApi")
    public static void checkGlError(String op)
    {
        int error;
        while ((error = GLES30.glGetError()) != GLES30.GL_NO_ERROR)
        {
            Log.e("ES20_ERROR", op + ": glError " + error);
            throw new RuntimeException(op + ": glError " + error);
        }
    }
    //从sh脚本中加载shader内容的方法
    public static String loadFromAssetsFile(String fname,Resources r)
    {
        String result=null;
        try
        {
            InputStream in=r.getAssets().open(fname);
            int ch=0;
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            while((ch=in.read())!=-1)
            {
                baos.write(ch);
            }
            byte[] buff=baos.toByteArray();
            baos.close();
            in.close();
            result=new String(buff,"UTF-8");
            result=result.replaceAll("\\r\\n","\n");
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        return result;
    }
//从raw文件夹中读取glsl文件
    public  static   String loadFromRawFile(Context context ,int resourceId){

            StringBuilder body = new StringBuilder();

            try {
                InputStream inputStream =
                        context.getResources().openRawResource(resourceId);
                InputStreamReader inputStreamReader =
                        new InputStreamReader(inputStream);
                BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

                String nextLine;

                while ((nextLine = bufferedReader.readLine()) != null) {
                    body.append(nextLine);
                    body.append('\n');
                }
            } catch (IOException e) {
                throw new RuntimeException(
                        "不能加载资源: " + resourceId, e);
            } catch (Resources.NotFoundException nfe) {
                throw new RuntimeException("资源没有发现: " + resourceId, nfe);
            }

            return body.toString();
        }
    }
Triangle类代码



package com.opengl.a3_1_triangle;

import android.annotation.SuppressLint;
import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLES30;
import android.opengl.Matrix;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

/**
 * Created by admin on 2016/11/13.
 */
public class Triangle {
    Context context;
    public  static  float [] mProjMatrix=new float[16];//4*4投影矩阵
    public  static  float [] mVMatrix=new float[16];//摄像机位置朝向的参数矩阵
    public  static  float[] mMVPMatrix;//最后起作用的总的变换矩阵

    int mProgram;//自定义的渲染管线程序id
    int muMVPMatrixHandle;//总的变换矩阵引用
    int maPositionHandle;//顶点位置属性的应用
    int maColorHandle;//顶点颜色属性的引用

    String mVertexShader;//顶点着色器的代码脚本
    String mFragmentShader;//片元着色器代码脚本
    static  float[] mMMatrix=new float[16];//具体物体的移动旋转矩阵,包括旋转,平移,缩放
    FloatBuffer mVertexBuffer;//顶点坐标数据缓冲
    FloatBuffer mColorBuffer;//顶点着色数据缓冲

    int vCount=0;
    float xAngle=0;//绕x轴旋转的角度
    public  Triangle(MyTDView mv, Context context){
        this.context=context;
        //调用初始化顶点数据的initVertexData的方法
        initVertexData();
        //调用初始化着色器的initShader的方法
       // initShader(mv);
        initShaderGLSL(mv);
    }
    public  void initVertexData(){
        //顶点坐标数据的初始化
        vCount=3;
        final  float UNIT_SIZE=0.2f;


        float vertices[]=new  float[]{
                -4*UNIT_SIZE,0,0,
                0,-4*UNIT_SIZE,0,
                4*UNIT_SIZE,0,0,

        };
        ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
        vbb.order(ByteOrder.nativeOrder());//设置字节顺序为本地操作系统顺序
        mVertexBuffer = vbb.asFloatBuffer();//转换为浮点(Float)型缓冲
        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();//转换为浮点(Float)型缓冲
        mColorBuffer.put(colors);//在缓冲区内写入数据
        mColorBuffer.position(0);//设置缓冲区起始位置
    }
    /*
//如果使用从sh脚本中读取着色器代码用这个代码
    //初始化着色器的方法
    @SuppressLint("NewApi")
    public void initShader(MyTDView mv)
    {
        //加载顶点着色器的脚本内容
        mVertexShader=ShaderUtil.loadFromAssetsFile("vertex.sh", mv.getResources());
        //加载片元着色器的脚本内容
        mFragmentShader=ShaderUtil.loadFromAssetsFile("frag.sh", mv.getResources());
        //基于顶点着色器与片元着色器创建程序
        mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader);
        //获取程序中顶点位置属性引用
        maPositionHandle = GLES30.glGetAttribLocation(mProgram, "aPosition");
        //获取程序中顶点颜色属性引用
        maColorHandle= GLES30.glGetAttribLocation(mProgram, "aColor");
        //获取程序中总变换矩阵引用
        muMVPMatrixHandle = GLES30.glGetUniformLocation(mProgram, "uMVPMatrix");
    }
    */
//从指定的glsl格式读取代码
    public  void initShaderGLSL(MyTDView mv)
    {
        // 加载顶点着色器的脚本内容
        mVertexShader = ShaderUtil.loadFromRawFile(context,
                R.raw.vertex);
        // 加载片元着色器的脚本内容
        mFragmentShader = ShaderUtil.loadFromRawFile(context,
                R.raw.frag);
        //基于顶点着色器与片元着色器创建程序
        mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader);
        //获取程序中顶点位置属性引用
        maPositionHandle = GLES30.glGetAttribLocation(mProgram, "aPosition");
        //获取程序中顶点颜色属性引用
        maColorHandle= GLES30.glGetAttribLocation(mProgram, "aColor");
        //获取程序中总变换矩阵引用
        muMVPMatrixHandle = GLES30.glGetUniformLocation(mProgram, "uMVPMatrix");
    }


    @SuppressLint("NewApi")
    public  void  drawSelf(){
        //指定使用某套shader程序
        GLES30.glUseProgram(mProgram);
        //指定使用某套shader程序
        GLES30.glUseProgram(mProgram);
        //初始化变换矩阵
        Matrix.setRotateM(mMMatrix,0,0,0,1,0);
        //设置沿Z轴正向位移1
        Matrix.translateM(mMMatrix,0,0,0,1);
        //设置绕x轴旋转
        Matrix.rotateM(mMMatrix,0,xAngle,1,0,0);
        //将变换矩阵传入渲染管线
        GLES30.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, Triangle.getFianlMatrix(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);
    }
    public static float[] getFianlMatrix(float[] spec)
    {
        mMVPMatrix=new float[16];
        Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, spec, 0);
        Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
        return mMVPMatrix;
    }
}

MyTDView代码
package com.opengl.a3_1_triangle;

import android.content.Context;
import android.opengl.GLES30;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

/**
 * Created by admin on 2016/11/13.
 */
public class MyTDView extends GLSurfaceView{
    final float ANGLE_SPAN=0.375f;

    RotateThread rthread;
    SceneRenderer mRenderer;//自定义渲染器的引用
    public MyTDView(Context context){
        super(context);
        this.setEGLContextClientVersion(3); //设置opengl es的版本为3
        mRenderer=new SceneRenderer(context);
        this.setRenderer(mRenderer);
        this.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);//持续渲染模式    还有一种模式是当有请求时才渲染
    }
    private  class SceneRenderer implements  GLSurfaceView.Renderer
    {
        Context  context;
        Triangle tle;
        public  SceneRenderer(Context context) {
            this.context=context;
        }
        public void onDrawFrame(GL10 gl)  //绘制
        {
            //清除深度缓冲与颜色缓冲
            GLES30.glClear( GLES30.GL_DEPTH_BUFFER_BIT | GLES30.GL_COLOR_BUFFER_BIT);
            //绘制三角形对象
            tle.drawSelf();
        }
        public void onSurfaceChanged(GL10 gl, int width, int height)    //当窗口更改的时候调用
        {
            //设置视窗大小及位置
            GLES30.glViewport(0, 0, width, height);
            //计算GLSurfaceView的宽高比
            float ratio = (float) width / height;
            //调用此方法计算产生透视投影矩阵
            Matrix.frustumM(Triangle.mProjMatrix, 0, -ratio, ratio, -1, 1, 1, 10);
            //调用此方法产生摄像机9参数位置矩阵
            Matrix.setLookAtM(Triangle.mVMatrix, 0, 0,0,3,0f,0f,0f,0f,1.0f,0.0f);
        }
        public void onSurfaceCreated(GL10 gl, EGLConfig config)   //创建窗口
        {
            //设置屏幕背景色RGBA
            GLES30.glClearColor(0,0,0,1.0f);
            //创建三角形对对象
            tle=new Triangle(MyTDView.this,context);
            //打开深度检测
            GLES30.glEnable(GLES30.GL_DEPTH_TEST);
            rthread=new RotateThread();
            rthread.start();
        }
    }
    public  class RotateThread extends  Thread //自定义的内部类线程  重新发发起一个线程让绕x轴旋转
    {
        public   boolean flag=true;
        @Override
        public  void run()//重写run方法
        {
            while(flag){
                mRenderer.tle.xAngle=mRenderer.tle.xAngle+ANGLE_SPAN;
                try {
                    Thread.sleep(20);
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
}

顶点着色器的代码

#version 300 es
uniform mat4 uMVPMatrix;//总的变换矩阵
layout (location=0) in vec3 aPosition;//顶点的位置
layout (location=1) in vec4 aColor;//顶点颜色
out vec4 vColor;//用于传递给片元着色器的变量
void main()
{
gl_Position=uMVPMatrix*vec4(aPosition,1);

vColor=aColor;
}

片元着色器代码

#version 300 es
precision mediump float;
in vec4 vColor;//接受从顶点着色器过来的参数
out vec4 fragColor;//输出到片元这颜色
void main(){
fragColor=vColor;//此片元的颜色值
}
MyActivity类代码
package com.opengl.a3_1_triangle;

import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MyActivity extends Activity {   //创建继承Activity的主控制类

  MyTDView mview;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);//调用父类
        //设置为竖屏模式
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
          mview=new MyTDView(this);//创建MyTDView类而对象
         mview.requestFocus();
        mview.setFocusableInTouchMode(true);
        setContentView(mview);
    }
    @Override
    public void onResume()//继承Activity后重写的onResume方法
    {
        super.onResume();
        mview.onResume();//通过MyTDView类的对象调用onResume方法
    }
    @Override
    public void onPause()//继承Activity后重写的onPause方法
    {
        super.onPause();
        mview.onPause();//通过MyTDView类的对象调用onPause方法
    }
}






  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
使用NDK和OpenGL ES 3.0来绘制一个三角形可以分为以下几个步骤: 1. 首先,创建一个安卓项目,并配置NDK环境。 2. 在项目的jni目录下,创建一个C/C++源文件triangle.c。 3. 在triangle.c文件中,引入相关的头文件,包括<jni.h>和<GLES3/gl3.h>。 4. 在triangle.c文件中,实现一个JNI函数,用于绘制三角形。函数的参数为Surface对象。 5. 在JNI函数中,通过EGL和GLES初始化OpenGL环境,并创建一个EGLSurface用于后续的绘制操作。 6. 在JNI函数中,创建一个顶点数组和顶点缓冲,并将顶点数据存入顶点缓冲。 7. 在JNI函数中,编写着色器代码,包括顶点着色器和片段着色器,并编译和链接它们。 8. 在JNI函数中,通过glClearColor()函数设置清空屏幕时的颜色。 9. 在JNI函数中,通过glClear()函数清空屏幕,并启用深度测试。 10. 在JNI函数中,通过glViewport()函数设置视口大小。 11. 在JNI函数中,通过glUseProgram()函数使用着色器程序。 12. 在JNI函数中,通过glVertexAttribPointer()函数设置顶点数据的属性,并启用顶点属性。 13. 在JNI函数中,通过glDrawArrays()函数绘制三角形。 14. 在JNI函数中,通过eglSwapBuffers()函数交换绘制的缓冲区。 15. 在JNI函数中,清理OpenGL环境,并释放资源。 16. 在Java层的MainActivity中,通过JNI调用C/C++函数进行绘制。 以上是绘制一个三角形的大致步骤。具体的细节和代码实现可以参考相关的OpenGL ES 3.0和NDK的文档和示例代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值