在 OpenGL 开发中,肯定会接触到顶点着色器(vertex shader)和片段着色器(fragment shader),着色器语言的语法这里不做过多介绍,下面是一些着色器的示例:
OpenGL ES 2.0 顶点着色器示例:
uniform mat4 u_Matrix; // 统一变量
attribute vec4 a_Position; // 属性(输入)
void main() {
gl_Position = u_Matrix * a_Position;
}
OpenGL ES 3.0 顶点着色器示例:
#version 300 es
uniform mat4 u_Matrix; // 统一变量
layout(location = 0) in vec4 v_Position; // 属性(输入)
void main() {
gl_Position = u_Matrix * v_Position;
}
在OpenGL ES 2.0 中,使用 attribute
和 varying
表示输入输出变量;而在 OpenGL ES 3.0 中,直接使用 in
、out
表示输入输出变量。虽然存在这样的差异,但是传值的接口和参数都是一致的。
那么,在应用程序中,是如何将数据传入到这些统一变量和属性中的呢?
后面的接口都是提供的 c++ 接口,java 接口函数命名和参数均与之相同,仅做了一次 native 的调用。
统一变量(uniform)是存储应用程序通过 API 传递给着色器的 只读常量 数值的变量。例如上例中的 u_Matrix
。
如果统一变量在顶点着色器和片段着色器中均有声明,则声明的类型必须相同,且值也需相同。
我们想要给某个变量赋值,首先我们要获取到它,之后才能操作它。通过下面这个接口我们可以获取到一个统一变量。
GLint glGetUniformLocation(GLuint program, const GLchar *name);
参数解释:
u_Matrix
。值得注意的是,在 OpenGL ES 3.0 中,我们可以通过 layout(location = 0)
的形式直接指定变量的位置。
通过上述函数我们可以获取到统一变量的 location
,接下来通过这个 location 我们就可以给其赋值了。
给统一变量赋值有很多函数,不同的统一变量类型对应不同的函数,如下:
void glUniform1f(GLint location, GLfloat v0);
void glUniform1fv(GLint location, GLsizei count, const GLfloat *value);
void glUniform1i(GLint location, GLint v0);
void glUniform1iv(GLint location, GLsizei count, const GLint *value);
void glUniform2f(GLint location, GLfloat v0, GLfloat v1);
void glUniform2fv(GLint location, GLsizei count, const GLfloat *value);
void glUniform2i(GLint location, GLint v0, GLint v1);
void glUniform2iv(GLint location, GLsizei count, const GLint *value);
void glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
void glUniform3fv(GLint location, GLsizei count, const GLfloat *value);
void glUniform3i(GLint location, GLint v0, GLint v1, GLint v2);
void glUniform3iv(GLint location, GLsizei count, const GLint *value);
void glUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
void glUniform4fv(GLint location, GLsizei count, const GLfloat *value);
void glUniform4i(GLint location, GLint v0, GLint v1, GLint v2, GLint v3);
void glUniform4iv(GLint location, GLsizei count, const GLint *value);
void glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
void glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
void glUniformMatrix2x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
void glUniformMatrix3x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
void glUniformMatrix2x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
void glUniformMatrix4x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
void glUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
void glUniformMatrix4x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
参数解释:
uniform mat4 u_matrix
那么数量就是 1,如果是 uniform vec3 u_Position[3]
那么数量就是 3。GL_FALSE
)还是行顺序优先(GL_TRUE
)。这里的属性只是指顶点着色器的输入变量,因为片段着色器的输入是顶点着色器的输出。
在 OpenGL ES 2.0 中,是由 attribute
修饰的变量,而在 OpenGL ES 3.0 中,则是由 in
或者 inout
修饰的变量。
同理,首先我们要获取到它,才能操作它。通过下面这个接口我们可以获取到一个统一变量。
GLint glGetAttribLocation(GLuint program, const GLchar *name);
参数解释:
a_Position
。值得注意的是,在 OpenGL ES 3.0 中,我们可以通过 layout(location = 0)
的形式直接指定变量的位置。
通过上述函数我们可以获取属性的 location
,接下来通过这个 location 我们就可以给其赋值了。
常量顶点属性对于一个图元的所有顶点都相同,所以对一个图元的所有顶点只需指定一个值,使用如下函数传值:
void glVertexAttrib1f(GLuint index, GLfloat x);
void glVertexAttrib1fv(GLuint index, const GLfloat *v);
void glVertexAttrib2f(GLuint index, GLfloat x, GLfloat y);
void glVertexAttrib2fv(GLuint index, const GLfloat *v);
void glVertexAttrib3f(GLuint index, GLfloat x, GLfloat y, GLfloat z);
void glVertexAttrib3fv(GLuint index, const GLfloat *v);
void glVertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
void glVertexAttrib4fv(GLuint index, const GLfloat *v);
其中顶点的默认值为 (0.0, 0.0, 0.0, 1.0)
,上述函数分别覆盖指定的值。
顶点数组指定每个顶点的属性,使用如下函数传值:
void glVertexAttribPointer(GLuint index,
GLint size,
GLenum type,
GLboolean normalized,
GLsizei stride,
const void *pointer);
void glVertexAttribIPointer(GLuint index,
GLint size,
GLenum type,
GLsizei stride,
const void *pointer);
参数解释:
GL_BYTE
,GL_UNSIGNED_BYTE
,GL_SHORT
,GL_UNSIGNED_SHORT
,GL_INT
,GL_UNSIGNED_INT
,GL_FLOAT
,GL_HALF_FLOAT
,GL_FIXED
等。分别有一个统一变量 u_Matrix
和属性 a_Position
。
uniform mat4 u_Matrix;
attribute vec4 a_Position;
void main() {
gl_Position = u_Matrix * a_Position;
}
#include
#include "ShaderUtils.h"
const char* vertexShaderSource =
"uniform mat4 u_Matrix;\n"
"attribute vec4 a_Position;\n"
"\n"
"void main() {\n"
" gl_Position = u_Matrix * a_Position;\n"
"}";
const char* fragmentShaderSource =
"precision mediump float;\n"
"\n"
"uniform vec4 u_Color;\n"
"\n"
"void main() {\n"
" gl_FragColor = u_Color;\n"
"}";
GLuint g_program; // program id
void demo() {
// 1. 创建着色器程序
g_program = CreateProgram(vertexShaderSource, fragmentShaderSource);
glUseProgram(g_program);
// 2. 获取统一变量和属性
GLint matrix_location = glGetUniformLocation(g_program, "u_Matrix");
GLint position_location = glGetAttribLocation(g_program, "a_Position");
// 3. 传值
const GLfloat matrix[] = {
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
};
glUniformMatrix4fv(matrix_location, 1, GL_FALSE, matrix);
const GLint vertex_size = 3; // 我们的顶点数组只有x, y, z三个分量
const GLfloat vertices[] = {
0.0f, 0.5f, 0.0f, // 第一个点(x, y, z)
-0.5f, -0.5f, 0.0f, // 第二个点(x, y, z)
0.5f, -0.5f, 0.0f // 第三个点(x, y, z)
};
glVertexAttribPointer(position_location, vertex_size, GL_FLOAT, GL_FALSE, 0, vertices);
glEnableVertexAttribArray(position_location); // 需要使用enable使其生效
// 4. 绘制
glDrawArrays(GL_TRIANGLES, 0, vertex_size);
glDisableVertexAttribArray(position_location);
}
上面的代码不一定能运行,主要就是展示一下基本的使用方式。当然,上面介绍的这些内容也是基础中的基础了,对初学者有所帮助就行。