com.cyphercove.doublehelix.FilmGrain.java Source code

Java tutorial

Introduction

Here is the source code for com.cyphercove.doublehelix.FilmGrain.java

Source

/*******************************************************************************
 * Copyright 2015 Cypher Cove, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package com.cyphercove.doublehelix;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Disposable;
import com.cyphercove.lwptools.core.GaussianBlur;

import java.util.Random;

public class FilmGrain implements Disposable, GaussianBlur.CustomShaderPreparer {

    static final float FILM_GRAIN_PER_LONG_SIDE = 600;
    static final float SCANLINE_PER_INCH = 100;
    static final float FILM_GRAIN_BRIGHTNESS = 0.05f;
    static final float SCANLINE_BRIGHTNESS = 0.10f;
    static final float VIGNETTE_BRIGHTNESS = 1f;

    Texture noiseTexture;
    Texture borderTexture;
    Texture scanlineTexture;
    ShaderProgram filmGrainShaderProgram;
    Random mRandom = new Random();
    Vector2 mFilmGrainInverse = new Vector2();
    Vector2 mScanLineInverse = new Vector2();

    SpriteBatch batch;
    boolean forBloom;

    int u_grainInverse, u_grainOffset, u_flicker, u_noiseTexture, u_scanLineTexture, u_scanLineInverse,
            u_borderTexture, u_flipped;

    public FilmGrain(Texture noiseTexture, Texture borderTexture, Texture scanlineTexture, boolean forBloom) {
        this.forBloom = forBloom;

        this.noiseTexture = noiseTexture;
        this.borderTexture = borderTexture;
        this.scanlineTexture = scanlineTexture;

        String prefix = forBloom ? "filmgrain_bloom" : "filmgrain";

        String vertexShaderSrc = Gdx.files.internal(prefix + "_vs.glsl").readString();
        String fragmentShaderSrc = Gdx.files.internal(prefix + "_fs.glsl").readString();
        filmGrainShaderProgram = new ShaderProgram(vertexShaderSrc, fragmentShaderSrc);

        if (!forBloom) {
            batch = new SpriteBatch(1, filmGrainShaderProgram);
            batch.setProjectionMatrix(new Matrix4().idt());
        }

        u_grainInverse = filmGrainShaderProgram.getUniformLocation("u_grainInverse");
        u_grainOffset = filmGrainShaderProgram.getUniformLocation("u_grainOffset");
        u_flicker = filmGrainShaderProgram.getUniformLocation("u_flicker");
        u_noiseTexture = filmGrainShaderProgram.getUniformLocation("u_noiseTexture");
        u_scanLineTexture = filmGrainShaderProgram.getUniformLocation("u_scanLineTexture");
        u_scanLineInverse = filmGrainShaderProgram.getUniformLocation("u_scanLineInverse");
        if (forBloom) {
            u_borderTexture = filmGrainShaderProgram.getUniformLocation("u_borderTexture");
            u_flipped = filmGrainShaderProgram.getUniformLocation("u_flipped");
        }
    }

    @Override
    public void dispose() {
        filmGrainShaderProgram.dispose();
        if (batch != null)
            batch.dispose();
    }

    public void resize(int width, int height) {

        if (width > height) {
            mFilmGrainInverse.x = FILM_GRAIN_PER_LONG_SIDE;
            mFilmGrainInverse.y = mFilmGrainInverse.x / (float) width * (float) height;
        } else {
            mFilmGrainInverse.y = FILM_GRAIN_PER_LONG_SIDE;
            mFilmGrainInverse.x = mFilmGrainInverse.y / (float) width * (float) height;
        }
        mFilmGrainInverse.scl(1f / noiseTexture.getWidth());

        float dpi = Gdx.graphics.getPpiX();
        float scanlinePixelsWide = ((float) width) * SCANLINE_PER_INCH / dpi;
        float scanlinePixelsTall = ((float) height) * SCANLINE_PER_INCH / dpi;
        mScanLineInverse.set(scanlinePixelsWide / scanlineTexture.getWidth(),
                scanlinePixelsTall / scanlineTexture.getHeight());
    }

    public void render() {
        batch.enableBlending();
        batch.setBlendFunction(GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_ALPHA);
        batch.begin();
        batch.setColor(Settings.filmGrain ? FILM_GRAIN_BRIGHTNESS : 0, Settings.scanLines ? SCANLINE_BRIGHTNESS : 0,
                Settings.vignette ? VIGNETTE_BRIGHTNESS : 0, 1);
        filmGrainShaderProgram.setUniformf(u_grainInverse, mFilmGrainInverse);
        filmGrainShaderProgram.setUniformf(u_scanLineInverse, mScanLineInverse);
        filmGrainShaderProgram.setUniformf(u_grainOffset, mRandom.nextFloat() * mFilmGrainInverse.x,
                mRandom.nextFloat() * mFilmGrainInverse.y);
        filmGrainShaderProgram.setUniformf(u_flicker, lerp(0.96f, 1f, mRandom.nextFloat()));
        noiseTexture.bind(1);
        scanlineTexture.bind(2);
        Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0);
        filmGrainShaderProgram.setUniformi(u_noiseTexture, 1);
        filmGrainShaderProgram.setUniformi(u_scanLineTexture, 2);
        batch.draw(borderTexture, -1, -1, 2, 2);
        batch.end();
    }

    static final float lerp(float one, float two, float position) {
        return (two - one) * position + one;
    }

    //For use with GaussianBlur bloom
    @Override
    public void applyCustomShaderParameters(SpriteBatch spriteBatch, boolean flipped) {
        spriteBatch.setColor(Settings.filmGrain ? FILM_GRAIN_BRIGHTNESS : 0,
                Settings.scanLines ? SCANLINE_BRIGHTNESS : 0, Settings.vignette ? VIGNETTE_BRIGHTNESS : 0, 1);
        if (flipped) {
            swapVector2(mFilmGrainInverse);
        }
        filmGrainShaderProgram.setUniformf(u_grainInverse, mFilmGrainInverse);
        filmGrainShaderProgram.setUniformf(u_scanLineInverse, mScanLineInverse);
        filmGrainShaderProgram.setUniformf(u_flipped, flipped ? 1f : 0f);
        filmGrainShaderProgram.setUniformf(u_grainOffset, mRandom.nextFloat() * mFilmGrainInverse.x,
                mRandom.nextFloat() * mFilmGrainInverse.y);
        filmGrainShaderProgram.setUniformf(u_flicker, lerp(0.96f, 1f, mRandom.nextFloat()));
        noiseTexture.bind(1);
        scanlineTexture.bind(2);
        borderTexture.bind(3);
        Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0);
        filmGrainShaderProgram.setUniformi(u_noiseTexture, 1);
        filmGrainShaderProgram.setUniformi(u_scanLineTexture, 2);
        filmGrainShaderProgram.setUniformi(u_borderTexture, 3);
        if (flipped) {
            swapVector2(mFilmGrainInverse);
        }
    }

    static void swapVector2(Vector2 vector) {
        vector.set(vector.y, vector.x);
    }
}