首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >树莓皮的高效3d

树莓皮的高效3d
EN

Stack Overflow用户
提问于 2017-05-01 17:13:37
回答 2查看 350关注 0票数 2

我试图在10x10x10网格中的覆盆子圆周率(2B型)上模拟一个三维LED阵列。我只想让他们打开和关闭基于模式生成算法。

我用pi3d编写了一些基本代码,用于1000个球的模型,保存在一个数组中。它在阵列中循环,并通过将球体的颜色改变为蓝色或黑色来打开或关闭每个引线。

守则的核心部分如下:

代码语言:javascript
复制
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编程知之甚少,所以任何建议或教程都会对我有潜在的帮助。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-05-13 22:53:52

问题是,有python代码为每个pi3d.Shape一次做一个矩阵乘法。虽然这是使用numpy完成的,而且速度尽可能快,但仍然很慢。

你可以把你所有的球体做成一个pi3d.MergeShape,然后每帧只需要一个抽签(),而且非常快.但

  1. 您的Sphere对象使用12边的默认值x12片,给出288个面和864个顶点,因此您的MergeShape将有864,000个顶点,这可能开始减缓GPU的速度。
  2. 捆绑着色器只对整个形状使用一个材质RGB值,您希望为每个球体指定不同的颜色,这将需要一个被黑的着色器(如果您习惯了黑客着色器的话,很容易完成),其中您在缓冲区数组的纹理坐标字段中指定了RGB值。

您的代码没有显示您正在使用的着色器,默认的是mat_light,它将为每个球提供一个平滑的3D效果,但是如果您可以管理点(参见演示SpriteBalls),那么您可以让成千上万的球体快速运行…但是你仍然需要修改着色器来改变每个顶点的漫射颜色。

或者你可以做一个纹理半蓝色,半黑色,并调整各个球的纹理坐标每帧。假设您已经将所有球合并成一个形状,这将是非常快速的(虽然将涉及一个粗壮的公式,以再现您的x,y,z嵌套循环的效果)

在接下来的几天里,我将尝试设计一个演示,演示如何执行这些选项并将其添加到demos中。

编辑我刚刚想起了Starfield.py演示,它使用可变颜色的‘广告牌’点。这可以渲染数千点每帧,但它有各种各样的复杂,模糊了相对简单的结构,正如我前面提到的,我将制作一个更简单的版本演示您的10x10x10阵列与颜色变化使用欧几里得距离中心。

这里是一个使用pi3d_demos/shaders/star_point的广告牌或雪碧版本

代码语言:javascript
复制
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坐标

代码语言:javascript
复制
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版本。希望这对某个阶段的人有帮助。

票数 1
EN

Stack Overflow用户

发布于 2017-05-01 17:31:24

在你做任何事情之前,先分析一下你的代码,看看它在哪里运行缓慢。值得注意的是,pi3D不一定会像我的3D引擎那样快速运行。

球体需要很多多边形才能画出光滑的边。即使保守估计每个球体只有32个多边形,你的多边形总数最终也是:

代码语言:javascript
复制
10 * 10 * 10 * 32 = 32000

一个简单的优化方法是用多维数据集替换球体:

代码语言:javascript
复制
10 * 10 * 10 * 6 = 6000

如果你想要球体的外观,你可以进一步减少多边形的数量,通过渲染一个面对摄像机的多边形平面(也就是广告牌),上面有一个球体的纹理。

代码语言:javascript
复制
10 * 10 * 10 * 1 = 1000

尝试乘而不是除法10 / 210 * 0.5是一样的,并且不要做两次相同的工作:

代码语言:javascript
复制
x_dim = x - dim * 0.5
y_dim = y - dim * 0.5
z_dim = z - dim * 0.5

if((x_dim * x_dim) + (y_dim * y_dim) + (z_dim * z_dim) <= i * dim):

最后,只在整个场景中调用draw()一次,而不是在每个领域调用一次。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43723263

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档