p5.js shader 基本的なサンプル 1 グラデーションシェーダー

本稿は「Basic Gradient Shader based on pixel coordinates」ページを翻訳したものです。

ピクセル座標にもとづいた基本的なグラデーションシェーダー

次のコードは、キャンバスのピクセル位置にもとづいたグラデーションを作成します。ここまで学んだ知識を使って、実験してみましょう。

使うのは次の知識です。

.jsファイル
// このサンプルでは、p5スケッチからシェーダーに値を送る
// この値は'uniform'変数と呼ばれる
// この作業は、p5.ShaderのsetUniform()メソッドで行う
// https://p5js.org/reference/#/p5.Shader/setUniform

// シェーダー変数
let theShader;

function preload() {
    // シェーダーをロードする
    theShader = loadShader('uniform.vert', 'uniform.frag');
}

function setup() {
    // シェーダーを動作させるにははWEBGLモードが必要
    createCanvas(windowWidth, windowHeight, WEBGL);
    noStroke();
}

function draw() {
    // アクティブなシェーダーをtheShaderに設定する
    shader(theShader);

    // ここでsetUniform()を使ってuniform変数をシェーダーに送っている
    // setUiform()は賢くて、送信している変数の種類を理解するので、
    // (processingと異なり)キャストする必要がない
    theShader.setUniform("u_resolution", [width, height]);
    // この2つは実際には使用していない
    theShader.setUniform("u_time", millis() / 1000.0);
    theShader.setUniform("u_mouse", [mouseX, map(mouseY, 0, height, height, 0)]);

    // rect()で画面にジオメトリを与える
    rect(500, 500, 10000, 10000);
}

function windowResized() {
    resizeCanvas(windowWidth, windowHeight);
}
.vertファイル

頂点シェーダーファイルはこれまでと同じです。

// グラフィックカードにシェーダーのレンダリング方法を知らせる定義が必要
#ifdef GL_ES
precision mediump float;
#endif

// ピクセルの位置を取得し、シェーダーをシェイプに正しくマッピングするために必ず含める
attribute vec3 aPosition;

void main() {
  // w成分として1.0を追加して、位置データをvec4にコピーする
  vec4 positionVec4 = vec4(aPosition, 1.0);

  // 出力がキャンバスにフィットするようスケーリングする
  positionVec4.xy = positionVec4.xy * 2.0 - 1.0;

  // 頂点情報を、フラグメントシェーダに送る
  gl_Position = positionVec4;
}
.fragファイル

キャンバスのサイズがvec2 u_resolutionというuniformとして渡されます。これはsketch.jsファイルで設定したものです。
theShader.setUniform(“u_resolution”, [width, height]);

uniformにはキャンバスの幅と高さが含まれています。 u_resolution.xには幅が、u_resolution.yには高さが含まれています。

uniform vec2 u_resolution;

main()関数では、gl_FragCoord.xy(フラグメント座標)からキャンバス上の各ピクセルの位置を取得します。gl_FragCoordは、頂点シェーダーファイルのgl_Positionで与えられた情報を自動的に使用します。

ピクセル(gl_FragCoord.xy)のx,y位置をキャンバスの幅と高さ(u_resolution.xy) で割ることで、0.0-1.0の範囲に正規化された座標が得られます。したがってst.xはx軸で0から1まで変化し、st.yはy軸で0から1に変化します。

変数をstにしているのは、シェーダーを記述するときの標準的な慣習からです。

シェーダーでピクセルの位置を使用する場合には、シェーダーをこのようにして開始するよう習慣づけるべきです。

vec2 st = gl_FragCoord.xy/u_resolution.xy;

コードはつねに、gl_FragColorにピクセルのカラーを設定して終わります。これを記述しないとコードは実行されません。st.xを入力に使用すると、x軸での黒から赤へのグラデーションが作成できます。

// R = x軸でのピクセル位置で決まる(0から1), G = 0, B = 0, A = 1
gl_FragColor = vec4(st.x,0.0,0.0,1.0); 

また、x軸でのピクセル位置に応じて緑のグラデーションを作ることもできますし、st.xとst.y両方を使用してピクセルの色を決めることもできます。ただしgl_FragColorは1度に1つしかアクティブにできないので、設定するgl_FragColorは1つだけにします。

// 1度にアクティブにできるgl_FragColorは1つだが、コメントアウトして試してみよう。
// 緑チャンネル
gl_FragColor = vec4(0.0,st.x,0.0,1.0);

// x位置とy位置両方
gl_FragColor = vec4(st.x,st.y,0.0,1.0);

.fragファイル全体は次のようになります。

// グラフィックカードにシェーダーのレンダリング方法を知らせる定義が必要
#ifdef GL_ES
precision mediump float;
#endif

// このサンプルでは、対象ピクセルがキャンバスのどこにあるかが知りたいので、キャンバスのサイズが必要になる
// これは、sketch.jsファイルからuniformとして送られてくる
uniform vec2 u_resolution;

void main() {
 // ピクセルの位置を解像度(キャンバスのサイズ)で割って、キャンバス上での正規化された位置を得る
	vec2 st = gl_FragCoord.xy/u_resolution.xy;

  // 赤のグラデーションとして、x軸のピクセル位置を使う。
  // 位置が0.0に近いほど、黒くなる(st.x = 0.0)
  // 位置が幅(1.0として定義)に近いほど、赤くなる(st.x = 1.0)
  gl_FragColor = vec4(st.x,0.0,0.0,1.0); // R,G,B,A

  // 1度にアクティブにできるgl_FragColorは1つだが、コメントアウトして試してみよう。
  // 緑チャンネル
  //gl_FragColor = vec4(0.0,st.x,0.0,1.0);

  // x位置とy位置両方
  //gl_FragColor = vec4(st.x,st.y,0.0,1.0);
}

以下は訳者による記述です。

図を使って整理してみる

シェーダーの世界は不慣れで難しいので、ここで整理しておきましょう。

sketch.jsでは、シェイプ(ジオメトリ)が描かれます。この頂点のデータは自動的に頂点シェーダーに送られます(attribute変数のaPosition)。頂点シェーダーではこれを加工してgl_Positionに割り当てます。すると頂点の情報が自動的にフラグメントシェーダーに送られます。

sketch.jsではまた、作成したシェーダーのsetUniform()メソッドを使って、フラグメントシェーダーにキャンバスの幅と高さ(上図では400と300)を送っています(u_resolution)。フラグメントシェーダーでは、GLSLで計算できるよう、ピクセルのキャンバス上での位置を表すgl_FragCoord.xyを、送られてきたu_resolutionのxyで割って、ピクセルの位置が0-1の間におさまるようにしています(正規化)。

具体的にキャンバスが400 x 300の場合で言うと、左上隅(0,0)にあるピクセルは(0,0)、右上隅の(400,0)は(1,0)、左下隅の(0,300)は(0,1)、右下隅の(400,300)は(1,1)に置き換えることができます(GLSLとp5.jsのキャンバスでは座標システムが異なるので注意が要ります。GLSLではyの正方向は上です)。

そして最後、gl_FragColorにvec4(st.x,0.0,0.0,1.0)を割り当てます。これは0-1の範囲で変化するRGBAの並びで、BとGは0、Aは1.0と固定されていて、Rはst.xなのでRだけが変化します。たとえばst.xが0のときは(0,0,0,1.0)なので完全に不透明な黒で、st.xが0.1のときは(0.1,0,0,1.0)なので完全に不透明な黒よりは少し赤い黒になります。st.xはx軸沿いに0から1まで変化するので、黒から赤に変化するグラデーションになります。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA