[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: [pygame] Bump mapping



Jasper Phillips wrote:
On Wed, 30 Jun 2004, Sami Hangaslammi wrote:


I was bored, so here. ;)

def shadeMapSurfArray( surface, shadeSurf, elevation=55.0, compensate=False ):
array = pygame.surfarray.array3d(surface)
shade = pygame.surfarray.array3d(shadeSurf).astype(N.Float64)
if compensate:
compensation = sin(elevation)
shade /= compensation
shade /= 255.0

# repeat the shade pattern as many times as needed
# to cover the whole surface
x,y = surface.get_size()
xx,yy = shadeSurf.get_size()
mx,my = (x+xx)//xx, (y+yy)//yy
shade = N.concatenate((shade,)*mx,0)
shade = N.concatenate((shade,)*my,1)
shade = shade[:x,:y,...]
array = array * shade
array = N.where(array > 255, 255, array).astype(N.Int0)
return pygame.surfarray.make_surface(array)

Probably someone more familiar with the surfarray can make it faster,
but should beat get_at/set_at at least.

Cool, thanks!  This helped quite a bit, as I was fumbling around trying to
find a way to multiply 2 arrays of different sizes, and also didn't know
about N.where().

For 380x360  I got  .35 seconds for a ~3.7x speed improvement.
For 1220x840 I got 1.30 seconds for a ~7.5x speed improvement.
For the next size up (~2000x2000) it appears to suck up memory/cpu and hang!
Not sure what's going on here. :-/
Let's see, 2000x2000*4/1024 = 15.6MB.
When dealing with large arrays you'll have to be a little more careful with Numeric. It tends to want to make copies of the arrays frequently. At 15MB each it won't take many copies to swamp a machine.

Here is your solution. Almost all the functions in Numeric take on optional last argument representing the "destination" array.

This happens automatically when you use the "/=" and "+=" inplace style operators. One one line you have "array = array * shade". Just remember that this is going to take a temporary 15MB when you get to 2k images.

Instead use either of these lines,
array *= shade
Numeric.multiply(array, shade, array)

Also, if you really are using the "compensate" mode. Precalculate that elevation and compensation map. At 2k that's a heck of a lot of calls to sin() and radians() for every call to shadeMap. I'd say same goes for convering "shade" to an array of 0-1 floats. Although the shade maps aren't as large, doing it once when you load the shade image is a big win over doing the conversion every time you want to use it.

Numeric also has a "max" function, which should be better optimized than the "where". Plus it can place the results directly in the original.
if compensate:
Numeric.maximum(array, 255, array)


  -This requires changing the rest of the code to store shades as
  (shade,shade,shade) instead of (shade,0,0)
Actually, you should be able to use just "shade", instead of backing it into an array with 3 values. Numeric will automatically apply the value in the "2D" array to each of the values in the "3D" array. If anything this will help with the memory situation.
shade = pygame.surfarray.array3d(shadeSurf)[0].astype(N.Float64)


PS  Here's my tweaked version:
Perhaps an entry for the new PCR, once it's all cleaned up (hint hint)