pixies - sprites with zero filtering

Blitz3D Forums/Blitz3D Programming/pixies - sprites with zero filtering

skidracer(Posted 2003) [#1]
The following was created while fixing my original overlay system in code archives.

It demonstrates use of sprites to achieve perfect pixel-texel mapping which results in zero filtering / blurring.

Odd sized textures and zoomed cameras may require some tweaks.

; pixies.bb
; by simon armstrong

; pixies are pixel perfect sprite overlays

; LoadPixie(camera,file$)
; CreatePixie(camera,texture)

; returns a sprite parented to a camera 
; with following features 
;  1:1 pixel-texel scale for zero filtered sharp overlays
;  position pixies in screen coordinates

; 20.8.2003 untested with odd sized sprites

displaywidth=800
displayheight=600

Graphics3D displaywidth,displayheight  

cam=CreateCamera() 
CameraRange cam,.1,1000 

pixie=LoadPixie(cam,"simon2.bmp")		;800,600,

While Not KeyHit(1) 
	PositionEntity pixie,MouseX(),MouseY(),0
	RenderWorld 
	UpdateWorld 
	TurnEntity cam,1,2,0	;test texel drift
	Flip 
	Wend 
End 

Function LoadPixie(camera,file$)
	texture=LoadTexture(file)
	Return CreatePixie(camera,texture)
End Function

Function CreatePixie(camera,texture)
; change these for viewports
	viewwidth=GraphicsWidth()
	viewheight=GraphicsHeight()
; find existing pixiespace parented to camera
	magic=0
	n=CountChildren(camera)
	For i=1 To n
		If EntityName(GetChild(camera,i))="pixiespace" 
			magic=GetChild(GetChild(camera,i),1)
		EndIf
	Next
	If magic=0
		magic=CreatePivot(camera) 
		NameEntity(magic,"pixiespace")
		aspect#=Float(viewheight)/viewwidth
		PositionEntity magic,-1,aspect,1 
		scale#=2.0/viewwidth 
		ScaleEntity magic,scale,-scale,-scale 
		magic=CreatePivot(magic)
		PositionEntity magic,-.5,-.5,0
	EndIf
; create sprite from texture as child of magic overlay	
	sprite=CreateSprite()			;,magic ???
	EntityParent sprite,magic		;cludge for blitz bug in createsprite(parent)
	brush=CreateBrush()
	BrushFX brush,1
	BrushTexture brush,texture
	PaintEntity sprite,brush
	FreeBrush brush
	SpriteViewMode sprite,2 
	scale#=1.0/viewwidth 
	ScaleSprite sprite,TextureWidth(texture)*scale,TextureHeight(texture)*scale
	Return sprite
End Function



hub(Posted 2003) [#2]
The displayed sprite is twice larger than the the file bitmap ?


skidracer(Posted 2003) [#3]
Not in my tests, what size image are you using?


simonh(Posted 2003) [#4]
Impressive - I didn't think pixel-perfect sprites were possible to be honest, and after looking at your code, I'm still unsure as to what exactly your magic trick is to make them unfiltered.

Anyway, will probably be using this code for my Super Hamster Ball editor, and possibly the game too, so thanks in advance!


sswift(Posted 2003) [#5]
His "magic trick" is twofold.

1. The sprite is at the same resolution as the size of the region it covers at a specific resolution. Ie, a 256x256 sprite covers a 256x256 pixel region, regardless of the screen resoltion. In other words, at higher resoltions, your HUD objects will be smaller.

2. He offsets the object by half a pixel towards the top left of the screen. The reason for this has to do with how bilinear filtering works. Unless you offset by half a pixel, then each pixel is actually displaying a point in the center of 4 pixels. So every pixel's getting averaged with the 4 colors that surround it. The result is that if you took a texture which is vertical or horizontal white and black lines, then unless you offset it like this, what you will actually see on the screen is a solid 50% gray square.

Of course there's one little problem with doing this, I think and that is that there may be a one pixel wide border outside your 640x480 region on the right and bottom of the screen. Though I have not actually tested this to see if that is what you get or not. I do know what if you offset towards the bottom right instea dof towards the upper left you get a border on the top and left sides of the screen.


R0B0T0(Posted 2003) [#6]
Cool, I've been experimenting trying to get this effect for quite a while.

Thanks for your help!


simonh(Posted 2003) [#7]
Ah, I see. Thanks for the explanation sswift.


hub(Posted 2003) [#8]
uhm...


i obtain simon.bmp with the saveimage command.

Here the file


skidracer(Posted 2003) [#9]
Ouch, yes, blitz is scaling/blurring textures to nearest power of 2 internally (your bitmap ends up as 256x64 texture in blitz).

I can fix this soon by copying pixie images to a large texture page, which I was going to do for an optimized version that uses a single surface collection of quads.

In the meantime, stick with 32,64,128,256 wide bitmaps for your pixies and you should be ok.


skidracer(Posted 2003) [#10]
All fixed, no it will still be broken for odd sizes, but of for non power of 2...

; pixies.bb
; by skidracer

; pixies are pixel perfect sprite overlays

; LoadPixie(camera,imagefile$)

; returns a sprite parented to a camera 
; with following features 
;  1:1 pixel-texel scale for zero filtered sharp overlays
;  position pixies in screen coordinates

; 20.8.2003 untested with odd sized sprites
; 21.8.2003 modified to handle odd textures

displaywidth=800
displayheight=600

Graphics3D displaywidth,displayheight  

cam=CreateCamera() 
CameraRange cam,.1,1000 

pixie=LoadPixie(cam,"quitter.bmp")

While Not KeyHit(1) 
	PositionEntity pixie,MouseX(),MouseY(),0
	RenderWorld 
	UpdateWorld 
	TurnEntity cam,1,2,0	;test texel drift
	Flip 
	Wend 
End 

Function LoadPixie(camera,file$)
; load squared texture
	texture=LoadTexture(file)
	width=TextureWidth(texture)
	height=TextureHeight(texture)
	image=LoadImage(file)
	iwidth=ImageWidth(image)
	iheight=ImageHeight(image)
	If iwidth<>width Or iheight<>height
		buffer=TextureBuffer(texture)
		ibuffer=ImageBuffer(image)
		For y=0 To height-1
			For x=0 To width-1
				WritePixel x,y,ReadPixel(x,y,ibuffer),buffer
			Next
		Next
		ScaleTexture texture,Float(width)/iwidth,Float(height)/iheight ; will blitzmax need float()?
		width=iwidth
		height=iheight
	EndIf
	FreeImage image
; change these for viewports
	viewwidth=GraphicsWidth()
	viewheight=GraphicsHeight()
; find existing pixiespace parented to camera
	magic=0
	n=CountChildren(camera)
	For i=1 To n
		If EntityName(GetChild(camera,i))="pixiespace" 
			magic=GetChild(GetChild(camera,i),1)
		EndIf
	Next
	If magic=0
		magic=CreatePivot(camera) 
		NameEntity(magic,"pixiespace")
		aspect#=Float(viewheight)/viewwidth
		PositionEntity magic,-1,aspect,1 
		scale#=2.0/viewwidth 
		ScaleEntity magic,scale,-scale,-scale 
		magic=CreatePivot(magic)
		PositionEntity magic,-.5,-.5,0
	EndIf
; create sprite from texture as child of magic overlay	
	sprite=CreateSprite()
	EntityParent sprite,magic		;cludge for blitz bug in createsprite(parent)
	brush=CreateBrush()
	BrushFX brush,1
	BrushTexture brush,texture
	PaintEntity sprite,brush
	FreeBrush brush
	SpriteViewMode sprite,2 
	scale#=1.0/viewwidth 
	ScaleSprite sprite,width*scale,height*scale
	Return sprite
End Function



BlitzSupport(Posted 2003) [#11]
Oh, good stuff. Looking forward to trying this later, as I didn't think it was possible either!

I still say Death to the Pixies, though.


Harder(Posted 2007) [#12]
This has been very helpful guys, thanks. My only curiousity, as the code is a bit beyond me, is how I can modify this so I can change my camera zoom level. Any thoughts?

Tyler


Stevie G(Posted 2007) [#13]
Use a second, static camera far away from the main scene and render it on top of the main camera. This way the main camera's zoom is irrelevant.

Something like this ....

GUIcamera = CreateCamera()
CameraClsMode GUIcamera,False,True
CameraRange GUIcamera, .1,5
PositionEntity GUIcamera, 10000,10000,10000
EntityOrder GUIcamera,-999

Then use GUIcamera instead of Camera in the pixie routines.

Stevie


John Pickford(Posted 2007) [#14]
I use multiple cameras in my stuff. Rather than keeping stuff 'far away' which is potentially problematic I keep EVERYTHING hidden by default and show stuff only for rendering. HUD items might be parented to a pivot called HUD_SWITCH (or even the hud camera) to make this easy.

  showentity GAME_SWITCH ;pivot which is the parent of everything in game, including the main camera
  renderworld()
  hideentity GAME_SWITCH

  showentity HUD_SWITCH
  renderworld()
  hideentity HUD_SWITCH


or better still just have a function that takes ths switch as a parent

function do_render_world(switch)

  showentity (switch)
  renderworld()
  hideentity (switch)

end function



slenkar(Posted 2007) [#15]
has there been anything added to the code archives that is better than this?
its been 3 years