LWJGL Forum

Programming => OpenGL => Topic started by: frucost on October 22, 2014, 01:44:15

Title: Resolution issue with framebuffers
Post by: frucost on October 22, 2014, 01:44:15
Hey guys,

I've recently experimented with framebuffers in openGL to achieve a pixelated look.
I'm basically rendering my scene into a 320*192px wide texture and afterwards drawing the texture to the screen (as far as I know the idea behind framebuffers).
To avoid distortion I'm using the methods 'recalculateViewport' and 'fitViewport' (Graphics.java), which adjust the viewport to preserve the aspect ratio of the 320*192px scene.
This is working as intended but inside the viewport the resolutions seems to be "reversed". :/

Example (with lower resolution):

Display dimensions: 200*64px
Render resolution: 8*4px

As expected, the viewport is set to 128*64px and moved 36px to the right.
But now the problem: instead of the resolution of 8*4px and a pixel-size of 16*16, I'm getting a resolution of 4*8 pixels with each rendered pixel being 32*8 actual pixels wide.

What I expected:
(http://s14.directupload.net/images/141022/you4jd95.png)

What I got:
(http://s14.directupload.net/images/141022/sc6n7qcu.png)

Here's a screenshot of the program with those settings:
(http://s14.directupload.net/images/141022/8ogcrdrj.png)

It would be great if someone had an idea how to fix this, I'm searching for a solution for hours now.

- Mario :D

PS: Please excuse mistakes, english is not my first language :)

Game.java

package net.frucost.game;

import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.EXTFramebufferObject.*;

import net.frucost.berry.core.Berry;

import org.lwjgl.opengl.Display;

public class Game {
   private long         lasttick;
   
   private final int    tps           = 60;
   private final int    tickdelay     = 1000 / tps;
   
   public int           framebuffer;
   public int           texture;
   
   public static void main(String[] args) {
       new Game().init();
   }
   
   public void init() {
       Berry.init();
       Berry.statemachine.addState("menu.main", new MenuState());
       Berry.statemachine.setNextState("menu.main");
       
       // create framebuffer and colortexture
       framebuffer = glGenFramebuffersEXT();
       texture = glGenTextures();

       // init framebuffer and colortexture
       glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
       glBindTexture(GL_TEXTURE_2D, texture);
       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, (int) Berry.graphics.resolution.x, (int) Berry.graphics.resolution.y, 0, GL_RGBA, GL_INT, (java.nio.ByteBuffer) null);
       glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texture, 0);
       glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
       
       loop();
   }
   
   public void loop() {
       while(!Berry.app.closeRequested()) {
           if(System.currentTimeMillis() > lasttick + tickdelay) {
               update();
               lasttick += tickdelay;
           }
           render();
       }
   }
   
   public void update() {
       Berry.tick();
       Berry.statemachine.getCurrentState().update();
   }
   
   public void render() {
       { // Render scene into framebuffer
           glViewport(0, 0, (int) Berry.graphics.resolution.x, (int) Berry.graphics.resolution.y);
           glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture
           glBindFramebufferEXT((GL_FRAMEBUFFER_EXT), framebuffer); // Bind backbuffer
           glPushMatrix();
           {
               glClearColor(0, 0, 0, 1);
               glClear(GL_COLOR_BUFFER_BIT);

               Berry.statemachine.getCurrentState().render();
           }
           glPopMatrix();
       }
       { // Render framebuffer to screen
           glEnable(GL_TEXTURE_2D);
           glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // Bind frontbuffer
           
           Berry.graphics.fitViewport();
           
           glPushMatrix();
           {
               glColor3f(1, 1, 1);
               glClearColor(0, 0, 0, 1);
               glClear(GL_COLOR_BUFFER_BIT);
               glBindTexture(GL_TEXTURE_2D, texture);
               glBegin(GL_QUADS);
               {
                   glTexCoord2f(0.0f, 0.0f);
                   glVertex2i(0, 0);
                   glTexCoord2f(0.0f, 1.0f);
                   glVertex2i((int) Berry.graphics.resolution.x, 0);
                   glTexCoord2f(1.0f, 1.0f);
                   glVertex2i((int) Berry.graphics.resolution.x, (int) Berry.graphics.resolution.y);
                   glTexCoord2f(1.0f, 0.0f);
                   glVertex2i(0, (int) Berry.graphics.resolution.y);
               }
               glEnd();
               glDisable(GL_TEXTURE_2D);
           }
       }
       
       Display.update();
       Display.sync(60);
   }
}


