LoginSignup
25
26

More than 5 years have passed since last update.

今すぐコピペして試せる!WebGL(OpenGL)のシェーダー超入門

Last updated at Posted at 2015-10-27

シェーダーであそぼう!

gl2.gif

WebGLのおかげでシェーダー言語がびっくりするくらいお手軽になりました。
触って、弄って、あなただけのエフェクトを作ってみましょう!

やること

  • WebGL
  • レンダリングの流れ(超ざっくり)
  • シェーダー言語(たっぷり)

必要なもの

WebGLについて

OpenGLの仲間たち

内容 開発言語
OpenGL 大本のAPI Cなど
OpenGL ES モバイル用のOpenGLのサブセット CやJavaなど
WebGL OpenGL ESをブラウザ向けに移植したもの JavaScript ←ここをやります

バージョンの対応表

OpenGL OpenGL ES WebGL
1.x 1.x ---
2.x 2.0 1.0 ←ここをやります
3.x 3.0 2.0(策定中)
4.x 3.x -

OpenGL ES 3.0以降はAndroid4.3以降、iOS7以降でサポートされます。
OpenGL ES 3.0は2.0と互換性があるので、とりあえずOpenGL ES 2.0(WebGL 1.0)を勉強しておけば間違いは起きないと思います。

WebGLとOpenGLの違い

glプレフィックス関数を、glオブジェクトのメソッドにしたくらいの違いしかないので、
WebGLがわかればOpenGLもほぼほぼわかるようになります。

WebGL OpenGL
gl.vertexAttribPointer() glVertexAttribPointer()
gl.drawElements() glDrawElements()
gl.createTexture() glGenTextures()

WebGLを動かす

基本的な流れ(超ざっくり)

細かくやるとシェーダー言語よりよっぽど難しいので超ざっくりです。。。

  1. モデル(球など)を作る
    • 3D空間の点{x, y, z}の配列と、テクスチャマッピング{u, v}の配列のセット
  2. テクスチャを用意する
  3. カメラを定義する(視野変換行列)
  4. シェーダーをコンパイルする

  5. 以下ループ

    1. モデルの移動・変形を定義する(行列)
    2. シェーダーに渡す
    3. 描画

サンプルを実行してみる

適当なフォルダに index.html というファイルを作って、以下のURLの内容をコピペしてブラウザで開いてください。
シェーダーもテクスチャも全て含まれています。

シェーダー言語

手っ取り早くシェーダーをいじり倒したい方は いじる まで飛ばしてください。

言語の概要

GLSL(OpenGL Shading Language)

  • C言語によく似た言語
  • グローバル変数から拾ってグローバル変数に書き込むのが基本的な流れ
  • モデルのポリゴンの頂点に対して、バーテックスシェーダーとフラグメントシェーダーが実行される

※JavaScriptからの変数の渡し方の解説は割愛

バーテックスシェーダー
// グローバル宣言 (種類 型 変数名;)
attribute vec3 position;    // モデル - 3D空間上の座標
attribute vec2 texCoord;    // モデル - テクスチャマッピング
uniform mat4 mvMatrix;      // モデルの移動・変形
uniform mat4 pMatrix;       // カメラ(視野)
varying vec2 v_texCoord;    // fragmentシェーダーに渡す変数

void main() {
    gl_Position = pMatrix * mvMatrix * vec4(position, 1.0);
    v_texCoord = texCoord;
}
フラグメントシェーダー
precision mediump float;    // floatの精度の指定(フラグメントシェーダーでは必須)

// グローバル宣言 (種類 型 変数名;)
varying vec2 v_texCoord;    // バーテックスシェーダーから受け取る変数
uniform sampler2D sampler;  // テクスチャサンプラー

void main() {
    gl_FragColor = texture2D(sampler, v_texCoord);
}

(余談)レンダリング結果をキャプチャすればGPGPU(GPUによる多目的演算)的なこともできます。
ただし、GPGPUは専用の仕組みを使うことをお勧めします。

  • OpenGL 4.0以降・OpenGL ES 3.1以降だとコンピュートシェーダー
  • AndroidだとRenderScript

文法

基本はポインタのないC言語のようなもの

繰り返します。ポインタはありません。なのでとても簡単です。

グローバル宣言

  • JavaScriptで用意したデータをここから取り出すためのもの
    • attribute
    • uniform
  • バーテックスシェーダーからフラグメントシェーダーに値を渡すためのもの
    • varying

attribute

おさらい:シェーダーはモデルのポリゴンの頂点に対して実行されます

JavaScript
// この変数を使うよ!
var location = gl.getAttribLocation(program, 'position');
gl.enableVertexAttribArray(location); 

// positionBuffer(頂点情報の一覧)を、
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// 'position'に割り当てるよ!
var location = gl.getAttribLocation(program, 'position');
gl.vertexAttribPointer(location, 3, gl.FLOAT, false, 0, 0);
バーテックスシェーダー
attribute vec3 position; // バッファの一要素が入っている

attributeはフラグメントシェーダーでは使えません。

uniform

JavaScript
// 行列pMatrixを'pMatrix'に割り当てるよ!
var gl.getUniformLocation(program, 'pMatrix');
gl.uniformMatrix4fv(location, false, pMatrix);
シェーダー
uniform mat4 pMatrix; // 行列pMatrixが入っている

varying

バーテックスシェーダー
varying vec2 v_texCoord;    // 宣言して

...

v_texCoord = texCoord; // データを詰めておく
フラグメントシェーダー
varying vec2 v_texCoord;    // バーテックスシェーダーで詰めた値が取り出せる
種類 使える場所 使い道
attribute vertexのみ 座標の配列
uniform vertex, fragment両方 行列やフラグなど
varying vertex, fragmentでペア バーテックスからフラグメントに値を渡す変数

暗黙のグローバル変数

ここに値を割り当てておくと描画されます。

名前 使える場所 意味
gl_Position vertex 座標
gl_FragColor fragment

全て値型

種類 意味
float 1次元の数値
vec2, vec3, vec4 2~4次元のベルトル
mat4 4x4の行列
sampler2D 座標情報と一緒にtexture2D()関数に渡すとテクスチャの色情報が得られるもの

シェーダーの中では主にvec2~4を弄ります

生成

// コンストラクタ
vec2 v2 = vec2(0.0, 0.2);
vec3 v3 = vec3(0.0, 0.2, 0.8);
vec4 v4 = vec4(0.0, 0.2, 0.8, 0.4);

// sampler2D sampler, vec2 texCoordを用意して、
vec4 v4 = texture2D(sampler, texCoord);

変数の操作

各成分へのアクセス

添字で各ベクトル成分にアクセスできます。

vec4 v4;

座標
v4.r v4.x
v4.g v4.y
v4.b v4.z
v4.a v4.w

どちらを使っても同じ値にアクセスできます。

成分の入れ替え・スライス

vec4 v4 = vec4(0.1, 0.2, 0.3, 0.4);
vec4 v4a = v4.brga; // (0.3, 0.1, 0.2, .0.4)
vec2 v2a = v4.xz; // (0.1, 0.3)

四則演算

各成分ごとに四則演算されます。

vec2 result = vec2(0.1, 0.2) * vec2(2.0, 2.0);
// -> (0.2, 0.4)
vec2 result2 = result / vec2(2.0, 2.0); // 掛けたベクトルで割ると
// -> (0.1, 0.2) 元に戻る

ベクトル演算にはdot(), cross()を使います。ソースは割愛。

いじる

フラグメントシェーダー
precision mediump float;

varying vec2 v_texCoord;
uniform sampler2D sampler;

void main() {
    // rgb成分を入れ替えると・・・?
    gl_FragColor = texture2D(sampler, v_texCoord).gbra; 
}

ランダムな値が欲しい

引用元

float rand(vec2 co) {
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

// フラグメントシェーダーでtexCoordをずらすと・・・?
vec2 texCoord = v_texCoord + (rand(gl_FragCoord.xy) - 0.5) / 300.0;

外からの変数を追加する

JavaScript
gl.uniform1f(gl.getUniformLocation(program, 'i'), false, i++);
フラグメントシェーダー
uniform float i; // 毎フレーム1ずつ変化する変数

締め

WebGLで試すと簡単で早いので色々試すのがいいと思います

やらなかったこと

以下は端折りましたが、調べるとより理解が深まると思います。

25
26
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
25
26