2012-09-15 26 views
1

我其實有2個問題。 1)我看到成千上萬個體素的這些渲染,但是使用八叉樹我仍然可以在屏幕上獲得大約500個體素。人們如何一次呈現如此多的體素?

import warnings,sys 
sys.setrecursionlimit(50000) 

class storedItem(): 
def __init__(self,coord,data): 
    self.coord = coord 
    self.data = data 
    self.node = None 
def remove(self): 
    self.node.children.remove(self) 
    self.node = None 

class Node(): 
    def __init__(self,parent,lower,upper): 
    self.parent = parent 
    self.children = [] 
    self.lowerbound = lower 
    self.upperbound = upper 
    self.setVolume() 

def setVolume(self): 
    dx = self.upperbound[0] - self.lowerbound[0] 
    dy = self.upperbound[1] - self.lowerbound[1] 
    dz = self.upperbound[2] - self.lowerbound[2] 
    self.volume = dx*dy*dz 

def inbound(self,coord): 
    if self.lowerbound[0] <= coord[0] and self.lowerbound[1] <= coord[1] and self.lowerbound[2] <= coord[2]: 
     if self.upperbound[0] >= coord[0] and self.upperbound[1] >= coord[1] and self.upperbound[2] >= coord[2]: 
      return True 
    return False 

def returnValueOrChildnode(self,coord): 
    if not self.inbound(coord): 
     return self.parent 
    for child in self.children: 
     if child.__class__ == Node: 
      if child.inbound(coord): 
       return child 
     elif child.__class__ == storedItem: 
      if child.coord == coord: 
       return child 
    return None 

def deleteOrReturnChildNode(self,coord): 
    if not self.inbound(coord): 
     return self.parent 
    for child in self.children: 
     if child.__class__ == Node: 
      if child.inbound(coord): 
       return child 
     elif child.__class__ == storedItem: 
      if child.coord == coord: 
       self.children.remove(child) 
       del(child) 
       return True 
    return None 


def insertStoredItem(self,item): 
    if len(self.children) < 8: 
     self.children.append(item) 
     item.node = self 
     return True 

    if len(self.children) == 8: 
     for child in self.children: 
      if child.__class__ == Node: 
       if child.inbound(item.coord): 
        return child.insertStoredItem(item) 
      elif item.coord == child.coord: 
       warnings.warn('Already an item at this location, replacing it') 
       self.children.remove(child) 
       self.children.append(item) 

     self.breakupIntoChildren() 
     self.insertStoredItem(item) 


def breakupIntoChildren(self): 
    #if self.volume == 8: 
    # raise Exception("Node full. Cannot add to this node") 
    nodes = [] 
    delta = (self.upperbound[0] - self.lowerbound[0] +1)/2 
    x1,x2,x3 = (self.lowerbound[0],self.lowerbound[0]+delta -1,self.upperbound[0]) 
    y1,y2,y3 = (self.lowerbound[1],self.lowerbound[1]+delta -1,self.upperbound[1]) 
    z1,z2,z3 = (self.lowerbound[2],self.lowerbound[2]+delta -1,self.upperbound[2]) 

    nodes.append(Node(self,(x1,y1,z1),(x2,y2,z2))) 
    nodes.append(Node(self,(x2 + 1,y1,z1),(x3,y2,z2))) 
    nodes.append(Node(self,(x1,y1,z2 +1),(x2,y2,z3))) 
    nodes.append(Node(self,(x2 + 1,y1,z2 + 1),(x3,y2,z3))) 
    nodes.append(Node(self,(x1,y2 + 1,z1),(x2,y3,z2))) 
    nodes.append(Node(self,(x2 + 1,y2 + 1,z1),(x3,y3,z2))) 
    nodes.append(Node(self,(x1,y2 + 1,z2 + 1),(x2,y3,z3))) 
    nodes.append(Node(self,(x2 + 1,y2 + 1,z2 + 1),(x3,y3,z3))) 


    while self.children: 
     child = self.children[0] 
     for node in nodes: 
      if node.inbound(child.coord): 
       node.insertStoredItem(child) 
       self.children.remove(child) 

    self.children = nodes 



class Octree(): 
def __init__(self,size,maxsearch=1000): 
    if size % 2: 
     raise Exception("Size must be multiple of 2") 
    self.root = Node(None, (0,0,0),(size,size,size)) 
    self.size = size 
    self.maxsearch=maxsearch 

def search(self,coord): 
    searching = True 
    node = self.root 
    count = 0 
    while searching: 
     result = node.returnValueOrChildnode(coord) 
     if result is None: 
      searching = False 
     elif result.__class__ == storedItem: 
      result = result.data 
      searching = False 
     elif result.__class__ == Node: 
      node = result 
     count += 1 
     if count > self.maxsearch: #just incase something goes wrong 
      searching=False 
      result = None 
      raise Exception("Max Search depth limit reached") 

    return result 

def insert(self,coord,data): 
    if not self.root.inbound(coord): 
     print coord, self.size, self.root.upperbound, self.root.lowerbound 
     raise Exception("Coordinate outside scope of octree") 

    item = storedItem(coord,data) 
    self.root.insertStoredItem(item) 

