By Dave Pagurek, Austin Lee Slominski, Adam Ferriss
現代のコンピューターには、グラフィックス処理ユニット(GPU)と呼ばれる特別なハードウェアが搭載されています。シェーダーはGPU上で動作する特殊なプログラムで、驚くべきことを実現できます。GPUを活用して多くのピクセルを同時に並列処理することで、高速で特にコンピューターグラフィックスの特定のタスク(ノイズの生成、ぼかしなどのフィルターの適用、ポリゴンのシェーディングなど)に適しています。
シェーダープログラミングは最初は難しく感じるかもしれません。p5.jsの2D描画とは異なるアプローチが必要です。このチュートリアルでは、シェーダープログラミングの基本を概説し、他のリソースを紹介します。
セットアップ
ブラウザでGPUをプログラムする方法は、WebGLと呼ばれるAPIを使用することです。p5.jsはシェーダーを扱うのに優れたツールです。多くのWebGLのボイラープレートセットアップを処理してくれるので、シェーダーコード自体に集中できるからです。シェーダーを始める前に、p5.jsのキャンバスをWebGLモードで設定する必要があります。これはcreateCanvas()
の3番目のパラメーターにWEBGL
定数を追加することで行います。
...
function setup() {
createCanvas(200, 200, WEBGL);
}
...
シェーダープログラムは、頂点シェーダーとフラグメントシェーダーの2つの部分で構成されています。頂点シェーダーは、ジオメトリの各頂点に対して1回実行されるプログラムで、画面上のどこに描画されるかを決定します。フラグメントシェーダーは、そのジオメトリの各ピクセルに対して1回実行されるプログラムで、その色を決定します。
元の形状 | カスタム頂点シェーダーは形状内の頂点の位置を調整できます | カスタムフラグメントシェーダーは形状内の色を調整できます |
これらはそれぞれ別のファイルに保存され、loadShader()
関数を使用してp5.jsにロードされます。シェーダーがロードされると、setup()
またはdraw()
内で使用できます。以下の例は、p5.js内で基本的なシェーダーをセットアップする方法を示しています:
let myShader;
function preload() {
// 各シェーダーファイルをロードします(心配しないでください、これらについては後で説明します!)
myShader = loadShader('shader.vert', 'shader.frag');
}
function setup() {
// キャンバスはWEBGLモードで作成する必要があります
createCanvas(windowWidth, windowHeight, WEBGL);
}
function draw() {
// shader()はアクティブなシェーダーを設定し、次に描画されるものに適用されます
shader(myShader);
// シェーダーをキャンバス全体を覆う長方形に適用します
plane(width, height);
}
また、createShader()
という追加の関数もあり、これを使用してスケッチ内で定義された文字列から直接シェーダーをロードできます。
シェーダーの記述
次に、loadShader()
で参照した頂点シェーダーとフラグメントシェーダーのファイルの中身を見てみましょう。
シェーディング言語(GLSL)
シェーダーファイルはグラフィックスライブラリシェーディング言語、つまりGLSL(OpenGL 2.0とGLSL ES 1.00に基づく)で書かれており、私たちが慣れ親しんでいるものとは非常に異なる構文と構造を持っています。GLSLはCに似た構文を持っており、JavaScriptには存在しない概念がいくつか含まれています。
まず、シェーディング言語は型に関してはるかに厳密です。作成する各変数には、それが格納しているデータの種類をラベル付けする必要があります。以下は一般的な型のリストです:
vec2(x,y) // 2つのfloatからなるベクトル
vec3(x,y,z) // 3つのfloatからなるベクトル(r,g,bでもよい)
vec4(x,y,z,w) // 4つのfloatからなるベクトル(r,g,b,aでもよい)
float // 小数点を持つ数値
int // 小数点のない整数
sampler2D // テクスチャへの参照
mat2 // 2x2行列
mat3 // 3x3行列
mat4 // 4x4行列
bool // trueまたはfalse
一般的に、シェーディング言語はJavaScriptよりもはるかに厳密です。セミコロンの欠落は許されず、エラーメッセージが表示されます。floatや整数など、異なる種類の数値を互換的に使用することはできません。また、小数点なしで定義されたfloatについても警告が出るため、0.0
や1.0
のような数値をよく目にすることになります。
GLSLで異なる点をいくつか紹介します:
Javascript | GLSL | |
---|---|---|
すべての変数に型が必要です。 |
|
|
関数はパラメーターの型と戻り値の型を宣言する必要があります。 |
|
|
整数とfloatの間の変換は自分で行う必要があります。 |
|
|
GLSLのループは定数値で停止する必要があります。条件付きで終了したい場合は、 |
|
|
多くの制約がある一方で、GLSLの方が扱いやすい面もあります!ベクトルを使用する際、GLSLには多くの便利なショートカットが含まれています:
|
|
すべての値が同じベクトルを作成したい場合、同じ値を繰り返し指定する必要はなく、1回だけ指定すれば十分です。 |
|
「スウィズリング」と呼ばれる方法を使用して、大きなベクトルから小さなベクトルを取得できます。これは、 |
|
頂点シェーダー
以下は、p5.jsによって提供される変換とカメラの視点を適用する簡単な頂点シェーダーです:
| シェーダーは |
| シェーダーの属性には、頂点ごとに変化する値が含まれており、p5.jsはこれを使用して各頂点の位置などの情報を共有します。このシェーダーの属性は |
| シェーダーのユニフォームは、描画される形状全体で一定の値です。このシェーダーの各ユニフォームは |
| すべての頂点シェーダーには |
これがまだ十分に理解できなくても心配しないでください。頂点シェーダーは重要な役割を果たしますが、多くの場合、フラグメントシェーダーで作成したものをジオメトリ上に正しく表示するだけの責任があります。おそらく、多くのプロジェクトで同じ頂点シェーダーを再利用することになるでしょう。以下は、頂点ごとの色やテクスチャ座標などの情報も扱う標準的な頂点シェーダーです。
precision highp float;
attribute vec3 aPosition;
attribute vec2 aTexCoord;
attribute vec4 aVertexColor;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
varying vec2 vTexCoord;
varying vec4 vVertexColor;
void main() {
// カメラの変換を適用
vec4 viewModelPosition = uModelViewMatrix * vec4(aPosition, 1.0);
// WebGLに頂点の位置を伝える
gl_Position = uProjectionMatrix * viewModelPosition;
// データをフラグメントシェーダーに渡す
vTexCoord = aTexCoord;
vVertexColor = aVertexColor;
}
フラグメントシェーダー
フラグメントシェーダーは、シェーダーの色出力を担当し、ここで多くのシェーダープログラミングを行います。以下は、単に赤色を表示する非常に簡単なフラグメントシェーダーです:
| フラグメントシェーダーも、float |
| 頂点シェーダーと同様に、フラグメントシェーダーも 変数 |
これで頂点シェーダーとフラグメントシェーダーができたので、これらを別々のファイル(それぞれshader.vert
とshader.frag
)に保存し、loadShader()
を使用してスケッチにロードすることができます。
ユニフォーム:スケッチからシェーダーへのデータ渡し
このような単純なシェーダーは、それ自体で有用な場合もありますが、p5.jsスケッチからシェーダーに変数を伝える必要がある場合があります。これがユニフォームが必要になる時です。ユニフォームは、スケッチからシェーダーに送ることができる変数の一種です。これにより、JavaScriptからシェーダーをより細かく制御することが可能になります。
ユニフォームは、main()
の外側のファイルの先頭で定義されます。頂点シェーダーとフラグメントシェーダーの両方でアクセスできます。以下の例では、p5.jsメソッドmillis()
から返される値が’time’ユニフォームに渡され、頂点シェーダーに動きを導入しています。
これはフラグメントシェーダーでも機能します。次の例では、JavaScriptのスケッチ部分から色を変更できるようにする色のユニフォームmyColor
を作成します。シェーダーでは色チャンネルの値が0-255ではなく0-1の範囲であることを忘れないでください。
p5.jsが提供するユニフォームの完全なリストは、p5.js WebGLモードアーキテクチャドキュメントで確認できます。
Varyings: 頂点シェーダーからフラグメントシェーダーへのデータ受け渡し
Varying変数は、頂点シェーダーとフラグメントシェーダーの間でデータを共有します。これにより、フラグメントシェーダー内で位置やその他のジオメトリデータを使用することが可能になります。
例えば、フラグメントシェーダーで形状のテクスチャ座標を使用したい場合があります。これらはvec2
の形式で提供され、座標は0から1の間の値を取ります。これは最初、p5.jsからattribute
として提供され、頂点シェーダーでのみアクセス可能です。標準の頂点シェーダーがこれをフラグメントシェーダーに渡す方法を見てみましょう:
| |
| テクスチャ座標は最初、 |
| |
| ここで、 |
| |
| attributeの値をvarying変数に割り当てることで、フラグメントシェーダーが読み取れる場所にデータをコピーしています。 |
|
頂点シェーダーでvTexCoord
というvarying
を定義したので、フラグメントシェーダーでもそれを使用できるようになりました。以下は、x値を赤チャンネルに、y値を青チャンネルにマッピングする簡単なフラグメントシェーダーです。vTexCoord
は頂点シェーダーでは頂点ごとに定義されますが、フラグメントシェーダーではピクセルごとに値が定義されることに注意してください。ピクセルごとの値を得るために、WebGLは各面の頂点値間をスムーズに補間します。
|
|
フィルターシェーダー
p5.jsでは、フィルターはキャンバス上のすべてのピクセルを見て、それらを別のものに置き換えるものです。キャンバスの色を反転させたり、キャンバスの内容にぼかしを適用したりする多くの組み込みフィルターがあります。フラグメントシェーダーを書くことで、独自のフィルターを作成できます。
フィルターシェーダーにはフラグメントシェーダーだけが必要です。頂点シェーダーは主に形状の位置決めを担当し、フィルターは常にキャンバス全体に適用されるため、p5.jsがデフォルトの頂点シェーダーを提供します。loadShader
の代わりに、createFilterShader(src)
を使用し、シェーダーのソースコードを含む文字列を渡します。
フィルターシェーダーで利用可能なuniform
がいくつかあり、それらすべてについてはcreateFilterShader
のドキュメントで読むことができます。始めるにあたって知っておくべき主な2つは以下の通りです:
uniform sampler2D tex0
はキャンバスの内容を含むテクスチャです。varying vec2 vTexCoord
には現在のピクセルのキャンバス上の座標が含まれており、0から1の範囲です。
これらを組み合わせると、texture2D(tex0, vTexCoord)
はキャンバス上の現在のピクセルの色を返し、それを修正できます。この例では、赤と緑のチャンネルを青チャンネルで置き換えることで、カスタムの白黒フィルターを作成します:
試してみたいもう一つのことは、texture2D
の出力ではなく入力を修正することです。使用するテクスチャ座標を調整することで、元の画像からのオフセットを作成したり、ピクセルごとにオフセットが異なる場合はワープ効果を作成したりできます:
まとめ
これらのスキルを使って基本的なシェーダーを作成できますが、シェーダープログラミングはさらに深く進むことができ、このチュートリアルの範囲を超える多くのシェーダーのトピックがあります。p5.jsのシェーダーは、ビジュアル、エフェクト、テクスチャを作成し、3Dジオメトリにマッピングするための強力なツールとなります。
シェーダーについてもっと学びたいですか?これらのウェブサイトをチェックしてみてください!
- The Book of Shaders、Patricio Gonzalez VivoとJen Loweによるシェーダーガイド。
- p5.js shaders、Casey ConchinhaとLouise Lessélによるシェーダーガイド。
- Shadertoy、ブラウザエディタで書かれたシェーダーの巨大なオンラインコレクション。
- p5js Shader Examples、Adam Ferrissによるリソースコレクション。
- OpenGL ES 2.0 Specification、GLSLの超技術的な仕様
- WebGL Quick Reference card、やや密度の高い技術的リファレンスですが、GLSL関数に関する多くの有用な情報が含まれています
- Shaderific GLSL ES reference、組み込みのGLSL関数とデータ型に関するやや簡略化されたリファレンス
用語集
シェーダー
多くのビジュアルエフェクトやフィルターを効率的に生成できる特別なグラフィックスカードプログラム。
GLSL
Graphics Library Shader Language(GLSL)は、シェーダーを書くために使用されるプログラミング言語です。
Uniform
スケッチからシェーダーに渡される変数。
Varying
頂点シェーダーからフラグメントシェーダーに渡される変数
ベクトル(vec2
/ vec3
/ vec4
)
数値のグループを格納するデータ型で、最も一般的には2つ、3つ、または4つの数値を格納し、色、位置などを表現します。
Float
小数点を持つ浮動小数点数を格納するデータ型。
Int
小数点のない整数を格納するデータ型。
Sampler
シェーダーに渡されるテクスチャを表すデータ型。GLSLでは通常sampler2D
として表現されます。
Attribute
p5.jsスケッチで生成され、頂点シェーダーで利用可能になるGLSL変数。ほとんどの状況では、これらはp5.jsによって提供されます。
Texture
シェーダープログラムに渡される画像。texture2D()
関数を使用してサンプリングできます。
Type
int、float、vectorなど、データの形式を説明するラベル。
Vertex Shader
3D空間でジオメトリの位置決めを担当するシェーダープログラムの部分。
Fragment Shader
シェーダーによって出力される各ピクセルの色と外観を担当するシェーダープログラムの部分。