I have a shader and I want to run the shader on the whole rendered image at the end instead of each individual sprite that I render on screen. For this purpose I created an FBO, I render the all my images to the FBO and then I render to colorBufferTexture generated by the FBO. There is just one problem, the shader does not work when I render the FBO texture. The withoutFbo() method render the image with the shader working just fine, but the withFbo() method only renders the image without the shader. Also I know that the shader in withFbo() is working because if I try to render something other then the colorBufferTexture from the FBO the shader will work. So why isn't the shader working with the texture generated by the FBO?
Note: The shader is working with this code, the problem is that it doesn't work when I use the fboTexture. If I call batch.draw(sprite, ...) then the shader will work in both the withFbo() and withoutFbo() methods, but I call batch.draw(fboTexture, ...) then it will simply render the FBO texture without applying the shader.
public class Application3 extends ApplicationAdapter
{
float time;
Sprite sprite;
FrameBuffer fbo;
SpriteBatch batch;
Texture noiseTexture;
TextureRegion fboTexture;
ShaderProgram shaderProgram;
#Override public void create()
{
ShaderProgram.pedantic = false;
batch = new SpriteBatch();
sprite = new Sprite(new Texture("game.png"));
sprite.setSize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
shaderProgram = new ShaderProgram(Gdx.files.internal(Shader.GLITCH2.getVertex()), Gdx.files.internal(Shader.GLITCH2.getFragment()));
noiseTexture = new Texture(Gdx.files.internal(Shader.GLITCH2.getPath() + "noise.png"));
noiseTexture.bind(0);
fbo = new FrameBuffer(Pixmap.Format.RGBA8888, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), false);
fboTexture = new TextureRegion(fbo.getColorBufferTexture());
fboTexture.flip(false, true);
}
#Override public void render()
{
//withFbo();
withoutFbo();
}
void withoutFbo()
{
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
time += Gdx.graphics.getDeltaTime();
shaderProgram.setUniformi("u_noise", 0);
shaderProgram.setUniformf("u_time", time);
batch.setShader(shaderProgram);
batch.begin();
batch.draw(sprite, sprite.getX(), sprite.getY(), sprite.getWidth(), sprite.getHeight());
batch.setShader(null);
batch.end();
}
void withFbo()
{
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
time += Gdx.graphics.getDeltaTime();
fbo.begin();
batch.begin();
batch.draw(sprite, sprite.getX(), sprite.getY(), sprite.getWidth(), sprite.getHeight());
batch.end();
fbo.end();
shaderProgram.setUniformi("u_noise", 0);
shaderProgram.setUniformf("u_time", time);
batch.setShader(shaderProgram);
batch.begin();
batch.draw(fboTexture, sprite.getX(), sprite.getY(), sprite.getWidth(), sprite.getHeight());
batch.setShader(null);
batch.end();
}
}
Related
My goal is to draw all my visuals to a framebuffer which has a size that matches 1 by 1 with my pixel art assets. And then output the framebuffer texture to the current screen size. (A common solution for scaling pixel art).
The problem I am having while trying to achieve this is 2-fold
The frameBuffer texture is drawn too small. I expect the texture to match the whole window.
resizing the screen further distorts the proportions and position. I expect the frameBuffer texture to keep filling the screen after resizing it.
This is my trimmed down code.
public class BattleScreen extends GameScreen {
public static final int VISIBLE_WIDTH = 21;
public static final int VISIBLE_HEIGHT = 21;
private static OrthographicCamera mapCamera = null;
private SpriteBatch spriteBatch;
private OrthographicCamera hudCamera;
private Hud hud;
private EntityStage entityStage;
private FrameBuffer fbo;
private void initializeVariables(UnitOwner enemy, MapManager mapMgr) {
mapCamera = new OrthographicCamera();
mapCamera.setToOrtho(false, VISIBLE_WIDTH, VISIBLE_HEIGHT);
fbo = new FrameBuffer(Pixmap.Format.RGBA8888, 672, 672, false);
spriteBatch = new SpriteBatch(900);
entityStage = new EntityStage();
hudCamera = new OrthographicCamera();
hudCamera.setToOrtho(false, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
}
#Override
public void show() {
resize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
mapCamera.position.set(currentMap.getMapWidth() * 0.5f, currentMap.getMapHeight() * 0.5f, 0f);
mapRenderer = new OrthogonalTiledMapRenderer(mapMgr.getCurrentTiledMap(), Map.UNIT_SCALE, spriteBatch);
mapRenderer.setView(mapCamera);
final FitViewport vp = new FitViewport(VISIBLE_WIDTH, VISIBLE_HEIGHT, mapCamera);
currentMap.getTiledMapStage().setViewport(vp);
entityStage.setViewport(vp);
}
private void update(final float delta) {
mapCamera.position.x = Utility.clamp(mapCamera.position.x, currentMap.getTilemapWidthInTiles() - (mapCamera.viewportWidth * 0.5f), 0 + (mapCamera.viewportWidth * 0.5f));
mapCamera.position.y = Utility.clamp(mapCamera.position.y, currentMap.getTilemapHeightInTiles() - (mapCamera.viewportHeight * 0.5f), 0 + (mapCamera.viewportHeight * 0.5f));
mapCamera.update();
hudCamera.update();
}
private void renderElements(final float delta) {
Gdx.gl.glClearColor(0, 0, 0, 0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
fbo.begin();
Gdx.gl.glClearColor(0, 0, 0, 0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
renderMap();
renderUnits();
renderHUD(delta);
fbo.end();
spriteBatch.begin();
Texture texture = fbo.getColorBufferTexture();
TextureRegion textureRegion = new TextureRegion(texture);
textureRegion.flip(false, true);
spriteBatch.draw(textureRegion, 0, 0, fbo.getWidth(), fbo.getHeight());
spriteBatch.end();
}
private void renderMap() {
spriteBatch.setProjectionMatrix(mapCamera.combined);
mapRenderer.setView(mapCamera);
currentMap.getTiledMapStage().getViewport().apply();
mapRenderer.render();
}
private void renderUnits() {
spriteBatch.begin();
entityStage.getViewport().apply();
for (Entity unit : units) {
final Color temp = spriteBatch.getColor();
spriteBatch.setColor(new Color(temp.r, temp.g, temp.b, unit.getEntityactor().getColor().a));
spriteBatch.draw(getFrame(), getEntityactor().getX(), unit.getEntityactor().getY(), 1.0f, 1.0f);
spriteBatch.setColor(temp);
}
}
private void renderHUD(final float delta) {
spriteBatch.setProjectionMatrix(hud.getStage().getCamera().combined);
hud.getStage().getViewport().apply();
hud.render(delta);
}
#Override
public void resize(final int width, final int height) {
currentMap.getTiledMapStage().getViewport().update(width, height, false);
entityStage.getViewport().update(width, height, false);
hud.resize(width, height);
pauseMenu.resize(width, height);
}
}
What I tried is changing the width and height on this line in the render method
spriteBatch.draw(textureRegion, 0, 0, 1000, 1000);
And then it does fill up the whole screen nicely
Which is weird because the screen is 672 x 672 in size.
Fixed problem 1 with the following adaptation
mapCamera.setToOrtho(false, fbo.getWidth(), fbo.getHeight()); //adapt camera to fbo
mapCamera.update();
spriteBatch.setProjectionMatrix(mapCamera.combined);
spriteBatch.draw(textureRegion, 0, 0, fbo.getWidth(), fbo.getHeight());
spriteBatch.end();
mapCamera.setToOrtho(false, VISIBLE_WIDTH, VISIBLE_HEIGHT); //reset camera
mapCamera.update();
The spriteBatch is being reused for the map, the entities and the ui for efficiency. So I had to configure it to project the buffer texture on the screen
I have a tiled map and an animation. However, the map covers this last and I don't know why. If I add only the animation, I can see it, but not if also I add the map. Please answer me, I can't find the solution!
Here is my code:
public class MyGdxGame extends ApplicationAdapter implements GestureDetector.GestureListener
{
Texture texture;
SpriteBatch batch;
Sprite sprite;
TextureAtlas textureAtlas;
Animation animation;
float time;
OrthographicCamera orthographicCamera;
Texture corridore;
Sprite spriteCorridore;
TiledMap map;
OrthogonalTiledMapRenderer renderer;
#Override
public void create () {
batch = new SpriteBatch();
corridore = new Texture("a.png");
spriteCorridore = new Sprite(corridore);
orthographicCamera = new OrthographicCamera(500,500);
orthographicCamera.position.x=500;
orthographicCamera.position.y=250;
map = new TmxMapLoader().load("mappa.tmx");
renderer = new OrthogonalTiledMapRenderer(map);
textureAtlas = new TextureAtlas(Gdx.files.internal("corsa.atlas"));
animation = new Animation(1/20f,textureAtlas.findRegion("a"),textureAtlas.findRegion("b"),
textureAtlas.findRegion("c"),textureAtlas.findRegion("d"),textureAtlas.findRegion("e"),
textureAtlas.findRegion("f"),textureAtlas.findRegion("g"),textureAtlas.findRegion("h"),
textureAtlas.findRegion("i"));
}
#Override
public void render () {
time = time + Gdx.graphics.getDeltaTime();
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(orthographicCamera.combined);
batch.begin();
batch.draw((TextureRegion)animation.getKeyFrame(time,true),orthographicCamera.position.x-spriteCorridore.getWidth()/2,orthographicCamera.position.y-spriteCorridore.getHeight()/2);
renderer.setView(orthographicCamera);
renderer.render();
orthographicCamera.translate(1, 0);
orthographicCamera.update();
batch.end();
}
}
Render your animation at the top of TiledMapRenderer so change your rendering order.
#Override
public void render () {
time = time + Gdx.graphics.getDeltaTime();
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
renderer.setView(orthographicCamera);
renderer.render();
batch.setProjectionMatrix(orthographicCamera.combined);
batch.begin();
batch.draw((TextureRegion)animation.getKeyFrame(time,true),orthographicCamera.position.x-spriteCorridore.getWidth()/2,orthographicCamera.position.y-spriteCorridore.getHeight()/2);
batch.end();
orthographicCamera.translate(1, 0);
orthographicCamera.update();
}
I have an issue in LibGDX where when i call upon Gdx.input.getY(), it selects a pixel that's on the other side of the application relative to the center of the screen.
public class Main extends ApplicationAdapter {
private SpriteBatch batch;
private Texture img;
private OrthographicCamera camera;
int xPos;
int yPos;
private Vector3 tp = new Vector3();
BitmapFont font;
#Override
public void create () {
batch = new SpriteBatch();
img = new Texture("crosshair.png");
camera = new OrthographicCamera();
camera.setToOrtho(false, 1280, 720);
font = new BitmapFont();
}
#Override
public void render () {
yPos = Gdx.input.getY();
xPos = Gdx.input.getX();
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
camera.unproject(tp.set(xPos, yPos, 0));
batch.begin();
font.draw(batch,xPos + " , " + yPos, Gdx.input.getX() - 25, Gdx.input.getY() - 5);
batch.draw(img, xPos, yPos);
batch.end();
}
#Override
public void dispose () {
batch.dispose();
img.dispose();
}
Subtracting the viewport height with the touch location won't work, because that would be subtracting world coordinates with touch coordinates. (and even for a pixel perfect projection it would be height - 1 - y). Instead use the unproject method to convert touch coordinates to world coordinates.
There are two problems with your code:
You are never setting the batch projection matrix.
Even though you are using the unproject method, you are never using its result.
So instead use the following:
#Override
public void render () {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(camera.combined);
batch.begin();
camera.unproject(tp.set(Gdx.input.getX(), Gdx.input.getY(), 0));
font.draw(batch,tp.x+ " , " + tp.y, tp.x - 25, tp.y - 5);
batch.draw(img, tp.x, tp.y);
batch.end();
}
I would suggest to read the following pages, which describe this and the reasoning behind it in detail:
https://github.com/libgdx/libgdx/wiki/Coordinate-systems
https://xoppa.github.io/blog/pixels/
https://github.com/libgdx/libgdx/wiki/Viewports
It's better to try this
yPos = camera.viewportHeight - Gdx.input.getY();
When I tried draw a Texture using SpriteBatch it was result flip like this:
Here what I do:
I create MyRect object which draw bounding rectangle and an image.
Here MyRect class preview:
public class MyRect {
private Vector2 position;
private int width;
private float height;
private Texture img;
private Sprite sprite;
public MyRect(int x, int y, int width, int height){
img = new Texture("badlogic.jpg");
sprite = new Sprite(img);
position = new Vector2(x,y);
this.width = width;
this.height = height;
}
public void draw(ShapeRenderer shapeRenderer, SpriteBatch batch){
// Gdx.gl.glClearColor(0, 0, 0, 1);
// Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
shapeRenderer.begin(ShapeType.Line);
// Draw Background color
shapeRenderer.setColor(55 / 255.0f, 80 / 255.0f, 100 / 255.0f, 1);
shapeRenderer.rect(position.x, position.y, width, height);
shapeRenderer.end();
batch.begin();
// batch.disableBlending();
batch.draw(img, position.x, position.y,
width, height);
batch.end();
}
}
Parameters ShapeRenderer and SpriteBatch pass by GameScreen class.
GameScreen preview:
public class GameScreen implements Screen{
private MyRect myRect;
private ShapeRenderer shapeRenderer;
private SpriteBatch batch;
private OrthographicCamera cam;
public GameScreen(){
myRect = new MyRect(10,10,50,50);
int gameHeight=100;
cam = new OrthographicCamera();
cam.setToOrtho(true, 136, gameHeight);
batch = new SpriteBatch();
batch.setProjectionMatrix(cam.combined);
shapeRenderer = new ShapeRenderer();
shapeRenderer.setProjectionMatrix(cam.combined);
}
#Override
public void render(float delta) {
// TODO Auto-generated method stub
myRect.draw(shapeRenderer, batch);
}
}
Why is this happen? Am I doing it wrong?
Your camera is upside down because you called setToOrtho(true, ...) on it instead of setToOrtho(false, ...)
It is valid to use an upside-down camera (which might be more comfortable if you've used Flash or some other Y-down system before), but then you need to flip all your TextureRegions (aka Sprites): sprite.flip(false, true). Alternatively, you can create a TextureAtlas using TexturePacker (look it up in libgdx documentation) and set the flipY option, so it flips them ahead of time for you. Eventually, for performance you will need to use a TextureAtlas anyway.
By the way, when you start drawing multiple instances of MyRect, you are going to need to move the spriteBatch.begin() and end() and shapeRenderer.begin() and end() out of the individual MyRect's draw method or you will run into a performance problem. And as such, there will need to be two draw methods (one for sprite batch and one for shape renderer).
apparently, the only thing I see different than how I would do, is try to change:
this meybe if it works"for test"
cam.setToOrtho(false, 136, gameHeight);
this is how I use the camera.
I do not know whether to use true, you have to do some different way, to draw the batch, in anyway. if the camera false, it looks good, I think it has to flip the image before to draw uv
It may be a dumb question but I need a little help / explanations. I can't manage to resize correctly my textures when I resize the window in libgdx. I want to keep the aspect of my textures or have them at least reduced on a lower screen size, like on a mobile device. Here is a sample of my code, my original window size is 600*700 I tried a lot of things but nothing works :\
Can you help me ? Thanks in advance.
public class GameScreen implements Screen {
private World world;
private int ppxX, ppxY;
private SpriteBatch batch;
private OrthographicCamera camera;
private float cameraX, cameraY;
public GameScreen(World world) {
this.world = world;
camera = new OrthographicCamera();
}
public void show() {
batch = new SpriteBatch();
}
#Override
public void render(float delta) {
world.update(delta);
updateCamera();
ppxX = Gdx.graphics.getWidth() / 600;
ppxY = Gdx.graphics.getHeight() / 700;
Gdx.gl.glClearColor(0f, 0f, 0f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(camera.combined);
batch.begin();
batch.draw(world.getTexture(), 0, 0, ppxX, ppxY);
for (GameElement e : world.getListElements()) {
e.update(delta);
batch.draw(e.getTexture(), e.getPositionX()*ppxX, e.getPositionY()*ppxY, e.getWidth()*ppxX , e.getHeight()*ppxY );
}
batch.end();
}
You are on the right track, but there is couple of things you have to change.
Use ViewPorts
this.camera = new OrthographicCamera();
this.viewport = new FitViewport(WORLD_SIZE_X, WORLD_SIZE_Y, this.camera);
this.batch = new SpriteBatch();
this.batch.setProjectionMatrix(this.camera.combined);
Resize correctly.
#Override
public void resize(int width, int height) {
this.viewport.update(width, height);
}
Update the camera and render using World Units. Do not make the mistake of thinking in Screen Pixels.
public void render(float delta) {
this.camera.update();
this.batch.setProjectionMatrix(this.camera.combined);
this.batch.begin();
// draw using WORLD_SIZE
this.batch.end();
}