webglでなんとかGLSLを書けるよう

にするまで

自己紹介

  • @nobkz
  • Rockstar Engineer
  • Shen/JS/Python/Erlang/Haskell/Clojure/OCaml/Go/Rust/Scala/Haxe

    UX、フロントエンド、バックエンド、DSL作る人

今回のゴール

僕が発表すること

  • Glslの概要基本
  • webGLの概要
  • webglで、glslを書けるようにする

glslの概要

glslとは?

  • OpenGL Shading Languageの略
  • OpenGLのレンダリングパプライン上で走る
  • C言語をベースとした言語

シェーダとは?

  • 陰影処理と言われる
  • 頂点データ ー> ピクセルの列
  • GPUで計算される

プログラマブルシェーダ

  • 頂点処理、ピクセル処理をコントロールできる
  • GPUの処理の一部をプログラミング
  • 独自のレンダリング、GPGPUなどの計算ができる

Vertex Shader        Fragment Shader

  • 頂点シェーダは、頂点の行列変換を行なう
  • フラメントシェーダは、ピクセルの色を決定する

バーテックス

シェーダ

ラスタライズ

フラグメント

シェーダ

頂点

データ

ピクセル

データ

頂点情報

ピクセル情報

処理の

流れ

WebGLで

GLSLを書くまで

目的 

WebGLでGLSLの環境構築

  • webGLでGLSLをコンパイル、実行する
  • webGLの概要
  • jsから、GLSLへの情報の受け渡しなど

webGLとは?

  • HTML5の3DCG技術
  • OpenGL由来のAPI
  • three.jsなど多数ラッパーライブラリがある
  • 固定機能パイプラインが無い

固定機能パイプラインが無い?

  • つまり、頂点変換の固定の処理が無い
  • 絶対に頂点シェーダを書かないといけない
  • 自前で、行列変換をしなければならない
  • 行列変換のライブラリも多数

canvasタグについて

  • canvasは複数のコンテキストを持つ
  • canvasから、webglのコンテキストを取得
  • 現在では2dとwebglのコンテキストしか無い

html,シェーダソース

  • 簡単に紹介する
  • html, vertex shader, fragment shader
  • jsで有効にしていく

index.html

<!doctype html>
<html lang="ja">
  <head>
    <meta charset="UTF-8"/>
    <title>Document</title>
  </head>
  <body>
    <canvas id="canvas"></canvas>
    <script src="script.js"></script>
  </body>
</html>

hello.vert

attribute vec3 position;
varying vec2 pos;

void main(void){
  pos = position.xy;
  gl_Position = vec4(position, 1.0);
}

hello.frag

precision mediump float;

uniform vec2 origin;
varying vec2 pos;
      
void main(void){  
  gl_FragColor = vec4(vec3(0.1/length(pos - origin)), 1.0);
}

JSでGLSLを走らせる

  • コンテキスト取得
  • シェーダソースの取得
  • シェーダのコンパイル
  • vertex shaderとfragment shaderをリンク
  • 頂点データの用意
  • IBO、VBOの作成
  • IBO、VBOのバインド
  • uniformのバインド
  • 描画処理

コンテキスト取得

var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl");

シェーダソースの取得

Promise.all(
    [getFileText("hello.vert"),
     getFileText("hello.frag")]
).then(function(values){
    onShaderLoad(values[0], values[1]);
});

function getFileText(filePath){
    return new Promise(function(resolve, reject){
        var request = new XMLHttpRequest();
        request.open("GET",filePath);
        request.onload = function(){
            resolve(request.responseText);
        };
        request.onError = function(){
            reject("load Error");
        }
        request.send();
    });
}

Ajaxでテキスト取得

しているが、

別にglslの

コードの文字列が

取得できれば

どうでも良い

シェーダのコンパイル

var vertexShader = compileShader(gl, gl.VERTEX_SHADER, vsText);
var fragmentShader = compileShader(gl, gl.FRAGMENT_SHADER, fsText);

function compileShader(gl, type, text) {
    // シェーダオブジェクト作成
    var shader = gl.createShader(type);
    // シェーダソースを登録
    gl.shaderSource(shader, text);
    // コンパイル
    gl.compileShader(shader);
    if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        return shader;
    } else {
        return null;
    }
}

シェーダのリンク

var program = linkShader(vertexShader, fragmentShader);

function linkShader(vertexShader, fragmentShader) {
    // プログラムオブジェクト作成
    var program = gl.createProgram();
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);

    gl.linkProgram(program);

    if (gl.getProgramParameter(program, gl.LINK_STATUS)) {
        gl.useProgram(program);
        return program;
    } else {
        return null;
    }
}

頂点データの用意

 var vertex_position = [
     0.0,  0.0, 0.0,
    -0.8, -0.8, 0.0,
     0.8, -0.8, 0.0,
     0.8,  0.8, 0.0,
    -0.8,  0.8, 0.0
 ];

var index = [
        0,1,2,
        0,2,3,
        0,3,4,
        0,4,1
];

Vertex Buffer Objectとは?

  • GPU側に置かれたバッファ
  • 頂点データを格納する
  • 頂点位置のみでなく、色、法線ベクトルなど登録できる

VBO作成

createVBO(gl, vertex_position);

function createVBO(gl, data){
    var vbo = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);
    gl.bindBuffer(gl.ARRAY_BUFFER, null);
    return vbo;
}

VBOのバインド

setAttribute("position", program, vertex_position, 3);

function setAttribute(vertexAtributeName, program, data, stride) {
   // シェーダプログラムのattributeの位置を取得
    var attribLocation = gl.getAttribLocation(program, vertexAtributeName);
    // vboを作成し、頂点データを格納
    var vbo = createVBO(data);
    // vboをバインド
    gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
    // attribute属性の有効化
    gl.enableVertexAttribArray(attribLocation);
    // 頂点データ登録
    gl.vertexAttribPointer(attribLocation, stride, gl.FLOAT, false, 0, 0);
}

Index Buffer Objectとは

  • 頂点インデクッスを格納するオブジェクト
  • 頂点の処理の順番を決める
  • つまり、レンダリングする順番
  • 頂点データの再利用が可能になる

IBO作成

var ibo = createIBO(gl,index);

function createIBO(gl, data) {
    var ibo = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, 
                            new Int16Array(data),
                            gl.STATIC_DRAW);
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
    return ibo;
}

IBOをバインド


gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);

IBOより描画

gl.drawElements(gl.TRIANGLES, index.length, gl.UNSIGNED_SHORT, 0);
gl.flush();

まとめと補足

まとめと補足

  • canvasからコンテキストを取得
  • シェーダソース取得 -> コンパイル -> リンク -> バインド
  • 行列変換などは、説明してない
  • 参考にしたサイト
    • https://www.khronos.org/registry/webgl/specs/latest/
    • http://wgld.org/
    • https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API

ありがとう

ございました

webglのGLSLについて

By Nobukazu Hanada

webglのGLSLについて

  • 1,348