+ Covers physics, vortices, bloom, and shaders at implementation level.
- trailLength is unused, and frame-dependent decay is insufficiently explained.
a VFX Artist focused on bioluminescent fluid simulations and particle-based environmental effects.
| Category | Development › Coding |
|---|---|
| Tags | DraftingAnalyzingDeveloperCode |
I want you to act as a VFX Artist focused on bioluminescent fluid simulations and particle-based environmental effects. Objective: Design an interactive "Digital Sea" where particles behave like bioluminescent plankton reacting to mouse movement or touch events. Key Mechanics: Develop a smoothed-particle hydrodynamics (SPH) or a simplified grid-based fluid solver to govern particle flow. Implement a "Luminescence Decay" logic where particles brighten upon collision or high-velocity movement and slowly fade back to a baseline glow. Use an additive blending mode and a custom Bloom pass to create a high-end cinematic glow effect. Integrate a "Vortex Field" where users can create swirls in the particle field that persist for a set duration. Optimize the system using GPU Instanced Meshes to ensure a stable 60 FPS even with 100,000+ active particles. Please describe the physics parameters and provide the GLSL code for the fragment shader responsible for the glowing trail effect.
This prompt assigns a VFX Artist role to design an interactive Digital Sea. It includes SPH or grid fluid logic, luminescence decay, vortex fields, and optimization for 100,000+ particles.
ChatGPT is the most complete and implementable. Gemini is visually rich but has numeric and shader issues, while [C] is missing.
+ Covers physics, vortices, bloom, and shaders at implementation level.
- trailLength is unused, and frame-dependent decay is insufficiently explained.
+ Connects the GPU pipeline to visual direction with concrete values.
- Decay timing and viscosity units are inaccurate, and the trail is symmetric.
| Criterion | ChatGPT | Gemini | Leader |
|---|---|---|---|
| Instruction following | 10 | 8 | ChatGPT +25% |
| Accuracy | 8 | 6 | ChatGPT +33% |
| Specificity | 9 | 9 | Tie |
| Structure | 9 | 9 | Tie |
| Right length | 8 | 7 | ChatGPT +14% |
Scored 1–10 by gpt-5.6-sol with model names hidden (2026-09-25). This is an AI review, not a measurement.
We gave three models the same input and copied their answers unedited. Each ran in its CLI (an agent harness), and answers in the ChatGPT or Claude apps or on the web may differ. Outputs are in Korean.
100,000개 이상의 입자를 안정적으로 처리하려면 입자마다 SPH 이웃 탐색을 수행하기보다, 저해상도 Eulerian 속도 필드 + GPU 인스턴싱 입자 조합을 권장합니다.
구조는 다음과 같습니다.
const fluidParams = {
particleCount: 100000,
// 시뮬레이션 공간
gridWidth: 256,
gridHeight: 256,
worldWidth: 2.0,
worldHeight: 2.0,
// 유체
viscosity: 0.012,
velocityDamping: 0.985,
pressureStrength: 0.18,
curlStrength: 0.65,
advectionStrength: 1.0,
// 입력 반응
mouseForce: 2.8,
mouseRadius: 0.12,
inputSmoothing: 0.82,
// 입자
particleDrag: 0.985,
maxVelocity: 1.8,
particleSize: 0.006,
// 발광
baseLuminescence: 0.08,
collisionLuminescence: 1.4,
velocityLuminescence: 0.55,
luminescenceDecay: 0.92,
luminescenceResponse: 0.35,
// 와류
vortexStrength: 3.5,
vortexRadius: 0.22,
vortexLifetime: 2.8,
vortexDecay: 0.94,
// Bloom
bloomThreshold: 0.7,
bloomStrength: 1.25,
bloomRadius: 4.0
};
입자의 발광값은 다음과 같이 갱신할 수 있습니다.
luminescence =
max(
baseLuminescence,
luminescence * luminescenceDecay +
collisionAmount * collisionLuminescence +
velocityLength * velocityLuminescence
);
충돌은 화면 경계, 장애물 필드, 또는 주변 입자의 속도 차이를 기반으로 단순화할 수 있습니다.
float collisionAmount = clamp(
length(velocity - sampledFluidVelocity) * 0.5,
0.0,
1.0
);
사용자가 드래그하면 입력 위치에 다음 힘을 추가합니다.
vec2 offset = particlePosition - vortexPosition;
float distanceToVortex = length(offset);
float influence = 1.0 - smoothstep(0.0, vortexRadius, distanceToVortex);
vec2 tangent = vec2(-offset.y, offset.x);
velocity += normalize(tangent) *
vortexStrength *
influence *
deltaTime;
와류는 위치, 반경, 강도, 남은 시간을 가진 이벤트로 저장합니다.
{
position: vec2,
radius: 0.22,
strength: 3.5,
remaining: 2.8
}
매 프레임 다음과 같이 약화시킵니다.
vortex.strength *= 0.94;
vortex.remaining -= deltaTime;
아래 셰이더는 인스턴스화된 쿼드 하나를 입자 하나로 사용하는 방식입니다. vUv는 쿼드 좌표이며, vVelocity, vLuminescence, vLife는 인스턴스 버텍스 셰이더에서 전달됩니다.
#version 300 es
precision highp float;
in vec2 vUv;
in vec2 vVelocity;
in float vLuminescence;
in float vLife;
uniform vec3 uColor;
uniform float uTime;
uniform float uBloomThreshold;
layout(location = 0) out vec4 outColor;
float gaussian(float x, float sigma) {
return exp(-(x * x) / (2.0 * sigma * sigma));
}
void main() {
// 중심을 기준으로 한 쿼드 좌표
vec2 p = vUv * 2.0 - 1.0;
float speed = length(vVelocity);
vec2 direction = speed > 0.001
? normalize(vVelocity)
: vec2(1.0, 0.0);
vec2 normal = vec2(-direction.y, direction.x);
// 속도가 빠를수록 잔상이 길어짐
float trailLength = mix(0.35, 1.8, smoothstep(0.0, 1.5, speed));
float along = dot(p, direction);
float across = dot(p, normal);
// 진행 방향 뒤쪽으로 늘어진 꼬리
float trailStart = -0.95;
float trailEnd = 0.75;
float trailMask = smoothstep(trailStart - 0.15, trailStart, along) *
(1.0 - smoothstep(trailEnd, trailEnd + 0.15, along));
// 중앙은 밝고 외곽은 부드럽게 감쇠
float width = mix(0.34, 0.12, smoothstep(0.0, 1.5, speed));
float radialGlow = gaussian(across, width);
// 입자 머리 부분의 강한 핵
float head = exp(-length(p * vec2(1.0, 1.8)) * 5.0);
// 뒤로 갈수록 밝기가 약해지는 잔상
float tailFade = 1.0 - smoothstep(-0.95, 0.55, along);
float glow = trailMask *
radialGlow *
mix(0.45, 1.0, tailFade);
glow += head * 1.4;
// 생명주기와 발광량 반영
float intensity = glow *
max(vLuminescence, 0.05) *
mix(0.65, 1.0, vLife);
// 약간의 유기적 깜빡임
float flicker = 0.94 +
0.06 * sin(uTime * 8.0 + vLife * 23.0);
intensity *= flicker;
// HDR 값으로 출력해야 Bloom이 충분히 발생함
vec3 emissive = uColor * intensity * 3.5;
// 아주 약한 투명도 가장자리
float alpha = clamp(glow * vLuminescence * 1.5, 0.0, 1.0);
if (alpha < 0.01) {
discard;
}
outColor = vec4(emissive, alpha);
}
렌더 상태는 다음과 같이 설정합니다.
material.blending = THREE.AdditiveBlending;
material.transparent = true;
material.depthWrite = false;
material.toneMapped = false;
Bloom은 한 번에 강하게 블러하기보다 다음 단계가 안정적입니다.
HDR Scene
↓
Brightness Threshold
↓
Half Resolution Blur
↓
Quarter Resolution Blur
↓
Additive Composite
↓
Tone Mapping
Threshold fragment shader의 핵심은 다음과 같습니다.
vec3 color = texture(uScene, vUv).rgb;
float brightness = max(max(color.r, color.g), color.b);
float mask = smoothstep(
uThreshold,
uThreshold + 0.2,
brightness
);
outColor = vec4(color * mask, 1.0);
최종 합성:
vec3 scene = texture(uScene, vUv).rgb;
vec3 bloom = texture(uBloom, vUv).rgb;
vec3 finalColor = scene + bloom * uBloomStrength;
outColor = vec4(finalColor, 1.0);
RGBA32F 또는 적절한 정밀도의 텍스처에 저장InstancedMesh 또는 raw instanced buffer 사용depthWrite = false로 투명 입자 깊이 쓰기 비용 제거이 구성은 실제 SPH보다 근사도가 낮지만, 100,000~500,000개 입자 규모에서 인터랙티브한 생물발광 바다와 안정적인 성능을 얻기에 적합합니다.
| 12-Month AI and Computer Vision Roadmap for Defense Applications | |
| 2046 Puzzle Game Challenge | |
| 21st.dev component prompt | |
| 3D FACTORY | |
| 3D FPS Game |