Android Tutorial Shaders in libGDX

Discussion in 'Tutorials & Examples' started by Informatix, Oct 8, 2014.

  1. Informatix

    Informatix Expert Licensed User

    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:
    Code:
    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:
    Code:
    '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:
    Code:
    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:
    Code:
    texDroid.Initialize("wheel.png")
    5) That's all for the B4A part. Let's have a look to the vertex shader:
    Code:
    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;
    }
    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):
    Code:
    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);
      }
    }
    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:
    Code:
    Shader.Begin
    Shader.SetUniform1f(
    "halfwidth", Width / 2)
    Shader.End
    Easy, isn't it?
     

    Attached Files:

    ilan, wonder, Jaames and 3 others like this.
  2. shashkiranr

    shashkiranr Active Member Licensed User

    You make it easy :) thank you :)
     
    Jaames likes this.
  3. walterf25

    walterf25 Well-Known Member Licensed User

    Hi Informatix, i hadn't tried your water shader example until today, and while running it for a while i noticed something that I came across a while ago, if you let the app run for a long period of time (maybe 3 to 5 minutes) you start seeing that the top part of the water (surface) starts to loose it's uniformity, i don't know if that's the right way to describe it, but i have posted 2 images, the first one shows the water as soon as the app is started, and the 2nd one shows how it looks after you run the app for a while. I followed a tutorial online to do the same effect a while back and i noticed the same behavior, i was wondering if you could maybe figure out why this happens?


    normal.jpg
    Normal Behavior


    Pixelated.jpg
    Water surface looks pixelated.

    Here is also the link to the apk file of the project I did, following a tutorial online of course, if you install it you will see the same behavior on the water waves after a while of running the app.

    https://www.dropbox.com/s/jendbj5x5p55g96/WaterShader.apk?dl=0



    Thanks Informatix, i hope you can explain why this happens and how to avoid it.

    Walter
     
    Last edited: Nov 6, 2014
  4. Jaames

    Jaames Active Member Licensed User

    +1
     
  5. Informatix

    Informatix Expert Licensed User

    Thanks for the report. I can reproduce the problem on my device. I have no idea for now about the cause. That looks like the filter goes from Linear to Nearest after some time. And the rendering becomes choppy despite it still runs at 60 fps.
     
    Last edited: Nov 6, 2014
  6. Informatix

    Informatix Expert Licensed User

    I modified the WaterSurface demo to use my SpecialFX library (which uses less wrapped code) and the result is the same.
    I will test later the original Java code with the unwrapped libGDX lib.
     
    Last edited: Nov 6, 2014
  7. Informatix

    Informatix Expert Licensed User

    OK, I've found what's the cause. Change the precision of floats to highp in the fragment shader. It is a bit strange as mediump is recommended for Android, but in this particular case, you don't have the choice. I will update the examples of libGDX later today.
     
    walterf25 likes this.
  8. wonder

    wonder Expert Licensed User

    Hi Fred,

    I haven't tried the shaders yet, but after watching some tutorial videos, I'm willing to give it a try.
    Should I use the regular lib or the SpecialFX lib? Which one do you recommend?
     
  9. Informatix

    Informatix Expert Licensed User

    I don't understand. What do you mean by the "regular lib"? Using only libGDX? You can use of course only libGDX. You will use SpecialFX only if you need it, e.g. for ping pong buffering.
     
  10. wonder

    wonder Expert Licensed User

    Yes, I meant libGDX. Sorry for not clarifying. I'd like to start experimenting with shaders, I saw some pretty interesting demos on youtube.
    My question was in the sense of understanding the advantages of your SpecialFX library.

    I don't know what ping-pong buffering is, but if you have no time to explain, don't worry, I'll find my way around the web. :)
     
  11. Informatix

    Informatix Expert Licensed User

    The ping-pong buffering technique is used when you need to do several passes of a shader. You copy the result of the initial framebuffer into another framebuffer, then you send back the result in the first framebuffer. That saves memory as you use only two framebuffers.
    SpecialFX has a few optimizations that you cannot implement with B4A and is also useful when you want to combine several shaders.
     
  12. wonder

    wonder Expert Licensed User

    Thanks!! I'll look into your examples and see what I can come up with! :)
     
  13. walterf25

    walterf25 Well-Known Member Licensed User

    Cool, sorry i had not seen your response, i now it's been a while since i posted that issue, but I will try your suggestions and let you know how it goes.

    Thanks,
    Walter
     
  14. wonder

    wonder Expert Licensed User

    Hello,

    I have a texture (dynamically generated) to which I would like to apply a certain effect.
    Is it possible to utilize the shader program in a way where u_texture represents only this texture, instead of the entire framebuffer? If so, how?

    Thanks in advance.
     
  15. wonder

    wonder Expert Licensed User

    I found a solution:

    LG_Create:
    Code:
    Batch.Initialize 
    Shader.Pedantic = 
    False
    Shader.InitializeWithFiles( _
        lGdx.Files.Internal(
    "shaderprog/vertex.txt"), _
        lGdx.Files.Internal(
    "shaderprog/fragment_color.txt")) 
    tex.Initialize(
    "wheel.png")
    tex.SetFilter(tex.FILTER_Linear, tex.FILTER_Linear)
    FBO.Initialize(fbo.FORMAT_RGBA8888, tex.Width, tex.Height)

    LG_Render:
    Code:
    '=====================================================
    'Step 1:
    '=====================================================
    'Draw the texture into a framebuffer object of the
    'same size (256x256). Custom shader is applied.
    '-----------------------------------------------------
    FBO.Begin
        
    Camera.SetToOrtho2(False, tex.Width, tex.Height)
        
    Camera.Update
        Batch.ProjectionMatrix = 
    Camera.Combined
        Batch.Shader = Shader 
        Batch.Begin
            Batch.DrawTex(tex, 
    00)
        Batch.End
    FBO.End
    '=====================================================


    '=====================================================
    'Step 2:
    '=====================================================
    'Revert back to the default shader and draw the
    'modified texture. Draw other stuff.
    '-----------------------------------------------------
    Camera.SetToOrtho2( _
        
    False, lGdx.Graphics.Width, lGdx.Graphics.Height)
    Camera.Update
    Batch.ProjectionMatrix = 
    Camera.Combined
    Batch.Shader = Batch.CreateDefaultShader
    Batch.Begin
        Batch.DrawTex(FBO.ColorBufferTexture, 
    300300)
        Batch.DrawTex(SomeOtherTexture, 
    100100)
    Batch.End
    '=====================================================
     
    Last edited: Nov 19, 2017
  16. Informatix

    Informatix Expert Licensed User

    u_texture is the texture on which the shader is applied, so I don't understand what you mean. If you look at the shader examples of LibGDX, I don't use any FrameBuffer (and if you use a FrameBuffer, you apply the shader to the texture of this FrameBuffer when rendering). ShaderProgram_BrightnessContrast for example shows how to apply the same shader to different textures displayed on the same screen, with different settings for different results.
     
  17. wonder

    wonder Expert Licensed User

    What I meant was, from what I understand, the u_texture object in the shader program always refers to the "yet to be rendered" contents (framebuffer) of lgBatch.
    After looking at you example, as you suggested, I see that after each shader pass you call the Flush method.
    I think I start to understand how it works now...
     
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice