Rotated tiled texture quads show seams and artifacts

143 views
Skip to first unread message

Ragzouken

unread,
Sep 17, 2008, 8:14:21 AM9/17/08
to pyglet-users
I have a tilemap of tiled, textured quads. When I used glRotate
(within a group) to rotate that map, my tiles show seams, and I think
even adjacent pixels in the texture start to show up in tiles where
they shouldn't be. Here is an example: http://dump.ragzouken.com/Screenshot.png

Is it possible to easily fix this? I don't need rotation that badly,
but it would be nice to have for completeness.

Alex Holkner

unread,
Sep 17, 2008, 8:21:32 AM9/17/08
to pyglet...@googlegroups.com

Add 1 pixel of padding between adjacent images in a texture. The
padding should be the same colour as the border colour.
pyglet.resource.image/TextureAtlas don't currently do this because
this kind of image manipulation isn't really supported by pyglet; and
it's easy to pad the images yourself. The following monkey patch will
also automatically add padding, but is only correct if your images
have 0 alpha at the border:


def _texture_atlas_add(self, img):
pad = 1
x, y = self.allocator.alloc(img.width + pad * 2, img.height + pad * 2)
self.texture.blit_into(img, x + pad, y + pad, 0)
region = self.texture.get_region(x + pad, y + pad, img.width, img.height)
return region
pyglet.image.atlas.TextureAtlas.add = _texture_atlas_add


Alex.

josch

unread,
Sep 28, 2008, 6:13:01 PM9/28/08
to pyglet-users
hey! i tried out this code snippet because after scaling my scene i
somehow got this strange effect and here is my story:

first i had a 32x32 image in a 32x32 TextureAtlas. i flipped the image
around a few times and drew those in a batch.
then i added scaling and obviously one can see the borders overlap:

after that i added this code snippet of yours and i had still an
issue: instead of showing parts of the neighbor texture (in this case
of itself) i got a black border.
this is the 32x32 texture in a 34x34 Texture atlas with your code
snippet above:

not caring about those still messed up borders i thought about an
optimization of your code:
with your code one has effectively 2px empty space between the images
but from my understanding it should suffice to only have 1px empty
space so i modified your code to this:
def _texture_atlas_add(self, img):
pad = 1
x, y = self.allocator.alloc(img.width + pad, img.height + pad)
self.texture.blit_into(img, x + pad, y + pad, 0)
region = self.texture.get_region(x + pad, y + pad, img.width,
img.height)
return region
pyglet.image.atlas.TextureAtlas.add = _texture_atlas_add
i expected that the same issues would happen as with your code above
but surpsingly now it seems to work!
this is the 32x32 texture in a 33x33 Texture atlas with modified
texture atlas add:

so... why does it work? does it work?

josch

unread,
Sep 28, 2008, 6:19:22 PM9/28/08
to pyglet-users
somehow i was unable to post my message as long as the links were in
it - is this normal??
i tried a lot of modifications - this one works

here are the three example pictures in correct order:
just replace IMG by imageshack

32x32 image in a 32x32 TextureAtlas:
http://img60.IMG.us/img60/3980/screenshottestpyat4.png

32x32 texture in a 34x34 Texture atlas with your code snippet:
http://img155.IMG.us/img155/3340/screenshottestpy1ke4.png

32x32 texture in a 33x33 Texture atlas with modified
textureatlas.add():
http://img508.IMG.us/img508/5309/screenshottestpy2cc6.png

Alex Holkner

unread,
Sep 28, 2008, 7:07:20 PM9/28/08
to pyglet...@googlegroups.com

I can't see those images (domain doesn't seem to be hosted); but I'll
believe what you say.

Be aware that with a TextureAtlas of non-power-of-2 dimensions, you
may have inadvertantly created a rectangular texture -- these do not
support texture coordinate wrapping, so you would never see artifacts
on the images so long as you only had one image in the atlas.

