如果你想使用OpenGL ES 2.0,你应该知道一些着色器的基础知识。 Libgdx附带了一个标准着色器,它将通过SpriteBatch处理渲染内容。 但是,如果要在Opengl ES 2.0中渲染网格,您将必须自己提供一个有效的着色器。 基本上在Opengl ES 2.0中,一切都是用着色器渲染的。 这就是为什么它被称为可编程管道。 在着色器中徘徊的想法可能会吓倒一些使用ES 2.0的人,但是值得仔细去阅读,因为着色器允许您做一些非常不可思议的事情。 而理解基础实际上是很直观的。
What are shaders?
OpenGL中的着色器是用C语言编写的小程序,称为GLSL,它在GPU上运行,并处理渲染事物所需的数据。 着色器可以简单地被视为GPU上的处理阶段。 它接收一组输入,您可以执行一组操作,最后将其重新发送出去。 想像这样的功能参数和返回值。 通常在OpenGL ES 2.0中呈现某些内容时,数据将首先通过顶点着色器发送,然后通过Fragment着色器发送。
顶点着色器
顾名思义,顶点着色器负责对顶点执行操作。 更具体地说,程序的每次执行都在一个顶点上运行。 这是一个重要的理解。 在顶点着色器中所做的一切只发生在一个顶点上。
这是一个简单的顶点着色器:
attribute vec4 a_position;
uniform mat4 u_projectionViewMatrix;
void main(){
gl_Position = u_projectionViewMatrix * a_position;
}
那看起来不错,现在呢? 首先你有一个名为a_position的顶点属性。 这个属性是一个vec4,这意味着它是一个4维的向量。 在该样本中,它保存顶点的位置信息。
接下来你有u_projectionViewMatrix。 这是一个保存视图和投影变换数据的4x4矩阵。 如果这些术语对你来说模糊,我建议您阅读这些主题.理解它是非常有用的。
在main方法中,我们对顶点执行操作。 在这种情况下,着色器所有顶点位置与矩阵相乘并将其分配给gl_Position。 gl_Position是OpenGL中预定义的关键字,不能用于其他任何东西,只能通过处理的顶点。
Fragment shaders
片段着色器以与顶点着色器作用非常相似。 但是不是在顶点上处理它,而是为每个片段处理它一次。 为了简单起见,将片段看作一个像素。 现在您可能会注意到这是一个非常显着的区别。
假设三角形覆盖300像素的区域。 这个三角形的顶点着色器将被执行3次。 片段着色器虽然可执行300次。 所以当编写着色器时,请牢记这一点。 在片段着色器中所做的一切都将呈指数级增加!
这是一个非常基本的片段着色器:
void main(){
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
这个片段着色器将简单地渲染每个片段的颜色为纯红色。 gl_FragColor是另一个预定义的关键字。 它用于输出片段的最终颜色。 注意我们如何使用vec4(x,y,z,w)来定义着色器内的向量。 在这种情况下,矢量用于定义片段的颜色。
一个简单的Shader程序
现在我们对着色器的作用和工作原理有一个基本的了解,让我们在libgdx中创建一个Demo。 这是通过ShaderProgram类完成的。 ShaderProgram由顶点着色器和片段着色器组成。 您可以从文件加载,也可以传入字符串,并将着色器代码保存在java文件中。
这是着色器的设置:
String vertexShader = "attribute vec4 a_position; \n" +
"attribute vec4 a_color;\n" +
"attribute vec2 a_texCoord0;\n" +
"uniform mat4 u_projTrans;\n" +
"varying vec4 v_color;" +
"varying vec2 v_texCoords;" +
"void main() \n" +
"{ \n" +
" v_color = vec4(1, 1, 1, 1); \n" +
" v_texCoords = a_texCoord0; \n" +
" gl_Position = u_projTrans * a_position; \n" +
"} \n" ;
String fragmentShader = "#ifdef GL_ES\n" +
"precision mediump float;\n" +
"#endif\n" +
"varying vec4 v_color;\n" +
"varying vec2 v_texCoords;\n" +
"uniform sampler2D u_texture;\n" +
"void main() \n" +
"{ \n" +
" gl_FragColor = v_color * texture2D(u_texture, v_texCoords);\n" +
"}";
这是相当标准的使用位置属性,颜色属性和纹理坐标属性的着色器设置。
这是libgdx自己的库中的属性列表,用于那些希望轻松地将着色器附加到SpriteBatchs和libgdx的其他部分的库中:
a_position
a_normal
a_color
a_texCoord, requires a number at the end, i.e. a_texCoord0, a_texCoord1, etc...
a_tangent
a_binormal
要创建ShaderProgram,我们执行以下操作:
ShaderProgram shader = new ShaderProgram(vertexShader, fragmentShader);
您可以通过shader.isCompiled()确保着色器正确编译。 编译日志可以使用shader.getLog()发出。
我们还创建一个匹配的网格并加载纹理:
mesh = new Mesh(true, 4, 6, VertexAttribute.Position(), VertexAttribute.ColorUnpacked(), VertexAttribute.TexCoords(0));
mesh.setVertices(new float[]
{-0.5f, -0.5f, 0, 1, 1, 1, 1, 0, 1,
0.5f, -0.5f, 0, 1, 1, 1, 1, 1, 1,
0.5f, 0.5f, 0, 1, 1, 1, 1, 1, 0,
-0.5f, 0.5f, 0, 1, 1, 1, 1, 0, 0});
mesh.setIndices(new short[] {0, 1, 2, 2, 3, 0});
texture = new Texture(Gdx.files.internal("data/bobrgb888-32x32.png"));
在render方法中,我们简单地调用shader.begin()并传递uniforms ,然后使用着色器渲染网格:
texture.bind();
shader.begin();
shader.setUniformMatrix("u_projTrans", matrix);
shader.setUniformi("u_texture", 0);
mesh.render(shader, GL20.GL_TRIANGLES);
shader.end();
就是这样!
OpenGL ES 2.0中的着色器的好处是,您可以使用一个巨大的着色器库。 在WebGL中完成的任何事情都可以轻松地移植到手机上运行。 去做个实验吧!
网友评论