Mensimulasikan kehidupan di browser: Membuat sistem partikel hidup untuk situs web UntilLabs – Beragampengetahuan
Hingga laboratorium ini mengajukan salah satu pertanyaan paling signifikan dalam layanan kesehatan modern: kemungkinan menyelamatkan nyawa. Jadi ketika kami di baseline.studio mulai mendesain beranda mereka, kami tidak menginginkan animasi abstrak, kami menginginkan sesuatu yang terasa nyata, sesuatu yang mencerminkan sains dalam inti karya mereka. Idenya sederhana namun ambisius: mengambil foto orang sungguhan dan merekonstruksinya menjadi sistem partikel hidup—sebuah pemandangan digital yang dibentuk oleh data nyata, pergerakan alami, dan perilaku yang didorong oleh fisika. Sebuah sistem yang terasa hidup karena dibangun dari kehidupan itu sendiri.
Inilah cara kami mencapai tujuan ini.
Mari kita uraikan prosesnya:
Contents
1. Menanam piksel pertama: bidang partikel sederhana
Sebelum realisme bisa eksis, ia memerlukan sebuah panggung. Tempat di mana ribuan partikel dapat hidup, bergerak, dan dimanipulasi secara efisien.
Hal ini menimbulkan pertanyaan mendasar:
Bagaimana cara merender puluhan ribu titik independen pada frame rate tinggi?
Untuk mencapai tujuan ini, kami mendirikan dua landasan:
- satu Sistem partikel yang dapat diperluas menggunakan
GL_POINTS - satu Pipa rendering modern Dibangun di atas FBO dan QuadShader layar penuh
Bersama-sama mereka membentuk kanvas fleksibel untuk semua efek masa depan.
Bidang partikel yang sederhana dan dapat diskalakan
Kami menghasilkan 60.000 partikel Di dalam bola menggunakan koordinat bola yang sesuai. Ini memberi kita:
- distribusi volume alami
- Kepadatan yang cukup untuk mewakili gambar resolusi tinggi selanjutnya
- Pertahankan 60 FPS yang stabil.
const geo = new THREE.BufferGeometry();
const positions = new Float32Array(count * 3);
const scales = new Float32Array(count);
const randomness = new Float32Array(count * 3);
for (let i = 0; i < count; i++) {
const i3 = i * 3;
// Uniform spherical distribution
const theta = Math.random() * Math.PI * 2.0;
const phi = Math.acos(2.0 * Math.random() - 1.0);
const r = radius * Math.cbrt(Math.random());
positions[i3 + 0] = r * Math.sin(phi) * Math.cos(theta);
positions[i3 + 1] = r * Math.sin(phi) * Math.sin(theta);
positions[i3 + 2] = r * Math.cos(phi);
scales[i] = Math.random() * 0.5 + 0.5;
randomness[i3 + 0] = Math.random();
randomness[i3 + 1] = Math.random();
randomness[i3 + 2] = Math.random();
}
geo.setAttribute("position", new THREE.BufferAttribute(positions, 3));
geo.setAttribute("aScale", new THREE.BufferAttribute(scales, 1));
geo.setAttribute("aRandomness", new THREE.BufferAttribute(randomness, 3));
Merender dengan GL_POINTS + shader khusus
GL_POINTS memungkinkan kita menggambar setiap partikel hasil imbangsangat cocok untuk skala ini.
Vertex Shader – gerakan yang digerakkan oleh GPU
uniform float uTime;
attribute float aScale;
attribute vec3 aRandomness;
varying vec3 vColor;
void main() {
vec4 modelPosition = modelMatrix * vec4(position, 1.0);
// GPU animation using per-particle randomness
modelPosition.xyz += vec3(
sin(uTime * 0.5 + aRandomness.x * 10.0) * aRandomness.x * 0.3,
cos(uTime * 0.3 + aRandomness.y * 10.0) * aRandomness.y * 0.3,
sin(uTime * 0.4 + aRandomness.z * 10.0) * aRandomness.z * 0.2
);
vec4 viewPosition = viewMatrix * modelPosition;
gl_Position = projectionMatrix * viewPosition;
gl_PointSize = uSize * aScale * (1.0 / -viewPosition.z);
vColor = vec3(1.0);
}
varying vec3 vColor;
void main() {
float d = length(gl_PointCoord - 0.5);
float alpha = pow(1.0 - smoothstep(0.0, 0.5, d), 1.5);
gl_FragColor = vec4(vColor, alpha);
}
Kami merender partikel menjadi FBO di luar layar sehingga kami dapat melihat keseluruhan pemandangan sebagai tekstur. Hal ini memungkinkan kami menerapkan gradasi warna, efek, dan pasca-pemrosesan tanpa menyentuh shader partikel, menjaga sistem tetap fleksibel dan mudah untuk diulang.
Tiga komponen bekerja sama:
- Buat portal: Pisahkan adegan 3D ke dalam TIGA.Adegannya sendiri
- FBO (Objek Framebuffer): Menangkap pemandangan sebagai tekstur
- Shader kuarter:Render quad layar penuh dengan pasca-pemrosesan
// Create isolated scene
const [contentScene] = useState(() => {
const scene = new THREE.Scene();
scene.background = new THREE.Color("#050505");
return scene;
});
return (
<>
{/* 3D content renders to contentScene, not the main scene */}
{createPortal(children, contentScene)}
{/* Post-processing renders to main scene */}
<QuadShader program={postMaterial} renderTarget={null} />
</>
);
Menggunakan useFBO @react-two/drei, kami membuat target render yang cocok dengan layar:
const sceneFBO = useFBO(fboWidth, fboHeight, {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBAFormat,
type: THREE.HalfFloatType, // 16-bit for HDR headroom
});
useFrame((state, delta) => {
const gl = state.gl;
// Step 1: Render 3D scene to FBO
gl.setRenderTarget(sceneFBO);
gl.clear();
gl.render(contentScene, camera);
gl.setRenderTarget(null);
// Step 2: Feed FBO texture to post-processing
postUniforms.uTexture.value = sceneFBO.texture;
// Step 3: QuadShader renders to screen (handled by QuadShader component)
}, -1); // Priority -1 runs BEFORE QuadShader's priority 1
2. Alam itu fraktal
Meminjam gerak alami: Gerak Brown
Sekarang sistem partikel sudah ada, saatnya membuatnya berperilaku seperti aslinya. Di alam, molekul tidak bergerak dalam garis lurus atau mengikuti gaya tunggal—gerakannya timbul dari lapisan keacakan yang tumpang tindih. Di sinilah gerak fraktal Brown berperan.
Dengan menggunakan fBM dalam sistem partikel, kami tidak hanya menganimasikan titik di layar; kami juga menganimasikannya. Kami meminjam logika yang sama untuk membentuknya gerak molekuler.
float random(vec2 st) {
return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
}
// 2D Value Noise - Based on Morgan McGuire @morgan3d
//
float noise(vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
// Four corners of the tile
float a = random(i);
float b = random(i + vec2(1.0, 0.0));
float c = random(i + vec2(0.0, 1.0));
float d = random(i + vec2(1.0, 1.0));
// Smooth interpolation
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(a, b, u.x) +
(c - a) * u.y * (1.0 - u.x) +
(d - b) * u.x * u.y;
}
// Fractal Brownian Motion - layered noise for natural variation
float fbm(vec2 st, int octaves) {
float value = 0.0;
float amplitude = 0.5;
vec2 shift = vec2(100.0);
// Rotation matrix to reduce axial bias
mat2 rot = mat2(cos(0.5), sin(0.5), -sin(0.5), cos(0.5));
for (int i = 0; i < 6; i++) {
if (i >= octaves) break;
value += amplitude * noise(st);
st = rot * st * 2.0 + shift;
amplitude *= 0.5;
}
return value;
}
// Curl Noise
vec2 curlNoise(vec2 st, float time) {
float eps = 0.01;
// Sample FBM at offset positions
float n1 = fbm(st + vec2(eps, 0.0) + time * 0.1, 4);
float n2 = fbm(st + vec2(-eps, 0.0) + time * 0.1, 4);
float n3 = fbm(st + vec2(0.0, eps) + time * 0.1, 4);
float n4 = fbm(st + vec2(0.0, -eps) + time * 0.1, 4);
// Calculate curl (perpendicular to gradient)
float dx = (n1 - n2) / (2.0 * eps);
float dy = (n3 - n4) / (2.0 * eps);
return vec2(dy, -dx);
}
3. Tantangan Besar: Dari Realitas ke Data

