前言
大家好,这里是 CSS 兼 WebGL 魔法使——alphardex。本文就让我们来用kokomi.js的raymarching 组件来画一个精灵球吧~
泼给莫,给托打贼!
绘画开始
首先 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);
|
按钮
中间有个白色的按钮,其实就是个圆柱体
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);
|
上下球壳
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);
|
这里法线中间的按钮还是被球壳给挡住了,我们就要把挡住的部分给挖空
中间镂空
创建 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();
|
美化
这里可以自由发挥,不论是光照、相机视角还是材质等要素都可以优化
笔者优化后的结果如下
项目地址
https://code.juejin.cn/pen/7085983190216605700