3
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

はじめてのアドベントカレンダーAdvent Calendar 2023

Day 24

GLSLで始めるShaderコーディング入門

Last updated at Posted at 2023-12-21

今回は次のキラキラシェーダーの作り方について解説します。
GLSLコーティングの初心者の方もイメージを掴めるように心がけます。
なお、サンプルはWebでGLSLを実行できるエディタ** The Book of Shaders Editor **で確認しました。

開発アプリPR

3分作曲-musicLine-



キラキラシェーダーの構成要素

キラキラシェーダーはパターンカラーアルファの3種類の要素があります。タイル状に十字を敷き詰めたパターンをアニメーションするカラー・アルファでブレンドすることで作っています。

構成要素 イメージ
パターン
カラー
アルファ

パターンの作成

ST座標

precision mediump float;
uniform vec2 u_resolution;

void main() {
    vec2 st = gl_FragCoord.xy / u_resolution.xy; // 正規化
    gl_FragColor = vec4(st, 0., 1.); 
}

gl_FragCoordはピクセル座標で左下原点(0, 0)で右上が画面サイズ(例えば(300, 300))となります。
stgl_FragCoordを画面サイズu_resolutionで割ることで、左下原点(0, 0)で右上(1, 1)となるST座標へ変換します。

このサンプルでは座標を可視化するため、gl_FragColorの赤と緑の値にXとYの値を渡して色を表示しています。なので、値が大きくなる程色がつきます。
例えば、座標(1, 1)はRGB(1, 1, 0)となり黄色になります。

タイル座標

...
float repeat_count = 30.; // 繰り返し回数
float aspect = .8; // 縦横比
vec2 scaled_st = repeat_count * vec2(1., aspect) * st; // スケーリング 
vec2 difference_st = scaled_st + vec2(0, .3) * mod(floor(scaled_st).x, 5. ); // ずらす
vec2 tile =  fract(difference_st); // 0 ~ 1 の間を繰り返す
gl_FragColor = vec4(tile, 0., 1.); 

パターンは同じ形が繰り返し描画されるため、ST座標をタイル状に分割します。この例ではY軸方向に少しずらしてタイルの単調さを軽減しています。

scaled_stで繰り返す回数分座標空間を大きくします。なお十字は縦長のため、アスペクト比をかけています。
difference_stではX座標に応じて、Y軸方向へ座標をずらしています。modは余剰のため、この例では0 ~ 4を繰り返します。
fractは小数点のみ取得するので、0 ~ 1を繰り返すタイルパターンになります。

十字パターン

vec2 tile_center = abs(tile - .5); // 端 0.5 中心 0.
float circle = step(.6, 1. - length(tile_center)); // 円
float grid = min(1., step(.42, .5 - tile_center.x) + step(.42, .5 - tile_center.y / aspect)); // グリッド
float cross = circle * grid;
gl_FragColor = vec4(vec3(cross), 1.);

tile_centerはタイルの中心からの距離で、タイル中心が(0, 0)になるように-0.5しています。
stepは閾値で 0 か 1 に変換します。circleはタイル中心からの距離を閾値とし円に型抜きします。gridはX軸とY軸の各々で距離を閾値として格子状に型抜きします。circlegridのANDを取ることで十字ができます。

(菱形パターン)

vec2 tile_center = abs(tile - .5);    // 端 0.5 中心 0.
float tile_dist = 1. - step(.3, tile_center.x + tile_center.y);
float diamond = step(1., tile_dist);
gl_FragColor = vec4(vec3(diamond), 1.);

ここの処理を変更することで色々な図形パターンを作ることができます。


カラーの作成

vec2 tile_pos = floor(difference_st);
vec3 shift = vec3(200.0 * (random(tile_pos * 2.) + u_time * 0.2), 1.0, 1.0);
vec3 random_col = shift_col(vec3(1.0, 0.3, 0.3), shift);
gl_FragColor = vec4(random_col, 1.);

