"我不是在看见形状,我是在穿越一片没有定义的空间。"
------ 一个刚刚学会 SDF 的光线
🧭 前言:图形学,也讲浪漫主义
在经典图形学的渲染舞台上,光线追踪是老牌演员,像个理工科贵族一样追踪光线与三角形的命运纠缠。但今天,我们要换个思路。
我们要走进一片未知的空间,不用三角形,不用网格,而是:
- 在密度场中穿行
- 用距离场感知世界
- 用 marching cubes 唤醒形状
这是一段属于**Ray Marching(光线步进)和Marching Cubes(行进立方体)**的故事。
🛰️ 一、Ray Marching 是什么?一次可控的冒险
Ray Marching(光线步进),是一种用来在隐式表面中检测交点的方式。
不像光线追踪一头撞向三角形,Ray Marching 更像一个小心翼翼的探险家:
"我不会直接撞上去,我会一步一步接近你,直到靠得不能再近。"
它最常用于:
- 🪐 渲染 Signed Distance Fields(SDF)
- 🌫️ 体积效果(如烟雾、云)
- 🎨 Procedural Art(程序化艺术)
🚶♂️ 二、步进的旅程:SDF + 逐步靠近
光线步进的关键思维方式是:
- 你从相机发出一条光线。
- 沿着光线方向前进。
- 每次前进的距离是当前位置距离最近物体表面的"安全距离"。
- 如果这个距离非常小,说明你快撞上了,停下来。
- 否则,继续走。
"每一步都走得恰到好处,既不多也不少。"
💡 用代码描述一次步进旅程
csharp
function rayMarch(origin, direction, maxSteps = 100, maxDist = 100, epsilon = 0.001) {
let totalDist = 0
for (let i = 0; i < maxSteps; i++) {
const currentPoint = origin.clone().add(direction.clone().multiplyScalar(totalDist))
const dist = sceneSDF(currentPoint)
if (dist < epsilon) {
return currentPoint // 命中
}
if (totalDist > maxDist) break
totalDist += dist
}
return null // 没找到交点
}
🧠 三、SDF:光线的第六感
SDF(Signed Distance Function)就是给你一个点,它告诉你"离最近的表面有多远",而且还告诉你方向(正/负)。
✨ 例如,球的 SDF:
arduino
function sphereSDF(point, center, radius) {
return point.distanceTo(center) - radius
}
就像是:
"我离球面还有 2 米,那我就可以放心大胆往前走 2 米。"
多个形状还能合成:
javascript
function sceneSDF(p) {
const d1 = sphereSDF(p, new THREE.Vector3(0, 0, 0), 1)
const d2 = sphereSDF(p, new THREE.Vector3(1.5, 0, 0), 1)
return Math.min(d1, d2)
}
🧱 四、Marching Cubes:点亮体素空间的雕刻师
Ray Marching 在虚空中穿梭,而 Marching Cubes 则像是在虚空中"建模"。
它的工作方式如下:
- 把三维空间切成一个个小立方体(Voxel Grid)
- 每个角点都有一个标量值(比如密度、距离)
- 如果某些角点在表面之内,而另一些在外,就说明这个立方体被表面穿过
- 然后按照固定的规则(256 种情况),用三角形逼近这个表面
"我不关心表面是什么公式,我只关心它穿过了哪些格子。"
🗺️ 三维网格 + 阈值 = 隐式建模
ini
for (let x = 0; x < gridSize; x++) {
for (let y = 0; y < gridSize; y++) {
for (let z = 0; z < gridSize; z++) {
const cube = sampleCube(x, y, z)
const triangles = marchingCube(cube, isovalue)
geometry.push(...triangles)
}
}
}
🎨 五、三维创世神:Ray Marching + Marching Cubes 联手建模
- 用 SDF 描述形状(光线步进)
- 用网格转换成可见表面(marching cubes)
这是 Shader 艺术家和体素科学家都爱的组合。
你可以:
- 🌋 渲染三维爆炸的体积密度场
- 🧠 建模医学 CT 扫描数据
- 🪄 创建 procedural 生成的地形、怪物、形状
🧪 示例:创建一个动态"波动球"场景
csharp
function sceneSDF(p) {
const base = sphereSDF(p, new THREE.Vector3(0, 0, 0), 1)
const noise = Math.sin(p.x * 10 + time) * 0.1
return base + noise
}
让你的世界跳动起来!
🛠️ 六、用 Three.js 玩起来(理论→实践)
虽然 Ray Marching 和 Marching Cubes 都在 Shader 世界中更常见,但你可以用 JavaScript 实现一个简化版本。
ini
const resolution = 30
const mesh = generateMarchingCubesMesh(resolution, sceneSDF)
scene.add(mesh)
你甚至可以把
sceneSDF
替换成任何你想象中的公式,或者直接从 Perlin Noise 构建地下城!
📚 七、知识小结:当几何不是显式时
技术 | 本质 | 用途 | 优势 |
---|---|---|---|
Ray Marching | SDF + 步进 | 呈现隐式形状、体积效果 | 可视化无限细节 |
Marching Cubes | 网格构造器 | 从数据生成形状 | 可生成可渲染模型 |
SDF | 点 → 距离函数 | 建模、判断、体积光线 | 表达简单,组合灵活 |
🎆 八、结语:在不可见中雕刻世界
"当你无法准确描述一个形状,不妨退一步,只描述它的'感觉'。"
Ray Marching 和 Marching Cubes 让我们不再依赖笨重的几何建模,而是像一个魔法师,从密度、距离、渐变中召唤形状、雕刻体积。
在未来图形渲染、游戏、XR 和科学可视化中,它们会是你不可或缺的光影法术。
🧪 想继续试试:
- 实时生成的 SDF Shader 场景?
- Three.js + WebGL2 的体积渲染管线?
- 用 Marching Cubes 创建粘液怪兽?