In this tutorial, I'm going to modify the Perf_Flakes demo of the libGDX library to explain how to use shaders with your objects rendered by lgSpriteBatch. I'm NOT going to explain the basics of OpenGL, what are shaders and how to write a GLSL program. There are plenty of tutorials for that on Internet.
All the files can be downloaded at the end of this post.
1) I declare my Shader object in Globals:
2) I add all the code to compile my shader program in LG_Create, and I check that the compilation is OK:
3) I replace the default shader of my renderer with this new shader:
4) For this demo, I prefer to use a more colored image, so I replace the droid PNG by the wheel PNG:
5) That's all for the B4A part. Let's have a look to the vertex shader:
This is the default vertex shader used internally by lgSpriteBatch. I keep it unchanged. LibGDX will pass automatically the color and the texture coordinates of each vertex to this code. The renderer will pass also the combined matrix (projection+transformation).
6) The fragment shader (also called pixel shader):
What do I do here ? I swap the three color components of the pixel if the green value is above 0.7, otherwise I turn the colors to gray. So my wheel image will have gray and colored parts, and the colored part will be rendered with altered colors (pink instead of yellow/green).
To show the difference, I add another condition which applies the effect only on a half of the display. For this condition to work, I need to know what's the value of the viewport width / 2. So I add an uniform, "halfwidth", and I set this uniform in my B4A code, in LG_Resize:
Easy, isn't it?
All the files can be downloaded at the end of this post.
1) I declare my Shader object in Globals:
B4X:
Dim Shader As lgShaderProgram
2) I add all the code to compile my shader program in LG_Create, and I check that the compilation is OK:
B4X:
'Compiles the vertex and the fragment shaders
Shader.Pedantic = False
Shader.InitializeWithFiles(lGdx.Files.Internal("shaderprog/vertex.txt"), _
lGdx.Files.Internal("shaderprog/fragment_color.txt"))
'Is the shader program compiled?
If Not(Shader.IsCompiled) Then
Log("Could not compile shader: " & Shader.Log)
Return
End If
'Logs any warning
If Shader.Log.Length <> 0 Then
Log(Shader.Log)
End If
3) I replace the default shader of my renderer with this new shader:
B4X:
Batch.Shader = Shader
4) For this demo, I prefer to use a more colored image, so I replace the droid PNG by the wheel PNG:
B4X:
texDroid.Initialize("wheel.png")
5) That's all for the B4A part. Let's have a look to the vertex shader:
B4X:
attribute vec4 a_position;
attribute vec4 a_color;
attribute vec2 a_texCoord0;
uniform mat4 u_projTrans;
varying vec4 vColor;
varying vec2 vTexCoord;
void main() {
vColor = a_color;
vColor.a = vColor.a * (256.0/255.0);
vTexCoord = a_texCoord0;
gl_Position = u_projTrans * a_position;
}
6) The fragment shader (also called pixel shader):
B4X:
precision mediump float;
varying lowp vec4 vColor;
varying vec2 vTexCoord;
uniform sampler2D u_texture;
uniform float halfwidth;
void main()
{
vec3 color = texture2D(u_texture, vTexCoord).rgb;
if (gl_FragCoord.x > halfwidth)
{
gl_FragColor = vColor * texture2D(u_texture, vTexCoord);
}
else if (color.g > 0.7)
{
vec3 colorswapped = vec3(color.g, color.b, color.r);
gl_FragColor = vec4(colorswapped, texture2D(u_texture, vTexCoord).a);
}
else
{
float gray = (color.r + color.g + color.b) / 3.0;
vec3 grayscale = vec3(gray);
gl_FragColor = vec4(grayscale, texture2D(u_texture, vTexCoord).a);
}
}
To show the difference, I add another condition which applies the effect only on a half of the display. For this condition to work, I need to know what's the value of the viewport width / 2. So I add an uniform, "halfwidth", and I set this uniform in my B4A code, in LG_Resize:
B4X:
Shader.Begin
Shader.SetUniform1f("halfwidth", Width / 2)
Shader.End
Easy, isn't it?