Android 使用 Open GL ES 开发曲棍球游戏 一

创建一个简单的OpenGl 启动程序

1 . 创建一个Activiy叫做 AirHockey,并且使用GLSurfaceView作为布局。GLSurfaceView 是一个特殊的类,可以用来初始化话OpenGL,使是初始化过程变得简单一些。代码如下:

private boolean renderSet =false;

@Override

protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    glSurfaceView =new GLSurfaceView(this);

    //setContentView(R.layout.activity_first_open_glproject);

    final ActivityManager activityManager =     (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);

    assert activityManager !=null;

    final ConfigurationInfo configuration = activityManager.getDeviceConfigurationInfo();

    final boolean supportsEs2 = configuration.reqGlEsVersion >=0x20000;

    Log.e(TAG,supportsEs2+"");

    if(supportsEs2){

        glSurfaceView.setEGLContextClientVersion(2);

        glSurfaceView.setRenderer(new AirHockeyRenderer(this));

        renderSet =true;

    }else{

            Toast.makeText(this,"not support OpenGL Es 2.0",Toast.LENGTH_LONG).show();

    return ;

    }

    setContentView(glSurfaceView);

}

@Override

protected void onResume() {

    super.onResume();

    if(renderSet){

    glSurfaceView.onResume();

}

}

 @Override

 protected void onPause() {

  super.onPause();

    if(renderSet){

    glSurfaceView.onPause();

  }

}

AirHockeyRenderer 类是后面要说到的渲染类。

glSurfaceView.setEGLContextClientVersion(2); 配置了OpenGL 的版本为2

在onPause 和 onResume里面要处理oengl ,否则会导致页面切换的时候的崩溃。

OpenGL 会在单独的线程里面调用渲染器的方法(就是Render里面的方法)。如果主线程调用GLSurfaceView 实例可以调用queueEvent()方法传入一个Runnable给后台渲染线程,渲染线程可以调用Activity的runOnUIThread()来传递事件(event)给主线程。

2.创建Render类AirHockeyRenderer

import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;

import static android.opengl.GLES20.GL_LINES;

import static android.opengl.GLES20.GL_TRIANGLES;

import static android.opengl.GLES20.glClear;

import static android.opengl.GLES20.glClearColor;

import static android.opengl.GLES20.glDrawArrays;

import static android.opengl.GLES20.glEnableVertexAttribArray;

import static android.opengl.GLES20.glGetAttribLocation;

import static android.opengl.GLES20.glGetUniformLocation;

import static android.opengl.GLES20.glUniform4f;

import static android.opengl.GLES20.glUseProgram;

import static android.opengl.GLES20.GL_FLOAT;

import static android.opengl.GLES20.glVertexAttribPointer;

import static javax.microedition.khronos.opengles.GL10.GL_POINTS;

public class AirHockeyRendererimplements GLSurfaceView.Renderer {

private static final int POSITION_COMPONENT_COUNT =2;

private static final int BYTES_PER_FLOAT =4;

private final FloatBuffervertexData;

private int program;

private static final StringU_COLOR ="u_Color";

private int uColorLocation;

private static final StringA_POSIONT="a_Position";

private int aPositionLocation;

private ContextmContext;

public AirHockeyRenderer(Context context) {

mContext = context;

float[] tableVerticesWithTriangles = {

//Triagle1

                -0.5f,-0.5f,

0.5f,0.5f,

-0.5f,0.5f,

//Triagle2

                -0.5f,-0.5f,

0.5f,-0.5f,

0.5f,0.5f,

// Line 1

                -0.5f,0f,

0.5f,0f,

// Mallets

                0f,-0.25f,

0f,0.25f

        };

vertexData = ByteBuffer.allocateDirect(tableVerticesWithTriangles.length *BYTES_PER_FLOAT)

.order(ByteOrder.nativeOrder())

.asFloatBuffer();

vertexData.put(tableVerticesWithTriangles);

}

@Override

    public void onSurfaceCreated(GL10 gl, EGLConfig config) {

glClearColor(0.0f,0.0f,0.0f,0.0f);

//加载着色器代码

        String vertexShaderSource = TextResourceReader.readTextFileFromResource(mContext,R.raw.simple_vertex_shader);

String fragmentShaderSource = TextResourceReader.readTextFileFromResource(mContext,R.raw.simple_fragment_shader);

//编译着色器代码

        int vertexShader = ShaderHelper.compileVertextShader(vertexShaderSource);

int fragmentShader = ShaderHelper.compileFragmentShader(fragmentShaderSource);

//链接着色器程序

        program = ShaderHelper.linkProgram(vertexShader,fragmentShader);

ShaderHelper.validateProgram(program);

//使用程序

        glUseProgram(program);

uColorLocation =glGetUniformLocation(program,U_COLOR);

aPositionLocation =glGetAttribLocation(program,A_POSIONT);

//程序和具体的数据的指定

        vertexData.position(0);

glVertexAttribPointer(aPositionLocation,POSITION_COMPONENT_COUNT,GL_FLOAT,false,0,vertexData);

glEnableVertexAttribArray(aPositionLocation);

}

@Override

