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 的视图管线来旋转此形状。

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

  • 相关阅读:
    作业信息获取
    SQL Server数据库性能监控计数器
    c# 索引器學習
    C# DataGridView 小技巧
    C# DataTable 小技巧
    c# 小技巧
    C# new,Virtual,abstract,sealed,Override的簡單理解
    C# 位操作符
    C# 類型小技巧
    C# partial 局部类型
  • 原文地址:https://www.cnblogs.com/renhui/p/8004987.html
Copyright © 2011-2022 走看看