Skip to content

Instantly share code, notes, and snippets.

@mattdesl
Last active January 24, 2017 18:28
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mattdesl/4372018 to your computer and use it in GitHub Desktop.
Save mattdesl/4372018 to your computer and use it in GitHub Desktop.
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.glutils.FrameBuffer;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
/**
* A port of ShaderLesson5 from lwjgl-basics to LibGDX:
* https://github.com/mattdesl/lwjgl-basics/wiki/ShaderLesson5
*
* @author davedes
*/
public class ShaderLesson5 implements ApplicationListener {
public static void main(String[] args) {
LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
cfg.title = "Shader Lesson 5 - LibGDX";
cfg.useGL20 = true;
cfg.width = 640;
cfg.height = 480;
cfg.resizable = false;
new LwjglApplication(new ShaderLesson5(), cfg);
}
Texture tex, tex2;
SpriteBatch batch;
OrthographicCamera cam;
ShaderProgram blurShader;
FrameBuffer blurTargetA, blurTargetB;
TextureRegion fboRegion;
public static final int FBO_SIZE = 1024;
public static final float MAX_BLUR = 2f;
BitmapFont fps;
final String VERT =
"attribute vec4 "+ShaderProgram.POSITION_ATTRIBUTE+";\n" +
"attribute vec4 "+ShaderProgram.COLOR_ATTRIBUTE+";\n" +
"attribute vec2 "+ShaderProgram.TEXCOORD_ATTRIBUTE+"0;\n" +
"uniform mat4 u_projTrans;\n" +
" \n" +
"varying vec4 vColor;\n" +
"varying vec2 vTexCoord;\n" +
"void main() {\n" +
" vColor = "+ShaderProgram.COLOR_ATTRIBUTE+";\n" +
" vTexCoord = "+ShaderProgram.TEXCOORD_ATTRIBUTE+"0;\n" +
" gl_Position = u_projTrans * " + ShaderProgram.POSITION_ATTRIBUTE + ";\n" +
"}";
final String FRAG =
"#ifdef GL_ES\n" +
"#define LOWP lowp\n" +
"precision mediump float;\n" +
"#else\n" +
"#define LOWP \n" +
"#endif\n" +
"varying LOWP vec4 vColor;\n" +
"varying vec2 vTexCoord;\n" +
"\n" +
"uniform sampler2D u_texture;\n" +
"uniform float resolution;\n" +
"uniform float radius;\n" +
"uniform vec2 dir;\n" +
"\n" +
"void main() {\n" +
" vec4 sum = vec4(0.0);\n" +
" vec2 tc = vTexCoord;\n" +
" float blur = radius/resolution; \n" +
" \n" +
" float hstep = dir.x;\n" +
" float vstep = dir.y;\n" +
" \n" +
" sum += texture2D(u_texture, vec2(tc.x - 4.0*blur*hstep, tc.y - 4.0*blur*vstep)) * 0.05;\n" +
" sum += texture2D(u_texture, vec2(tc.x - 3.0*blur*hstep, tc.y - 3.0*blur*vstep)) * 0.09;\n" +
" sum += texture2D(u_texture, vec2(tc.x - 2.0*blur*hstep, tc.y - 2.0*blur*vstep)) * 0.12;\n" +
" sum += texture2D(u_texture, vec2(tc.x - 1.0*blur*hstep, tc.y - 1.0*blur*vstep)) * 0.15;\n" +
" \n" +
" sum += texture2D(u_texture, vec2(tc.x, tc.y)) * 0.16;\n" +
" \n" +
" sum += texture2D(u_texture, vec2(tc.x + 1.0*blur*hstep, tc.y + 1.0*blur*vstep)) * 0.15;\n" +
" sum += texture2D(u_texture, vec2(tc.x + 2.0*blur*hstep, tc.y + 2.0*blur*vstep)) * 0.12;\n" +
" sum += texture2D(u_texture, vec2(tc.x + 3.0*blur*hstep, tc.y + 3.0*blur*vstep)) * 0.09;\n" +
" sum += texture2D(u_texture, vec2(tc.x + 4.0*blur*hstep, tc.y + 4.0*blur*vstep)) * 0.05;\n" +
"\n" +
" gl_FragColor = vColor * vec4(sum.rgb, 1.0);\n" +
"}";
@Override
public void create() {
tex = new Texture(Gdx.files.internal("data/slider.png"));
tex2 = new Texture(Gdx.files.internal("data/ptsans_00.png"));
tex.setFilter(TextureFilter.Linear, TextureFilter.Linear);
tex2.setFilter(TextureFilter.Linear, TextureFilter.Linear);
//important since we aren't using some uniforms and attributes that SpriteBatch expects
ShaderProgram.pedantic = false;
blurShader = new ShaderProgram(VERT, FRAG);
if (!blurShader.isCompiled()) {
System.err.println(blurShader.getLog());
System.exit(0);
}
if (blurShader.getLog().length()!=0)
System.out.println(blurShader.getLog());
//setup uniforms for our shader
blurShader.begin();
blurShader.setUniformf("dir", 0f, 0f);
blurShader.setUniformf("resolution", FBO_SIZE);
blurShader.setUniformf("radius", 1f);
blurShader.end();
blurTargetA = new FrameBuffer(Format.RGBA8888, FBO_SIZE, FBO_SIZE, false);
blurTargetB = new FrameBuffer(Format.RGBA8888, FBO_SIZE, FBO_SIZE, false);
fboRegion = new TextureRegion(blurTargetA.getColorBufferTexture());
fboRegion.flip(false, true);
batch = new SpriteBatch();
fps = new BitmapFont();
cam = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
cam.setToOrtho(false);
}
@Override
public void resize(int width, int height) {
}
void resizeBatch(int width, int height) {
cam.setToOrtho(false, width, height);
batch.setProjectionMatrix(cam.combined);
}
void renderEntities(SpriteBatch batch) {
batch.draw(tex, 0, 0);
batch.draw(tex2, tex.getWidth()+5, 30);
}
@Override
public void render() {
//Start rendering to an offscreen color buffer
blurTargetA.begin();
//Clear the offscreen buffer with an opaque background
Gdx.gl.glClearColor(0.5f, 0.5f, 0.5f, 1f);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
//before rendering, ensure we are using the default shader
batch.setShader(null);
//resize the batch projection matrix before drawing with it
resizeBatch(FBO_SIZE, FBO_SIZE);
//now we can start drawing...
batch.begin();
//draw our scene here
renderEntities(batch);
//finish rendering to the offscreen buffer
batch.flush();
//finish rendering to the offscreen buffer
blurTargetA.end();
//now let's start blurring the offscreen image
batch.setShader(blurShader);
//since we never called batch.end(), we should still be drawing
//which means are blurShader should now be in use
//ensure the direction is along the X-axis only
blurShader.setUniformf("dir", 1f, 0f);
//update blur amount based on touch input
float mouseXAmt = Gdx.input.getX() / (float)Gdx.graphics.getWidth();
blurShader.setUniformf("radius", mouseXAmt * MAX_BLUR);
//our first blur pass goes to target B
blurTargetB.begin();
//we want to render FBO target A into target B
fboRegion.setTexture(blurTargetA.getColorBufferTexture());
//draw the scene to target B with a horizontal blur effect
batch.draw(fboRegion, 0, 0);
//flush the batch before ending the FBO
batch.flush();
//finish rendering target B
blurTargetB.end();
//now we can render to the screen using the vertical blur shader
//update our projection matrix with the screen size
resizeBatch(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
//update the blur only along Y-axis
blurShader.setUniformf("dir", 0f, 1f);
//update the Y-axis blur radius
float mouseYAmt = Gdx.input.getY() / (float)Gdx.graphics.getHeight();
blurShader.setUniformf("radius", mouseYAmt * MAX_BLUR);
//draw target B to the screen with a vertical blur effect
fboRegion.setTexture(blurTargetB.getColorBufferTexture());
batch.draw(fboRegion, 0, 0);
//reset to default shader without blurs
batch.setShader(null);
//draw FPS
fps.draw(batch, String.valueOf(Gdx.graphics.getFramesPerSecond()), 5, Gdx.graphics.getHeight()-5);
//finally, end the batch since we have reached the end of the frame
batch.end();
}
@Override
public void pause() {
}
@Override
public void resume() {
}
@Override
public void dispose() {
batch.dispose();
blurShader.dispose();
tex.dispose();
tex2.dispose();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment