用 raymarching 画一个精灵球

前言

大家好,这里是 CSS 兼 WebGL 魔法使——alphardex。本文就让我们来用kokomi.jsraymarching 组件来画一个精灵球吧~

泼给莫,给托打贼!

LKobMF.gif

绘画开始

首先 fork 下这个模板

https://codesandbox.io/s/kokomi-js-raymarching-starter-lk17vs

看到“从这开始吧~”的代码块,我们将从那里开始

材质

精灵球有 3 种颜色:黑色、白色和红色

首先定义好 3 种颜色的材质

1
2
3
4
5
6
7
8
9
10
// 材质
const BLACK_MAT = "1.0";
const WHITE_MAT = "2.0";
const RED_MAT = "3.0";

const mat = new marcher.SDFMaterial();
mat.addColorMaterial(BLACK_MAT, 0, 0, 0);
mat.addColorMaterial(WHITE_MAT, 255, 255, 255);
mat.addColorMaterial(RED_MAT, 255, 0, 0);
mar.setMaterial(mat);

球内

精灵球的内部是一个小黑球

1
2
3
4
5
const sphere = new marcher.SphereSDF({
sdfVarName: "d1",
materialId: BLACK_MAT,
});
layer.addPrimitive(sphere);

LK4vIP.png

按钮

中间有个白色的按钮,其实就是个圆柱体

1
2
3
4
5
6
7
8
const button = new marcher.CylinderSDF({
sdfVarName: "d2",
materialId: WHITE_MAT,
radius: 0.1,
height: 0.54,
});
button.rotate(90, "x");
layer.addPrimitive(button);

LK5GIx.png

上下球壳

raymarching 里只能创造整个球体,那如果只想要球体的一半呢?

创建一个临时用的方块,将其移动到球的半侧,再和球体求交集,就能获取一半的球体了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 球壳(上)
const shellUpper = new marcher.SphereSDF({
sdfVarName: "d3",
materialId: "3",
radius: 0.55,
});

const clipBoxUpper = new marcher.BoxSDF({
sdfVarName: "d4",
width: 0.55,
height: 0.55,
depth: 0.55,
});
clipBoxUpper.hide();
clipBoxUpper.translate(0, -0.6, 0);

layer.addPrimitive(clipBoxUpper);
layer.addPrimitive(shellUpper);

shellUpper.intersect(clipBoxUpper);

// 球壳(下)
const shellLower = new marcher.SphereSDF({
sdfVarName: "d6",
materialId: WHITE_MAT,
radius: 0.55,
});

const clipBoxLower = new marcher.BoxSDF({
sdfVarName: "d5",
width: 0.55,
height: 0.55,
depth: 0.55,
});
clipBoxLower.hide();
clipBoxLower.translate(0, 0.6, 0);

layer.addPrimitive(clipBoxLower);
layer.addPrimitive(shellLower);

shellLower.intersect(clipBoxLower);

LKI0pT.png

这里法线中间的按钮还是被球壳给挡住了,我们就要把挡住的部分给挖空

中间镂空

创建 2 个圆柱体(类似之前的按钮),半径和高度稍微调大点,用它们分别对上下的球壳进行扣除操作就能起到挖空中间的效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 球壳(上):挖除中间镂空部分后
const clipCylinderCenter1 = new marcher.CylinderSDF({
sdfVarName: "d7",
radius: 0.15,
height: 0.6,
materialId: RED_MAT,
});
clipCylinderCenter1.rotate(90, "x");

layer.addPrimitive(clipCylinderCenter1);

clipCylinderCenter1.subtract(shellUpper);

shellUpper.hide();

// 球壳下:挖除中间镂空部分后
const clipCylinderCenter2 = new marcher.CylinderSDF({
sdfVarName: "d8",
radius: 0.15,
height: 0.6,
materialId: WHITE_MAT,
});
clipCylinderCenter2.rotate(90, "x");

layer.addPrimitive(clipCylinderCenter2);

clipCylinderCenter2.subtract(shellLower);

shellLower.hide();

LKoECV.png

美化

这里可以自由发挥,不论是光照、相机视角还是材质等要素都可以优化

笔者优化后的结果如下

LKobMF.gif

项目地址

https://code.juejin.cn/pen/7085983190216605700

Author: alphardex
Link: https://alphardex.github.io/mygo/posts/58379/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.