three.js, SkyMesh를 이용한 환경광

SkyMesh는 하늘에 대한 시각적인 표현을 해주는 API입니다. 이 SkyMesh를 이용하여 시각적인 표현 뿐만 아니라 실제 광원으로써 사용하기 위한 코드에 대한 내용입니다.

먼저 SkyMesh를 사용하기 위해 다음과 같은 import 문이 필요합니다.

import { SkyMesh } from 'three/addons/objects/SkyMesh.js';

그리고 SkyMesh 객체를 생성합니다.

private sky!: SkyMesh;

private setupSky() {
  const sky = new SkyMesh();
  sky.scale.setScalar(9000);
  // this.scene.add(sky);

  sky.sunPosition.value.copy(this.sunLight.position);
  sky.turbidity.value = 3;
  sky.rayleigh.value = 2;
  sky.cloudSpeed.value = 0; // keep the clouds static, like the WebGL version
  sky.showSunDisc.value = false; // the sun is represented by the light

  this.sky = sky;
}

위의 코드는 SkyMesh 객체를 생성했고 있는데, SkyMesh의 위치를 잡기 위해 this.sunLight의 position을 참고하고 있습니다. 고도와 방위각을 통해 직접 그 위치를 정하고 싶다면 다음 코드를 참고하시기 바랍니다.

sunLight.position.setFromSphericalCoords(
  1, 
  THREE.MathUtils.degToRad(90 - params.elevation), 
  THREE.MathUtils.degToRad(params.azimuth)
);

이를 scene에 추가하면 하늘이 표현됩니다. 하지만 하늘 표현뿐만 아니라 SkyMesh를 이용해 환경광을 구성해야 합니다. 이를 위한 코드는 다음과 같습니다.

private setupEnvironment() {
  const pmremGenerator = new THREE.PMREMGenerator(this.renderer);
  const sceneEnv = new THREE.Scene();
  sceneEnv.add(this.sky);

  const renderTarget = pmremGenerator.fromScene(sceneEnv);
  this.scene.environment = renderTarget.texture;
  // renderTarget.dispose();

  this.scene.add(this.sky);
}

WebGPU 기능 감지

거의 모든 웹브라우저가 WebGPU를 지원하지만 확인은 다음과 같이 할 수 있습니다.

const adapter = await navigator.gpu?.requestAdapter();
if (!adapter) {
  return;
}

WebGPU 지원과는 별개로 WebGPU에서 제공하는 기능은 그 단위마다 지원 여부를 확인해야할 필요는 있습니다. 이에 대한 코드는 다음과 같습니다.

const hasFloat32Filtering = adapter.features.has('float32-filterable');
const hasTimestamps = adapter.features.has('timestamp-query');

three.js, TSL 노드 varying 예제

vertex shader의 결과를 fragment shader로 전달하기 위한 varying을 TSL 노드로 사용하기 위한 예제입니다. 먼저 varying으로 사용될 데이터를 정의합니다.

const colorVarying = vec3(0).toVarying(); // same as varying(vec3(0))

이 varying 데이터의 값을 재질의 vertex shader에 해당하는 positionNode에서 할당합니다.

material.positionNode = Fn(() => {
  ...

  colorVarying.assign(texture(colorTexture, colorUv).rgb)

  ...
}

varying 데이터를 재질의 다양한 fragment shader 중 하나에 해당하는 colorNode에서 사용합니다.

material.colorNode = colorVarying

three.js, WebGPU의 storage 데이터를 CPU와 GPU에서 변경하기

일단 GPU 단에 storage를 생성하기 위해서는 다음과 같은 코드가 필요합니다.

import * as THREE from 'three/webgpu';
import { storage, compute, float, Fn } from 'three/tsl';

const count = 4;
const initialData = new Float32Array([1.0, 2.0, 3.0, 4.0]);
const storageAttr = new THREE.StorageBufferAttribute(initialData, 1);
const storageNode = storage(storageAttr, 'float', count);

이 상태에서 CPU에서 storage의 값을 변경하기 위한 코드 예시는 다음과 같습니다.

// CPU 배열의 첫 번째 값을 100.0으로 변경
storageAttr.array[0] = 100.0;
    
// 변경 사항이 있음을 렌더러/GPU에 알림
storageAttr.needsUpdate = true; 

GPU에서 변경하는 코드 예시는 다음과 같구요.

// 첫 번째 값에 1을 더함
storageNode.element(0).addAssign(1.0);

다음 코드는 storage 데이터 전체를 CPU에서 접근할 수 있도록 비동기적으로 읽는 코드입니다.

async function readDataFromGPU(renderer) {
    // GPU 버퍼 데이터를 CPU TypedArray로 가져옴
    const arrayBuffer = await renderer.getArrayBufferAsync(storageAttr);
    const cpuResultArray = new Float32Array(arrayBuffer);
    
    console.log("CPU에서 읽어온 GPU 스토리지 값:", cpuResultArray);
}

(코드 검증이 필요함)