所以,我正在尝试创建一个在其外部有“块”的球体,有点像在Minecraft中构建的那样。 (我不知道圆圈外面的术语是什么)。问题是,我无法弄清楚如何获得像中点圆算法这样的方程来为球体工作。最好是在lua或java中,这样我可以更轻松地阅读任何答案。而且我不想知道如何用trig计算球体上的一个点,我已经知道如何做到这一点。
答案 0 :(得分:2)
我认为最简单的方法就是像中点圆算法,扩展到3D。
首先,让我们弄清楚我们想要填充哪些块。假设原点位于块(0,0,0)
和半径R
:
(x,y,z)
的框x²+y²+z² <= R²
;和(|x|+1)²+y²+z² > R²
或x²+(|y|+1)²+z² > R²
或x²+y²+(|z|+1)² > R²
第二部分让它变得棘手,但请记住(|a|+1)² = |a|² + 2|a| + 1
。例如,如果z
是球体内框的最大坐标,并且该框有一个面部显示,那么z
面特别是显示,因为x²+y²+(|z|+1)² = x²+y²+z²+2|z|+1
,并且至少与x
和y
的类似值一样大。
因此,计算1)在球体内的方框,2)将z
作为其最大坐标,以及3)具有最大可能的z值,即添加,非常容易1到z导致球体外的框。此外,4)所有x,y,z
都有正值。
然后可以通过24种不同的方式反映这些框的坐标,以生成球体表面上的所有框。这些都是坐标符号的8种组合乘以所有3种选择,其轴具有最大坐标。
以下是如何生成积极x,y,z
和z
最大的积分:
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 Algorithm或Bresenham's Circle Algorithm。外环确定距离原点不同z
距离处的圆的整数值半径,而内环计算包含球体的圆的x
和y
元素。 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()