Advertisement

2D sprite/texture is inverted when using framebuffer

Started by September 10, 2018 08:24 AM
4 comments, last by Koen 6 years, 5 months ago

Hi all, 

I know that most inverted texture issue is because of inverted coordinate system but this case maybe different.

so here are the facts.

1. I am rendering a 2D sprite using a quad and a texture in orthographic projection.

I was just following the code from learnopengl.com here

https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites

have a look at initRenderData()


void SpriteRenderer::initRenderData()
{
    // Configure VAO/VBO
    GLuint VBO;
    GLfloat vertices[] = { 
        // Pos      // Tex
        0.0f, 1.0f, 0.0f, 1.0f,
        1.0f, 0.0f, 1.0f, 0.0f,
        0.0f, 0.0f, 0.0f, 0.0f, 
    
        0.0f, 1.0f, 0.0f, 1.0f,
        1.0f, 1.0f, 1.0f, 1.0f,
        1.0f, 0.0f, 1.0f, 0.0f
    };

  ...
}

Please take note that this was rendered in orthographic projection,

which means the coordinates of this quads are in screenspace (top left are 0,0 and bottom right are width,height)

and please observed the texture coordinate as well, instead of 0,0 at the bottom left, with width, height at top right

the coordinates is inverted instead.

SO if we are using orthographic projection, does this mean texture coordinates are inverted as well?

Is this assumption correct?

image.png.f1aaa28341d095205b8fd3d9fca88f0a.png

image.png.faa640ddcf05e9992e3221026e82cffe.png

2.  When rendering this quad straight, everything is OK and rendered with proper orientation


void Sprite2D_Draw(int x, int y, int width, int height, GLuint texture)
{
	// Prepare transformations
	glm::mat4 model = glm::mat4(1.0f);	//initialize to identity

										// First translate (transformations are: scale happens first,
										// then rotation and then finall translation happens; reversed order)

	model = glm::translate(model, glm::vec3(x, y, 0.0f));

	//Resize to current scale
	model = glm::scale(model, glm::vec3(width, height, 1.0f));

	spriteShader.Use();

	int projmtx = spriteShader.GetUniformLocation("projection");
	spriteShader.SetUniformMatrix(projmtx, projection);

	int modelmtx = spriteShader.GetUniformLocation("model");
	spriteShader.SetUniformMatrix(modelmtx, model);

	int textureID = spriteShader.GetUniformLocation("texture");
	glUniform1i(textureID, 0);
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, texture);

	glBindVertexArray(_quadVAO);

	glBindBuffer(GL_ARRAY_BUFFER, _VBO);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (GLvoid*)0);

	glDrawArrays(GL_TRIANGLES, 0, 6);

	glBindBuffer(GL_ARRAY_BUFFER, 0);
}

draw call


	glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT);

	//display to main screen
	Sprite2D_Draw(0, 0, 500, 376, _spriteTexture);

result

image.png.44fb68ceee89a2408b460808b69085ed.png

BUT

when rendering that image into a frame buffer, the result is inverted!

here is the code


#if 1
		frameBuffer.Bind();

		glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT);

		//... display backbuffer contents here
		//draw sprite at backbuffer 0,0
		Sprite2D_Draw(0, 0, 1000, 750, _spriteTexture);

		//-----------------------------------------------------------------------------
		// Restore frame buffer		
		frameBuffer.Unbind(SCR_WIDTH, SCR_HEIGHT);
#endif

		glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT);

		//display to main screen
		Sprite2D_Draw(0, 0, 500, 376, frameBuffer.GetColorTexture());

result, why?

image.png.a65899c4261190da0212bf91fde61c20.png

for reference here is the FBO code


int	FBO::Initialize(int width, int height, bool createDepthStencil)
{
	int error = 0;

	_width = width;
	_height = height;

	// 1. create frame buffer
	glGenFramebuffers(1, &_frameBuffer);
	glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);

	// 2. create a blank texture which will contain the RGB output of our shader.
	// data is set to NULL
	glGenTextures(1, &_texColorBuffer);
	glBindTexture(GL_TEXTURE_2D, _texColorBuffer);

	glTexImage2D(
		GL_TEXTURE_2D, 0, GL_RGB, _width, _height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL
	);

	error = glGetError();

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	// 3. attached our texture to the frame buffer, note that our custom frame buffer is already active
	// by glBindFramebuffer
	glFramebufferTexture2D(
		GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texColorBuffer, 0
	);

	error = glGetError();

	// 4. we create the depth and stencil buffer also, (this is optional)
	if (createDepthStencil) {
		GLuint rboDepthStencil;
		glGenRenderbuffers(1, &rboDepthStencil);
		glBindRenderbuffer(GL_RENDERBUFFER, rboDepthStencil);
		glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, _width, _height);
	}

	error = glGetError();

	// Set the list of draw buffers. this is not needed?
	//GLenum DrawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
	//glDrawBuffers(1, DrawBuffers); // "1" is the size of DrawBuffers
	error = glGetError();
	if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
	{
		return -1;
	}
	// Restore frame buffer
	glBindFramebuffer(GL_FRAMEBUFFER, 0);

	return glGetError();;
}

void FBO::Bind()
{
	// Render to our frame buffer
	glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
	glViewport(0, 0, _width, _height);						// use the entire texture,
															// this means that use the dimension set as our total 
															// display area
}

void FBO::Unbind(int width, int height)
{
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
	glViewport(0, 0, width, height);
}

int FBO::GetColorTexture()
{
	return _texColorBuffer;
}

this is how it was initialized
 


	FBO frameBuffer;
	frameBuffer.Initialize(1024, 1080);	

	spriteShader.Init();
	spriteShader.LoadVertexShaderFromFile("./shaders/basic_vshader.txt");
	spriteShader.LoadFragmentShaderFromFile("./shaders/basic_fshader.txt");
	spriteShader.Build();

	hayleyTex = LoadTexture("./textures/smiley.jpg");
	Sprite2D_InitData();

Any one knows what is going on in here?

 

I believe the order of frame buffer rows in memory with respect to normalized device coordinates is -y to +y, which means that after rendering to the frame buffer, texture coordinate v = 0 actually refers to the bottom row of the rendered image. When you then render the frame buffer contents, v = 0 ends up at the top, and the result is flipped vertically. (I'm pretty sure this is the problem, although it's possible I've missed something.)

Advertisement

That's correct. In opengl, normalized device coordinates (more or less the output written to gl_Position in your shader) have x pointing left right, y pointing up and z pointing into the screen, with the origin in the center of the screen. uv coordinates for texture sampling have u pointing left right and v pointing up, with the origin in the bottom left corner of the texture. That way everything will be consistent.

DirectX has uv upside down, should that be of interest.

7 minutes ago, Koen said:

That's correct. In opengl, normalized device coordinates (more or less the output written to gl_Position in your shader) have x pointing left, y pointing up and z pointing into the screen, with the origin in the center of the screen. uv coordinates for texture sampling have u pointing left and v pointing up, with the origin in the bottom left corner of the texture. That way everything will be consistent.

Just to prevent possible confusion, is it possible you meant +x and +u point right, rather than left?

1 hour ago, Zakwayda said:

Just to prevent possible confusion, is it possible you meant +x and +u point right, rather than left?

Yes, I did. Sorry about that. Funny thing is I always wondered why my brother confuses left and right when he has to think quickly, while it seems I can't even get it right in a slowly typed forum post ?

This topic is closed to new replies.

Advertisement