베지어(bezier)와 기울기

베지어 함수의 코드는 다음과 같다.

vec3 bezier(vec3 P0, vec3 P1, vec3 P2, vec3 P3, float t) {
  float u = 1.0 - t;
  float tt = t * t;
  float uu = u * u;
  float uuu = uu * u;
  float ttt = tt * t;

  vec3 p = uuu * P0; // (1-t)^3 * P0
  p += 3.0 * uu * t * P1; // 3*(1-t)^2*t*P1
  p += 3.0 * u * tt * P2; // 3*(1-t)*t^2*P2
  p += ttt * P3; // t^3*P3

  return p;
}

베지어 상의 접선에 대한 함수 코드는 다음과 같다.

vec3 bezierGrad(vec3 P0, vec3 P1, vec3 P2, vec3 P3, float t) {
  return 3.0 * (1.0 - t) * (1.0 - t) * (P1 - P0) +
    6.0 * (1.0 - t) * t * (P2 - P1) +
    3.0 * t * t * (P3 - P2);
}

접선에 대한 벡터를 90도 회전하면 베지어의 법선 벡터를 구할 수 있다.

Instanced Mesh in Shader

인스턴스드 매시는 다음처럼 생성할 수 있습니다. 지오메트리의 좌표 구성을 위해 BoxGeometry의 것을 가져다 쓰는 경우입니다. 지오메트리의 index와 position만을 필요로 하니 아래처럼 했고, 그냥 new THREE.InstancedBufferGeometry.copy(baseGeometry)로 하면 지오메트리를 그대로 복사합니다.

const baseGeometry = new THREE.BoxGeometry(0.5, 0.5, 0.5);
const geometry = new THREE.InstancedBufferGeometry();

geometry.index = baseGeometry.index;
geometry.attributes.position = baseGeometry.attributes.position;

// 위의 코드는 참조인지라 아래의 코드로 대체하는게 맞죠.
// geometry.setIndex(baseGeometry.index);
// geometry.setAttribute("position", baseGeometry.attributes.position);

인스턴스로 만들 개수를 지정해야 합니다.

const count = 100;
geometry.instanceCount = count;

인스턴스화된 것들에 대한 개별 요소들은 위치, 회전, 크기, 색상에 대해 개별적으로 지정이 가능한데 위치와 색상에 대한 지정 코드입니다.

const offsets = new Float32Array(count * 3);
for (let i = 0; i < count; i++) {
  offsets[i * 3 + 0] = (Math.random() - 0.5) * 10; // x
  offsets[i * 3 + 1] = (Math.random() - 0.5) * 10; // y
  offsets[i * 3 + 2] = (Math.random() - 0.5) * 10; // z
}
geometry.setAttribute("instanceOffset", new THREE.InstancedBufferAttribute(offsets, 3));

const colors = new Float32Array(count * 3);
for (let i = 0; i < count; i++) {
  colors[i * 3 + 0] = Math.random(); // R
  colors[i * 3 + 1] = Math.random(); // G
  colors[i * 3 + 2] = Math.random(); // B
}
geometry.setAttribute("instanceColor", new THREE.InstancedBufferAttribute(colors, 3));

쉐이더를 통해 직접 인스턴스 매시를 렌더링하기 위해 재질을 설정하는 코드입니다.

const material = new THREE.ShaderMaterial({
  vertexShader: /*glsl*/ `
    attribute vec3 instanceOffset;
    attribute vec3 instanceColor;
    varying vec3 vColor;
    
    void main() {
      vec3 transformed = position + instanceOffset;
      vColor = instanceColor;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed, 1.0);
    }
  `,
  fragmentShader: /*glsl*/ `
    varying vec3 vColor;
    void main() {
      gl_FragColor = vec4(vColor, 1.0);
    }
  `
});

이제 장면에 매시를 넣으면 화면에 딱... 표시되어야 합니다.

const mesh = new THREE.Mesh(geometry, material);
this._scene.add(mesh);