OpenGL ES

(三)绘制形状(Drawing Shapes)

2017-03-20  本文已影响169人  thebestofrocky

原文:https://developer.android.com/training/graphics/opengl/draw.html

After you define shapes to be drawn with OpenGL, you probably want to draw them. Drawing shapes with the OpenGL ES 2.0 takes a bit more code than you might imagine, because the API provides a great deal of control over the graphics rendering pipeline.
定义了形状之后,接下来就是绘制形状。绘制这些形状需要的代码比你想象的要多一些,因为有大量控制绘图管道控制的API。

This lesson explains how to draw the shapes you defined in the previous lesson using the OpenGL ES 2.0 API.
这里学习如何用OpenGL ES 2.0 API 绘制上一课节定义的图形。

Initialize Shapes

初始化形状

Before you do any drawing, you must initialize and load the shapes you plan to draw. Unless the structure (the original coordinates) of the shapes you use in your program change during the course of execution, you should initialize them in the onSurfaceCreated() method of your renderer for memory and processing efficiency.
在绘制之前,必须初始化和加载那些要画的形状。为了提高内存和处理效率,应该在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();
    }
    ...
}

Draw a Shape

绘制形状

Drawing a defined shape using OpenGL ES 2.0 requires a significant amount of code, because you must provide a lot of details to the graphics rendering pipeline. Specifically, you must define the following:
使用OpenGL ES 2.0绘制形状需要大量的代码,因为你需要为绘制管道提供大量的信息。尤其需要提供以下的信息:

You need at least one vertex shader to draw a shape and one fragment shader to color that shape. These shaders must be complied and then added to an OpenGL ES program, which is then used to draw the shape. Here is an example of how to define basic shaders you can use to draw a shape in the Triangle class:
你至少需要一个顶点shader来绘制形状,一个碎片shader来给这个形状着色。这些shaders必须先编译,然后添加到一个OpenGL ES program,然后用这个program来绘制形状。下面这个例子描述了怎么定义一些基本的shaders来绘制三角形。

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;" +
        "}";

    ...
}

Shaders contain OpenGL Shading Language (GLSL) code that must be compiled prior to using it in the OpenGL ES environment. To compile this code, create a utility method in your renderer class:
Shaders包含OpenGL Shading语言代码,在OpenGL ES环境中使用这些代码之前必须先编译。在render类里建立一个utility方法来编译这些代码。

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;
}

In order to draw your shape, you must compile the shader code, add them to a OpenGL ES program object and then link the program. Do this in your drawn object’s constructor, so it is only done once.
绘制形状时,你必须先编译shader代码,然后把编译好的shader加到OpenGL ES program对象里,再连接program. 这些操作只需要做一次,所以放在形状对象的构造器中。

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);
    }
}

At this point, you are ready to add the actual calls that draw your shape. Drawing shapes with OpenGL ES requires that you specify several parameters to tell the rendering pipeline what you want to draw and how to draw it. Since drawing options can vary by shape, it's a good idea to have your shape classes contain their own drawing logic.
现在你可以真正地开始画图了。用OpenGL ES绘图时,需要你明确一些参数,告诉绘图管道你想要画什么,怎么画。因为不同的图形需要的参数不一样,所以把绘图相关的逻辑让在不同的形状类里面比较方便。

Create a draw() method for drawing the shape. This code sets the position and color values to the shape’s vertex shader and fragment shader, and then executes the drawing function.
创建一个draw()方法用来绘图。在里面设定顶点shader的位置值和碎片shader的颜色值。然后调用这个方法。

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);
}

Once you have all this code in place, drawing this object just requires a call to the draw() method from within your renderer’s onDrawFrame() method:
一旦你写好了这些代码,在render的onDrawFrame()方法中调用就好了。

public void onDrawFrame(GL10 unused) {
    ...

    mTriangle.draw();
}

When you run the application, it should look something like this:
当你运行这个应用,看起来是这样子的。


Figure 1. Triangle drawn without a projection or camera view.

There are a few problems with this code example. First of all, it is not going to impress your friends. Secondly, the triangle is a bit squashed and changes shape when you change the screen orientation of the device. The reason the shape is skewed is due to the fact that the object’s vertices have not been corrected for the proportions of the screen area where the GLSurfaceView is displayed. You can fix that problem using a projection and camera view in the next lesson.
这个例子还有一些问题。首先,很low; 然后三角形有点变形,当屏幕旋转后,形状也会变化。之所以会变形是因为,在显示GLSurfaceView的屏幕上,三角形的顶点没有设置正确。在下节课中,你可以用投影和相机视角来解决这个问题。🙄

Lastly, the triangle is stationary, which is a bit boring. In the Adding Motion lesson, you make this shape rotate and make more interesting use of the OpenGL ES graphics pipeline.
最后,三角形是固定的,没意思。在附加的运动课程中,你可以让三角形旋转,利用OpenGL ES绘图管道做一些更有意思的事情。下一课

上一篇下一篇

猜你喜欢

热点阅读