index.vert

#version 300 es

layout (location = 0) in vec3 aVertexPosition;
layout (location = 1) in vec3 aVertexNormal;

uniform mat4 uMatModel;
uniform mat4 uMatView;
uniform mat4 uMatProj;
uniform mat4 uMatNormal;
uniform vec3 uLightDir;
uniform vec4 uAmbient;
uniform vec3 uMaterialColor;

out vec4 vVertexColor;

void main() {
  vec3 N = aVertexNormal;
  
  vec3 invL = normalize(uMatNormal * vec4(uLightDir, 0.0)).xyz;
  float diffuse = clamp(dot(N, invL), 0.1, 1.0);
  
  vVertexColor = vec4(uMaterialColor, 1.0) * vec4(vec3(diffuse), 1.0) + uAmbient;
  gl_Position = uMatProj * uMatView * uMatModel * vec4(aVertexPosition, 1.0);
}

index.frag

#version 300 es

precision highp float;

in vec4 vVertexColor;

out vec4 fragColor;

void main() {
  fragColor = vVertexColor;
}

render.ts

import { SketchGl, type SketchConfig, type SketchFn } from "sketchgl"
import { Uniforms, Program } from "sketchgl/program"
import { Geometry } from "sketchgl/geometry"
import { Matrix4, type RawVector3 } from "sketchgl/math"
import { AngleCamera, AngleCameraController } from "sketchgl/camera"

import vert from "./index.vert?raw"
import frag from "./index.frag?raw"

import rabbitModel from "@/model/json/bunny.json" assert { type: "json" }

const sketch: SketchFn = (skCanvas) => {
  const { gl, canvas } = skCanvas

  const uniforms = new Uniforms(gl, [
    "uMatView",
    "uMatModel",
    "uMatProj",
    "uMatNormal",
    "uLightDir",
    "uAmbient",
    "uMaterialColor"
  ])
  const uMaterialColor: RawVector3 = [0.965, 0.776, 0.918]

  const program = new Program(gl)
  program.attach(vert, frag)
  program.activate()

  const rabbit = new Geometry(gl)
  rabbit.registAttrib("vertice", { location: 0, components: 3, buffer: new Float32Array(rabbitModel.vertices) })
  rabbit.registAttrib("normal", { location: 1, components: 3, buffer: new Float32Array(rabbitModel.normals) })
  rabbit.registIndices(new Uint16Array(rabbitModel.indices))
  rabbit.setup()

  const camera = new AngleCamera("ORBIT")
  camera.goHome([-0.5, 0.5, 10])
  camera.azimuth = -20
  camera.elevation = -20
  camera.focus = [0, 0, 0]
  camera.update()

  AngleCameraController.init(canvas, camera)

  const matP = Matrix4.perspective(camera.fov, canvas.width / canvas.height, camera.near, camera.far)
  const matM = Matrix4.identity()

  uniforms.init(program.glProgram)
  uniforms.fmatrix4("uMatModel", matM.values)
  uniforms.fmatrix4("uMatProj", matP.values)
  uniforms.fmatrix4("uMatNormal", matM.inverse().values)
  uniforms.fvector3("uLightDir", [-0.5, 0.5, 200])
  uniforms.fvector3("uMaterialColor", uMaterialColor)
  uniforms.fvector4("uAmbient", [0.2, 0.1, 0.2, 1.0])

  gl.clearColor(0.98, 0.969, 0.941, 1.0)
  gl.clearDepth(1.0)

  gl.enable(gl.DEPTH_TEST)
  gl.depthFunc(gl.LEQUAL)

  gl.enable(gl.CULL_FACE)

  return {
    drawOnFrame() {
      gl.viewport(0, 0, gl.canvas.width, gl.canvas.height)
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

      const matV = camera.View
      uniforms.fmatrix4("uMatView", matV.values)

      rabbit.bind()
      rabbit.draw()
    },
    control(ui) {
      ui.rgb("Color", uMaterialColor, (c) => {
        uniforms.fvector3("uMaterialColor", c)
      })
    }
  }
}

export const onload = () => {
  const config: SketchConfig = {
    canvas: {
      el: "gl-canvas",
      fit: "square",
      autoResize: true
    }
  }
  SketchGl.init(config, sketch)
}