OpenGLES的简单应用绘图,以及照相机画面变色变形渲染(五)

基础五:实战画个三角形
例子出自:http://blog.csdn.net/niu_gao/article/details/8624490
在其他基础中提到了我们使用GLSurfaceView来做,那么就要用它的继承扩展子类,自定义的,因为可能要设置其他触发效果。这里以My GLSurfaceView为例。

一、初始化OpenGL ES的环境
My GLSurfaceView的构造器中:
需要初始化OpenGL ES的版本
设置Renderer
并设置Renderer的绘制模式。
代码分别为:
setEGLContextClientVersion(2);//2.0版本
setRenderer(this);//需要实现Renderer类,之后再说
setRenderMode(RENDERMODE_WHEN_DIRTY);//只有在绘制数据改变 时才绘制view
上文提到的Renderer被实现之后,会有重写三个实现方法
public void onSurfaceCreated(GL10 unused, EGLConfig config)
public void onSurfaceChanged(GL10 unused, int width, int height)
public void onDrawFrame(GL10 unused)
在基础2中已经提到过方法都是做什么的了,这里主要说这三个方法配合OpenGL的库来如何使用
在onSurfaceCreated方法中写: GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);//设置背景的颜色
在onDrawFrame方法中写:GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);//重绘背景色
在onSurfaceChanged方法中写:GLES20.glViewport(0, 0, width, height);
以上就是对OpenGL ES的环境搭建初始化

二、定义形状
定义形状只是单纯的定义一个形状在画布坐标系上的顶点坐标位置,与绘制顺序暂无关系。
在定义形状时,所有的形状以坐标点的连线来呈现,OpenGL中采用了三角形原则和逆时针原则。
在定义形状时的逆时针原则是指,将图形坐标以逆时针的顺序写到数组中,到时会定义绘制顺序,这样不会导致错乱。
2D图形绘制和3D图形绘制时,代表坐标的数字个数当然不同,2D图形绘制是X、Y两个值来代表一个坐标,所以先定义每个顶点坐标的数字个数,一般且大多数人,这样写:
static final int COORDS_PER_VERTEX =2;//3D同理改为3


当然,你就算绘制2D图形,能不能用3D的代码达到效果呢,当然能,上句代码设置为3,到时Z坐标设置为0就行了。
接下来设置每个顶点的基本坐标,一般这样写,用float是使图形更精确,并且一般都用float:
这个是三角形的三个点坐标,Z坐标都设置为0了,看得出来这里的上句代码设置的为3,当3D效果处理的。

static float triangleCoords[] = { // 按逆时针方向顺序:
         0.0f,  0.622008459f, 0.0f,   // top
        -0.5f, -0.311004243f, 0.0f,   // bottom left
         0.5f, -0.311004243f, 0.0f    // bottom right
    };



三、将形状顶点坐标存入缓冲,以备调用
OpenGL是针对设备GPU的底层库,是C语言的API,如果想缓冲入一个数组,并被C语言库调用的话,就不能使用Java常规数据类型如String、Byte等。
在字节存储方向上,Java中有特定类型ByteBuffer用来做字节缓冲。
定义好字节缓冲后,可以转为浮点缓冲。
首先初始化一个ByteBuffer对象,并告之应该有多大(float类型对应4倍字节):
ByteBuffer bb = ByteBuffer . allocateDirect ( triangleCoords . length * 4 );
然后设置这个bb对象的字节顺序,按照本地字节序
bb . order ( ByteOrder . nativeOrder ());
将字节缓冲转为浮点缓冲
vertexBuffer = bb . asFloatBuffer ();
然后将浮点坐标数组放入浮点缓冲中
vertexBuffer . put ( triangleCoords );
最后设置从哪里读起
vertexBuffer . position ( 0 );
这样vertexBuffer就存好并设置好初始读取位置了。
如果有要求绘制顺序,那么绘制顺序的数组也需要放入缓冲供C语言库调用,那首先定义绘制顺序如下写法:
private short drawOrder [] = { 0 , 1 , 2 , 0 , 2 , 3 }; // 顶点的绘制顺序
那在形状的顶点坐标存入浮点缓冲中的同时,也要将绘制顺序顶点存入short缓冲中,如下:
// 为绘制列表初始化字节缓冲
        ByteBuffer dlb = ByteBuffer.allocateDirect(
        // (对应顺序的坐标数 * 2)short是2字节
                drawOrder.length * 2);
        dlb.order(ByteOrder.nativeOrder());
        drawListBuffer = dlb.asShortBuffer();
        drawListBuffer.put(drawOrder);
        drawListBuffer.position(0);