    public void onSurfaceChanged(GL10 gl,int width,int height) {

}

@Override

    public void onDrawFrame(GL10 gl) {

glClear(GL_COLOR_BUFFER_BIT);

//绘制三角形

        glUniform4f(uColorLocation,1.0f,1.0f,1.0f,1.0f);

glDrawArrays(GL_TRIANGLES,0,6);

//绘制分割线

        glUniform4f(uColorLocation,1.0f,0.0f,0.0f,1.0f);

glDrawArrays(GL_LINES,6,2);

//绘制点

        glUniform4f(uColorLocation,0.0f,0.0f,1.0f,1.0f);

glDrawArrays(GL_POINTS,8,1);

glUniform4f(uColorLocation,1.0f,0.0f,1.0f,1.0f);

glDrawArrays(GL_POINTS,9,1);

}

}

Renderer 是渲染器类,主要与三个方法:

onSurfaceCreated,当surface被创建的时候调用,当程序被唤醒,或者从其他activity切换回来的时候也有可能被调用,所以说这个方法可能会被调用多次。

onSurfaceChanged,当surface被创建以后,surface每次尺寸变化的时候,都会被调用。比如横竖屏切换的时候,surface尺寸就会发生变化。

onDrawFrame,每绘制一帧的时候,就会调用这个方法,这个方法一定要绘制一点东西,就是是清屏幕。

下面讲一下渲染类里面的具体操作。

AirHockeyRenderer 的构造函数里面,我们要讲我们自己定义的二维定点数据传输到native层。然后后面可以使用FloatBuffer来操作native层里面的顶点数据。

onSurfaceCreated,里面增加glClearColor(0.0f,0.0f,0.0f,0.0f);用于设置清空屏幕的颜色。然后我们要在这方法里面编写着色器程序。为什么还有着色器其程序,请参考文档的参考书籍。

TextResourceReader 为着色器加载程序。使用java 的io操作。

ShaderHelper 为着色器编译工具类。包括编译和链接能力。

compileShader 编译着色器程序主要是三步。

final int shaderObjectId =glCreateShader(type);//创建着色器对象

glShaderSource(shaderObjectId,shaderCode);//上传做着色器代码

glCompileShader(shaderObjectId);//编译

linkProgram 连接着色器程序也是分为三步:

int programObjectId =glCreateProgram();//新建着色器程序

glAttachShader(programObjectId,vertexShaderId);//附上顶点着色器

glAttachShader(programObjectId,fragmentShaderId);//附上片段着色器

glLinkProgram(programObjectId);//链接程序,把这些着色器联合起来

validateProgram 是一个程序检测方法,可以自行参考书籍理解。

最后,这些着色器程序准备好了,我们要调用

glUseProgram(program); 告诉OpenGL 在屏幕上绘制东西的时候要使用这里定义的程序。

接下来将怎么在onDrawFrame绘画方法里面使用程序绘画曲棍球桌面。

在绘画之前,首先要提前获得uniform的位置。当OpenGL 把着色器链接成一个程序的时候。它实际是用一个位置变化把片段着色器里面定义的每个uniform都关联起来了。这些位置编号可以用来给着色器发送数据。

uColorLocation =glGetUniformLocation(program,U_COLOR);

和uniform一样,在使用定点数据之前,我们首先要获得其位置。我们可以通过glBindAttribLocation由我们自己给他分配位置编号。也可以让OpenGL自动给这些属性分配位置编号。在我们程序里面,使用自动分配的方式。这样使代码更加容易管理。

aPositionLocation =glGetAttribLocation(program,A_POSIONT);

接着我们就要使用这些属性。然后关联器着色器和我们定义的定点数据。

vertexData.position(0);

glVertexAttribPointer(aPositionLocation,POSITION_COMPONENT_COUNT,GL_FLOAT,false,0,vertexData);

关联后,调用glEnableVertexAttribArray(aPositionLocation); 来告诉OpengGL 从哪里取需要的数据。

最后一步,就是绘画了。

glUniform4f(uColorLocation,1.0f,1.0f,1.0f,1.0f);//指定了使用的片段着色器,和颜色

glDrawArrays(GL_TRIANGLES,0,6);//指定了形状,和顶点列表的开始位置和个数。

这是绘制一个三角形,

glUniform4f(uColorLocation,1.0f,0.0f,0.0f,1.0f);

glDrawArrays(GL_LINES,6,2);

这是一个分割线

glUniform4f(uColorLocation,0.0f,0.0f,1.0f,1.0f);

glDrawArrays(GL_POINTS,8,1);

需要注意的是,为了让点能够看得见,我们在定点着色器程序里面gl_PointSize =10.0;这样让指定了点的大小。

参考书籍:OpenGL ES 应用开发实践指南(Kevin Brothaler)

github源码: https://github.com/SnailCoderGu/OpenGL

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,271评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,725评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,252评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,634评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,549评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,985评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,471评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,128评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,257评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,233评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,235评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,940评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,528评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,623评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,858评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,245评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,790评论 2 339