Setelah memecahkan masalah gerak, langkah selanjutnya adalah memberikan partikel sesuatu yang bermakna untuk direpresentasikan:
Foto asli, diubah menjadi bidang titik.
Dari foto → titik cloud → JSON
Dengan alat cloud titik 3D apa pun, kami:
- Mengambil gambar langsung/model 3D resolusi tinggi
- dihasilkan a titik awan
- Ekspor setiap piksel/titik ke JSON:
Ini berhasil, tetapi menghasilkan 20 MBJSON ——Terlalu berat.
Solusi: Tekstur sebagai Data
Daripada mengirimkan JSON, kami menyimpan data partikel di dalam tekstur. Dengan menggunakan alat internal, kami dapat mengurangi 20MB JSON menjadi:
| tekstur | Tujuan | pengkodean |
|---|---|---|
position_h |
Posisi (posisi tinggi) | RGB = XYZ high bytes |
position_l |
Posisi (posisi rendah) | RGB = XYZ low bytes |
color |
warna | RGB = linear RGB |
density |
Kepadatan per partikel | R = density |
File metadata kecil menjelaskan tata letaknya:
{
"width": 256,
"height": 256,
"particleCount": 65536,
"bounds": {
"min": [-75.37, 0.0, -49.99],
"max": [75.37, 0.65, 49.99]
},
"precision": "16-bit (split across high/low textures)"
}
Semua file digabungkan? ~604 KB — Pengurangan yang sangat besar.