这样drawListBuffer中就存好了绘制顺序顶点的short缓冲了。

四、绘制图形
在renderer的创建方法中,初始化形状顶点坐标缓冲。
在绘制图形中,有一个关键词为shader的东西,从头至尾都会涉及,也是OpengGL的主角。
Shader——着色器,在绘制图形时,有顶点着色器和片元着色器两种:
VertexShader-用于渲染形状的顶点的OpenGLES 图形代码。
FragmentShader-用于渲染形状的外观(颜色或纹理)的OpenGLES 代码。
而Program这个int型对象,是从Opengl中直接创建的,包含了以上两种Shader(需要手动添加)。
VertexShader是用来绘制形状,而FragmentShader是用来上色的。
这两个Shader在Java代码中以final String呈现,实际上是 OpenGLShading Language (GLSL) 代码语句组成,样例写法如下:
private final String vertexShaderCode =
    "attribute vec4 vPosition;" +
    "void main() {" +
    "  gl_Position = vPosition;" +
    "}";

private final String fragmentShaderCode =
    "precision mediump float;" +
    "uniform vec4 vColor;" +
    "void main() {" +
    "  gl_FragColor = vColor;" +
    "}";


这个语言的代码,需要被编译,所以在一般的Renderer中都会有loadShader这个静态方法专用来编译Shader,写法如下:
public static int loadShader(int type, String shaderCode){

    // 创建一个vertex shader类型(GLES20.GL_VERTEX_SHADER)
    // 或fragment shader类型(GLES20.GL_FRAGMENT_SHADER)
    int shader = GLES20.glCreateShader(type);

    // 将源码添加到shader并编译之
    GLES20.glShaderSource(shader, shaderCode);
    GLES20.glCompileShader(shader);

    return shader;
}


编译好Shader后,要将Shader放入Program中,并进行链接,写法如下:
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
    int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

    mProgram = GLES20.glCreateProgram();             // 创建一个空的OpenGL ES Program
    GLES20.glAttachShader(mProgram, vertexShader);   // 将vertex shader添加到program
    GLES20.glAttachShader(mProgram, fragmentShader); // 将fragment shader添加到program
    GLES20.glLinkProgram(mProgram);                  // 创建可执行的 OpenGL ES program


以上第四节内容,是在说两种Shader的创建,以及Shader的编译,最后添加到一个Program中。
现在有了着色器程序——Shader+Program,接下来就是将之前准备好的顶点缓冲和着色器程序对接起来。这样就绘制好了抽象的图形,要想将它显示出来,只需把这个对接的过程方法写在Renderer的 onDrawFrame() 方法中无限重画。
一般会将这个对接过程写为draw方法中,写法如下:
public void draw() {
    // 将program加入OpenGL ES环境中
    GLES20.glUseProgram(mProgram);

    // 获取指向vertex shader的成员vPosition的 handle
    mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");

    // 启用一个指向三角形的顶点数组的handle
    GLES20.glEnableVertexAttribArray(mPositionHandle);

    // 准备三角形的坐标数据
    GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                                 GLES20.GL_FLOAT, false,
                                 vertexStride, vertexBuffer);//这个就是将顶点缓冲与程序连接的代码

    // 获取指向fragment shader的成员vColor的handle 
    mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");

    // 设置三角形的颜色
    GLES20.glUniform4fv(mColorHandle, 1, color, 0);

    // 画三角形
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);

    // 禁用指向三角形的顶点数组
    GLES20.glDisableVertexAttribArray(mPositionHandle);
}


上一节
下一节


你可能感兴趣的:(AndroidAdvanced)