簡體   English   中英

如何保持粒子之間的協調以及哪個紋理像素包含每個人的信息?

[英]How to keep coordination between particles and which texture pixel contains each one’s information?

以 4x4x4 網格為例,我有 64 個頂點(我將其稱為粒子),它們以相對於彼此的特定位置開始。 這 64 個粒子將沿 x、y 和 z 方向移動,從而失去彼此的初始位置。 然而,每個循環,新的粒子位置和速度都需要根據粒子與其原始鄰居之間的原始起始關系進行計算。

我了解到我需要為此使用紋理,因此需要使用幀緩沖區,並且現在能夠編寫兩個 3DTextures,這些觸發器提供寫入和讀取功能來執行此操作。 然而,在下一個循環中,當 gl_FragCoord 被傳遞給片段着色器時,粒子的新位置(例如可以與另一個粒子切換),我看不到任何機制,通過該機制保持粒子的紋理的原始坐標信息將寫入粒子的當前信息。 是否有一些我不明白的機制允許移動粒子將它們的數據存儲在靜態網格(3D 紋理)中,每個粒子的數據總是填充相同的坐標,所以我可以使用 texelFetch 來獲取粒子的數據,以及原始鄰居的數據? 我可以更改 gl_FragCoord,並在我想要的位置有一個像素輸出,還是一個不可更改的輸入變量?

一旦我解決了這個問題,我希望然后實現一個變換反饋來執行頂點的實際移動,而無需將紋理轉儲到 CPU 並提取位置數據並將其重新上傳到 GPU 以供下一個周期使用。

是否有關於如何使用幀緩沖區中寫入的紋理跟蹤每個粒子的原始位置、原始鄰居以及相對於這些原始鄰居的當前位置的建議?

我對你的困惑感到困惑😄

這是一個簡單的 JavaScript 粒子系統。 每個粒子從隨機位置開始並沿隨機方向移動

 'use strict'; const ctx = document.querySelector('canvas').getContext('2d') const {width, height} = ctx.canvas; const numParticles = 128; const particleParameters = []; // info that does not change let currentParticleState = []; // info that does change let nextParticleState = []; // computed from currentState for (let i = 0; i < numParticles; ++i) { particleParameters.push({ velocity: [rand(-100, 100), rand(-100, 100)], }); currentParticleState.push({ position: [rand(0, width), rand(0, height)], }); nextParticleState.push({ position: [0, 0], }); } function rand(min, max) { return Math.random() * (max - min) + min; } function euclideanModulo(n, m) { return (( n % m) + m) % m; } let then = 0; function render(now) { now *= 0.001; // convert to seconds const deltaTime = now - then; then = now; for (let i = 0; i < numParticles; ++i) { const curPos = currentParticleState[i].position; const nxtPos = nextParticleState[i].position; const data = particleParameters[i]; nxtPos[0] = euclideanModulo(curPos[0] + data.velocity[0] * deltaTime, width); nxtPos[1] = euclideanModulo(curPos[1] + data.velocity[1] * deltaTime, height); } const t = nextParticleState; nextParticleState = currentParticleState; currentParticleState = t; ctx.clearRect(0, 0, width, height); for (let i = 0; i < numParticles; ++i) { const [x, y] = currentParticleState[i].position; ctx.fillRect(x - 1, y - 1, 3, 3); } requestAnimationFrame(render); } requestAnimationFrame(render);
 canvas { border: 1px solid black; }
 <canvas></canvas>

這里的粒子系統仍然在 JavaScript 中,但運行起來更像 WebGL。 我不知道這是否會或多或少令人困惑。 重點是更新粒子位置的代碼fragmentShader器不能選擇它更新的內容。 它只是更新gl.outColor 除了gl.fragCoordgl.currentProgram.uniforms之外,它也沒有輸入。 currentParticleState 是一個包含 4 個值數組的數組,而之前它是一個具有位置屬性的對象數組。 particleParameters 也只是一個包含 4 個值數組的數組,而不是具有速度值的對象數組。 這是為了模擬這樣一個事實,即這些將是真實 WebGL 中的紋理,因此positionvelocity等任何意義都將丟失。

