代码就是 Shader - Shadertoy BETA 默认的那个。
主要当成转 gif 的教程,你也可以把以下代码当作改写 shader 的框架代码。
照着改写 Shader 代码比较简单,不过对熟悉 taichi 的语法还是很有意义的。
Shader 框架代码
empty.py
import taichi as ti
# ti.init(debug=True, arch=ti.cpu)
ti.init(arch=ti.gpu)
GUI_TITLE = "Empty"
w, h = wh = (640, 360) # GUI 宽高
pixels = ti.Vector(3, dt=ti.f32, shape=wh)
iResolution = ti.Vector([w, h])
@ti.func
def mainImage(iTime: ti.f32, i: ti.i32, j: ti.i32):
""" C 源代码
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// Time varying pixel color
vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));
// Output to screen
fragColor = vec4(col,1.0);
"""
fragCoord = ti.Vector([i, j])
# Normalized pixel coordinates (from 0 to 1)
uv = fragCoord / iResolution
# Time varying pixel color
uv_xyx = ti.Vector([uv[0], uv[1], uv[0]]) # 暂不支持直接 .xyx
col = 0.5 + 0.5 * ti.cos(iTime + uv_xyx + ti.Vector([0, 2, 4]))
# Output to screen
fragColor = col # 这里 RGB 就够了
return fragColor
@ti.kernel
def render(t: ti.f32):
"render 基本不用动,改 mainImage 就可以了"
for i, j in pixels:
pixels[i, j] = mainImage(t, i, j)
return
def main(output_img=False):
"output_img: 是否输出图片"
gui = ti.GUI(GUI_TITLE, res=wh)
for ts in range(1_000_000):
if gui.get_event(ti.GUI.ESCAPE):
exit() # 按 ESC 键退出
# render 接受的输入为现实的时间,这里用 ts 计数模拟
render(ts * 0.03)
gui.set_image(pixels.to_numpy())
if output_img:
# 输出到 frame 文件夹下;4 位顺序命名
gui.show(f'frame/{ts:04d}.png')
else:
gui.show()
if __name__ == '__main__':
main(output_img=True)
# main()
制作 GIF
gui.show(f'frame/{ts:04d}.png')
这一句会输出图片到 frame
文件夹,4位数字顺序编号。
然后你会得到一堆图片
这时你需要一个 ffmpeg
我习惯分两步转换:先转成 mp4,再导出成需要的格式,即 gif。
最后删掉原始的图片和转换的结果,只保留 mp4。因为体积小。
转换为 mp4
ffmpeg -framerate 60 -i ./frame/%04d.png -c:v libx264 -r 30 out.mp4
- 这里的
-framerate
指读取时的帧率,这里输出为-r 30
,所以相当于60/30=2
倍加速。
论坛的 gif 大小限制 < 4MB,一定的加速和缩小分辨率有助于减小 gif 大小。 -
-i ./frame/%04d.png
输入文件。
frame/%04d
和 python 里的统一就行 -
-c:v libx264
x264 编码 -
-r 30
输出 30 fps -
out.mp4
输出文件名
MP4 转 GIF
ffmpeg -ss 00:00 -t 5 -i out.mp4 -vf "fps=25,scale=320:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 out.gif
-
-ss 00:00 -t 5
从 00:00 开始,剪辑 5 秒 -
-i out.mp4
输入文件 -
-vf
-filter:v
的缩写,视频过滤器,后面的字符串是指定的参数fps=30
-
scale=320:-1
等宽高比缩放到宽 320;lanczos 缩放算法 -
split[s0][s1]
将视频分为两个流 s0, s1
细节参见 FFmpeg Filters Documentation -
[s0]palettegen[p]
视频流 s0 生成调色板 p -
[s1][p]paletteuse
视频流 s1 通过调色板 p 进行下采样
-
-loop 0
gif 无限循环;-1
不循环(只播放一遍) -
out.gif
输出文件
GIF 后处理
ffmpeg 输出的 gif 还能再压缩一下,这里就不折腾 ffmpeg 的参数了。
建议直接去找在线的 gif 压缩工具。
文件大小对比
MP4 (21 s): 390 KB
GIF (7s): 3.38 MB
GIF (在线压缩后): 2.4 MB
成品 GIF
时间点抓得好能得到无缝循环