Skip to main content

Draw instance

这个实例主要展示了引擎实例化渲染如何使用。

一个简单的示例

Result
Loading...
Live Editor
function render(props) {
  const drawModelVertex = `
    attribute vec2 uv;
    attribute vec3 position;
    // Add instanced attributes just like any attribute
    attribute vec3 offset;
    attribute vec3 random;
    uniform mat4 modelViewMatrix;
    uniform mat4 projectionMatrix;
    uniform float uTime;
    varying vec2 vUv;
    varying vec3 vNormal;

    void rotate2d(inout vec2 v, float a){
        mat2 m = mat2(cos(a), -sin(a), sin(a),  cos(a));
        v = m * v;
    }

    void main() {
        vUv = uv;

        // copy position so that we can modify the instances
        vec3 pos = position;

        // scale first
        pos *= 0.9 + random.y * 0.2;

        // rotate around y axis
        rotate2d(pos.xz, random.x * 6.28 + 4.0 * uTime * (random.y - 0.5));

        // rotate around x axis just to add some extra variation
        rotate2d(pos.zy, random.z * 0.5 * sin(uTime * random.x + random.z * 3.14));

        pos += offset;

        gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
    }
    `;

  const drawModelFragment = `
    precision highp float;

    uniform float uTime;
    uniform sampler2D tMap;
    varying vec2 vUv;

    void main() {
        vec3 tex = texture2D(tMap, vUv).rgb;

        gl_FragColor.rgb = tex;
        gl_FragColor.a = 1.0;
    }
    `;

  const refDom = useRef(null);

  const store = leva.useCreateStore();

  const fov = 15;
  const nearZ = 0.1;

  const farZ = 100;

  // const { name, aNumber } = leva.useControls({ name: 'World1', aNumber: 2 }, { store });

  const image = useBaseUrl('/assets/acorn.jpg');
  const json = useBaseUrl('/assets/acorn.json');

  const init = () => {
    const canvas = refDom.current;

    canvas.width = canvas.clientWidth;
    canvas.height = canvas.clientHeight;
    const renderer = new Renderer(canvas, {
      alpha: true,
    });

    const camera = new PerspectiveCamera(fov, canvas.width / canvas.height, nearZ, farZ);
    camera.position.z = 15;

    function resize(target) {
      const { width, height } = target.getBoundingClientRect();
      renderer.setSize(width, height);
      camera.aspect = width / height;
    }

    const scene = new Scene();

    const texture = new Texture(renderer, {
      flipY: true,
    });

    texture.fromSrc(image);

    const program = new Program(renderer, {
      vertexShader: drawModelVertex,
      fragmentShader: drawModelFragment,
      uniforms: {
        uTime: { value: 0 },
        tMap: { value: texture },
      },
      cullFace: renderer.gl.BACK,
    });

    const num = 60;

    fetch(json).then(res => res.json()).then((data) => {
      let offset = new Float32Array(num * 3);
      let random = new Float32Array(num * 3);
      for (let i = 0; i < num; i++) {
        offset.set([Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1], i * 3);

        // unique random values are always handy for instances.
        // Here they will be used for rotation, scale and movement.
        random.set([Math.random(), Math.random(), Math.random()], i * 3);
      }

      const geometry = new Geometry(renderer, {
        position: { size: 3, data: new Float32Array(data.position) },
        uv: { size: 2, data: new Float32Array(data.uv) },
        normal: { size: 3, data: new Float32Array(data.normal) },

        // simply add the 'instanced' property to flag as an instanced attribute.
        // set the value as the divisor number
        offset: { divisor: 1, size: 3, data: offset },
        random: { divisor: 1, size: 3, data: random },
      });

      const mesh = new Mesh(renderer, { mode: renderer.gl.TRIANGLES, geometry, program });
      mesh.setParent(scene);

      const raf = new Raf((t) => {
        if (mesh) mesh.rotation.y -= 0.005;
        program.setUniform('uTime', t)
        renderer.render({ scene, camera });
      });
    });

    return {
      canvas,
      resize,
    }
  }

  useEffect(() => {
    const { canvas, resize } = init();

    observe(canvas, resize);

    return () => {
      unobserve(canvas, resize);
    };
  }, []);

  return (
    <div className="live-wrap">
      <div className="leva-wrap">
        <Leva
          fill
        ></Leva>
        <LevaPanel store={store} fill></LevaPanel>
      </div>
      <canvas className="scene-canvas" ref={refDom}></canvas>
    </div>
  );
}