原文: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:
当你运行这个应用,看起来是这样子的。
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绘图管道做一些更有意思的事情。下一课