시네마틱 촬영, 강렬한 빛이 있는 행성 위의 추상적인 [주제/환경] 장
Wikiprompt, 무료 프롬프트 백과사전에서
시네마틱 촬영, 강렬한 빛이 있는 행성 위의 추상적인 [주제/환경] 장 3D 입자 효과 엔지니어로 활동하는 AI용 프롬프트: 메시-투-파티클 모핑, FBO, GPGPU, 노이즈 난기류를 갖춘 WebGL 전환 시스템 설계하기.
프롬프트 내용저장
🌐
퍼포먼스가 높은 WebGL 기반 전환 시스템을 설계하기 위해, 3D 파티클 효과 엔지니어로서 다음의 개념적 셰이더 로직과 핵심 JavaScript 구현을 제공합니다.
---
### 1. 개념적 셰이더 로직
#### (A) 파티클 위치 업데이트 (GPGPU - FBO 기반)
**Vertex Shader (Pass 1: Update)**
```glsl
// 입력: 이전 위치, 목표 위치(앵커), 속도, 노이즈 오프셋
attribute vec3 aPrevPosition;
attribute vec3 aTargetPosition;
attribute vec3 aVelocity;
attribute float aLife;
uniform float uTime;
uniform float uDelta;
uniform float uNoiseScale;
uniform float uForceStrength;
varying vec3 vColor;
// 3D Simplex 노이즈 (간략화된 의사코드)
float snoise(vec3 v) { /* ... 표준 구현 ... */ }
void main() {
vec3 pos = aPrevPosition;
vec3 dirToTarget = aTargetPosition - pos;
float dist = length(dirToTarget);
// 인력 (Attraction)
vec3 attraction = normalize(dirToTarget) * min(dist * 0.1, 1.0);
// 노이즈 난류 (Noise Turbulence)
vec3 noiseVec = vec3(
snoise(pos * uNoiseScale + uTime * 0.5),
snoise(pos * uNoiseScale + uTime * 0.3 + 100.0),
snoise(pos * uNoiseScale + uTime * 0.7 + 200.0)
);
// 속도 갱신 (관성 + 힘)
vec3 newVelocity = aVelocity * 0.95 + (attraction + noiseVec) * uForceStrength;
// 위치 갱신
vec3 newPos = pos + newVelocity * uDelta;
// 속도 기반 동적 색상 (Velocity-based Color)
float speed = length(newVelocity);
vColor = mix(vec3(0.2, 0.6, 1.0), vec3(1.0, 0.3, 0.8), smoothstep(0.0, 2.0, speed));
// 결과 저장 (FBO 텍스처로 출력)
gl_FragColor = vec4(newPos, 1.0);
// 속도는 별도 텍스처에 저장하거나, 위치 텍스처의 alpha 채널에 인코딩
}
```
**Fragment Shader (Pass 1: Update)**
```glsl
precision highp float;
varying vec3 vColor;
void main() {
gl_FragColor = vec4(vColor, 1.0);
}
```
#### (B) 파티클 렌더링 (Pass 2: Draw)
**Vertex Shader (Draw)**
```glsl
attribute vec2 aUV; // FBO 텍스처 좌표
uniform sampler2D uPositionTexture;
uniform mat4 uProjectionMatrix;
uniform mat4 uModelViewMatrix;
uniform float uPointSize;
void main() {
// FBO에서 위치 읽기
vec3 pos = texture2D(uPositionTexture, aUV).xyz;
vec4 mvPosition = uModelViewMatrix * vec4(pos, 1.0);
gl_PointSize = uPointSize * (300.0 / -mvPosition.z);
gl_Position = uProjectionMatrix * mvPosition;
}
```
**Fragment Shader (Draw)**
```glsl
precision highp float;
varying vec3 vColor; // 필요 시 varying으로 전달
void main() {
// 원형 파티클 (거리 기반 알파)
vec2 center = gl_PointCoord - 0.5;
float dist = length(center);
if (dist > 0.5) discard;
float alpha = 1.0 - smoothstep(0.0, 0.5, dist);
gl_FragColor = vec4(vColor, alpha);
}
```
---
### 2. 핵심 JavaScript 구현 (Three.js + GPGPU)
```javascript
import * as THREE from 'three';
import { GPUComputationRenderer } from 'three/examples/jsm/misc/GPUComputationRenderer.js';
class ParticleMorphSystem {
constructor(scene, renderer, meshA, meshB, particleCount = 20000) {
this.scene = scene;
this.renderer = renderer;
this.particleCount = particleCount;
// 1. FBO 초기화 (GPGPU)
this.gpuCompute = new GPUComputationRenderer(256, 256, renderer);
this.positionTexture = this.gpuCompute.createTexture();
this.velocityTexture = this.gpuCompute.createTexture();
// 2. 메시에서 파티클 앵커 포인트 추출
const anchorsA = this.extractAnchors(meshA, particleCount);
const anchorsB = this.extractAnchors(meshB, particleCount);
// 3. 초기 위치 및 목표 위치 설정
this.initParticleData(anchorsA, anchorsB);
// 4. GPGPU 셰이더 설정
this.setupGPGPU();
// 5. 렌더링용 파티클 지오메트리 생성
this.createParticleGeometry();
}
// 3D 모델의 버텍스 데이터를 파티클 속성 버퍼로 매핑
extractAnchors(mesh, count) {
const geometry = mesh.geometry;
const positions = geometry.attributes.position.array;
const vertexCount = positions.length / 3;
// 샘플링 인덱스 (균등 분포)
const indices = new Uint32Array(count);
for (let i = 0; i < count; i++) {
indices[i] = Math.floor(Math.random() * vertexCount);
}
// Float32Array로 앵커 포인트 저장
const anchors = new Float32Array(count * 3);
for (let i = 0; i < count; i++) {
const idx = indices[i] * 3;
anchors[i * 3] = positions[idx];
anchors[i * 3 + 1] = positions[idx + 1];
anchors[i * 3 + 2] = positions[idx + 2];
}
return anchors;
}
initParticleData(anchorsA, anchorsB) {
const data = this.positionTexture.image.data;
const velData = this.velocityTexture.image.data;
for (let i = 0; i < this.particleCount; i++) {
// 시작 위치: 메시 A의 표면
data[i * 4] = anchorsA[i * 3];
data[i * 4 + 1] = anchorsA[i * 3 + 1];
data[i * 4 + 2] = anchorsA[i * 3 + 2];
data[i * 4 + 3] = 1.0; // life
// 목표 위치: 메시 B의 표면 (별도 텍스처에 저장하거나 uniform으로 전달)
// 여기서는 간단히 두 번째 텍스처에 저장
velData[i * 4] = anchorsB[i * 3];
velData[i * 4 + 1] = anchorsB[i * 3 + 1];
velData[i * 4 + 2] = anchorsB[i * 3 + 2];
velData[i * 4 + 3] = 0.0; // 초기 속도 0
}
}
setupGPGPU() {
// 위치 업데이트 셰이더
const updateShader = `
uniform float uTime;
uniform float uDelta;
uniform float uNoiseScale;
uniform float uForceStrength;
uniform sampler2D uTargetPositions;
void main() {
vec2 uv = gl_FragCoord.xy / resolution.xy;
vec4 posData = texture2D(positionTexture, uv);
vec4 velData = texture2D(velocityTexture, uv);
vec3 target = texture2D(uTargetPositions, uv).xyz;
vec3 pos = posData.xyz;
vec3 vel = velData.xyz;
vec3 dirToTarget = target - pos;
float dist = length(dirToTarget);
// 인력
vec3 attraction = normalize(dirToTarget) * min(dist * 0.1, 1.0);
// 노이즈 난류 (3D Simplex)
vec3 noiseVec = vec3(
snoise(pos * uNoiseScale + uTime * 0.5),
snoise(pos * uNoiseScale + uTime * 0.3 + 100.0),
snoise(pos * uNoiseScale + uTime * 0.7 + 200.0)
);
// 속도 및 위치 갱신
vel = vel * 0.95 + (attraction + noiseVec) * uForceStrength;
pos += vel * uDelta;
gl_FragColor = vec4(pos, 1.0);
}
`;
this.positionVariable = this.gpuCompute.addVariable('positionTexture', updateShader);
this.gpuCompute.setVariableDependencies(this.positionVariable, [this.positionVariable]);
// 목표 위치 텍스처 생성
const targetTexture = this.gpuCompute.createTexture();
targetTexture.image.data.set(this.anchorsB);
this.gpuCompute.addUniform('uTargetPositions', targetTexture);
}
createParticleGeometry() {
// 파티클 렌더링용 지오메트리
const geometry = new THREE.BufferGeometry();
const uvs = new Float32Array(this.particleCount * 2);
// FBO 텍스처 좌표 생성 (256x256 그리드)
const size = 256;
for (let i = 0; i < this.particleCount; i++) {
uvs[i * 2] = (i % size) / size;
uvs[i * 2 + 1] = Math.floor(i / size) / size;
}
geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));
// 파티클 머티리얼
const material = new THREE.ShaderMaterial({
uniforms: {
uPositionTexture: { value: null },
uProjectionMatrix: { value: null },
uModelViewMatrix: { value: null },
uPointSize: { value: 3.0 }
},
vertexShader: `...`, // 위의 Draw Vertex Shader
fragmentShader: `...`, // 위의 Draw Fragment Shader
transparent: true,
depthWrite: false,
blending: THREE.AdditiveBlending
});
this.particles = new THREE.Points(geometry, material);
this.scene.add(this.particles);
}
// 애니메이션 루프에서 호출
update(time, delta) {
// GPGPU 업데이트 실행
this.gpuCompute.compute();
// 렌더링 패스에 위치 텍스처 전달
this.particles.material.uniforms.uPositionTexture.value =
this.gpuCompute.getCurrentRenderTarget(this.positionVariable).texture;
// 셰이더에 시간 전달
this.gpuCompute.getUniforms().uTime.value = time;
this.gpuCompute.getUniforms().uDelta.value = delta;
}
}
```
---
### 3. 핵심 개념 설명
**메시 데이터 → 파티클 속성 버퍼 매핑:**
1. **버텍스 추출**: `geometry.attributes.position.array`에서 모든 버텍스 좌표를 읽습니다.
2. **랜덤 샘플링**: 파티클 수만큼 버텍스 인덱스를 무작위로 선택합니다 (균등 분포 보장).
3. **앵커 포인트 저장**: 선택된 좌표를 `Float32Array`로 변환하여 FBO 텍스처에 저장합니다.
4. **GPU 전송**: 텍스처 데이터로 변환하여 GPGPU 계산의 입력으로 사용합니다.
**성능 최적화 팁:**
- FBO 해상도를 파티클 수에 맞게 조정 (예: 20000개 → 256x256 = 65536 슬롯)
- 노이즈 함수는 GLSL 내장 구현 사용 (ASHIMA 또는 Stefan Gustavson의 Simplex)
- 파티클 수가 많을 경우 LOD(Level of Detail) 시스템 도입
이 시스템은 토러스에서 커스텀 GLTF 모델로의 형태 변환을 수천 개의 인터랙티브 파티클로 표현하며, 모든 계산이 GPU에서 수행되어 60fps를 유지할 수 있습니다.
전체 프롬프트를 보려면 로그인하세요
Continue with:
By logging in, you agree to our Terms of Use and Privacy Policy
사용법
이 프롬프트는 coding와 함께 사용하도록 설계되었습니다. 위의 프롬프트 내용을 복사하여 원하는 AI 도구에 붙여넣으세요.
최상의 결과를 얻으려면 자리 표시자(대괄호 또는 대문자로 표시)를 특정 요구 사항으로 사용자 지정할 수 있습니다.
토론
댓글 0개