Pygame:使用深度缓冲逐像素绘制扫描线栅格化三角形的最快方法吗?

问题描述

我正在尝试在pygame中创建一个简单的3D渲染引擎(我知道,不是最快的程序,但我至少想尝试一下),我使用了this将3D坐标投影到2D屏幕。

接下来,我正在使用ChiliTomatoNoodle's很棒的教程(尽管稍作改动),使用水平(扫描线)光栅化,然后是我自己的zBuffer实现,将3个顶点变成一个实心的实心三角形。

最慢和最有问题的部分是底部的for x in range(lineLength)循环,在该循环中,我尝试将值放入Numpy数组:

# Draw a triangle with a flat top with horizontal rasterization
def drawFlatBottom(p1,p2,p3,col):
    global pxarray
    global zbuffer

    # Calculate slope of line (Is RUN/RISE,not other way around to prevent a slope of infinity)
    try:
        m1 = (p2[0] - p1[0]) / (p2[1] - p1[1]) # I may be doing this wrong,but this is a hotfix for Now to avoid divide by 0 errors
    except ZeroDivisionError:
        m1 = 0
    try:
        m2 = (p3[0] - p1[0]) / (p3[1] - p1[1])
    except ZeroDivisionError:
        m2 = 0

    # Calculate the z slope. Again,is RUN/RISE
    try:
        mz1 = (p2[2] - p1[2]) / (p2[1] - p1[1])
    except ZeroDivisionError:
        mz1 = 0
    try:
        mz2 = (p3[2] - p1[2]) / (p3[1] - p1[1])
    except ZeroDivisionError:
        mz2 = 0

    yStart = int(p1[1])
    yEnd = int(p3[1])

    if yEnd > height:
        yEnd = height
    if yStart < 0:
        yStart = 0

    # Repeat for each row
    y = yStart
    while y < yEnd:
        # Get the x positions where it intercepts with the edge
        xStart = int(m1 * (y - p1[1]) + p1[0])
        xEnd = int(m2 * (y - p1[1]) + p1[0])

        if xEnd > width:
            xEnd = width
        if xStart < 0:
            xStart = 0

        lineLength = xEnd - xStart

        # Do the same with the z positions
        zStart = mz1 * (y - p1[1]) + p1[2]
        zEnd = mz2 * (y - p1[1]) + p1[2]

        # Find the new slope,RUN/RISE
        try:
            mz = (zEnd - zStart) / lineLength
        except ZeroDivisionError:
            mz = 0
        z = zStart

        # Fill each pixel
        for x in range(lineLength):
            # Check if the pixel in the z buffer is further away than this pixel
            if zbuffer[x + xStart][y] > z:
                # Write the new value to the depth buffer
                zbuffer[x + xStart][y] = z

                # Draw the pixel
                pxarray[x + xStart][y] = col

            z += mz

        y += 1

函数drawFilledTop()中,p1p2p3在屏幕空间中都是格式为[x,y,z]的列表,并且是三角形的3个顶点。 col表示(255,255,255)形式的RGB值。

pxarray一个numpy数组,该数组在调用函数drawFlatTop()之前在帧的开头声明:

pxarray = pygame.PixelArray(win)

然后将其关闭

pygame.PixelArray.close(pxarray)

zbuffer与pxarray完全相同,只是一个600 x 480的数组,值999以确保绘制第一个三角形:

zbuffer = np.full((width,height),999,dtype=float)

问题

所有这些工作都很好,但速度非常慢。我希望在迭代每个像素时会有点慢,但是我不知道三联A游戏如何在大量的照明和着色器等情况下获得如此高的帧率,但是这个带有4个三角形的简单场景几乎无法达到30fps

我用pprofile生成一个单帧需要花费多长时间的报告,它显示了我绘制所有4个三角形的函数花费了总运行时间的 44%,即0.316672总运行时间0.342588秒。

这是三角形的实际图形(这是所有平底三角形,drawFlatTop看起来相似):

364|         0|            0|            0|  0.00%|        # Fill each pixel
365|     11890|    0.0352542|  2.96503e-06| 10.29%|        for x in range(lineLength):
366|         0|            0|            0|  0.00%|            # Check if the pixel in the z buffer is further away than this pixel
367|     11673|    0.0446463|  3.82475e-06| 13.03%|            if zbuffer[x + xStart][y] > z:
368|         0|            0|            0|  0.00%|                # Write the new value to the depth buffer
369|     11673|    0.0573983|  4.91719e-06| 16.75%|                zbuffer[x + xStart][y] = z
370|         0|            0|            0|  0.00%|
371|         0|            0|            0|  0.00%|                # Draw the pixel
372|     11673|    0.0462677|  3.96366e-06| 13.51%|                pxarray[x + xStart][y] = col
373|         0|            0|            0|  0.00%|
374|     11673|    0.0377977|  3.23804e-06| 11.03%|            z += mz

问题

向数组中的屏幕/位置值绘制一堆像素的最佳方法是什么?我不希望这么大量的运行时间专门用于将像素绘制到屏幕上!

非常感谢您可以提供的任何帮助:)

编辑: 这些测量是用一个三角形占据屏幕的一小部分来进行的。现在,如果屏幕甚至被像素覆盖一半,它的帧率就会降低到可笑的水平,而在大部分被屏幕覆盖的情况下,它甚至达不到1 FPS。

由于我打算在Arduino上做一个基本的3D游戏,并且时钟速度比我的计算机慢得多,所以我愿意尝试做任何事情来优化此图形。

有没有更快的遍历像素的方法?我什至可以在汇编中写一些东西来最大化效率吗? python是否允许/是否可行?再次感谢您的帮助

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)