(三)绘制形状(Drawing Shapes)

原文: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绘制形状需要大量的代码,因为你需要为绘制管道提供大量的信息。尤其需要提供以下的信息:

  • Vertex Shader - OpenGL ES graphics code for rendering the vertices of a shape.
    顶点Shader-OpenGL 用来绘制形状的顶点

  • Fragment Shader - OpenGL ES code for rendering the face of a shape with colors or textures.
    碎片shader-OpenGL ES用来绘形状面的颜色和texture(乱猜的,专业术语暂时跳过😪)

  • Program - An OpenGL ES object that contains the shaders you want to use for drawing one or more shapes.
    Program- 一个OpenGL ES对象,包含了你需要用来绘制形状的shaders.

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. 这些操作只需要做一次,所以放在形状对象的构造器中。

  • Note: Compiling OpenGL ES shaders and linking programs is expensive in terms of CPU cycles and processing time, so you should avoid doing this more than once. If you do not know the content of your shaders at runtime, you should build your code such that they only get created once and then cached for later use.
    编译OpenGL ES shaders和连接program很耗CPU资源,所以避免重复操作。如果在runtime时不知道shaders的内容,你应该确保这些shaders只创建一次,并且缓存以便后续使用(😤你妹,连个粟子都不举)
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绘图管道做一些更有意思的事情。下一课

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

推荐阅读更多精彩内容