Graphics.java (a instance of this class is referenced by 'Berry.graphics')
package net.frucost.berry.engine;

import java.util.ArrayList;
import java.util.logging.Logger;

import net.frucost.berry.utils.LogUtils;
import net.frucost.berry.utils.Vec2;

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;

import static org.lwjgl.opengl.GL11.*;

public class Graphics {
/**
* An array of valid DisplayModes (fullscreen-capable, with bitdepth of 32 and frequency of 60hz)
*/
private DisplayMode[] displaymodes;

/**
* Currently active Displaymode
*/
private DisplayMode displaymode;

/**
* Resolution of the rendering-area, will be scaled up to fit into window
*/
public final Vec2 resolution = new Vec2(320, 192);

/**
* Size of the actual window
*/
public Vec2 dimension = new Vec2(1280, 720);

/**
* Cached position of the fit viewport
*/
public Vec2 viewportPos;

/**
* Cached dimension of the fit viewport
*/
public Vec2 viewportDim;

/**
* Class logger object
*/
private Logger logger = LogUtils.get(Graphics.class);

/**
* Collect valid DisplayModes, initialize Display and fit Viewport
*/
public void init() {
// Collect and filter DisplayModes
logger.fine("Collecting displaymodes");
try {
ArrayList<DisplayMode> displaylist = new ArrayList<DisplayMode>();
for(DisplayMode dm : Display.getAvailableDisplayModes()) {
if(dm.getBitsPerPixel() == 32 && dm.getFrequency() == 60 && dm.isFullscreenCapable()) {
logger.finest(dm.toString() + " (1:" + ((float) dm.getWidth() / (float) dm.getHeight()) + ")");
displaylist.add(dm);
}
}
this.displaymodes = new DisplayMode[displaylist.size()];
for(int i = 0; i < displaylist.size(); i++) {
this.displaymodes[i] = displaylist.get(i);
if(displaylist.get(i).getWidth() == dimension.x && displaylist.get(i).getHeight() == dimension.y) {
setDisplaymode(displaylist.get(i), false);
}
}
}
catch(LWJGLException e) {
logger.severe("Failed to collect displaymodes: " + e.getLocalizedMessage());
}
// Create LWJGL display
try {
Display.setResizable(true);
Display.create();
initGL();
}
catch(LWJGLException e) {
logger.severe("Failed to create LWJGL display: " + e.getLocalizedMessage());
}
}

/**
* Initialize openGL
*/
public void initGL() {
fitViewport();

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}

/**
* Recalculate position and dimension of the viewport inside the window
*/
public void recalculateViewport() {
// Fit and center view
double w2 = resolution.x, h2 = resolution.y, w1 = Display.getWidth(), h1 = Display.getHeight();

double outerAspectRatio = w1 / h1;
double innerAspectRatio = w2 / h2;

double factor = (innerAspectRatio >= outerAspectRatio) ? (w1 / w2) : (h1 / h2);

double w = w2 * factor;
double h = h2 * factor;
double l = (w1 - w) / 2;
double t = (h1 - h) / 2;

viewportPos = new Vec2(l, t);
viewportDim = new Vec2(w, h);
}

/**
* Apply position and dimension of the viewport
*/
public void fitViewport() {
if(viewportPos == null || viewportDim == null) {
recalculateViewport();
}
glMatrixMode(GL_PROJECTION);
{
glLoadIdentity();
System.out.println(viewportPos + ", " + viewportDim);
glViewport((int) viewportPos.x, (int) viewportPos.y, (int) viewportDim.x, (int) viewportDim.y);
                       // (0|0) in the upper-left corner
                       glOrtho(0, resolution.x, resolution.y, 0, 1, -1);
}
glMatrixMode(GL_MODELVIEW);
}

/**
* @return a list of valid DisplayModes
*/
public DisplayMode[] getDisplaymodes() {
return displaymodes;
}

/**
* @return the current DisplayMode
*/
public DisplayMode getDisplaymode() {
return displaymode;
}

/**
* Try to enter a DisplayMode
*
* @param displaymode
* @param fullscreen
*/
public void setDisplaymode(DisplayMode displaymode, boolean fullscreen) {
for(DisplayMode mode : displaymodes) {
if(mode.getWidth() == displaymode.getWidth() && mode.getHeight() == displaymode.getHeight()) {
this.displaymode = mode;
setFullscreen(fullscreen);
updateDisplayMode();
return;
}
}
logger.warning("Tried to set unregistered displaymode: " + displaymode + (fullscreen ? " fullscreen" : ""));
}

/**
* Try to enter a DisplayMode
*
* @param width
* @param height
* @param fullscreen
*/
public void setDisplaymode(int width, int height, boolean fullscreen) {
setDisplaymode(new DisplayMode(width, height), fullscreen);
}

/**
* Try to enter/leave fullscreen mode
*
* @param fullscreen
*/
public void setFullscreen(boolean fullscreen) {
try {
Display.setFullscreen(fullscreen);
}
catch(LWJGLException e) {
logger.severe("Failed to set fullscreen mode");
}
}

/**
* @return Whether fullscreen mode is active
*/
public boolean isFullscreen() {
return Display.isFullscreen();
}

/**
* Try to apply displaymode
*/
public void updateDisplayMode() {
try {
Display.setDisplayMode(displaymode);
logger.config("Updated displaymode: " + displaymode);
}
catch(LWJGLException e) {
    logger.severe("Failed to update displaymode: " + e.getLocalizedMessage());
}
}
}
Title: Re: Resolution issue with framebuffers
Post by: Cornix on October 22, 2014, 07:10:09
I have not looked over your code but I can tell you that you could simply change the way you render to get a look like that much simpler and easier.
There is absolutely no reason to use FrameBuffers for what you want to do. Just scale everything up.
Title: Re: Resolution issue with framebuffers
Post by: frucost on October 22, 2014, 10:39:19
Hey, thanks for your answer but how would you do that?
As far as I know the GL_NEAREST scaling-policy can only be applied to textures and not to the glOrtho-view itself.
Am I wrong with that? And if so, how would I?

