开始正式学习OpenGL ES开发!
本博客是我在学习过程中做的记录,也希望和各位分享我的学习过程,如有错误,欢迎留言指正,共同学习。
定义输入坐标
开始绘制图形之前,我们必须先给OpenGL输入一些顶点数据。OpenGL是一个3D图形库,所以我们在OpenGL中指定的所有坐标都是3D坐标(x、y和z)。OpenGL不是简单地把所有的3D坐标变换为屏幕上的2D像素;OpenGL仅当3D坐标在3个轴(x、y和z)上都为-1.0到1.0的范围内时才处理它。所有在所谓的标准化设备坐标(Normalized Device Coordinates)范围内的坐标才会最终呈现在屏幕上(在这个范围以外的坐标都不会显示)。
由于我们希望渲染一个三角形,我们一共要指定三个顶点,每个顶点都有一个3D位置。我们会将它们以标准化设备坐标的形式(OpenGL的可见区域)定义为一个float
数组。
1 2 3 4 5
| private float[] vertexPoints = new float[]{ 0.0f, 0.5f, 0.0f, -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f };
|
由于OpenGL是在3D空间中工作的,而我们渲染的是一个2D三角形,我们将它顶点的z坐标设置为0.0。这样子的话三角形每一点的深度都是一样的,从而使它看上去像是2D的。
定义这样的顶点数据以后,我们会把它作为输入发送给图形渲染管线的第一个处理阶段:顶点着色器。它会在GPU上创建内存用于储存我们的顶点数据,还要配置OpenGL如何解释这些内存,并且指定其如何发送给显卡。顶点着色器接着会处理我们在内存中指定数量的顶点。
一旦你的顶点坐标已经在顶点着色器中处理过,它们就应该是标准化设备坐标了,标准化设备坐标是一个x、y和z值在-1.0到1.0的一小段空间。任何落在范围外的坐标都会被丢弃/裁剪,不会显示在你的屏幕上。下面你会看到我们定义的在标准化设备坐标中的三角形(忽略z轴):
与通常的屏幕坐标不同,y轴正方向为向上,(0, 0)坐标是这个图像的中心,而不是左上角。最终你希望所有(变换过的)坐标都在这个坐标空间中,否则它们就不可见了。
分配本地内存
因为OpenGL
作为本地系统库运行在系统中,虚拟机需要分配本地内存,供其存取。
1 2 3 4 5 6 7 8 9
| public SimpleRenderer() { vertexBuffer = ByteBuffer.allocateDirect(vertexPoints.length * 4) .order(ByteOrder.nativeOrder()) .asFloatBuffer(); vertexBuffer.put(vertexPoints); vertexBuffer.position(0); }
|
顶点着色器
1 2 3 4 5 6 7 8 9 10
|
private String vertextShader = "#version 300 es\n" + "layout (location = 0) in vec4 vPosition;\n" + "void main() {\n" + " gl_Position = vPosition;\n" + " gl_PointSize = 10.0;\n" + "}\n";
|
输入属性的数组
(一个名为vPosition
的4分量向量),layout (location = 0)
表示这个变量的位置是顶点属性0。
将vPosition
输入属性拷贝到名为gl_Position
的特殊输出变量。
将浮点数据10.0
拷贝到gl_PointSize
的变量中。
片段着色器
1 2 3 4 5 6 7 8 9 10
|
private String fragmentShader = "#version 300 es\n" + "precision mediump float;\n" + "out vec4 fragColor;\n" + "void main() {\n" + " fragColor = vec4(1.0,1.0,1.0,1.0);\n" + "}\n";
|
声明着色器中浮点变量的默认精度。
着色器声明一个输出变量fragColor
,这个是一个4分量的向量。
表示将颜色值(1.0,1.0,1.0,1.0)
,输出到颜色缓冲区。
编译着色器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
private static int compileShader(int type, String shaderCode) { final int shaderId = GLES30.glCreateShader(type); if (shaderId != 0) { GLES30.glShaderSource(shaderId, shaderCode); GLES30.glCompileShader(shaderId); final int[] compileStatus = new int[1]; GLES30.glGetShaderiv(shaderId, GLES30.GL_COMPILE_STATUS, compileStatus, 0); if (compileStatus[0] == 0) { String logInfo = GLES30.glGetShaderInfoLog(shaderId); System.err.println(logInfo); GLES30.glDeleteShader(shaderId); return 0; } return shaderId; } else { return 0; } }
|
创建 OpenGL 程序和着色器链接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
public static int linkProgram(int vertexShaderId, int fragmentShaderId) { final int programId = GLES30.glCreateProgram(); if (programId != 0) { GLES30.glAttachShader(programId, vertexShaderId); GLES30.glAttachShader(programId, fragmentShaderId); GLES30.glLinkProgram(programId); final int[] linkStatus = new int[1]; GLES30.glGetProgramiv(programId, GLES30.GL_LINK_STATUS, linkStatus, 0); if (linkStatus[0] == 0) { String logInfo = GLES30.glGetProgramInfoLog(programId); System.err.println(logInfo); GLES30.glDeleteProgram(programId); return 0; } return programId; } else { return 0; } }
|
绘制
准备工作结束,下来就开始绘制图形了。
1
| public class SimpleRenderer implements GLSurfaceView.Renderer
|
实现GLSurfaceView.Renderer
接口
onSurfaceCreated
1 2 3 4 5 6 7 8 9 10 11 12
| @Override public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) { GLES30.glClearColor(0.5f, 0.5f, 0.5f, 0.5f);
final int vertexShaderId = compileShader(GLES30.GL_VERTEX_SHADER, vertextShader); final int fragmentShaderId = compileShader(GLES30.GL_FRAGMENT_SHADER, fragmentShader); GLES30.glUseProgram(linkProgram(vertexShaderId, fragmentShaderId)); }
|
onSurfaceChanged
1 2 3 4 5 6
| @Override public void onSurfaceChanged(GL10 gl10, int width, int height) { GLES30.glViewport(0, 0, width, height); }
|
onDrawFrame
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @Override public void onDrawFrame(GL10 gl10) { GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer); GLES30.glEnableVertexAttribArray(0);
GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 3);
GLES30.glDisableVertexAttribArray(0); }
|
点 GLES30.GL_POINTS
线 GLES30.GL_LINE_STRIP
三角形 GLES30.GL_TRIANGLES
通过glDrawArrays
方法来执行最后的绘制,GL_POINTS
代表绘制的类型(图元类型),而参数0,1
则代表绘制的点的范围,它是一个左闭右开的区间。
常用图元类型
图元类型 |
描述 |
GL_POINTS |
点精灵图元,对指定的每个顶点进行绘制。 |
GL_LINES |
绘制一系列不相连的线段。 |
GL_LINE_STRIP |
绘制一系列相连的线段。 |
GL_LINE_LOOP |
绘制一系列相连的线段,首尾相连。 |
GL_TRIANGLES |
绘制一系列单独的三角形。 |
GL_TRIANGLE_STRIP |
绘制一系列相互连接的三角形。 |
GL_TRIANGLE_FAN |
绘制一系列相互连接的三角形 |
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
| public class SimpleRenderer implements GLSurfaceView.Renderer { private float[] vertexPoints = new float[]{ 0.0f, 0.5f, 0.0f, -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f }; private final FloatBuffer vertexBuffer;
private String vertextShader = "#version 300 es\n" + "layout (location = 0) in vec4 vPosition;\n" + "void main() {\n" + " gl_Position = vPosition;\n" + " gl_PointSize = 10.0;\n" + "}\n";
private String fragmentShader = "#version 300 es\n" + "precision mediump float;\n" + "out vec4 fragColor;\n" + "void main() {\n" + " fragColor = vec4(1.0,1.0,1.0,1.0);\n" + "}\n";
public SimpleRenderer() { vertexBuffer = ByteBuffer.allocateDirect(vertexPoints.length * 4) .order(ByteOrder.nativeOrder()) .asFloatBuffer(); vertexBuffer.put(vertexPoints); vertexBuffer.position(0); }
@Override public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) { GLES30.glClearColor(0f, 0f, 0f, 0f);
final int vertexShaderId = compileShader(GLES30.GL_VERTEX_SHADER, vertextShader); final int fragmentShaderId = compileShader(GLES30.GL_FRAGMENT_SHADER, fragmentShader); GLES30.glUseProgram(linkProgram(vertexShaderId, fragmentShaderId)); }
@Override public void onSurfaceChanged(GL10 gl10, int width, int height) { GLES30.glViewport(0, 0, width, height); }
@Override public void onDrawFrame(GL10 gl10) { GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT); GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer); GLES30.glEnableVertexAttribArray(0); GLES30.glDrawArrays(GLES30.GL_LINE_LOOP, 0, 3);
GLES30.glDisableVertexAttribArray(0); }
private static int compileShader(int type, String shaderCode) { final int shaderId = GLES30.glCreateShader(type); if (shaderId != 0) { GLES30.glShaderSource(shaderId, shaderCode); GLES30.glCompileShader(shaderId); final int[] compileStatus = new int[1]; GLES30.glGetShaderiv(shaderId, GLES30.GL_COMPILE_STATUS, compileStatus, 0); if (compileStatus[0] == 0) { String logInfo = GLES30.glGetShaderInfoLog(shaderId); System.err.println(logInfo); GLES30.glDeleteShader(shaderId); return 0; } return shaderId; } else { return 0; } }
public static int linkProgram(int vertexShaderId, int fragmentShaderId) { final int programId = GLES30.glCreateProgram(); if (programId != 0) { GLES30.glAttachShader(programId, vertexShaderId); GLES30.glAttachShader(programId, fragmentShaderId); GLES30.glLinkProgram(programId); final int[] linkStatus = new int[1]; GLES30.glGetProgramiv(programId, GLES30.GL_LINK_STATUS, linkStatus, 0); if (linkStatus[0] == 0) { String logInfo = GLES30.glGetProgramInfoLog(programId); System.err.println(logInfo); GLES30.glDeleteProgram(programId); return 0; } return programId; } else { return 0; } }
}
public class MainActivity extends AppCompatActivity { private GLSurfaceView mGLSurfaceView;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setupViews(); }
private void setupViews() { mGLSurfaceView = new GLSurfaceView(this); setContentView(mGLSurfaceView); mGLSurfaceView.setEGLContextClientVersion(3); GLSurfaceView.Renderer renderer = new SimpleRenderer(); mGLSurfaceView.setRenderer(renderer); } }
|
参考:
《OpenGL ES 3.0 编程指南第2版》
《OpenGL ES应用开发实践指南Android卷》