實際繪制粒子的代碼無關緊要。

 'use strict'; const ctx = document.querySelector('canvas').getContext('2d') const {width, height} = ctx.canvas; const numParticles = 128; const particleParameters = []; // info that does not change let currentParticleState = []; // info that does change let nextParticleState = []; // computed from currentState for (let i = 0; i < numParticles; ++i) { particleParameters.push( [rand(-100, 100), rand(-100, 100)], ); currentParticleState.push( [rand(0, width), rand(0, height)], ); nextParticleState.push( [0, 0], ); } function rand(min, max) { return Math.random() * (max - min) + min; } function euclideanModulo(n, m) { return (( n % m) + m) % m; } const gl = { fragCoord: [0, 0, 0, 0], outColor: [0, 0, 0, 0], currentProgram: null, currentFramebuffer: null, bindFramebuffer(fb) { this.currentFramebuffer = fb; }, createProgram(vs, fs) { return { vertexShader: vs, // not using fragmentShader: fs, uniforms: { }, } }, useProgram(p) { this.currentProgram = p; }, uniform(name, value) { this.currentProgram.uniforms[name] = value; }, draw(count) { for (let i = 0; i < count; ++i) { this.fragCoord[0] = i + .5; this.currentProgram.fragmentShader(); this.currentFramebuffer[i][0] = this.outColor[0]; this.currentFramebuffer[i][1] = this.outColor[1]; this.currentFramebuffer[i][2] = this.outColor[2]; this.currentFramebuffer[i][3] = this.outColor[3]; } }, }; // just to make it look more like GLSL function texelFetch(sampler, index) { return sampler[index]; } // notice this function has no inputs except // `gl.fragCoord` and `gl.currentProgram.uniforms` // and it just writes to `gl.outColor`. It doesn't // get to choose where to write. That is handled // by `gl.draw` function fragmentShader() { // to make the code below more readable const { resolution, deltaTime, currentState, particleParams, } = gl.currentProgram.uniforms; const i = Math.floor(gl.fragCoord[0]); const curPos = texelFetch(currentState, i); const data = texelFetch(particleParameters, i); gl.outColor[0] = euclideanModulo(curPos[0] + data[0] * deltaTime, resolution[0]); gl.outColor[1] = euclideanModulo(curPos[1] + data[1] * deltaTime, resolution[1]); } const prg = gl.createProgram(null, fragmentShader); let then = 0; function render(now) { now *= 0.001; // convert to seconds const deltaTime = now - then; then = now; gl.bindFramebuffer(nextParticleState); gl.useProgram(prg); gl.uniform('deltaTime', deltaTime); gl.uniform('currentState', currentParticleState); gl.uniform('particleParameters', particleParameters); gl.uniform('resolution', [width, height]); gl.draw(numParticles); const t = nextParticleState; nextParticleState = currentParticleState; currentParticleState = t; // not relavant!!! ctx.clearRect(0, 0, width, height); for (let i = 0; i < numParticles; ++i) { const [x, y] = currentParticleState[i]; ctx.fillRect(x - 1, y - 1, 3, 3); } requestAnimationFrame(render); } requestAnimationFrame(render);
 canvas { border: 1px solid black; }
 <canvas></canvas>

