preface

Hi, here is CSS and WebGL magic – Alphardex. In this article, we will meet a new friend kokomi. Js, who can bring you amazing 3D creative experience ~

Kokomi. Introduction of js

I created a lot of three.js related works before, but later I found that there were too few reusable things and the project structure was chaotic. In order to solve these two problems, the author decided to write a wheel to encapsulate some of the most commonly used functions of Three. js and make the structure of the project more clear, thus kokomi

The source of her name: Sangonomiya Kokomi

Her Github address: github.com/alphardex/k…

The preparatory work

In this article, we will use the CodesandBox platform to do all the coding. You can create an account directly with your Github account

Platform address: codesandbox.io

Basic scenario

Creating a TS Template

First, we click on the Create Sandbox in the upper right corner, find Vanilla Typescript from the list, and Create a simple TS template

The steps to address: codesandbox. IO/s/typescrip…

Install kokomi. Js

In the box below Dependencies on the left, type Kokomi. Js to install Kokomi

Since Kokomi.js relies on three.js, we also need to install it with its type: three and @types/three

Scenario building

In index.ts, change our Canvas container ID to # Sketch and introduce the createSketch function (implemented below)

index.ts

import "./styles.css";

import createSketch from "./app";

document.getElementById("app").innerHTML = `
<div id="sketch"></div>`;

createSketch();
Copy the code

In style.css, style the container so that it covers the screen

style.css

body {
  margin: 0;
}

#sketch {
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  background: black;
}
Copy the code

Create a new file app.ts and type the following code into it

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;
Copy the code

The right screen displays an error telling us to configure Babel first

Create a new.babalrc file and copy the following code in it

.babalrc

{
  "presets": [
    "env"]."plugins": [
    "transform-runtime"."@babel/plugin-proposal-class-properties"]."parserOpts": {
    "plugins": [
      "dynamicImport"]}}Copy the code

At this point we can see a black screen, indicating that Kokomi.js has been successfully introduced

Let’s create a 3D Hello World — a cute white square

class Sketch extends kokomi.Base {
  create() {
    const box = new kokomi.Box(this); box.addExisting(); }}Copy the code

You can see that a white square has appeared in the center of the screen

Next let’s add the orbital perspective

class Sketch extends kokomi.Base {
  create() {
    const box = new kokomi.Box(this);
    box.addExisting();

    new kokomi.OrbitControls(this); }}Copy the code

So we can drag and drop the image freely

Let’s rotate the cube

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); }); }}Copy the code

Congratulations, you have now completed the basic 3D scene construction

IO /s/kokomi-js…

Material management

Kokomi. Js provides the AssetManager class to manage the loading of materials uniformly

Define the material

First, create a resources.ts file with a list of defined assets

The story list object has three fields:

  • Name: indicates the name of the material
  • Texture (2D), cubeTexture (3D), gltfModel (3D), font (font)
  • Path: indicates the material 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;
Copy the code

We introduced a god son, ah no fox model, this model is taken from the GLTF sample model, can also be replaced with their favorite other models

Load the material

Instantiate the AssetManager class and pass in the resource resourceList as a parameter

Listen to emitter’s Ready event, read the foxModel in the items, and add it to the scene, completing the loading of the material

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); }); }}Copy the code

IO /s/kokomi-js…

Here’s an optimization: You can split the Fox model into a class component so that you can maintain your own component logic

componentization

Create a new folder components, create fox.ts, and write the fox model component

The basic idea for writing components is to inherit the Component class and write the Component state logic in it. The addExisting function adds components to the scene. The Update function is the animation frame that will be synchronized to the global animation of the scene

fox.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;
Copy the code

Apply this component to the main scenario

app.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"); }); }}Copy the code

The Fox instance here has its own functionality and state, so there is no duplication of functionality when we create other classes

IO /s/kokomi-js…

Special effects to create

Create a new shader folder and create a fragment. GLSL fragment shader inside it and copy the following code

void mainImage(out vec4 fragColor,in vec2 fragCoord){
    vec2 p=fragCoord/iResolution.xy;
    vec3 color=vec3(p,0.);
    fragColor=vec4(color,1.);
}
Copy the code

In the main scenario, the ScreenQuad class is instantiated, shadertoyMode is enabled, and the slice shader is introduced

app.ts

import fragmentShader from "./shaders/fragment.glsl";

class Sketch extends kokomi.Base {
  create() {
    const screenQuad = new kokomi.ScreenQuad(this, {
      shadertoyMode: true, fragmentShader }); screenQuad.addExisting(); }}Copy the code

You can see that the UV plane is displayed, on which we can create Shader

IO /s/kokomi-js…

Take, for example, the most common Raymarching on Shadertoy

This demo address: codesandbox. IO/s/raymarchi…

The last

This article brings us a preliminary understanding of Kokomi. Js. Next I will continue to improve her, please look forward to it.