如果你想使用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中完成的任何事情都可以輕松地移植到手機上運行。 去做個實驗吧!