Programming C, C++, Java, PHP, Ruby, Turing, VB
Computer Science Canada 
Programming C, C++, Java, PHP, Ruby, Turing, VB  

Username:   Password: 
 RegisterRegister   
 Python and openGL and fractals, oh my!
Index -> Programming, Python -> Python Help
View previous topic Printable versionDownload TopicSubscribe to this topicPrivate MessagesRefresh page View next topic
Author Message
Insectoid




PostPosted: Sun Apr 29, 2012 2:14 pm   Post subject: Python and openGL and fractals, oh my!

I built this basic fractal heightmap generator/renderer in Python with openGL using the Pyglet library. It works, but very slowly.

As it happens, I know very little about Python, openGL, or fractals. Consequently, the code for it is not very python-like, and the openGL draws are very, very slow, and the generator is pretty slow too. In order to speed up the draws, I tried to move all my vertices into a Pyglet VertexList but couldn't quite get that to work. At the moment, I generate a 2D list of color data and where each list element is one pixel. I then iterate over this list and draw each point. This is my bottleneck, I believe. A VertexList is supposed to run a lot faster since it sits in video RAM and that's my objective (unless there's a better way, like drawing everything to a buffer initially and then flipping that buffer to the screen, then never updating the buffer).

You'll need Pyglet installed to run this. Also, Pyglet on Python 2.6+ is broken on OS X 10.6, so mac users will need to run with Python2.5.

Python:

#!/usr/bin/python2.5
import random
import pyglet
from pyglet.gl import *

#This function generates a 3D heightmap
def fractal3D (_initial_range, passes, _roughness, size):
        heightMap = [[255 for i in range (size)] for j in range (size)] #Create a new map, all points seeded to 255 (make this pretty much whatever you want)
        randomRange = float (_initial_range) #Range to increase/decrease points by
        roughness = float (2**(_roughness*-1)) #amount to reduce range by per iteration
        sideLength = size - 1
        while (sideLength >= 2):
                halfSide = int (sideLength/2)
                x = 0
                #squaring step (using square-diamond algorithm)
                for i in range (0, size-1, sideLength):
                        for j in range (0, size-1, sideLength):
                                avg = (heightMap[i][j]+ heightMap[i+sideLength][j]+heightMap[i][j+sideLength]+heightMap[i+sideLength][j+sideLength])/4.0 + random.randint (-1*int(randomRange), int(randomRange))
                                heightMap[i+halfSide][j+halfSide] = avg
                #diamond step
                for i in range (0, size, halfSide):
                        for j in range ((i+halfSide)%sideLength, size, sideLength):
                                avg  = (heightMap[(i-halfSide+size-1)%(size-1)][j]+heightMap[(i+halfSide)%(size-1)][j]+heightMap[i][(j+halfSide)%(size-1)]+heightMap[i][(j-halfSide+size-1)%(size-1)])/4.0 + random.randint (-1*int(randomRange), int(randomRange))
                                heightMap[i][j] = avg
                sideLength = int (sideLength/2)
                randomRange = randomRange*roughness #reduce range
        return heightMap

#These functions find the max/min of a 2D list.
def min2D (list2D):
        smallest = list2D[0][0]
        for i in list2D:
                for j in i:
                        if (j < smallest): smallest = j
        return smallest

def max2D (list2D):
        largest = list2D[0][0]
        for i in list2D:
                for j in i:
                        if (j > largest): largest = j
        return largest

#this function takes a 3D heightmap and returns a 2D array of color data
def buildColorMap3D(fractal):
        minY = min2D(fractal)*-1 #this translates the array up, to remove negative values (no negative colors)
        heightSpan = max2D(fractal)+minY #the difference between the highest and lowest point, used to compress large/small values to within the valid color spectrum
        colorMap = [[0 for i in range(len(fractal[0]))] for j in range(len(fractal))] #initialized the color map
        for i in range (len(colorMap)):
                for j in range (len(colorMap)):
                        blue = green = 0 #initialize blue & green to zero
                        red = int((fractal[i][j]+minY)*767/heightSpan) #compresses height data to the valid color spectrum (256*256*256-1). NOT the full spectrum since you'll never have 0 red and blue/green >0
                        if (red > 255): #color overflow spills into the next color
                                blue = red-255
                                red = 255
                                if (blue > 255):
                                        green = blue - 255
                                        blue = 255
                                        if (green > 255):
                                                green = 255 #Didn't bother handling green overflow- it should never happen anyway.
                        colorMap[i][j] = (red, green, blue)
        return colorMap

print "Generating fractal..."   
#heightMap = createVertexList (fractal3D (100, 0, 1.0, 129))
heightMap = buildColorMap3D(fractal3D(100, 0, 1.0, 513)) #build the map
print "Done."
win = pyglet.window.Window()
win.set_size (513, 513)
                       


       
@win.event
def on_draw():
        glClear(GL_COLOR_BUFFER_BIT)
        glBegin (GL_POINTS)
        #heightMap.draw (GL_POINTS)
        #drawFractal2D(pointList, 0, 0, 1400.0, 480.0)
        #drawFractal3D(heightMap, 0, 0, 0,0)
        for i in range (len(heightMap)): #iterate over each pixel and draw it.
                for j in range (len(heightMap)):
                        glColor3ub (heightMap[i][j][0], heightMap[i][j][1], heightMap[i][j][2])
                        glVertex2i (i, j)
                        #glVertex2i (4*i, 4*j)
                        #glVertex2i (4*i-4, 4*j)
                        #glVertex2i (4*i-4, 4*j-4)
                        #glVertex2i (4*i, 4*j-4)
        glEnd()

pyglet.app.run()


Any little tweaks to turn this into Python code (rather than C code with Python syntax, which is pretty much what this is) or to improve efficiency would be appreciated.
Sponsor
Sponsor
Sponsor
sponsor
DemonWasp




PostPosted: Mon Apr 30, 2012 1:37 pm   Post subject: RE:Python and openGL and fractals, oh my!

The simplest way to improve efficiency is to batch your GL calls: right now you make two GL call per vertex, which is 2 * 256 * 256 = 131K calls per frame. There are calls where you can just pass an array (I'm not familiar with pyglet, so I don't know them offhand), which should let you make 2 calls per frame (ignoring glClear, glBegin, glEnd), assuming you set up your arrays correctly.
Display posts from previous:   
   Index -> Programming, Python -> Python Help
View previous topic Tell A FriendPrintable versionDownload TopicSubscribe to this topicPrivate MessagesRefresh page View next topic

Page 1 of 1  [ 2 Posts ]
Jump to:   


Style:  
Search: