prefacio
Hola a todos, este es el mago de CSS y WebGL - alphardex. En este artículo, conoceremos a un nuevo amigo: kokomi.js , que puede brindarte una increíble experiencia de creación en 3D~
kokomi.js Kosuke
Creé muchos trabajos relacionados con three.js antes , pero luego descubrí que hay muy pocas cosas que se pueden reutilizar, y la estructura del proyecto también es caótica. Para resolver estos dos problemas, el autor decidió escribir una rueda para encapsular algunas de las funciones más utilizadas de three.js y aclarar la estructura del proyecto, por lo que existe kokomi.js
Origen de su nombre: Sangonomiya Kokomi
Su dirección de Github: github.com/alphardex/k…
Listo para trabajar
En este artículo usaremos la plataforma codesandbox para hacer todo el trabajo de codificación. La cuenta se puede registrar directamente con la cuenta de Github
Dirección de la plataforma: codesandbox.io
escena básica
Crear plantilla ts
Primero, hacemos clic en la esquina superior derecha, Create Sandbox
lo buscamos en la lista Vanilla Typescript
y creamos la plantilla ts más simple
La dirección de este paso: codesandbox.io/s/typescript…
Anso kokomi.js
Dependencies
En el cuadro de entrada inferior a la izquierda , kokomi.js
ingréselo para instalar kokomi.js
Dado que kokomi.js depende de three.js, también debemos instalarlo y su tipo: three
y@types/three
Construcción de escena
En index.ts, modifique la identificación de nuestro contenedor de lienzo #sketch
e introduzca una createSketch
función (implementada a continuación)
índice.ts
import "./styles.css";
import createSketch from "./app";
document.getElementById("app").innerHTML = `
<div id="sketch"></div>`;
createSketch();
复制代码
在style.css中,给容器设置一定的样式,使其铺满屏幕
style.css
body {
margin: 0;
}
#sketch {
width: 100vw;
height: 100vh;
overflow: hidden;
background: black;
}
复制代码
新建文件app.ts,在里面输入如下代码
app.ts
import * as kokomi from "kokomi.js";
class Sketch extends kokomi.Base {
create() {}
}
const createSketch = () => {
const sketch = new Sketch();
sketch.create();
return sketch;
};
export default createSketch;
复制代码
右边画面会报错,提示我们先要配置下babel
新建.babalrc文件,在里面拷贝如下代码
.babalrc
{
"presets": [
"env"
],
"plugins": [
"transform-runtime",
"@babel/plugin-proposal-class-properties"
],
"parserOpts": {
"plugins": [
"dynamicImport"
]
}
}
复制代码
这时我们能看到一片黑屏,表明kokomi.js已经被顺利引入了
接下来让我们创建一个3D世界的Hello World——一个可爱的白色方块
class Sketch extends kokomi.Base {
create() {
const box = new kokomi.Box(this);
box.addExisting();
}
}
复制代码
可以看到屏幕中心已经出现了一个白色的方块
接下来让我们添加轨道视角
class Sketch extends kokomi.Base {
create() {
const box = new kokomi.Box(this);
box.addExisting();
new kokomi.OrbitControls(this);
}
}
复制代码
这样我们就能自由地拖拽画面了
让方块旋转起来吧
class Sketch extends kokomi.Base {
create() {
const box = new kokomi.Box(this);
box.addExisting();
new kokomi.OrbitControls(this);
this.update((time: number) => {
box.spin(time);
});
}
}
复制代码
恭喜,此刻你已经完成了最基础的3D场景的搭建
该步骤地址:codesandbox.io/s/kokomi-js…
素材管理
kokomi.js提供了AssetManager
类,用来统一管理素材的加载
定义素材
首先,创建一个resources.ts文件,里面定义好素材列表
素材列表对象有3个字段:
- name:素材名
- type:素材类型,目前支持texture(2D贴图)、cubeTexture(3D贴图)、gltfModel(模型)、font(字体)
- path:素材路径
resources.ts
import type * as kokomi from "kokomi.js";
import foxModel from "/public/models/Fox/glTF/Fox.gltf";
const resourceList: kokomi.ResourceItem[] = [
{
name: "foxModel",
type: "gltfModel",
path: foxModel
}
];
export default resourceList;
复制代码
我们引入了一个神子,啊不狐狸的模型,这个模型是从gltf样例模型中取出来的,也可以替换成自己喜爱的其他模型
加载素材
实例化AssetManager
类,并将素材resourceList
作为参数传入
监听emitter
的ready
事件(素材加载完毕事件),读取items
里的foxModel
,将其加入场景中,即完成了素材的加载
app.ts
import * as THREE from "three";
import * as kokomi from "kokomi.js";
import resourceList from "./resources";
class Sketch extends kokomi.Base {
create() {
new kokomi.OrbitControls(this);
this.camera.position.copy(new THREE.Vector3(6, 4, 3));
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
this.scene.add(ambientLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 0.6);
dirLight.position.copy(new THREE.Vector3(1, 2, 3));
this.scene.add(dirLight);
const assetManager = new kokomi.AssetManager(this, resourceList);
assetManager.emitter.on("ready", () => {
const foxModel = assetManager.items.foxModel;
foxModel.scene.scale.set(0.02, 0.02, 0.02);
this.scene.add(foxModel.scene);
});
}
}
复制代码
该步骤地址:codesandbox.io/s/kokomi-js…
这里有个优化点:可以将Fox模型抽成一个class
组件,这样可以维护一个属于自己的组件逻辑
组件化
新建文件夹 components
,新建 fox.ts
,编写狐狸的模型组件
La idea básica de escribir componentes: heredar la Component
clase, escribir la lógica de estado del componente en ella, la addExisting
función es responsable de agregar el componente a la escena, la update
función es el cuadro de animación y se sincronizará con el animación global de la escena
zorro.ts
import * as THREE from "three";
import * as kokomi from "kokomi.js";
import type * as STDLIB from "three-stdlib";
type ActionName = "idle" | "walk" | "run";
class Fox extends kokomi.Component {
gltf: STDLIB.GLTF;
mixer: THREE.AnimationMixer;
actions: Record<string, THREE.AnimationAction>;
constructor(base: kokomi.Base, gltf: STDLIB.GLTF) {
super(base);
this.gltf = gltf;
const mixer = new THREE.AnimationMixer(this.gltf.scene);
this.mixer = mixer;
this.actions = {};
this.setActions();
}
addExisting(): void {
this.gltf.scene.scale.set(0.02, 0.02, 0.02);
this.base.scene.add(this.gltf.scene);
}
update(time: number): void {
const delta = this.base.clock.getDelta();
this.mixer.update(delta);
}
setActions() {
this.actions.idle = this.mixer.clipAction(this.gltf.animations[0]);
this.actions.walk = this.mixer.clipAction(this.gltf.animations[1]);
this.actions.run = this.mixer.clipAction(this.gltf.animations[2]);
}
playAction(name: ActionName = "idle") {
const prevAction = this.actions.current;
const nextAction = this.actions[name];
nextAction.reset();
nextAction.play();
if (prevAction) {
nextAction.crossFadeFrom(prevAction, 1, true);
}
this.actions.current = nextAction;
}
}
export default Fox;
复制代码
Aplicar el componente a la escena principal.
aplicación.ts
import Fox from "./components/fox";
class Sketch extends kokomi.Base {
...
create() {
...
this.assetManager.emitter.on("ready", () => {
const fox = new Fox(this, this.assetManager.items.foxModel);
fox.addExisting();
fox.playAction("idle");
});
}
}
复制代码
La instancia de fox aquí tiene su propia función y estado, de modo que cuando creamos otras class
, no habrá conflictos duplicados en la funcionalidad.
La dirección de este paso: codesandbox.io/s/kokomi-js…
creación de efectos especiales
Cree una nueva carpeta de sombreado y cree un nuevo fragment.glsl
fragmento de sombreado en ella, copie el siguiente código dentro
void mainImage(out vec4 fragColor,in vec2 fragCoord){
vec2 p=fragCoord/iResolution.xy;
vec3 color=vec3(p,0.);
fragColor=vec4(color,1.);
}
复制代码
En la escena principal, crea una instancia de la ScreenQuad
clase, ábrela shadertoyMode
e introduce el fragment shader
aplicación.ts
import fragmentShader from "./shaders/fragment.glsl";
class Sketch extends kokomi.Base {
create() {
const screenQuad = new kokomi.ScreenQuad(this, {
shadertoyMode: true,
fragmentShader
});
screenQuad.addExisting();
}
}
复制代码
Puede ver que se muestra el plano uv y podemos crear Shader en él.
La dirección de este paso: codesandbox.io/s/kokomi-js…
Por ejemplo, el Raymarching más común en Shadertoy
La dirección de demostración: codesandbox.io/s/raymarchi…
finalmente
Este artículo lo lleva a una comprensión preliminar de kokomi.js. El autor continuará mejorándola en el futuro, así que estad atentos.