Sekarang kita dapat memuat gambar-gambar ini dalam kode dan menggunakan shader vertex dan fragmen untuk mewakili model/gambar di layar. Kami mengirimkannya ke vertex shader sebagai seragam, memuat dan menggabungkannya.
//... previous code
// === 16-BIT POSITION RECONSTRUCTION ===
// Sample both high and low byte position textures
vec3 sampledPositionHigh = texture2D(uParticlesPositionHigh, aParticleUv).xyz;
vec3 sampledPositionLow = texture2D(uParticlesPositionLow, aParticleUv).xyz;
// Convert normalized RGB values (0-1) back to byte values (0-255)
float colorRange = uTextureSize - 1.0;
vec3 highBytes = sampledPositionHigh * colorRange;
vec3 lowBytes = sampledPositionLow * colorRange;
// Reconstruct 16-bit values: (high * 256) + low for each XYZ channel
vec3 position16bit = vec3(
(highBytes.x * uTextureSize) + lowBytes.x,
(highBytes.y * uTextureSize) + lowBytes.y,
(highBytes.z * uTextureSize) + lowBytes.z
);
// Normalize 16-bit values to 0-1 range
vec3 normalizedPosition = position16bit / uParticleCount;
// Remap to world coordinates
vec3 particlePosition = remapPosition(normalizedPosition);
// Sample color from texture
vec3 sampledColor = texture2D(uParticlesColors, aParticleUv).rgb;
vColor = sampledColor;
//...etc
Gabungkan semuanya, tambahkan beberapa penyesuaian untuk mengontrol setiap parameter titik, dan voila!
Anda dapat menonton demo langsung di sini.
4. Gunakan shader untuk menyesuaikan partikel
Berkat implementasi sebelumnya yang menggunakan target render dan FBO, kita dapat dengan mudah menambahkan target render lain untuk efek pasca-pemrosesan. Kami juga menambahkan LUT (tabel pencarian) untuk konversi warna, memungkinkan desainer menukar tekstur LUT sesuai kebutuhan – perubahan diterapkan langsung ke hasil akhir.
Kehidupan sekarang disimpan dan ditampilkan di web. Gambaran lengkap menjadi satu: foto asli dipecah menjadi data, direkonstruksi secara fisik, dianimasikan menggunakan noise berlapis, dan dikirimkan melalui jalur rendering yang dirancang agar cepat, fleksibel, dan konsisten secara visual. Dari bidang partikel hingga tekstur data, gerakan alami, dan arahan seni berbasis LUT, setiap langkah memenuhi tujuan yang sama yang kami tetapkan di awal: membuat pengalaman terasa hidup.
rencana pengembangan website
metode pengembangan website
jelaskan beberapa rencana untuk pengembangan website, proses pengembangan website, kekuatan dan kelemahan bisnis pengembangan website
, jasa pengembangan website, tahap pengembangan website, biaya pengembangan website
#Mensimulasikan #kehidupan #browser #Membuat #sistem #partikel #hidup #untuk #situs #web #UntilLabs
