WebGL Advent Calendar 2015の13日目の記事です。
GLSLを教わる機会があったので、力試しに炎エフェクト作ってみました(glslsandboxのものを参考にしてます)
形状がいまいちですが、炎のカラーバージョンを2つ作ってみました!
コードはこちら → GLSL Editor - 炎エフェクトDEMO
プログラムの解説
炎のエフェクトは、メタボールを横揺れ用に8個 x 縦揺れ用に8個を描画しています。
メタボール1つだけ表示するとこんな感じです。
始めのコードは以下のようなシンプルなものになります。
メタボール1つの場合
// メタボール生成
float metaball(vec2 pos, vec2 offset, float scale){
// ポジション修正
pos = pos - offset;
float len = length(pos);
float draw = 0.0;
// 円を描く範囲
if(len < scale){
draw = (1.0 - len / scale);
}
return draw;
}
// 各ピクセルの原点からの距離を計測し色として出力する
void main(){
// gl_FragCoord.xy = 0〜512 → *2.0で0〜1024 → -resoで-512〜512 → /resoで-1.0〜1.0座標
vec2 p = (gl_FragCoord.xy * 2.0 - resolution) / resolution;
float ball = 0.0;
float x = 0.0; // 後で使う
float y = 0.0; // 後で使う
float moveX = x;
float moveY = y;
float scale = 0.56;
// メタボール生成
ball += metaball(p, vec2(moveX, moveY), scale);
// blue color
gl_FragColor = vec4(vec3(ball*0.25+ball*0.1, ball*0.45+ball*0.1, ball), 1.0);
}
上記コードを修正し、メタボール8個を横揺れで動かすとこんな感じです。
それぞれのメタボールはY軸を固定、X軸は-0.3〜0.3をループ移動しています。
メタボール8個で横揺れ炎の場合
void main(){
// gl_FragCoord.xy = 0〜512 → *2.0で0〜1024 → -resoで-512〜512 → /resoで-1.0〜1.0座標
vec2 p = (gl_FragCoord.xy * 2.0 - resolution) / resolution;
float ball = 0.0;
float speed = 10.0; // 炎のスピード
float yPos = 0.8; // Y軸の調整
// 横揺れの炎8つ
for(int i = 1; i <= 8; i++){
float x = float(i) * 1.0;
float y = float(8-i) * 0.18;
float moveX = cos(x+time*speed) * 0.31;
float moveY = y - yPos;
float scale = 0.56;
// メタボール生成
ball += metaball(p, vec2(moveX, moveY), scale);
}
// blue color
gl_FragColor = vec4(vec3(ball*0.25+ball*0.1, ball*0.45+ball*0.1, ball), 1.0);
}
メタボール8個で縦揺れ炎の場合
void main(){
// gl_FragCoord.xy = 0〜512 → *2.0で0〜1024 → -resoで-512〜512 → /resoで-1.0〜1.0座標
vec2 p = (gl_FragCoord.xy * 2.0 - resolution) / resolution;
float ball = 0.0;
float speed = 10.0; // 炎のスピード
float yPos = 0.8; // Y軸の調整
// 縦揺れの炎8つ
for(int i = 1; i <= 8; i++){
float x = float(i) * 0.0;
float y = float(8-i) * 0.22;
float moveX = x;
float moveY = y - yPos + sin(float(i)+time*speed) * 0.2;
float scale = 0.4;
// メタボール生成
ball += metaball(p, vec2(moveX, moveY), scale);
}
// blue color
gl_FragColor = vec4(vec3(ball*0.25+ball*0.1, ball*0.45+ball*0.1, ball), 1.0);
}
最後に縦揺れと横揺れのコードを結合させます。
ここではballという変数に両方の描画結果を加算するだけで、描画内容がブレンドされます。
こういう部分はシェーダーって素晴らしいですね♪
縦揺れと横揺れ結合の場合
// 横揺れの炎8つ
for(int i = 1; i <= 8; i++){
float x = float(i) * 1.0;
float y = float(8-i) * 0.18;
float moveX = cos(x+time*speed) * 0.31;
float moveY = y - yPos;
// moveX = 0.0;
// moveY = 0.0;
float scale = 0.56;
// メタボール生成
ball += metaball(p, vec2(moveX, moveY), scale);
}
// 縦揺れの炎8つ
for(int i = 1; i <= 8; i++){
float x = float(i) * 0.0;
float y = float(8-i) * 0.22;
float moveX = x;
float moveY = y - yPos + sin(float(i)+time*speed) * 0.2;
// moveX = 0.0;
// moveY = 0.0;
float scale = 0.4;
// メタボール生成
ball += metaball(p, vec2(moveX, moveY), scale);
}
// blue color
gl_FragColor = vec4(vec3(ball*0.25+ball*0.1, ball*0.45+ball*0.1, ball), 1.0);
炎の動きの調整が少し難しいですが、以外と簡単にできたので興味ある方はぜひ試してみて下さい!
明日は、takahito-tejimaさんよろしくです。