Jump to content
  • Advertisement
Sign in to follow this  
discman1028

OpenGL Fixed-resolution ortho projection

This topic is 3642 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey all, Trying to do pixel-perfect 2D plotting for a console emulator. [GLUT & C] Currently using an orthographic projection and glBegin(GL_POINTS)/glEnd(). Since I want to be able to zoom 2x/3x, etc, I am using glCopyTexImage2d() after writing to the framebuffer to copy the results into a texture to be aligned to a screen-aligned quad (using this instead of pbuffers or PBO's, for max compatibility). Anyhow, it's all working dandy, except for when I disable GL_LINEAR filtering for the magnification of textures. I would prefer to use GL_NEAREST, so that I know for sure that I have my pixel perfect positioning correct --> 1 texel for each pixel (or one texel for 4 pixels in the case of 2X zoom, etc). So I am not sure if I am off-by-one somewhere (I am doing a 0.375,0.375 translate as well, as suggested by opengl.org), and/or don't know my filtering params well enough. Any help is appreciated. :) Screenshot of what my test image looks like (vertical stripes of light&dark green&blue):
#define NES_HEIGHT				240
#define NES_WIDTH				256

static void SetOrthographicProjection();
static void ResetPerspectiveProjection();
static void glt_ChangeWindowSize( s32 width, s32 height );

static s32 g_WindowWidth;
static s32 g_WindowHeight;
static u32 g_RenderTargetTexture;	// OpenGL texture handle.

void glt_Init(int* argc, char* argv[])
{
	glutInit(argc, argv);

	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA /*| GLUT_ALPHA*/ );
	glutInitWindowPosition(0, 0);
	glutInitWindowSize(NES_WIDTH, NES_HEIGHT);
	glutCreateWindow("Test");
	glutReshapeFunc(glt_ChangeWindowSize);

	// Create a texture to copy into.
	glGenTextures(1, &g_RenderTargetTexture);
	glBindTexture(GL_TEXTURE_2D, g_RenderTargetTexture);
	glTexImage2D(GL_TEXTURE_2D, 0, 4, NES_WIDTH, NES_WIDTH, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); // NES_WIDTH x NES_WIDTH to get a square, power-of-2 sized texture
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR/*GL_NEAREST preferred!*/);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
}

void glt_SetDisplayFunc( void (*func)(void) )
{
	glutDisplayFunc( func );
}

void glt_ChangeWindowSize( s32 width, s32 height )
{
	g_WindowWidth = width;
	g_WindowHeight = height;
}

void glt_RunMainLoop()
{
	glutMainLoop();
}

void glt_InvalidateScreen()
{
	glutPostRedisplay();
}

void glt_TestDraw()
{
	int x, y;					// Screen pos.
	f32 fScreenHeight;
	f32 fScreenWidth;
	f32 fXOffset;
	

	// Clear screen to black.
	glClearColor(0.0f,0.0f,0.0f,1.0f);
	glClear(GL_COLOR_BUFFER_BIT /*| GL_DEPTH_BUFFER_BIT*/);
	
	// No need for depth test, we'll draw back-to-front.
	// TODO: Maybe we'll change this later so hardware can take care of it for us using the Z value.
	glDisable(GL_DEPTH_TEST);

	// Push modelview matrix (state leftover from 3D drawing).
	glPushMatrix();

	// Push projection.
	SetOrthographicProjection(NES_WIDTH, NES_HEIGHT, TRUE);

	glViewport(0, 0, NES_WIDTH, NES_HEIGHT); // == size of texture == NES resolution.

	// Start from modelview scratch for 2D drawing.
	glLoadIdentity();

	// Small translation to get pixel-perfect positioning.
	glTranslatef(0.375f, 0.375f, 0.0f);

	// Draw.
	glBegin(GL_POINTS);
	for( y = 0; y < NES_HEIGHT; y++ )
	{
		for( x = 0; x < NES_WIDTH; x++ )
		{
			glColor3f( 0.0f, x%2==0?1.0f:0.5f, y>(NES_HEIGHT/2)?1.0f:0.0f );
			glVertex2i( x, y );
		}
	}
	glEnd();

	//
	// Copy the current framebuffer to a texture.
	//
	glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, NES_WIDTH, NES_WIDTH, 0);

	// Pop projection.
	ResetPerspectiveProjection();

	// Push projection.
	SetOrthographicProjection(g_WindowWidth, g_WindowHeight, FALSE);

	glViewport(0, 0, g_WindowWidth, g_WindowHeight); // Should always be == to current size of window!
	
	glClearColor(0.2f,0.2f,0.2f,1.0f);
	glClear(GL_COLOR_BUFFER_BIT); //Clear screen to gray color

	glColor3f(0.0f,0.0f,1.0f); // Blue... but blue should not show since we use tex!!
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, g_RenderTargetTexture);

	glBegin(GL_QUADS);
	// Stretch to fit screen:
	//glTexCoord2f(0.0f,0.0f);						glVertex2i( 0, 0 );
	//glTexCoord2f(1.0f,0.0f);						glVertex2i( g_WindowWidth, 0 );
	//glTexCoord2f(1.0f,1.0f*NES_HEIGHT/NES_WIDTH);	glVertex2i( g_WindowWidth, g_WindowHeight );
	//glTexCoord2f(0.0f,1.0f*NES_HEIGHT/NES_WIDTH);	glVertex2i( 0, g_WindowHeight );
	// Fit to height, preserve aspect ratio:
	fScreenHeight = g_WindowHeight*NES_WIDTH/NES_HEIGHT; // Goes overboard, due to power-of-2 texture.
	fScreenWidth = fScreenHeight;
	fXOffset = (g_WindowWidth-fScreenWidth)/2.0f;
	glTexCoord2f(0.0f,0.0f);	glVertex2i( fXOffset, 0 );
	glTexCoord2f(1.0f,0.0f);	glVertex2i( fXOffset+fScreenWidth, 0 );
	glTexCoord2f(1.0f,1.0f);	glVertex2i( fXOffset+fScreenWidth, fScreenHeight );
	glTexCoord2f(0.0f,1.0f);	glVertex2i( fXOffset, fScreenHeight );	
	glEnd();

	// Pop projection.
	ResetPerspectiveProjection();

	// Pop modelview matrix (restore state for 3D drawing).
	glPopMatrix();

	glutSwapBuffers();
}

void SetOrthographicProjection(s32 width, s32 height, BOOL bFlipY)
{
	// switch to projection mode
	glMatrixMode(GL_PROJECTION);
	// save previous matrix which contains the 
	//settings for the perspective projection
	glPushMatrix();
	// reset matrix
	glLoadIdentity();
	// set a 2D orthographic projection
	gluOrtho2D(0, width, 0, height);

	if( bFlipY )
	{
		// invert the y axis, down is positive
		glScalef(1, -1, 1);
		// mover the origin from the bottom left corner
		// to the upper left corner
		glTranslatef(0, -height, 0);
	}

	glMatrixMode(GL_MODELVIEW);
}

void ResetPerspectiveProjection()
{
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
}





[Edited by - discman1028 on October 26, 2008 9:00:02 PM]

Share this post


Link to post
Share on other sites
Advertisement
Actually, it seems GL_NEAREST works fine for minification, but I get a black screen upon GL_NEAREST magnification upon resizing my window to be larger, unless I use GL_LINEAR...

(In fact magnification is the important factor for me, I will probably never be minifying my rander target anyways.)

[Edited by - discman1028 on October 26, 2008 8:51:58 PM]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!