這是實際 WebGL 中的相同代碼

 'use strict'; function main() { const gl = document.querySelector('canvas').getContext('webgl2') if (!gl) { return alert('sorry, need webgl2'); } const ext = gl.getExtension('EXT_color_buffer_float'); if (!ext) { return alert('sorry, need EXT_color_buffer_float'); } const {width, height} = gl.canvas; const numParticles = 128; const particleParameters = []; // info that does not change let currentParticleState = []; // info that does change let nextParticleState = []; // computed from currentState for (let i = 0; i < numParticles; ++i) { particleParameters.push(rand(-100, 100), rand(-100, 100), 0, 0); currentParticleState.push(rand(0, width), rand(0, height), 0, 0); } function rand(min, max) { return Math.random() * (max - min) + min; } const particleParamsTex = twgl.createTexture(gl, { src: new Float32Array(particleParameters), internalFormat: gl.RGBA32F, width: numParticles, height: 1, minMax: gl.NEAREST, }); const currentStateTex = twgl.createTexture(gl, { src: new Float32Array(currentParticleState), internalFormat: gl.RGBA32F, width: numParticles, height: 1, minMax: gl.NEAREST, }); const nextStateTex = twgl.createTexture(gl, { internalFormat: gl.RGBA32F, width: numParticles, height: 1, minMax: gl.NEAREST, }); // create a framebuffer with 1 attachment (currentStateTex) // and record that it's numParticles wide and 1 pixel tall let currentStateFBI = twgl.createFramebufferInfo(gl, [ { attachment: currentStateTex, }, ], numParticles, 1); // create a framebuffer with 1 attachment (nextStateTex) // and record that it's numParticles wide and 1 pixel tall let nextStateFBI = twgl.createFramebufferInfo(gl, [ { attachment: nextStateTex, }, ], numParticles, 1); const particleVS = ` #version 300 es in vec4 position; void main() { gl_Position = position; } `; const particleFS = ` #version 300 es precision highp float; uniform vec2 resolution; uniform float deltaTime; uniform sampler2D particleParamsTex; uniform sampler2D currentStateTex; out vec4 outColor; vec4 euclideanModulo(vec4 n, vec4 m) { return mod(mod(n, m) + m, m); } void main() { int i = int(gl_FragCoord.x); vec4 curPos = texelFetch(currentStateTex, ivec2(i, 0), 0); vec4 velocity = texelFetch(particleParamsTex, ivec2(i, 0), 0); outColor = euclideanModulo(curPos + velocity * deltaTime, vec4(resolution, 1, 1)); } `; const drawVS = ` #version 300 es uniform sampler2D currentStateTex; uniform vec2 resolution; void main() { gl_PointSize = 3.0; // we calculated pos in pixel coords vec4 pos = texelFetch(currentStateTex, ivec2(gl_VertexID, 0), 0); gl_Position = vec4( pos.xy / resolution * 2. - 1., // convert to clip space 0, 1); } `; const drawFS = ` #version 300 es precision mediump float; out vec4 outColor; void main() { outColor = vec4(0, 0, 0, 1); } `; // compile shaders, link program, look up locations. const particleProgramInfo = twgl.createProgramInfo(gl, [particleVS, particleFS]); const drawProgramInfo = twgl.createProgramInfo(gl, [drawVS, drawFS]); // create a -1 to +1 quad vertices and put in a buffer. const quadBufferInfo = twgl.primitives.createXYQuadBufferInfo(gl, 2); let then = 0; function render(now) { now *= 0.001; // convert to seconds const deltaTime = now - then; then = now; // bind the framebuffer and set the viewport to match twgl.bindFramebufferInfo(gl, nextStateFBI); gl.useProgram(particleProgramInfo.program); twgl.setBuffersAndAttributes(gl, particleProgramInfo, quadBufferInfo); twgl.setUniformsAndBindTextures(particleProgramInfo, { resolution: [width, height], deltaTime: deltaTime, currentStateTex: currentStateFBI.attachments[0], particleParamsTex, }); // call drawArrays or drawBuffers twgl.drawBufferInfo(gl, quadBufferInfo); const t = nextStateFBI; nextStateFBI = currentStateFBI; currentStateFBI = t; // bind the canvas and set the viewport to match twgl.bindFramebufferInfo(gl, null); gl.useProgram(drawProgramInfo.program); twgl.setUniforms(drawProgramInfo, { resolution: [width, height], currentStateTex: currentStateFBI.attachments[0], }); gl.drawArrays(gl.POINTS, 0, numParticles); requestAnimationFrame(render); } requestAnimationFrame(render); } main();
 canvas { border: 1px solid black; }
 <canvas></canvas> <script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM