Search code examples
libgdxglslshaderparallax

Libgdx Parallax shader


I'm trying to make a shader-backed parallax effect using shader code listed here. But what I get is only a white screen.

Also the forum post here says that "code doesn't use a SpriteBatch, it renders the Mesh directly with the texture parameters set up in the shader".

I've never seen ShaderProgram to be used without setting it to a batch before, so might be I'm missing something very fundamental here.

 public class Parallax {

        private final ShaderProgram parallaxShader;
        private final FullscreenQuad fullscreenQuad;
        private final Array<Texture> backgrounds;

        public Parallax(final String parallaxTexturesSubfolderName) {
            parallaxShader = initShader();
            fullscreenQuad = new FullscreenQuad();

            backgrounds = populateBackgrounds(parallaxTexturesSubfolderName);
        }

        private Array<Texture> populateBackgrounds(String parallaxTexturesSubfolderName) {
            final Array<Texture> textures = new Array<Texture>();
            for (int i = 0; i < 4; i++) {
                final Texture texture = new Texture(Gdx.files.internal("parallax/" + parallaxTexturesSubfolderName + "/" + "img" + i + ".png"));
                  texture.setWrap(Texture.TextureWrap.MirroredRepeat, Texture.TextureWrap.MirroredRepeat);
                textures.add(texture);
            }
            return textures;
        }


        private ShaderProgram initShader() {
            ShaderProgram.pedantic = false;
            final String vertexShader = Gdx.files.internal("shaders/parallax-vert.glsl").readString();
            final String fragmentShader = Gdx.files.internal("shaders/parallax-frag.glsl").readString();

            final ShaderProgram shader = new ShaderProgram(vertexShader, fragmentShader);
            if (!shader.isCompiled()) {
                Gdx.app.log("ShaderTest", shader.getLog());
                Gdx.app.exit();
            }
            return shader;
        }


        public void render(final Camera camera, final SpriteBatch batch) {
    //        batch.setShader(parallaxShader); //tried with batch also - doesn't seems to work
    //        batch.begin(); 

            for (int i = 0; i < backgrounds.size; i++) {
                backgrounds.get(i).bind(i);
            }

            float travelDistance = camera.position.x / 800f / 15f;
            parallaxShader.begin();
//            parallaxShader.setUniformMatrix("u_projTrans", camera.combined); 
            parallaxShader.setUniformf("travelDistance", travelDistance);
            parallaxShader.setUniformi("u_texture0", 0);
            parallaxShader.setUniformi("u_texture1", 1);
            parallaxShader.setUniformi("u_texture2", 2);
            parallaxShader.setUniformi("u_texture3", 3);
            fullscreenQuad.render(parallaxShader);
            parallaxShader.end();
    //        batch.setShader(null);
    //        batch.end();
        }

        public void dispose() {
          ...
        }
    }

FullscreenQuad is listed here. Thanks!


Solution

  • That FullscreenQuad class uses a_texCoord0 for the UVs name, but the shader from the tutorial uses a_texCoords. These names need to match.

    To avoid difficult-to-debug problems like this, I would recommend not turning off ShaderProgram.pedantic while doing development builds. But do use it for releases to avoid hard crashes.

    Unrelated to your issue: The tutorial's last three lines in the fragment shader put a lot of hope in non-guaranteed compile-time optimizations. GLSL already has an optimized linear interpolation function. It might also be faster to only calculate RGB, since alpha will be unused in the end. The last lines should be changed to something like this.

    gl_FragColor.rgb = mix(gl_FragColor.rgb, texel1.rgb, texel1.a);
    gl_FragColor.rgb = mix(gl_FragColor.rgb, texel2.rgb, texel2.a);
    gl_FragColor.rgb = mix(gl_FragColor.rgb, texel3.rgb, texel3.a);