如何栅格化球体

时间:2017-01-14 23:08:11

标签: math

所以,我正在尝试创建一个在其外部有“块”的球体,有点像在Minecraft中构建的那样。 (我不知道圆圈外面的术语是什么)。问题是,我无法弄清楚如何获得像中点圆算法这样的方程来为球体工作。最好是在lua或java中,这样我可以更轻松地阅读任何答案。而且我不想知道如何用trig计算球体上的一个点,我已经知道如何做到这一点。

4 个答案:

答案 0 :(得分:2)

我认为最简单的方法就是像中点圆算法,扩展到3D。

首先,让我们弄清楚我们想要填充哪些块。假设原点位于块(0,0,0)和半径R

的中间
  1. 我们只想填充球体内的盒子。这些只是(x,y,z)的框x²+y²+z² <= R²;和
  2. 我们只想填充脸部显示的盒子。如果一个方框显示了一个面,那么它的至少一个邻居在该范围内,因此:(|x|+1)²+y²+z² > R²x²+(|y|+1)²+z² > R²x²+y²+(|z|+1)² > R²
  3. 第二部分让它变得棘手,但请记住(|a|+1)² = |a|² + 2|a| + 1。例如,如果z是球体内框的最大坐标,并且该框有一个面部显示,那么z面特别是显示,因为x²+y²+(|z|+1)² = x²+y²+z²+2|z|+1,并且至少与xy的类似值一样大。

    因此,计算1)在球体内的方框,2)将z作为其最大坐标,以及3)具有最大可能的z值,即添加,非常容易1到z导致球体外的框。此外,4)所有x,y,z都有正值。

    然后可以通过24种不同的方式反映这些框的坐标,以生成球体表面上的所有框。这些都是坐标符号的8种组合乘以所有3种选择,其轴具有最大坐标。

    以下是如何生成积极x,y,zz最大的积分:

    maxR2 = floor(R*R);
    zx = floor(R);
    for (x=0; ;++x)
    {
        //max z for this x value.
        while(x*x+zx*zx > maxR2 && zx>=x)
            --zx;
        if (zx<x)
            break; //with this x, z can't be largest
    
        z=zx;
        for(y=0; ;++y)
        {
            while(x*x+y*y+z*z > maxR2 && z>=x && z>=y)
                --z;
            if (z<x||z<y)
                break; //with this x and y, z can't be largest
            FILLBOX(x,y,z); //... and up to 23 reflections of it
        }
    }
    

    注意:如果对您很重要,请在计算反射时小心,以便不要绘制,例如(0,y,z) (-0,y,z),因为它是同一个盒子两次。也不要交换具有相同值的轴,因为再次绘制相同的框两次(例如,如果你有(1,5,5),则不要交换y和{{1}并再次画画。

    注意z不必是整数。如果你加0.5,它看起来会更好一点。

    这是一个将上述所有内容考虑在内的示例(您需要支持webgl的浏览器)https://jsfiddle.net/mtimmerm/ay2adpwb/

答案 1 :(得分:1)

您可以在嵌套循环中使用Midpoint Circle AlgorithmBresenham's Circle Algorithm。外环确定距离原点不同z距离处的圆的整数值半径,而内环计算包含球体的圆的xy元素。 s位于z的垂直于Z轴的横截面。

答案 2 :(得分:0)

这是用我自己的MiniBasic版本编写的。

10  REM Shaded textured sphere 
20 INPUT width 
30 INPUT height 
40 INPUT bwidth 
50 INPUT bheight 
60 REM Sphere radius 
70 LET r = 100 
80 REM light direction 
90 LET lx = 0 
100 LET ly = 0.2 
110 LET lz = -1 
120 LET ambient = 0.1 
130 REM camera z position 
140 LET cz = -256 
150 REM Sphere colour 
160 LET red = 255 
170 LET green =0 
180 LET blue = 100 
190 REM Normalize light  
200 LET len = SQRT(lx*lx+ly*ly+lz*lz) 
210 LET lx = -lx/len 
220 LET ly = -ly/len 
230 LET lz = -lz/len 
240 FOR i = 0 TO height -1 
250 FOR ii = 0 TO width -1 
260 LET x = ii - width /2 
270 LET y = i - height/2 
280 LET dpx = x 
290 LET dpy = y 
300 LET dpz = -cz 
310 LET a = dpx*dpx + dpy*dpy + dpz*dpz 
320 LET b = 2 * ( dpz  * cz)  
330 LET c = cz * cz 
340 LET c = c - r * r 
350 LET bb4ac = b * b - 4 * a * c 
360 IF ABS(a) < 0.001 OR bb4ac < 0 THEN 560 
370 LET mu1 = (-b + SQRT(bb4ac)) / (2 * a) 
380 LET mu2 = (-b - SQRT(bb4ac)) / (2 * a) 
390 LET spx = mu1 * x 400 LET spy = mu1 * y 
410 LET spz = mu1 * 256 - 256 
420 LET nx = spx / r 
430 LET ny = spy / r 
440 LET nz = spz / r 
450 LET costh = nx*lx + ny *ly + nz*lz 
460 IF costh > 0 THEN 480 
470 LET costh = 0 
480 LET lum = ambient + (1 - ambient) * costh 
490 LET v = ACOS(nz)/PI 495 LET nx = nx  * 0.999 
510 LET u = ACOS(nx * SIN(PI * v)) / PI   
535 LET v = -ACOS(ny)/PI + 1.0  
536 LET u = 1 - u 
540 GETPIXELB u * bwidth, v * bheight, red, green, blue 
550 SETPIXEL ii, i,  red * lum,  green *lum,  blue *lum 
560 NEXT ii 
570 NEXT i 

http://www.malcolmmclean.site11.com/www/UserPrograms.html#texsphere

答案 3 :(得分:0)

这是一个使用 Matt Timmermans 示例的 Python 示例。如果您有多个球体,则使用 numba jits fuctions 和 parallel prange 会非常快。为任何起始坐标添加了体素。网格只是将 1s 放置在球体素在 3d 网格中的位置。还在步骤内添加了一个可选的填充。注意:这仅适用于非负坐标,因此使用 svoxels 坐标的所有体素的球体都需要在正空间中。

#/usr/bin/env python
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
from numba.experimental import jitclass
from numba import types, njit, prange


spec = [
    ('radius', types.float64),
    ('svoxel', types.int64[:]),
    ('grid', types.b1[:,:,:])
]

@jitclass(spec)
class RasterizeSphere(object):
    def __init__(self, svoxel, radius, grid):
        self.grid = grid
        self.svoxel = svoxel
        self.radius = radius
        R2 = np.floor(self.radius**2)
        zx = np.int64(np.floor(self.radius))
        x = 0
        while True:
            while x**2 + zx**2 > R2 and zx >= x:
                zx -= 1
            if zx < x:
                break
            z = zx
            y = 0
            while True:
                while x**2 + y**2 + z**2 > R2 and z >= x and z >= y:
                    z -= 1
                if z < x or z < y:
                    break
                self.fill_all(x, y, z)
                ###### Optional fills the inside of sphere as well. #######
                for nz in range(z):
                    self.fill_all(x, y, nz)
                y += 1
            x += 1

    def fill_signs(self, x, y, z):
        self.grid[x + self.svoxel[0], y + self.svoxel[1], z + self.svoxel[2]] = True
        while True:
            z = -z
            if z >= 0:
                y = -y
                if y >= 0:
                    x = -x
                    if x >= 0:
                        break
            self.grid[x + self.svoxel[0], y + self.svoxel[1], z + self.svoxel[2]] = True

    def fill_all(self, x, y, z):
        self.fill_signs(x, y, z)
        if z > y:
            self.fill_signs(x, z, y)
        if z > x and z > y:
            self.fill_signs(z, y, x)

@njit(parallel=True, cache=True)
def parallel_spheres(grid):
    # prange just to show the insane speedup for large number of spheres disable visualization below if using large amount of prange.
    for i in prange(2):
        radius = 4.5
        svoxel = np.array([5, 5, 5])
        max = np.int64(np.ceil(radius**2))
        rs = RasterizeSphere(svoxel, radius, grid)
        points = np.where(rs.grid)
        return np.array([*points])


def main():
    # Make max large enough to hold the spheres.
    max = 100
    points = parallel_spheres(np.zeros((max, max, max), dtype=bool))
    fig = plt.figure()
    ax = plt.axes(projection='3d')
    ax.scatter3D(*points)
    plt.show()


if __name__ == '__main__':
    main()