def remove(self,coord): 
    searching = True 
    node = self.root 
    count = 0 
    while searching: 
     result = node.deleteOrReturnChildNode(coord) 
     if result is True: 
      searching = False 
      return True 
     elif result is None: 
      searching = False 
     elif result.__class__ == Node: 
      node = result 
     count += 1 
     if count > self.maxsearch: #just incase something goes wrong 
      searching=False 
      result = None 
      raise Exception("Max Search depth limit reached") 

    return result 
def trace(frame, event, arg): 
    print "%s, %s:%d" % (event, frame.f_code.co_filename, frame.f_lineno) 
    return trace 

這就是我一直在使用的八叉樹代碼。我把它從一個網站上拿下來,這個網站非常整潔。它可以很好地去除內部的立方體。雖然它只是一個空心的盒子,這很奇怪。雖然它確實大大提高了FPS。爲了渲染立方體,我使用這個小類。

class Cube(object): 


def __init__(self, position, color,tree): 

    self.position = position 
    self.x = position[0] 
    self.y = position[1] 
    self.z = position[2] 
    self.color = color 
    self.tree = tree 

num_faces = 6 

vertices = [ (0.0, 0.0, 1.0), 
      (1.0, 0.0, 1.0), 
      (1.0, 1.0, 1.0), 
      (0.0, 1.0, 1.0), 
      (0.0, 0.0, 0.0), 
      (1.0, 0.0, 0.0), 
      (1.0, 1.0, 0.0), 
      (0.0, 1.0, 0.0) ] 

normals = [ (0.0, 0.0, +1.0), # front 
      (0.0, 0.0, -1.0), # back 
      (+1.0, 0.0, 0.0), # right 
      (-1.0, 0.0, 0.0), # left 
      (0.0, +1.0, 0.0), # top 
      (0.0, -1.0, 0.0) ] # bottom 

vertex_indices = [ (0, 1, 2, 3), # front 
        (4, 5, 6, 7), # back 
        (1, 5, 6, 2), # right 
        (0, 4, 7, 3), # left 
        (3, 2, 6, 7), # top 
        (0, 1, 5, 4) ] # bottom  

def render(self):     

    glColor(self.color) 

    # Adjust all the vertices so that the cube is at self.position 
    vertices = [tuple(Vector3(v) + self.position) for v in self.vertices] 

    # Draw all 6 faces of the cube 
    glBegin(GL_QUADS) 

    for face_no in xrange(self.num_faces): 

     glNormal3dv(self.normals[face_no]) 

     v1, v2, v3, v4 = self.vertex_indices[face_no] 

     glVertex(vertices[v1]) 
     glVertex(vertices[v2]) 
     glVertex(vertices[v3]) 
     glVertex(vertices[v4])    

    glEnd() 
def getneighbors(self): 
    x = self.x 
    y = self.y 
    z = self.z 
    return ((x,y,z+1),(x+1,y,z),(x,y+1,z),(x-1,y,z),(x,y-1,z),(x,y,z-1)) 

def checkneighbors(self): 
    if not self.tree: 
     return True 
    positions = self.getneighbors() 
    for pos in positions: 
     result = self.tree.search(pos) 
     if not result: 
      return True 
    return False 

我可以得到約30FPS與此代碼。我認爲屏幕上有大約62,210個方格。我通常得到大約30-40 FPS(這並不壞)。

+1

當你有兩個獨立的問題,你應該問他們獨立。我想你可以刪除第二個問題,因爲這裏有足夠的資源(例如http://stackoverflow.com/questions/4753055/perlin-noise-generation-for-terrain)。要回答你的第一個問題,你應該提供更多的細節。你現在怎麼渲染它們? – Nobody

+0

請顯示您的代碼。如果不顯示它,爲什麼代碼不能像你期望的那樣工作是不可能的。 –

+0

我添加了代碼。 –

回答

2

我會說的第一件事是:不要用對象來表示立方體。這聽起來像是正確的做法,但我不認爲遍歷數百萬個對象,並且每個對象上的方法執行都會對python執行太大。

我會推薦使用3D Numpy數組來存儲這個(voxel [x] [y] [z] = color)。這將是內存消耗的改善。 You can more information here

當你這樣做,並重寫你的算法,使用數組而不是許多對象,你需要意識到的下一件事是,你不需要渲染你有每個體素。你只需要渲染那些可見的。這意味着將更少的多邊形推送到GPU。

想一想......如果你有一個10x10x10的立方體,現在你正在寫6000四邊形。如果將立方體增長到100x100x100體素,您將寫入6000000個四邊形。這很快就失控了。

你只需要推動那些將被用戶看到的體素。在10x10x10體素的情況下,你只需要推「外」的。那是486個體素(9x9x6),如果你寫每個體素的每個面,這意味着2916個四面體。而對於100x100x100的情況,您只需要58806個體素(99x99x6),即352836個四邊形(這是您在第一個案例中寫出的四邊形數量的6%)。其餘的體素隱藏在外面。

優化不會在此結束,真的。但是我認爲這些小的優化可以讓你呈現比當前渲染更多的體素。還有更多高級優化,如果您想了解更多信息,可以閱讀here