DataTexture 를 이용한 GPGPU 구현, THREE.JS_01

P5js
Art & tech / 아트와 테크로 미래를 보다
5 min readJun 23, 2024

핑퐁버퍼를 활용한 gpgpu 구현

threejs 에는 GPGPU 구현을 위해 GPUComputationRenderer 라는 애드온을 제공하고 있습니다. 하지만 GPGPU로 구현하다보면 숨겨진 원리에 대해 궁금할때가 매번 찾아 오게 됩니다. 그리고 제대로된 GPGPU 구현을 위해서 기본 원리인 datatexture 와 핑퐁버퍼로 구현하는 GPGPU를 공부해 보게 되었습니다.

아주 기본 적인 버텍스 웨이더와 프래그먼트 쉐이더로 오브젝트를 더하기만 하면

const addobject = () => {
const geometry = new THREE.PlaneGeometry(1,1,10,10)
material = new THREE.ShaderMaterial({
uniforms:{
time:{value:0},
},
vertexShader: vertexShader,
fragmentShader: fragmentShader
})
const mesh = new THREE.Points(geometry, material)
scene.add(mesh)
}

//아래는 프래그먼트 쉐이더 코드
varying vec2 vUv;
uniform sampler2D uTexture;

void main(){
gl_FragColor = vec4(vUv,0.0,1.0);
}

포인트로 구성된 오브젝트를 확인 할 수 있습니다. 물론 프래그먼트 쉐이더에는 uv값을 전달받아 uv 컬러를 적용하였습니다.

이후 datatexture 를 생성합니다.
그렇다면 datatexture 란 무엇일까요? 데이터 텍스쳐란 우리가 흔히 텍스쳐를 이미지로 사용하는대신, 각 픽셀에 데이터 값을 직접할당하여, 즉 데이터 배열을 텍스쳐로 활용하는 방법을 말하는 것입니다.

    const size = 256
const number = size * size // 높이와 넓이 만큼의 픽셀 개수
const data = new Float32Array( 4 * number) // 각 픽셀에 r,g,b,a 혹은 x,y,z,w 만큰 4개의 데이터가 담길수 있다는 것을 의미합니다.

for(let i=0; i < size; i++){
for(let j=0; j < size; j++){

const index = i * size + j

data[ index * 4 ] = Math.random()
data[ index * 4 + 1 ] = Math.random()
data[ index * 4 + 2 ] = 0
data[ index * 4 + 3 ] = 1

}
}

먼저 위와 같이 데이터 텍스쳐를 만들기 위한 준비를 합니다.
위 코드는 데이터 텍스처를 준비하는 과정입니다.

  1. 텍스처 크기 설정: size 변수로 텍스처의 가로와 세로 크기를 설정합니다.
  2. 픽셀 개수 정의: number 변수는 텍스처의 전체 픽셀 개수를 정의합니다 (size 곱하기 size).
  3. 데이터 배열 생성: Float32Array 배열을 생성하여 각 픽셀에 4개의 데이터(r, g, b, a 또는 x, y, z, w)를 저장합니다. 4 * number인 이유는 각 픽셀이 4개의 값을 가지기 때문입니다.
  4. 데이터 할당: 이중 for 루프를 사용하여 각 픽셀의 데이터를 무작위 값으로 초기화합니다. data[index * 4]는 r (또는 x) 값을, data[index * 4 + 1]은 g (또는 y) 값을, data[index * 4 + 2]는 b (또는 z) 값을, data[index * 4 + 3]는 a (또는 w) 값을 설정합니다.

각 픽셀은 랜덤한 r, g 값을 가지며, b는 0, a는 1로 설정되었습니다.

이후 이데이터를 가진 데이터텍스쳐를 만들어 줍니다.

positions = new THREE.DataTexture(data, size, size, THREE.RGBAFormat, THREE.FloatType)
positions.needsUpdate = true //이 설정을 통해 텍스처 데이터가 변경되었을 때 GPU에 새로운 데이터를 업데이트하도록 지시


material = new THREE.ShaderMaterial({
uniforms:{
time:{value:0},
uTexture:{value: positions}
},
vertexShader: vertexShader,
fragmentShader: fragmentShader
})
const mesh = new THREE.Points(geometry, material)
scene.add(mesh)
}

//아래는 프래그먼트쉐이더 코드
varying vec2 vUv;
uniform sampler2D uTexture;

void main(){

vec4 tex = texture2D(uTexture, vUv);
gl_FragColor = vec4(tex.rgb,1.0);
}

그리고 만들어진 데이터 텍스쳐를 직접 프래그먼트쉐이더에 전달하면 r,g 값이 랜덤인 아래와 같은 이미지가 보여집니다.

이렇게 데이터 텍스쳐를 만들어 보았습니다. 다음으로 바로 이 데이터 텍스쳐를 핑퐁 버퍼를 활용하여 데이터를 업데이하기 위해 FBO와 핑퐁버퍼를 알아 보겠습니다.

--

--