風場視覺化:隨機重置

XXHolic發表於2022-03-19

引子

繪製軌跡的效果中,過一段時間就會發現,最後只剩下固定的幾條軌跡,原文中也提到了這種現象,也提供瞭解決思路,個人還是想結合原始碼再看看。

原因

隨著時間推移,有些粒子產生的偏移超過了範圍就會消失,所以需要隨機重置消失的風粒子。

解決方案

需要考慮的問題有:

  • 什麼時候重置?
  • 重置的判斷條件是什麼?
  • 重置的方式是什麼?

什麼時候重置?

繪製軌跡中,我們知道了產生的偏移是在最後更新粒子紋理資訊階段,這個時機判斷保留新的粒子狀態還是重置比較合適。

重置的判斷條件是什麼?

相關的主要邏輯如下:


const updateFrag = `
uniform float u_rand_seed;
uniform float u_drop_rate;
uniform float u_drop_rate_bump;

// pseudo-random generator
const vec3 rand_constants = vec3(12.9898, 78.233, 4375.85453);
float rand(const vec2 co) {
    float t = dot(rand_constants.xy, co);
    return fract(sin(t) * (rand_constants.z + t));
}

void main() {
  vec4 color = texture2D(u_particles, v_tex_pos);
  vec2 pos = vec2(
      color.r / 255.0 + color.b,
      color.g / 255.0 + color.a); // decode particle position from pixel RGBA

  vec2 velocity = mix(u_wind_min, u_wind_max, lookup_wind(pos));
  float speed_t = length(velocity) / length(u_wind_max);

  pos = fract(1.0 + pos + offset);
  // a random seed to use for the particle drop
  vec2 seed = (pos + v_tex_pos) * u_rand_seed;

  // drop rate is a chance a particle will restart at random position, to avoid degeneration
  float drop_rate = u_drop_rate + speed_t * u_drop_rate_bump;
  float drop = step(1.0 - drop_rate, rand(seed));

  vec2 random_pos = vec2(
      rand(seed + 1.3),
      rand(seed + 2.1));
  pos = mix(pos, random_pos, drop);
}
`
this.dropRate = 0.003; // how often the particles move to a random place
this.dropRateBump = 0.01; // drop rate increase relative to individual particle speed
// 程式碼省略
gl.uniform1f(program.u_rand_seed, Math.random());
gl.uniform1f(program.u_drop_rate, this.dropRate);
gl.uniform1f(program.u_drop_rate_bump, this.dropRateBump);

先介紹一下內建函式:

  • step(edge, x):如果 x < edge ,則返回 0.0 ,否則返回 1.0 。
vec2 seed = (pos + v_tex_pos) * u_rand_seed;

得到的偏移後的位置 pos 加上頂點位置 v_tex_pos ,乘以隨機 [0, 1) 之間的隨機數 u_rand_seed ,得到一個隨機粒子位置 seed

float drop_rate = u_drop_rate + speed_t * u_drop_rate_bump;

粒子插值百分比 speed_t 乘以自定義單個粒子流失率 u_drop_rate_bump ,再加上自定義整體流失率,得到綜合流失率 drop_rate

float drop = step(1.0 - drop_rate, rand(seed));

如果 rand(seed) 小於綜合非流失率 1.0 - drop_rate ,那麼 drop = 0 ,表示不會重置粒子,否則就會重置粒子。

重置的方式是什麼?

重置的方式就是上面的這部分:

const vec3 rand_constants = vec3(12.9898, 78.233, 4375.85453);
float rand(const vec2 co) {
    float t = dot(rand_constants.xy, co);
    return fract(sin(t) * (rand_constants.z + t));
}

vec2 seed = (pos + v_tex_pos) * u_rand_seed;
vec2 random_pos = vec2(
    rand(seed + 1.3),
    rand(seed + 2.1));

這個主要就是原文中所說生成偽隨機數。至於為什麼用這樣的計算方式,需要在數學方面下功夫。

參考資料

相關文章