💡

Processingでシェーダーを使う(基礎編)

2024/01/03に公開

Processingのシェーダーに関する情報が少ないように思うので、初心者の方に向けて基礎的な使い方を出来るだけ丁寧にまとめていきたいと思います。

そもそもシェーダーとは?

シェーダーとは、元々は名前の通り陰影をつけるために使われていたプログラムのことです。
ただ、現在では陰影に限らず背景の描画や画面のエフェクト、さらには計算にまで使われるものとなっています。
サンプル画像
普段使うプログラミング言語にJavaやC++、Rustがあるように、シェーダーにもGLSL、HLSLなどがあります。
その中でもProcessingで利用するのはGLSLです。

シェーダーの仕組み

シェーダーは、普段スケッチを実行するCPUではなくGPUというハードウェアで実行されるプログラムです。

GPUというのは、「CPUは数人の天才の集まりでGPUは大勢の一般人の集まり」と例えられるように、シェーダーを描画対象のピクセル全てに対して一気に実行するハードウェアです。

GPUは大量のピクセルを並列で描画するので、シェーダー内では基本的に処理しているものとは別のピクセルの情報を取得することはできません。

開発環境

  • Windows10
  • Processing 4.3
  • Visual Studio Code (拡張機能:WebGL GLSL Editor)

シェーダーを使う

Processing側

Processing側のソースコードは以下のように書きます

PShader shader;

void setup(){
  //P2Dにしてシェーダーを有効化する
  size(1280,720,P2D);
  //シェーダーの読み込み
  shader=loadShader("shader.glsl");
}

void draw(){
  background(0);
  //シェーダーに変数を渡す
  //"r"は"resolution(解像度)"の頭文字
  shader.set("r",(float)width,(float)height);
  //シェーダーを画面全体に適用
  filter(shader);
}

要点を順番に説明すると、

  1. size()の最後にP2Dを追加してシェーダーを有効化
  2. dataフォルダ内にあるシェーダーを読み込み
  3. シェーダー内の、後で説明するuniform変数に変数を渡す
  4. 画面全体に対してシェーダーを実行

という流れになります。
Processing側のソースコードを書き終えたら一旦保存しましょう。

シェーダー側

シェーダーを書く前に、まずは先ほど保存したスケッチのフォルダの中にdataという名前のフォルダを作り、その中にshader.glslを作りましょう。
シェーダーは以下の通りです

shader.glsl
//uniform変数を宣言
uniform vec2 r;

//Processingでいうdraw関数
void main(){
  //処理しているピクセルの色を決定
  gl_FragColor=vec4(gl_FragCoord.xy/r,0.0,1.0);
}

どうでしょう。

なにやら見慣れない表現が多いかと思います。
ただ、実際はそれほど難しい処理ではないので順番に見ていきましょう。

uniform変数

シェーダーを使うとき、例えば

  • 何色の光が当たってる?
  • 光源はどこにある?

などの情報が必要となることがあります。
そこで、CPU側から変数を渡すことのできるuniform変数を使います。

使い方は簡単で、

CPU側(渡す側)
[変数を渡したいシェーダー].set("[変数名]",[要素]);
GPU(受け取る側)
uniform [変数の型] [変数名];

と書けば後はProcessingが良い感じに変数を渡してくれます。

注意する点としては、渡したい変数の種類に応じてset()内の[要素]の部分を変更する必要がある点です。
今回の型はvec2(2つのfloat型の変数を持つベクトル)なので、[要素]の部分には画面の幅と高さをfloat型にキャストしたものを代入しています。

ピクセルの位置の取得

シェーダーにおいて、一番重要といっても差し支えのないものは処理しているピクセルの座標です。これが無いと始まりません。

GLSLではどのバージョンでも取得する方法は決まっていて、gl_FragCoord(vec4型)という変数を参照すれば簡単に取得できます。
ただ、gl_FragCoordはピクセルの位置をそのまま渡してきて都合が悪いことが多いので、多くの場合は描画する範囲の大きさで割って0~1の範囲にします。

ベクトルの計算(基本)

GPUは複数の値を一気に処理することができるので、シェーダーではいちいちベクトルを要素ごとに分解せずそのまま計算します。

シェーダーにはベクトルに対する四則演算を利用することができ、掛け算は数学のように内積、外積ではなく要素ごとの積を計算します。いわば配列aと配列bの各要素の計算をまとめてしているに過ぎません。

計算の例
//変数の初期化
vec2 a=vec2(2.0,3.0);
vec2 b=vec2(5.0,2.0);

vec2 c=a*b;//cはvec2(10.0,6.0)となる

GLSLにおいては、vec2/vec3/vec4が使えます。

ベクトルの計算(発展)

GLSLにおいては、「vec4の変数があるけどそのうち2つの要素しか使わないよ」みたいな状況がよく発生します。

そんなニーズに応えるためにGLSLにはswizzle演算子というものがあり、

swizzling
vec4 color=vec4(1.0,0.5,0.25,1.0);
//swizzle演算子を使用してx(1個目)とy(2個目)の要素を持つvec2を構築
vec2 output=color.xy;

という風に使います。

swizzle演算子で使うxyなどは全部書くとx,y,z,wの4つあり、それぞれ1,2,3,4番目の要素に対応しています。

ちなみに、x,y,z,w以外にもr,g,b,a/s,t,p,qという書き方がありますが、対応している番号はどれも同じです。

色の出力

最後に色の出力について解説します。

色の出力は簡単で、vec4型の変数で0~1の小数の範囲でr,g,b,aを指定し、それをgl_FragColorに渡すだけです。

色の決定
gl_FragColor=vec4(0.0,0.5,1.0,1.0);//fill(0,128,255,255);と同じ

簡単ですね。

今回のソースコードの解説

ソースコードを実行するとこのような画像が出力されます。(実際は綺麗なグラデーション)
実行結果
今回はgl_FragCoordrで割ったものをgl_FragColorの赤と緑の要素に代入しているので、右に行く(=ピクセルのx座標が大きくなる)ほど赤く、上に行く(=ピクセルのy座標が大きくなる)ほど緑になります。

つまりは、GLSLにおける原点は左下ということですね。

しれっと出てきましたが超重要なので覚えておきましょう。

最後に

シェーダーを使うのは今回が初めてという方にはなかなか重い内容だったかもしれませんが、この内容さえ理解できればきっと楽しいシェーダーライフが待っています。

なので分からない部分があれば質問したり、ぜひ他の記事や「WebGL」なども検索して学んでみて下さい。

以上、よきGLSLライフを!

Discussion