三维分形,从mandelbulb开始做起,比2d多了一维,结果发现制作难度多了不少。
三维制作从python开始,这样制作会快一点。找资料https://github.com/jtauber/mandelbulb,这个比较浅显易懂,但制作了2d图片效果,没有3d的制作。
通过改造用python画出了3D效果,但的确很难看,作为成长记录一下。
目前还在考虑如何做渲染,以期望达到他人连续的效果。
着色采用下面方式,每一层一个颜色,底部暗,顶部亮
c=hsv2rgb(height*4+100,(55+height)*2/256.0,(55+height)*2/256.0)
图片比较丑陋,先分享一下python代码吧。
import zlib
import struct
import array
from math import sqrt, atan2, cos, sin
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import math
import numpy as np
#R, G, B是 [0, 255]. H 是[0, 360]. S, V 是 [0, 1].
def hsv2rgb(h, s, v):
h = float(h%360)
s = float(s)
v = float(v)
h60 = h / 60.0
h60f = math.floor(h60)
hi = int(h60f) % 6
f = h60 - h60f
p = v * (1 - s)
q = v * (1 - f * s)
t = v * (1 - (1 - f) * s)
r, g, b = 0, 0, 0
if hi == 0: r, g, b = v, t, p
elif hi == 1: r, g, b = q, v, p
elif hi == 2: r, g, b = p, v, t
elif hi == 3: r, g, b = p, q, v
elif hi == 4: r, g, b = t, p, v
elif hi == 5: r, g, b = v, p, q
r, g, b = int(r * 255), int(g * 255), int(b * 255)
return r/255.0, g/255.0, b/255.0
def rgb2hsv(r, g, b):
r, g, b = r/255.0, g/255.0, b/255.0
mx = max(r, g, b)
mn = min(r, g, b)
df = mx-mn
if mx == mn:
h = 0
elif mx == r:
h = (60 * ((g-b)/df) + 360) % 360
elif mx == g:
h = (60 * ((b-r)/df) + 120) % 360
elif mx == b:
h = (60 * ((r-g)/df) + 240) % 360
if mx == 0:
s = 0
else:
s = df/mx
v = mx
return h, s, v
H = 400
W = 400
# just do as a dictionary as it starts off sparse
cc1=[]
xx1=[]
yy1=[]
zz1=[]
heights={}
for height in range(0, 55, 1):
print ("height %d" % height)
filename = "mandel8_%02d.values" % height
f = open(filename)
header = f.readline()
assert header == "H %s W %s\n" % (H, W), header
im=[[0 for col in range(W)]for row in range(H)]
for xx in range(W):
for yy in range(H):
value = int(f.readline().strip())
im[yy][xx]=value
if value == 0: #and (xx, yy) not in heights:
heights[xx, yy] = 1
a=[[0 for col in range(W)]for row in range(H)]
for i in range(0,W):
for j in range(1,H):
if(im[j][i]!=0 and im[j-1][i]==0):
a[j][i]=1
if(im[j][i]==0 and im[j-1][i]!=0):
a[j-1][i]=1
for i in range(0,H):
for j in range(1,W):
if(im[i][j]!=0 and im[i][j-1]==0):
a[i][j]=1
if(im[i][j]==0 and im[i][j-1]!=0):
a[i][j-1]=1
for xx in range(W):
for yy in range(H):
if a[yy][xx] != 0: #and (xx, yy) not in heights:
heights[xx, yy] = height
xx1.append(xx)
yy1.append(yy)
zz1.append(height)
#c=(height*4/256.0,height*4/256.0,height*4/256.0)
c=hsv2rgb(height*4+100,(55+height)*2/256.0,(55+height)*2/256.0)
cc1.extend(c)
def output_chunk(out, chunk_type, data):
out.write(struct.pack("!I", len(data)))
btt=bytes(chunk_type,encoding="utf8")
out.write(btt)
out.write(data)
checksum = zlib.crc32(data, zlib.crc32(btt))
out.write(struct.pack("!I", checksum))
def get_data(width, height, rgb_func):
compressor = zlib.compressobj()
data = array.array("B")
for y in range(height):
data.append(0)
for x in range(width):
data.extend([min(255, max(0, v)) for v in rgb_func(x, y)])
compressed = compressor.compress(data.tostring())
flushed = compressor.flush()
return compressed + flushed
def write_png(filename, width, height, rgb_func):
out = open(filename, "wb")
out.write(struct.pack("8B", 137, 80, 78, 71, 13, 10, 26, 10))
output_chunk(out, "IHDR", struct.pack("!2I5B", width, height, 8, 2, 0, 0, 0))
output_chunk(out, "IDAT", get_data(width, height, rgb_func))
output_chunk(out, "IEND", bytes("",encoding="utf8"))
out.close()
def func(x, y):
c = 4 * heights.get((x, y), 63)
return c, c, c
write_png("heightfield.png", W, H, func)
h = 400
w = 400
fig = plt.figure(figsize=(16,16))
#plt.axis('off') #不显示坐标尺寸
ax1 = fig.add_subplot(111,projection='3d') #第三个子图
ax1.set_title('mandelbulb' ) #在3维坐标显示变形图
ax1.set_xlabel('x')
ax1.set_ylabel('y')
ax1.set_zlabel('z')
myx=np.array(xx1).flatten()
myy=np.array(yy1).flatten()
myz=np.array(zz1).flatten()
myc=np.array(cc1).reshape(-1,3)
erea=1;
ax1.scatter(myx,myy,myz,s=erea, c=myc, alpha=1.0 )
#ax1.view_init(0, 40.0)
plt.show()