参考了一些有趣的shadertoy,实现了一个无限的分形山脉。
就是跑起来不是很快,metal后端不到10帧。有条件的建议用CUDA或者OpenGL跑一跑,可以在评论区分享一下效果。
这个程序需要使用一张纹理图作为生成山形的数据。
论坛可能会压缩图片,建议去 github仓库 下载原始文件。
纹理图片需要放在与程序文件相同目录下才能让程序运行。
分形的思想很简单,生成一层山体,然后在其上堆叠一层更小的山体,如此几次,实现更多细节。
当然还存在一些问题,山体上有很多毛刺,也能看到阶梯状的纹理。这是因为程序里只实现了双线性纹理过滤,导致纹理采样不够平滑。
如果使用三线性纹理过滤,这个问题会得到很好的解决。但是三线性纹理过滤写起来太繁琐了,还需要实现MipMap,我就不写了。
代码:
import taichi as ti
import numpy as np
import time
from PIL import Image
ti.init(arch=ti.gpu)
w, h = 800, 450
screen = ti.Vector(3, dt=ti.f32, shape=(w, h))
count_time = ti.var(ti.f32, shape=())
# 加载纹理
image = np.array(Image.open('texture.jpg'), dtype=np.float32)
image /= 256.0
tw, th = image.shape[0:2]
texture = ti.Vector(3, dt=ti.f32, shape=(tw, th))
texture.from_numpy(image)
count_time[None] = 0.0
# 双线性纹理过滤
@ti.func
def texture_bilinear_filter(u, v):
u %= 1.0
v %= 1.0
u, v = u * tw, v * th
left, right = int(u), int(u) + 1
bottom, top = int(v), int(v) + 1
t = u - left
s = v - bottom
col = ti.Vector([0.0, 0.0, 0.0])
col = (1-t)*((1-s)*texture[left,bottom] + s*texture[right,bottom]) + \
t*((1-s)*texture[left,top] + s*texture[right,top])
return col
@ti.kernel
def draw():
count_time += 0.1
for i, j in screen:
col = ti.Vector([0.0, 0.0, 0.0])
p = ti.Vector([i / float(w), j / float(h), 1.0]) - 0.5
d = ti.Vector([i / float(w), j / float(h), 1.0]) - 0.5
p.z += count_time * 80.0
d.y -= 0.4
k = 1.5
while k > 0.0:
t = ti.Vector([0.0, 0.0, 0.0])
e = ti.Vector([0.0, 0.0, 0.0])
s = 0.5
for m in range(0, 6):
e = texture_bilinear_filter(0.3 + p.x * s / 3000.0,
0.3 + p.z * s / 3000.0)
s += s
t += e / s
col = 1.0 + d.x - t * k
col.z -= 0.1
if t.x > (p.y * 0.007 + 1.3):
break
p += d
k -= 0.0015
screen[i, j] = col
# video_manger = ti.VideoManager(output_dir='./results', framerate=10, automatic_build=False)
gui = ti.GUI("screen", (w, h))
for i in range(100000):
draw()
gui.set_image(screen.to_numpy())
gui.show()
# video_manger.write_frame(screen.to_numpy())
# video_manger.make_video(gif=True, mp4=True)