zoukankan      html  css  js  c++  java
  • Android OpenGL ES 开发(四): OpenGL ES 绘制形状

    在上文中,我们使用OpenGL定义了能够被绘制出来的形状了,现在我们想绘制出来它们。使用OpenGLES 2.0来绘制形状会比你想象的需要更多的代码。因为OpenGL的API提供了大量的对渲染管线的控制能力。

    本文就将讲述如何使用OpenGL ES 2.0 API来绘制出来我们上节定义的形状。

    一、初始化形状

    在你做任何绘制操作之前,你必须要初始化并加载你准备绘制的形状。除非形状的结构(指原始的坐标)在执行过程中发生改变,你都应该在你的Renderer的方法onSurfaceCreated()中进行内存和效率方面的初始化工作。

    public class MyGLRenderer implements GLSurfaceView.Renderer {
    
        ...
        private Triangle mTriangle;
        private Square   mSquare;
    
        public void onSurfaceCreated(GL10 unused, EGLConfig config) {
            ...
    
            // initialize a triangle
            mTriangle = new Triangle();
            // initialize a square
            mSquare = new Square();
        }
        ...
    }
    

    二、绘制形状

    使用OpenGLES 2.0画一个定义好的形状需要比较多的代码,因为你必须为图形渲染管线提供一大堆信息。特别的,你必须定义以下几个东西:

    • Vertex Shader - 用于渲染形状的顶点的OpenGLES 图形代码。
    • Fragment Shader - 用于渲染形状的外观(颜色或纹理)的OpenGLES 代码。
    • Program - 一个OpenGLES对象,包含了你想要用来绘制一个或多个形状的shader。

    你至少需要一个vertexshader来绘制一个形状和一个fragmentshader来为形状上色。这些形状必须被编译然后被添加到一个OpenGLES program中,program之后被用来绘制形状。下面是一个展示如何定义一个可以用来绘制形状的基本shader的例子:

    public class Triangle {
    
        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;" +
            "}";
    
        ...
    }
    

    Shader们包含了OpenGLShading Language (GLSL)代码,必须在使用前编译。要编译这些代码,在你的Renderer类中创建一个工具类方法:

    public static int loadShader(int type, String shaderCode){
    
        // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
        // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
        int shader = GLES20.glCreateShader(type);
    
        // add the source code to the shader and compile it
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);
    
        return shader;
    }
    

    为了绘制你的形状,你必须编译shader代码,添加它们到一个OpenGLES program 对象然后链接这个program。在renderer对象的构造器中做这些事情,从而只需做一次即可。

    注:编译OpenGLES shader们和链接linkingprogram们是很耗CPU的,所以你应该避免多次做这些事。如果在运行时你不知道shader的内容,你应该只创建一次code然后缓存它们以避免多次创建。

    public class Triangle() {
        ...
    
        private final int mProgram;
    
        public Triangle() {
            ...
    
            int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
                                            vertexShaderCode);
            int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
                                            fragmentShaderCode);
    
            // create empty OpenGL ES Program
            mProgram = GLES20.glCreateProgram();
    
            // add the vertex shader to program
            GLES20.glAttachShader(mProgram, vertexShader);
    
            // add the fragment shader to program
            GLES20.glAttachShader(mProgram, fragmentShader);
    
            // creates OpenGL ES program executables
            GLES20.glLinkProgram(mProgram);
        }
    }
    

    此时,你已经准备好增加真正的绘制调用了。需要为渲染管线指定很多参数来告诉它你想画什么以及如何画。因为绘制操作因形状而异,让你的形状类包含自己的绘制逻辑是个很好主意。

    创建一个draw()方法负责绘制形状。下面的代码设置位置和颜色值到形状的vertexshader和fragmentshader,然后执行绘制功能:

    private int mPositionHandle;
    private int mColorHandle;
    
    private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
    private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
    
    public void draw() {
        // Add program to OpenGL ES environment
        GLES20.glUseProgram(mProgram);
    
        // get handle to vertex shader's vPosition member
        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
    
        // Enable a handle to the triangle vertices
        GLES20.glEnableVertexAttribArray(mPositionHandle);
    
        // Prepare the triangle coordinate data
        GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                                     GLES20.GL_FLOAT, false,
                                     vertexStride, vertexBuffer);
    
        // get handle to fragment shader's vColor member
        mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
    
        // Set color for drawing the triangle
        GLES20.glUniform4fv(mColorHandle, 1, color, 0);
    
        // Draw the triangle
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
    
        // Disable vertex array
        GLES20.glDisableVertexAttribArray(mPositionHandle);
    }
    

    一旦完成了所有这些代码,绘制该对象只需要在渲染器的onDrawFrame()方法中调用draw()方法:

    public void onDrawFrame(GL10 unused) {
        ...
    
        mTriangle.draw();
    }
    

    当你运行程序的时候,你就应该看到以下的内容:
    When you run the application, it should look something like this:

    此例子中的代码还有很多问题。首先,它不会打动你和你的朋友。其次,三角形会在你从竖屏变为横屏时被压扁。三角形变形的原因是其顶点们没有跟据屏幕的宽高比进行修正。而且这里展示出来的三角形是静止的,这样的图形是有点无聊的,在“添加动画”的文章中,我们会使用OpenGL ES 的视图管线来旋转此形状。

    在下一篇文章,我们将使用投影和摄像头视图来修正显示的问题。

  • 相关阅读:
    bzoj-2748 2748: [HAOI2012]音量调节(dp)
    bzoj-2338 2338: [HNOI2011]数矩形(计算几何)
    bzoj-3444 3444: 最后的晚餐(组合数学)
    codeforces 709E E. Centroids(树形dp)
    codeforces 709D D. Recover the String(构造)
    codeforces 709C C. Letters Cyclic Shift(贪心)
    codeforces 709B B. Checkpoints(水题)
    codeforces 709A A. Juicer(水题)
    Repeat Number
    hdu 1003 Max Sum (动态规划)
  • 原文地址:https://www.cnblogs.com/renhui/p/8004987.html
Copyright © 2011-2022 走看看