tile_posfloorでST座標を整数化することでタイル位置を算出しています。tile_posをシード値として乱数を取得することで、タイル毎に色をランダムに決定します。またu_timeで時間により色が変化します。
randomは乱数取得、shift_colは色変更の自作関数です。


highp float random(highp vec2 st){
	return fract(sin(dot(st.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

vec3 shift_col(vec3 RGB, vec3 shift) {
    vec3 RESULT = vec3(RGB);
    highp float VSU = shift.z * shift.y * cos(shift.x * 3.14159265 / 180.0);
    highp float VSW = shift.z * shift.y * sin(shift.x * 3.14159265 / 180.0);
    
    RESULT.x = (.299 * shift.z + .701 * VSU + .168 * VSW) * RGB.x
    	+ (.587 * shift.z - .587 * VSU + .330 * VSW) * RGB.y
    	+ (.114 * shift.z - .114 * VSU - .497 * VSW) * RGB.z;
    
    RESULT.y = (.299 * shift.z - .299 * VSU - .328 * VSW) * RGB.x
    	+ (.587 * shift.z + .413 * VSU + .035 * VSW) * RGB.y
    	+ (.114 * shift.z - .114 * VSU + .292 * VSW) * RGB.z;
    
    RESULT.z = (.299 * shift.z - .3 * VSU + 1.25 * VSW) * RGB.x
    	+ (.587 * shift.z - .588 * VSU - 1.05 * VSW) * RGB.y
    	+ (.114 * shift.z + .886 * VSU - .203 * VSW) * RGB.z;
    
    return (RESULT);
}

GLSLには乱数を取得する標準関数がないのでrandom関数を自作します。Shaderでの乱数の取得は一般的に計算のオーバーフローを使ったやり方で取得するみたいです。
shift_colは第一引数に、第二引数にシフト値を指定します。シフト値は(色相、彩度、明度)となっており、色相は360で一周し、彩度と明度は倍率となっています。


アルファの作成

float cycle_time = 5.; // 光る周期時間
float on_rat = .6; // 全体に対して光る割合
float PI = 3.14159265359;
highp float animation_offset = random(tile_pos); // アニメーションの開始オフセット      
float alpha = sin((animation(on_rat, cycle_time, u_time, animation_offset) - 0.25) * PI * 2.) * .5 + .5;
gl_FragColor = vec4(vec3(alpha), 1.);

sinを使うことでイーズイン・アウトのアニメーション効果をつけてループ再生します。animation_offsetを乱数にすることで、タイル毎にバラバラに光るようにしています。
animationは自作の関数です。

 highp float animation(highp float animation_ratio, highp float cycle_time, highp float time, highp float offset){
     highp float start_time = offset; // 0. ~ 1.
     highp float end_time = start_time + animation_ratio;
     highp float pos_in_cycle = mod(time, cycle_time) / cycle_time; // 0. -> 1.
     highp float pos1 = smoothstep(start_time, end_time, pos_in_cycle);
     highp float pos2 = smoothstep(start_time, end_time, pos_in_cycle + 1.); // 境界用
     return max(fract(pos1), fract(pos2));
 }

アニメーションの開始時間start_timeと終了時間end_timeは0~1+(a)に正規化しています。
時間timeと光る周期時間cycle_timeにより、pos_in_cycleサイクル中の進行位置を出しています。
アニメーション開始時間がサイクル終了よりの場合、end_timeが1以上になりアニメーションがサイクルを跨ぐ場合があります。そのパターンを考慮して、アニメーション進行位置をpos1pos2の2つを使います。


以上、今回のサンプルで取り上げたキラキラシェーダーの解説でした。
元記事ではAndroidアプリでの活用例を紹介しています!
また、ソースコード等の詳細も元記事を参照ください。


開発アプリPR

3分作曲-musicLine-


3
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?