쉐이더 코드 확인하기

const program = await renderer.debug.getShaderAsync(scene, camera, mesh)
console.log('**VERTEX SHADER CODE**\n\n', program.vertexShader)
console.log('**FRAGMENT SHADER CODE**\n\n', program.fragmentShader)

three.js, userData 노드

three.js는 Mesh 등과 같은 객체에 사용자 데이터를 넣기 위한 userData 속성이 있습니다. 이처럼 userData 속성에 저장된 값을 쉐이더에서 참조할 수 있도록 해주는 노드가 userData 노드입니다. 사용 예시는 다음과 같습니다.

mesh.userData.한글 = new THREE.Color('#ffff00')
material.outputNode = vec4(userData('한글'), 1)

이렇게 사용된 Mesh 객체를 통해 userData의 값이 변경되면 자동으로 TSL 코드를 통해 반영됩니다.

TypeScript, 메타 프로그래밍 …

TypeScript는 코드에 대한 코드 작성을 목적으로 하는 메타 프로그래밍 언어입니다. 현존하는 대다수의 거의 대부분의 프로그래밍 언어가 메타 프로그래밍이 가능하지만, 특히 TypeScript는 코드에서 타입을 자동으로 새롭게 재정의가 가능하다는 점에서 다른 프로그래밍 언어보다 조금은 더 메타 프로그래밍 언어적이라고 할 수 있습니다.

타입스크립트에서 메타 프로그래밍을 위해 제공하는 기능은 Generic, keyof, typeof, Conditional Types, Mapped Types, Template Literal Types 등이 있고 서브셋은 JavaScript의 메타 프로그래밍 기능인 Proxy, Reflect, Decorator(제안), Object.defineProperty() 등입니다. 타입스크립트에 한정하여 볼때 아래는 메타 프로그래밍을 위해 제공하는 아래의 예제 코드입니다.

// Generic
type Box<T> = { value: T };

// keyof
type Keys = keyof T;

// typeof
type T = typeof value;

// Indexed Access Type
type Value = T[K];

// Mapped Type
type Readonly<T> = {
    readonly [K in keyof T]: T[K];
};

// Conditional Type
type Result<T> = T extends string ? number : boolean;

// Template Literal Type
type Event<T extends string> = `${T}Changed`;

// infer
type Return<T> = T extends (...args: any[]) => infer R ? R : never;

TypeScript, 열거 타입은 열거형보다 객체 방식이 보다 권장됨

열거 타입을 정의하기 위해 TypeScript는 열거형을 제공합니다. 예를들어 다음 코드가 가능합니다.

const enum EDirection {
  Up,
  Down,
  Left,
  Right,
}

function walk(dir: EDirection) {}
walk(EDirection.Left);

위의 코드와 비슷한 맥락으로, 이번에는 열거형을 사용하지 않고 객체 방식으로 작성하면 다음과 같습니다.

const ODirection = {
  Up: 0,
  Down: 1,
  Left: 2,
  Right: 3,
} as const;

type Direction = typeof ODirection[keyof typeof ODirection];
function run(dir: Direction) {}
run(ODirection.Right);

three.js의 WebGLRenderer 객체의 생성에 대한 권장 방식이 변경됨

기존에 WebGLRenderer 객체를 생성하기 위한 코드는 다음과 같았습니다.

import * as THREE from "three"

...

let renderer = new THREE.WebGLRenderer({ antialias: true });

위의 방식 대신에 다음 방식이 권장됩니다.

import * as THREE from "three/webgpu"
import { WebGLRenderer } from 'three'

...

let renderer = new WebGLRenderer({ antialias: true });

권장되는 방식의 경우 WebGL에서도 TSL을 사용할 수 있습니다. 단, 다음과 같은 추가적인 코드가 필요합니다.

import { WebGLNodesHandler } from 'three/addons/tsl/WebGLNodesHandler.js'

...

renderer.setNodesHandler(new WebGLNodesHandler());

실제로 WebGL 환경에서 TSL에 대한 예시 코드는 다음과 같습니다.

import { mx_fractal_noise_float, positionLocal, Fn } from 'three/tsl';

const geometry = new THREE.TorusKnotGeometry(1, 0.4, 128, 32);
const material = new THREE.MeshStandardNodeMaterial({
  color: 0x00ffff,
  metalness: .9,
});

const roughnessNode = mx_fractal_noise_float(positionLocal.mul(2), 6, 4, .7, 2).sin();

material.roughnessNode = roughnessNode;

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

위 코드에 대한 실행 결과는 다음과 같습니다.