我实际上有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(这还不错)。
最佳答案
我要说的第一件事是:不要使用对象来表示多维数据集。听起来像是对的事情,但是我不认为要遍历数百万个对象并在每个对象上执行方法在python上执行得太多。
我建议使用3d Numpy数组来存储它(voxel [x] [y] [z] =颜色)。那将是内存消耗的改善。 You can more information here
完成上述操作并重新编写算法以使用数组而不是许多对象之后,您需要了解的下一件事是您不需要渲染所有拥有的体素。您只需要渲染可见的即可。这意味着将更少的多边形推入GPU。
考虑一下...如果您有一个10x10x10体素的多维数据集,那么现在您正在编写6000个四边形。如果将多维数据集增大为100x100x100体素,则将写入6000000个四边形。这很快就会失去控制。
您只需要推送对用户可见的那些体素。在10x10x10体素的情况下,您只需要推动“外部”的体素即可。那是486个体素(9x9x6),如果您将每个体素的每个面都写入,则意味着2916个四边形。对于100x100x100的情况,您只需要58806个体素(99x99x6),即352836个四边形(即您在第一种情况下编写的四边形数量的6%)。其余体素隐藏在外部体素的后面。
实际上,优化还没有结束。但是我认为这些小的优化将使您可以渲染比当前渲染更多的体素。还有许多更高级的优化,如果您想了解更多信息,可以阅读here。
关于python - 人们如何一次渲染这么多体素?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/12434908/