我正在尝试在10x10x10网格的树莓派(模型2B)上建模3d LED阵列。
我只是希望它们根据模式生成算法来打开和关闭。
我已经在pi3d中编写了一些基本代码来对数组中的1000个球体进行建模。它在阵列中循环,并通过将球体的颜色更改为蓝色或黑色来打开或关闭每个LED。
该代码的核心部分如下:
spheres = [[[pi3d.Sphere(x=x-5,y=y-5,z=z-5,radius=0.1) for x in range(dim)] for y in range(dim)] for z in range(dim)]
i = 0
while DISPLAY.loop_running():
k = mykeys.read()
if k == 27:
mykeys.close()
ISPLAY.destroy()
break
CAM.update(mymouse)
for x in range (dim):
for y in range(dim):
for z in range(dim):
colour=0.1
if(((x-dim/2.0) * (x-dim/2.0)) + ((y-dim/2.0) * (y-dim/2.0)) + ((z-dim/2.0) * (z-dim/2.0)) <= i * dim):
colour = 1.0
spheres[x][y][z].set_material((0.0,0.0,colour))
spheres[x][y][z].draw()
i=i+0.1
if i > 4:
i=0
效果很好,但给我约5 fps。将球体更改为立方体可以稍微改善这一点,但是至少我真的希望性能提高一个数量级。我知道我可以在数学上提高一些效率,但是我经历了类似的性能,即随机打开和关闭它们,因此我暂时不关注它。
我虽然也许这只是要求太多的树莓派,但后来又玩了与它捆绑在一起的《我的世界》游戏,发现它具有更高的复杂性,同时可以流畅地渲染。
我想知道是否可以使用其他方法,甚至是另一种语言为我提供所需的性能。
我对3D编程了解甚少,因此任何人都可以向我提出的任何建议或教程都将很有用。
最佳答案
问题是有python代码对每个pi3d进行矩阵乘法。一次成形一个。尽管这是使用numpy完成的,并且速度尽可能快,但仍然很慢。
您可以将所有球体都变成一个pi3d.MergeShape,这样每帧只需要一个draw(),就会非常快...但是
您的Sphere对象使用12边x 12切片的默认值,从而获得288个面和864个顶点,因此您的MergeShape将具有864,000个顶点,这可能会开始降低GPU的速度。
捆绑的着色器仅对整个Shape使用一种材料RGB值,您想为每个球体指定不同的颜色,这需要被黑的着色器(如果习惯用于黑化着色器,这很容易做到),您可以在其中指定RGB值缓冲区数组的纹理坐标字段。
您的代码没有显示您正在使用的着色器,默认值为mat_light,它将为每个球体提供平滑的3D效果,但是如果您可以使用点进行管理(请参见演示SpriteBalls),则可以使数千个球体快速运行。 ..但是您仍然需要修改着色器,以更改每个顶点的漫反射颜色。
或者,您可以制作一个半蓝色,半黑色的纹理,并调整每帧各个球体的纹理坐标。假设您已将所有球体合并为一个形状,这将非常快(尽管将涉及一个笨拙的numpy公式来重现x,y,z嵌套循环的效果)
在接下来的几天里,我将尝试设计一个演示如何显示这些选项的演示并将其添加到https://github.com/pi3d/pi3d_demos
编辑我只是想起了Starfield.py演示,该演示使用了可变颜色的“广告牌”点。这样每帧可以渲染成千上万个点,但是它具有各种复杂性,使相对简单的结构难以理解,正如我在上面提到的那样,我将使用一个距中心的欧几里得距离,制作一个更简单的版本来演示具有颜色变化的10x10x10阵列。
第二编辑这是使用pi3d_demos/shaders/star_point
的广告牌或sprite版本
import pi3d
import numpy as np
DIM = 10
half_d = DIM/2.0
arr_len = DIM ** 3
disp = pi3d.Display.create()
shader = pi3d.Shader('shaders/star_point')
cam = pi3d.Camera()
spheres = pi3d.Points(camera=cam, point_size=400.0, z=15.0,
vertices=[[x - half_d, y - half_d, z - half_d] for x in range(DIM) for y in range(DIM) for z in range(DIM)],
normals=np.zeros((arr_len, 3)), tex_coords=np.full((arr_len, 2), 1.0))
spheres.set_shader(shader)
arr_buf = spheres.buf[0].array_buffer # shortcut to numpy array shape (1000,8) [[vx,vy,vz,nx,ny,nz,u,v]]
# the star_point shader uses nx,ny,nz as RGB values, only the B value is being
# changed here i.e. arr_buff[:,5]
i = 0
while disp.loop_running():
spheres.draw()
ix = np.where(np.sum((arr_buf[:,:3] - [half_d, half_d, half_d]) ** 2, axis=1) <= i * DIM)[0]
arr_buf[:,5] = 0.1 # set all to midnight blue first
arr_buf[ix,5] = 1.0 # set ones within (i * DIM) ** 0.5 to blue
spheres.re_init() # have to update buffer
i += 0.1
if i > 4.0:
i = 0.0
这是使用MergeShape然后调整uv坐标的版本
import pi3d
import numpy as np
DIM = 10
half_d = DIM/2.0
arr_len = DIM ** 3
disp = pi3d.Display.create()
shader = pi3d.Shader('uv_light')
cam = pi3d.Camera()
tex_array = np.zeros((16,16,3), dtype=np.uint8)
tex_array[:8,:8] = [0, 0, 25] # top left midnight blue
tex_array[8:, 8:] = [0, 0, 255] # bottom right bright blue
tex = pi3d.Texture(tex_array, mipmap=False)
spheres = pi3d.MergeShape(camera=cam, z=15.0)
spheres.merge([[pi3d.Sphere(radius=0.1, sides=6, slices=6), x - half_d, y - half_d, z - half_d, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0]
for x in range(DIM) for y in range(DIM) for z in range(DIM)])
spheres.set_draw_details(shader, [tex])
arr_buf = spheres.buf[0].array_buffer # shortcut to numpy array shape (1000,8) [[vx,vy,vz,nx,ny,nz,u,v]]
arr_buf[:,6:8] *= 0.5 # scale uv to just use top left part of texture
base_tex_c = arr_buf[:,6:8].copy()
i = 0
while disp.loop_running():
spheres.draw()
ix = np.where(np.sum((arr_buf[:,:3] - [half_d, half_d, half_d]) ** 2, axis=1) <= i * DIM)[0]
arr_buf[:,6:8] = base_tex_c # set uv to base (top left)
arr_buf[ix,6:8] += 0.5 # set index ix to bottome right
spheres.re_init() # have to update buffer
i += 0.1
if i > 4.0:
i = 0.0
我发现默认的Sphere数组缓冲区的大小变得太大,因此将其减小为6x6版本。希望这会在某个阶段对某人有所帮助。