If you've ruled out that possibility; then great! Using 1-pixel
borders should work -- I believe I've tried this before and still had
artefacts, but must have had some other problem. Not sure why you're
getting black borders in my code but not yours; perhaps you could post
a complete example I can reproduce.

Alex.

josch

unread,
Sep 29, 2008, 12:52:50 AM9/29/08
to pyglet-users
you are right!

whether or not the black borders appear depends on what i set for the
texture atlas size and everything else than 33x33 fails with black
borders again.
here a complete example:

#!/usr/bin/python

import pyglet

def _texture_atlas_add(self, img):
pad = 1
x, y = self.allocator.alloc(img.width + pad, img.height + pad)
self.texture.blit_into(img, x + pad, y + pad, 0)
region = self.texture.get_region(x + pad, y + pad, img.width,
img.height)
return region
pyglet.image.atlas.TextureAtlas.add = _texture_atlas_add

atlas = pyglet.image.atlas.TextureAtlas(width=64, height=64)
tile = []
tile.append(atlas.add(pyglet.image.load('data/tiles/water/0
watrtl01.pcx/0.png')))
tile.append(tile[0].get_transform(flip_x=True))
tile.append(tile[0].get_transform(flip_y=True))
tile.append(tile[0].get_transform(flip_x=True, flip_y=True))
group = pyglet.graphics.TextureGroup(atlas.texture)

batch = pyglet.graphics.Batch()

tex_coords = []
vert_coords = []
for y in xrange(2):
y1 = y*32
y2 = y1+32
for x in xrange(2):
x1 = x*32
x2 = x1+32
tex_coords.extend(tile[2*y+x].tex_coords)
vert_coords.extend([x1, y1, x2, y1, x2, y2, x1, y2])

vl = batch.add(4*4, pyglet.gl.GL_QUADS, group, ('v2i', vert_coords),
('t3f', tex_coords), ('c4B', (255,255,255,255)*4*4))

window = pyglet.window.Window(256, 256)

@window.event
def on_draw():
window.clear()
pyglet.gl.glPushMatrix()
pyglet.gl.glScalef(4.0,4,0.0)
batch.draw()
pyglet.gl.glPopMatrix()

pyglet.app.run()

just put in a 32x32 image that then will be flipped and drawn to four
different positions.

josch

unread,
Oct 6, 2008, 1:34:24 PM10/6/08
to pyglet-users
okay i did try out other setups but still no luck....
when i try to glScalef() a scene that shows up perfectly fine unscaled
to any other value than the original size those pesky borders will
show up...
http://rabenfrost.net/josch/heroes/faulty.png (now hosted on my own
server as google seems to complain about free image hoster urls
here :/ )
i am out of ideas :(
maybe ragzouken has a solution but he didnt show up on irc lately
maybe alex can point out what i do wrong in the testcase in my post
above?

Alex Holkner

unread,
Oct 6, 2008, 5:19:49 PM10/6/08
to pyglet...@googlegroups.com

When OpenGL draws the edge pixels of each tile, it samples the edge
texel of that tile, and combines it with the adjacent texel ("bilinear
filtering"). Depending on how you're loading your images with pyglet,
you're likely to have just zeroed data (black) outside of the tile
image, which is why you're getting dark borders.

You need to ensure there's enough data for OpenGL to sample, even at
the edges. You can do this by creating tile images of size 34x34 for
when you intend to draw images at 32x32. Then use get_region (or
fiddle with the texture coordinates yourself) to draw the part of the
image from 1x1 to 33x33.

Ideally pyglet would be able to automatically "inflate" the borders of
images like this to save you a trip into your image editor; but the
current lack of pixel-level operations on images would make this quite
a pain to implement.

Alex.

josch

unread,
Oct 13, 2008, 5:03:09 AM10/13/08
to pyglet-users
So what would be a solution?
having a 34x34 image with an outer border that resembles the inner
32x32 tile border instead of the black background?
or having a transparent background?
what i do want is that the bilinear scaling filter combines the texels
of the current with the neighboring tile instead of taking the next
colors in the source texture.
isn't this a common task?

Alex Holkner

unread,
Oct 13, 2008, 5:16:35 PM10/13/08
to pyglet...@googlegroups.com
On Mon, Oct 13, 2008 at 10:03 AM, josch <j.sc...@web.de> wrote:
>
> So what would be a solution?
> having a 34x34 image with an outer border that resembles the inner
> 32x32 tile border instead of the black background?

That's it.

> or having a transparent background?
> what i do want is that the bilinear scaling filter combines the texels
> of the current with the neighboring tile instead of taking the next
> colors in the source texture.
> isn't this a common task?

Bilinear filtering is in texture fetch, not a raster op -- the only
way to do it is to draw your tiles at 1:1 scale with no rotation, copy
the framebuffer into a texture, then redraw that texture with the
desired transformation and bilinear filtering.

Alex.

josch

unread,
Oct 14, 2008, 1:51:20 AM10/14/08
to pyglet-users
On Oct 13, 11:16 pm, "Alex Holkner" <alex.holk...@gmail.com> wrote:
> Bilinear filtering is in texture fetch, not a raster op -- the only
> way to do it is to draw your tiles at 1:1 scale with no rotation, copy
> the framebuffer into a texture, then redraw that texture with the
> desired transformation and bilinear filtering.
>
> Alex.

Yes!! Great idea!!
How do I copy my batch (only a part of what i draw - not the whole
scene) into a texture?
something like
pyglet.image.get_buffer_manager().get_color_buffer() ?

Alex Holkner

unread,
Oct 14, 2008, 2:29:10 AM10/14/08
to pyglet...@googlegroups.com

Yep. The fixed_resolution.py example demonstrates this (albeit with
filtering deliberately disabled). There are also faster paths for
newer hardware using framebuffer objects (AFAIK cocos2d has an
implementation of this that you could use/borrow).

Alex.

josch

unread,
Oct 15, 2008, 5:13:03 AM10/15/08
to pyglet-users
On Oct 14, 8:29 am, "Alex Holkner" <alex.holk...@gmail.com> wrote:
> Yep.  The fixed_resolution.py example demonstrates this (albeit with
> filtering deliberately disabled).  There are also faster paths for
> newer hardware using framebuffer objects (AFAIK cocos2d has an
> implementation of this that you could use/borrow).
>
> Alex.

i dont need anything fancy that some hardware does not understand
since i only need it for a fraction of a second zoom animation that is
seldom used.
pyglet.image.get_buffer_manager() returns the whole OpenGL context -
is there an example how i only get that of my batch or do i need an
additional buffer for that?
any code examples?
cocos2d doesnt help me if it uses something that old hardware is not
capable of.

Alex Holkner

unread,
Oct 15, 2008, 5:15:12 AM10/15/08
to pyglet...@googlegroups.com
On Wed, Oct 15, 2008 at 8:13 PM, josch <j.sc...@web.de> wrote:
>
> On Oct 14, 8:29 am, "Alex Holkner" <alex.holk...@gmail.com> wrote:
>> Yep. The fixed_resolution.py example demonstrates this (albeit with
>> filtering deliberately disabled). There are also faster paths for
>> newer hardware using framebuffer objects (AFAIK cocos2d has an
>> implementation of this that you could use/borrow).
>>
>> Alex.
>
> i dont need anything fancy that some hardware does not understand
> since i only need it for a fraction of a second zoom animation that is
> seldom used.
> pyglet.image.get_buffer_manager() returns the whole OpenGL context -
> is there an example how i only get that of my batch or do i need an
> additional buffer for that?

Simply grab the framebuffer image after drawing just the batch and
before drawing anything else. If you need to, you can then clear the
framebuffer and draw the complete scene using that captured image as a
texture.

Alex.

Reply all
Reply to author
Forward
0 new messages