Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OpenGL still tries to blur even with GL_NEAREST (GL_TEXTURE_2D)

Tags:

opengl

An image says a thousand words, so what about two? I have this map art:

map image

In order to actually use this as a map I scale this texture 6 times. This however didn't go as expected:

desired-actual

All the OpenGL code is in my homebrew 2D OpenGL rendering library, and since OpenGL is a state machine it is hard to actually document the whole rendering process. But here is +/- what I do (the code is Python):

width, height = window.get_size()
glViewport(0, 0, width, height)

glMatrixMode(GL_PROJECTION)
glPushMatrix()
glLoadIdentity()

glOrtho(0.0, width, height, 0.0, 0.0, 1.0)

glMatrixMode(GL_MODELVIEW)
glPushMatrix()
glLoadIdentity()

# displacement trick for exact pixelization
glTranslatef(0.375, 0.375, 0.0)

glDisable(GL_DEPTH_TEST)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

glEnable(self.texture.target) # GL_TEXTURE_2D
glBindTexture(self.texture.target, self.texture.id)

glTexParameteri(self.texture.target, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexParameteri(self.texture.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST)

glPushMatrix()

glTranslatef(self.x, self.y, 0.0) # self.x and self.y are negative int offsets
glScalef(self.scale_x, self.scale_y, 1.0) # scale_x and scale_y are both 6

glBegin(GL_QUADS)
glTexCoord2i(0, 0)
glVertex2i(0, 0)
glTexCoord2i(0, 1)
glVertex2i(0, self.texture.height)
glTexCoord2i(1, 1)
glVertex2i(self.texture.width, self.texture.height)
glTexCoord2i(self.texture.width, 0)
glVertex2i(self.texture.width, 0)
glEnd()

glPopMatrix()
glDisable(self.texture.target)

However, this "blurring" bug doesn't occur when I use GL_TEXTURE_RECTANGLE_ARB. I'd like to also be able to use GL_TEXTURE_2D, so can someone please point out how to stop this from happening?

like image 970
orlp Avatar asked Dec 30 '11 16:12

orlp


2 Answers

When in doubt - replace your texture with same size black'n'white checkerboard (black/white 1px). It will give you a good sense of what is going - is it going to be uniformly gray (displacement is wrong) or is it going to have waves (scaling is wrong).

Make sure you don't have mip-maps automatically generated and used.

Generally you don't need any special displacement, texels match pixels with properly setup glOrtho.

Another important issue - use PowerOfTwo textures, as older GPU could used various schemes to support NPOT textures (scaling or padding transparently to user) which could result in just that sort of blurring.

To manually work with NPOT textures you will need to pad them with clear pixels till next POT size and scale your UV values glTextCoord2f(u,v) by the factor npotSize / potSize. Note that this is not compatible with tiling, but judging from your art you don't need it.

like image 118
Kromster Avatar answered Nov 15 '22 19:11

Kromster


# displacement trick for exact pixelization
glTranslatef(0.375, 0.375, 0.0)

Unfortunately this is not enough since you also need to scale the texture. For a unscaled, untranslated texture matrix, to address a certain pixel i from a texture with dimension N you need to apply the formula

(2i + 1)/(2N)

You can derive your scaling and translation from that – or determine the texture coordinates directly.

EDIT due to comment.

Okay, let's say your texture is 300 pixels wide, and you want to address exactly pixels 20 to 250, then the texture coordinates to choose for a identity texture matrix would be

(2*20 + 1)/(2*300) = 41/600 = 0.0650

and

(2*250 + 1)/(2*300) = 501/600 = 0.8350

But you could apply a transformation through the texture matrix as well. You want to map the pixels 0…299 (for 300 pixels width the index goes from 0 to 300-1 = 299) to 0…1. So let's put in those figures:

(2*0 + 1)/(2*300) =  1/600 =~= 0.0017 = a
(2*299 + 1)/(2*300) = 599/600 =~= 0.9984 = b

b-a =~= 0.9967

So you have to scale down the range 0…1 by 0.9967 and offset it by 0.0017 in the width. The same calculation goes for the height←→t coordinates. Remember that order of transformations matters. You must first scale then translate when performing the transformation, so in the matrix multiplications the translation is multiplied first:

// for a texture 300 pixels wide
glTranslatef(0.0017, …, …);
glScale(0.9967, …, …);

If you want use pixels instead of the range 0…1, further divide the scale by the texture width.

A BIG HOWEVER:

OpenGL-3 completely discarded the whole matrix manipulation functions and expect you to supply it ready to use matrices and shaders. And for OpenGL fragment shaders there is a nice function texelFetch which you can use to directly fetch texture pixels with absolute coordinates. Using that would make things a lot easier!

like image 22
datenwolf Avatar answered Nov 15 '22 20:11

datenwolf