// --- 输出缓冲区声明 ---
// 为位置、缩放、旋转和颜色分别声明四个输出缓冲区
layout(location = 0) out vec4 oPos;
layout(location = 1) out vec4 oScale;
layout(location = 2) out vec4 oRot;
layout(location = 3) out vec4 oColor;
// --- Uniform 参数 ---
// 由TouchDesigner传入的参数
uniform float uSpread; // 螺旋展开度
uniform float uAngle; // 角度,单位为度
uniform float uEdge; // 边缘控制参数
uniform float uSoftness; // 边缘软化参数,控制渐变曲线
uniform int uFrame; // 当前帧数,用于动画帧计算
uniform samplerBuffer sColorCurve; // 颜色查找缓冲区
#define PI 3.1415926 // 圆周率常量
#define PHI 137.5 // 黄金角度,37度的近似,用于叶序角度计算
// 获取输出缓冲区的深度(这里用作总点数,即叶序点数)
int N = int(uTDOutputInfo.res.z);
// --- 计算叶序螺旋上第n个点的空间位置 ---
// 输入:编号n,角度angle(度),展开度spread
// 输出:vec4(x, y, z, r),点的三维位置及半径r
vec4 phyllotaxis(int n, float angle, float spread){
vec2 center = vec2(0);
float r = spread * sqrt(n); // 半径随sqrt增加
float phi = angle * (PI / 180); // 角度转弧度
float theta = phi * n; // 当前点的旋转角度
vec2 xy = vec2(r * cos(theta) + center.x, // 极坐标转笛卡尔x
r * sin(theta) + center.y); // 极坐标转笛卡尔y
float z = n * 0.01; // z轴位移,制造深度感
return vec4(xy,z,r);
}
// --- 线性映射函数 ---
// 将x从区间[a,b]映射到[c,d]
float map(float x, float a, float b, float c, float d){
return c + (x - a) / (b - a) * (d - c);
}
// --- 绕z轴旋转矩阵 ---
// 输入角度angle(弧度),生成3x3旋转矩阵
mat3 rotation3dZ(float angle) {
float s = sin(angle);
float c = cos(angle);
return mat3(
c, s, 0.0,
-s, c, 0.0,
0.0, 0.0, 1.0
);
}
#define PALETTE 0
void main()
{
// 当前片元的x坐标对应叶序点编号n
int n = int(gl_FragCoord.x);
// 使用编号n在颜色查找曲线纹理缓冲中取值,用于控制颜色插值
vec2 lookup = vec2(
texelFetch(sColorCurve, n).r,
0.5
);
// 从调色板纹理中采样具体颜色
vec4 color = texture(sTD2DInputs[PALETTE], lookup);
// 根据编号n计算点的大小,随着点编号增加而变大
float size = 1 + float(n) / float(N);
// 计算展开度spread,点越靠近中心展开越小
float spread = map(n, 0, N, 0.5, uSpread);
// 计算环形边缘渐隐,结合软化参数控制边缘渐变曲线
float falloff = smoothstep(uEdge - uSoftness, uEdge + uSoftness, 1 - float(n)/float(N));
color.a = falloff * falloff; // alpha渐隐加平方,过度更柔和
// 点大小根据渐隐程度调整,加三次方衰减更柔和
size *= falloff * falloff * falloff;
// 计算点的3D位置
vec4 pos = phyllotaxis(n, uAngle, spread);
// 根据帧数计算旋转角度,实现旋转动画
pos.rgb = pos.rgb * rotation3dZ(PHI * PI / 180 * uFrame);
// 旋转信息,直接用位置向量扩展成4分量
vec4 rot = vec4(pos.rgb, 0);
// 输出各缓冲数据
oPos = TDOutputSwizzle(pos);
oScale = TDOutputSwizzle(vec4(size));
oRot = TDOutputSwizzle(rot);
oColor = TDOutputSwizzle(color);
}
简介
本教程旨在指导初学者使用 TouchDesigner 中的 GLSL (OpenGL Shading Language) 技术,以程序化方式创建复杂的自然形态。我们将重点复现一种被称为 Phyllotaxis (叶序) 的自然模式——即植物花瓣、种子或叶片的螺旋排列规律,这种模式在向日葵、仙人掌和松果等植物中普遍存在。
最终,你将学会不仅能生成静态的、符合数学美感的叶序图案,还能为其添加两种动态效果:一种是模拟植物生长的 西洋镜动画 (Zoetropic Animation) 效果,另一种是通过改变核心参数实现的图案动态演变。本文档将详细拆解其背后的数学原理与在 TouchDesigner 中的实现步骤。
核心概念
在开始构建之前,理解以下几个核心概念至关重要。
-
Phyllotaxis (叶序)
叶序是植物学中描述植物器官(如叶片、花瓣、种子)在茎上排列方式的术语。这些排列通常遵循特定的数学规律,以最高效的方式利用空间和光照。本教程中,我们使用 Vogel 公式 来模拟这一现象,该公式是描述向日葵头部种子排列的经典数学模型。 -
黄金角 (Golden Angle)
黄金角是与黄金分割率相关的角度,约为 137.5 度。在叶序模式中,当新的器官(例如一个花瓣)以这个角度在前一个的基础上旋转生长时,会形成最紧凑、无重叠且充满美感的螺旋图案。这是自然界中最常见的发散角,也是实现理想叶序图案的关键参数。 -
Vogel 公式
这是本教程算法的核心。它定义了第 n 个点(或植物器官)在极坐标系中的位置:-
旋转角度 (θ): θ = n * φ
-
半径 (r): r = c * sqrt(n)
其中,n 是点的索引(从 0 开始),φ 是发散角(即黄金角),c 是一个控制点之间径向距离的常数(即疏密程度)。
-
-
西洋镜动画 (Zoetropic Animation)
这是一种通过在每一帧中将整个坐标系旋转黄金角,从而产生的视觉错觉。虽然每个点自身的位置没有向外移动,但整体的旋转会让人感觉新的点在中心不断生成并向外推移,形成一种持续生长的动态效果。这是一种非常高效且富有欺骗性的动画技巧。 -
实例化 (Instancing)
这是一种高效的渲染技术,用于绘制成千上万个相同几何体(例如球体)的副本。我们只需提供一个基础几何体,然后通过纹理 (Texture) 为每个副本指定独一无二的属性,如位置、颜色、缩放和旋转。在本例中,我们将使用 GLSL 生成这些属性纹理。 -
基于物理的渲染 (PBR, Physically Based Rendering)
这是一种高级的渲染方法,它通过模拟真实世界中光线与材质的交互方式来创建更逼真的视觉效果。我们将使用 PBR 材质配合环境光,为最终的视觉效果增添质感和真实感。
核心技术详解:GLSL 中实现叶序算法
本项目的核心在于将 Vogel 的数学公式转化为 GLSL 着色器代码,并利用 GPU 的并行计算能力高效地生成成千上万个点的位置和其他属性。
-
基本原理
我们创建一个 GLSL Multi TOP,其分辨率的 X 轴设为我们想要生成的“器官”总数,Y 轴为 1。这样,输出的纹理就是一个像素行,其中每个像素都代表一个独立的“器官”或实例。在 GLSL 中,我们可以通过像素的 X 坐标 (gl_FragCoord.x) 来获取当前正在计算的像素索引,这个索引就对应 Vogel 公式中的 n。 -
GLSL 实现步骤
-
定义 Uniform 变量: 我们会创建 uAngle (角度)、uSpread (疏密度) 和 uFrame (用于动画的帧计数) 等 Uniform 变量。这使我们能够从 TouchDesigner 的 UI 界面实时控制着色器内的参数。
-
计算极坐标: 在着色器中,我们根据 Vogel 公式计算每个点(像素)的极坐标:
-
半径 r 由 uSpread * sqrt(float(n)) 计算得出。
-
角度 theta 由 float(n) * radians(uAngle) 计算得出。
-
-
坐标转换: 将计算出的极坐标 (r, theta) 转换为笛卡尔坐标 (x, y),以便在 3D 空间中定位:
-
x = r * cos(theta)
-
y = r * sin(theta)
-
-
多重颜色缓冲 (Multiple Color Buffers): 为了代码的组织性和效率,我们使用 GLSL 的一个高级功能——多重颜色缓冲。这意味着我们的着色器可以一次性输出多个纹理。在本例中,我们分别输出:
-
位置 (Position): 包含每个实例的 (x, y, z) 坐标。
-
缩放 (Scale): 控制每个实例的大小。
-
旋转 (Rotation): 控制每个实例的朝向。
-
颜色 (Color): 定义每个实例的颜色。
这使得数据流非常清晰,每个属性都有一个专门的纹理来承载。
-
-
实现西洋镜动画: 我们在 GLSL 中实现一个 3D 旋转矩阵。将最终计算出的位置乘以这个旋转矩阵,并通过 uFrame 变量让旋转角度在每一帧递增黄金角的度数。这就产生了持续旋转、看似生长的西洋镜动画效果。
-
动态属性与淡出: 为了使形态更有机,我们可以让实例的缩放和疏密度与其索引 n 相关联,例如中心处的实例更小更密集,边缘处的实例更大更稀疏。此外,我们还会实现一个平滑的淡出效果 (falloff),让边缘的实例在大小和透明度上逐渐消失,避免生硬的截断。
-
结论
通过本教程,我们深入探讨了如何将一个经典的自然数学算法——Vogel 公式,与 TouchDesigner 强大的 GPU 编程能力 (GLSL) 相结合。你不仅学会了如何生成静态的叶序图案,还掌握了通过多重颜色缓冲、实例化渲染、西洋镜动画以及 PBR 渲染等技术,来创造出复杂、动态且富有艺术美感的生成式视觉作品。
这项技术具有广阔的扩展应用潜力。你可以尝试将该算法扩展到三维空间,使用不同的几何体进行实例化,或者将音频数据与生长参数相结合,创造出与音乐同步的视觉效果。希望这份文档能为你打开通往程序化艺术创作的大门。
暂无评论内容