- Mario
Title: Re: Resolution issue with framebuffers
Post by: Cornix on October 22, 2014, 13:26:00
I dont see what GL_NEAREST has to do with it.
There is no build in anti-aliasing in openGL.
Title: Re: Resolution issue with framebuffers
Post by: frucost on October 22, 2014, 17:06:02
Well, if I'm just setting the resolution of the ortho-view, everything becomes blurry.
There has to be a way to change the interpolation mode, right?
Title: Re: Resolution issue with framebuffers
Post by: Cornix on October 22, 2014, 17:25:34
Could you show an image of how exactly it looks like?
Do you use GL_NEAREST or GL_LINEAR as the mag filter of your textures?
Title: Re: Resolution issue with framebuffers
Post by: frucost on October 22, 2014, 18:07:00
I'm using GL_NEAREST :)

Game.java, Line 36:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);


I'm at my laptop at the moment, i'll post an image of what I mean when I'm at home :D
Title: Re: Resolution issue with framebuffers
Post by: broumbroum on October 22, 2014, 19:08:49
You're mixing vertices indeed ...
You also may want to check if the Rectangle Texture Extension (https://www.opengl.org/wiki/Rectangle_Texture) gets better results, because of non-power-of-two texture sizes. That may be old school doing with newer G. Cards, but it's worth to check.
Quote from: frucost on October 22, 2014, 01:44:15
(..)This is working as intended but inside the viewport the resolutions seems to be "reversed". :/

               glBindTexture(GL_TEXTURE_2D, texture);
               glBegin(GL_QUADS);
               {
                   glTexCoord2f(0.0f, 0.0f);
                   glVertex2i(0, 0);
        glTexCoord2f(0.0f, 1.0f); // this is (1f, 0f)
                   glVertex2i((int) Berry.graphics.resolution.x, 0);
                   glTexCoord2f(1.0f, 1.0f);
                   glVertex2i((int) Berry.graphics.resolution.x, (int) Berry.graphics.resolution.y);
        glTexCoord2f(1.0f, 0.0f);// this is (0f, 1f)
                   glVertex2i(0, (int) Berry.graphics.resolution.y);
               }
               glEnd();
               glDisable(GL_TEXTURE_2D);

:D cu
Title: Re: Resolution issue with framebuffers
Post by: frucost on October 22, 2014, 19:41:40
THANKS!!

I feel so stupid right now :/
But it's working :D

Btw: Thanks for the link, I'll definitively have a look at those rectange textures ;)

(http://s14.directupload.net/images/141022/lqvhwonn.png)

- Mario