Fixed-resolution ortho projection

Started by
1 comment, last by discman1028 15 years, 5 months ago
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]
--== discman1028 ==--
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]
--== discman1028 ==--
In a nutshell, I'm having trouble with GL_NEAREST magnification in OpenGL. Seems to be killing my texture (or something).

(last bump)
--== discman1028 ==--

This topic is closed to new replies.

Advertisement