GL_NEAREST and Sprite Scale

619 views
Skip to first unread message

Garrett Roberts

unread,
Jan 1, 2015, 6:54:46 AM1/1/15
to pyglet...@googlegroups.com
I am relatively new to python and am creating a game engine called PyGM. I am trying to create Game Maker logic in Python. I chose Pyglet to handle the graphics on this project however I have run into a few issues.

The first issue was I could not scale a sprite without it becoming blurry due to the biliniar texture filtering. After some research I found a fix by using this code:

def texture_set_mag_filter_nearest(texture):
    glBindTexture(texture.target, texture.id)
    glTexParameteri(texture.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    glBindTexture(texture.target, 0)

there is however another issue. The sprites look fine when scaled to a whole number but when scaling to anything else(like 2.5) produces strange results.

Is there a way to fix this?

Greg Ewing

unread,
Jan 1, 2015, 7:38:00 PM1/1/15
to pyglet...@googlegroups.com
Garrett Roberts wrote:
> def texture_set_mag_filter_nearest(texture):
> glBindTexture(texture.target, texture.id)
> glTexParameteri(texture.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
> glBindTexture(texture.target, 0)
>
> The sprites look fine when scaled to a
> whole number but when scaling to anything else(like 2.5) produces
> strange results.

Can you be more specific? Strange in what way?

--
Greg


Salvakiya

unread,
Jan 3, 2015, 5:17:35 PM1/3/15
to pyglet...@googlegroups.com
there is a tearing in the sprite even though the sprite is not moving. This tear moves from left to right and is vertical. also the non transparent pixels around the sprite look fuzzy.

mattis n

unread,
Jan 29, 2015, 10:53:14 AM1/29/15
to pyglet...@googlegroups.com
 I don't know the solution to your problem, but I cannot get your code to work. It still scales using bilinear filtering for me. I am loading images using pyglet.resource.image (which returns a Texture). At this point I apply the code to each returned texture. Then I put these texture in sprites, scale it to 2, and put the sprites in a batch and draw them. 

As far as I can tell, the openGL target of these textures is GL_TEXTURE_RECTANGLE, and not GL_TEXTURE_2D. If I could change it to be GL_TEXTURE_2D , I think I could get it working as others have done (http://gamedev.stackexchange.com/questions/20297/how-can-i-resize-pixel-art-in-pyglet-without-making-it-blurry)
But trying to manually change the target of each texture makes the program crash when I draw the batch:

  File "C:\Python34\lib\site-packages\pyglet\sprite.py", line 144, in set_state
    glBindTexture(self.texture.target, self.texture.id)
  File "C:\Python34\lib\site-packages\pyglet\gl\lib.py", line 104, in errcheck
    raise GLException(msg)
pyglet.gl.lib.GLException: b'invalid operation'

Any ideas what I'm doing wrong?

Leif Theden

unread,
Jan 29, 2015, 1:12:39 PM1/29/15
to pyglet...@googlegroups.com
You will need to subclass pyglet.sprite.Sprite and override the following method:

def set_state(self):
    glEnable(self.texture.target)
    glBindTexture(self.texture.target, self.texture.id)
    glPushAttrib(GL_COLOR_BUFFER_BIT)
    glEnable(GL_BLEND)
    glTexParameteri(self.texture.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    glBlendFunc(self.blend_src, self.blend_dest)

The reason this works is due to the way pyglet handles the OpenGL state machine.  When a sprite is going to get drawn, pyglet will execute the OpenGL commands inside Sprite.set_state so that the texture is rendered as intended.  There really isn't an easier way.  I've tested this, and on my computer, it works fine, even with fractional scale values on the sprite.

mattis n

unread,
Jan 30, 2015, 4:05:45 AM1/30/15
to pyglet...@googlegroups.com
I can't get this to work. The upscaled image being filtered with bilinear filtering. Can you tell me if the following code does nearest neighbour scaling for you?

from pyglet.gl import *
import pyglet

class pixelatedSprite(pyglet.sprite.Sprite):
    def set_state(self):
        glEnable(self.texture.target)
        glBindTexture(self.texture.target, self.texture.id
        glPushAttrib(GL_COLOR_BUFFER_BIT)
        glEnable(GL_BLEND)
        glTexParameteri(self.texture.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
        glBlendFunc(self.blend_src, self.blend_dest)


window = pyglet.window.Window()

image = pyglet.image.load("gold.jpg")
# tried with image = pyglet.resource.image("gold.jpg") too - no dice

sprite = pixelatedSprite(image)
sprite.scale = 2

@window.event
def on_draw():
    window.clear()
    sprite.draw()

pyglet.app.run()

Leif Theden

unread,
Jan 30, 2015, 11:31:47 PM1/30/15
to pyglet...@googlegroups.com
so, i may have made my reply too fast.  mattis n, this works and is tested on my system.

from pyglet.gl import *
import pyglet


def make_pixelated(sprite):
""" Make the goup of this sprite pixelated.

:param sprite: pyglet.sprite.Sprite
:return: None
"""
import types


def set_state(self):
glEnable(self.texture.target)
glBindTexture(self.texture.target, self.texture.id)
glPushAttrib(GL_COLOR_BUFFER_BIT)
glEnable(GL_BLEND)
glTexParameteri(self.texture.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glBlendFunc(self.blend_src, self.blend_dest)

    group = sprite._group
group.set_state = types.MethodType(set_state, group)

window = pyglet.window.Window()
image = pyglet.image.load("gold.jpg")
batch = pyglet.graphics.Batch()
sprite = pyglet.sprite.Sprite(image, batch=batch)

make_pixelated(sprite)

sprite.scale = 2


@window.event
def on_draw():
window.clear()
    batch.draw()

pyglet.app.run()

I missed the part where it isn't the sprite that sets the state, it is the group that it belongs to.  What you see above is a hack that adds our 'pixelated render' to the group that renders the sprite.  The correct was of doing this isn't super obvious, due to how groups are created.  this is just works easier.



On Thursday, January 1, 2015 at 5:54:46 AM UTC-6, Salvakiya wrote:

mattis n

unread,
Feb 11, 2015, 11:16:52 AM2/11/15
to pyglet...@googlegroups.com
Thanks a lot, this is very helpful for me. As far as I can tell this only works when you use pyglet.image.load -- not pyglet.resource.image -- to load an image file. I am unsure why... Something to do with different classes (ImageData vs. Texture vs. TextureRegion) perhaps? 

Is pyglet.resource.image supposed to be more efficient?

Leif Theden

unread,
Feb 11, 2015, 11:39:52 AM2/11/15
to pyglet...@googlegroups.com
Yes, it is more efficient because it maintains a cache and loads images into a TextureBin, which more effectively manages GPU resources.  I'm not sure why exactly my hack won't work with images loaded from pyglet.resource, but I think you might be able to find a way.  You could try making a Sprite group using that code in set_state, then when you make sprites add them to that group.  If you will absolutely never be using filtered sprites, you could just modify pyglet.sprite.SpriteGroup and ship your modified pyglet.
Reply all
Reply to author
Forward
0 new messages