ラベル WebGL の投稿を表示しています。 すべての投稿を表示
ラベル WebGL の投稿を表示しています。 すべての投稿を表示

2015年5月11日月曜日

明解WebGLを頂きました.

WebGL開発支援サイト wgld.orgやWebGL総本山を運営する@h_doxas(杉本 雅広)さんがリックテレコムから「明解WebGL iOS/Androidも対応した3D CGプログラミングのWeb標準」を出版されました.

本を頂き,発売前に読む機会を得たので,感想を書きたいと思います.

明解WebGLは,「WebGLそのもの」について解説した入門書です.最近は,Unreal Engine 4やUnityもWebGLをサポートしており,WebGLを使ったコンテンツも増えてきており,WebGLへの注目も高まっています.しかし,意外なことに,何らかのライブラリを用いてWebGLを使った何かを作る方法を解説した本はいくつも出版されているものの,「WebGLそのもの」について解説した和書は2012年以来出版されていなかったのです.(確認漏れがあったらゴメンナサイ.)

ライブラリを使って面倒な部分を省略するのも悪くは無いのですが,それは基礎という土台があってこそです.基礎という土台が無ければ,何が省略されているのかも分からないので,何か困ったことがあったときに,思いつけることが限られてしまいます.

本書は,次のような方にお薦めです.
  • コンピュータグラフィックスに興味があるが,まだ触れたことが無い
  • 細かい解説を読むよりも,まずは手を動かして覚えたい
本書のあちこちに似たような表現が登場するのですが,本書では「まず動かしてみる」ということを重視しています.いきなり全てを理解する必要はない,まずは「できた」という感覚を大切にして欲しい,少しずつ学んで行けば良い,などなどです.

そのため,既にOpenGLなどを触ったことがある人には,少々物足りない内容に感じるかもしれません.

しかし,本書にはWebGLについて調べる場合のヒントや,実際に表示するモデルデータを入手する方法,WebGLでのデバッグ方法など,各種端末での実行やデバッグ方法など,入門書を読み終えた後に困りそうなポイントについても,さりげなくフォローされているため,OpenGLを触ったことがある人がさっとWebGLを覚えるのにも良いでしょう.

本書を読み終えた後は,WebGL開発支援サイト wgld.orgやWebGL総本山を参照し,更に理解を深め,アイデアを磨いていくと良いでしょう.

個人的には,何か新しいことを覚える場合にはまず手を動かしてみることを重視しているので,この本は今からWebGLを学ぼうと考えている人にはお薦めです.

紙面の都合上,プログラムの全てが載っているわけではないので,どうしてもサンプルサイトを見ながら書き写すことになり,ついコピーして貼り付けしたくなるかもしれませんが,是非一度は自分でプログラムを打ち込んでみてください.実際に手を動かしてみると,色々とミスをするかもしれませんが,それを解決していくことも経験になると思います.

実際,自分も仕事ではOpenGL系のAPIを利用しているのですが,WebGLで色々と試して学んでいたことで,仕事で問題が起きたときの気付きへとつながったことが何度もありました.

今後WebGLはますます盛り上がっていくと思います.皆さんも本書を読んで,WebGLに触れてみませんか?

2015年5月6日水曜日

WebGLContextAttributesの切り替え

WebGLでは,canvasからgetContext()でWebGLRenderingContextを取得する際に,WebGLContextAttributesといういくつかの設定を渡すことができる.
var canvas = getElementById('canvas');
var options = { alpha : true };
var gl = canvas.getContext('webgl', options) || canvas.getContext('experimental-webgl', options);

この設定は,コンテキストの生成時にのみ利用され,それ以降は変更することができない.

実験的に色々切り替えてみたいときに不便だし,なんとかならないかなぁ,と思っていたけれど,生成時にしか設定できないなら,canvasを生成し直せば良いのでは,と思ったのでやってみた.

// アルファチャンネルのオンオフを設定するチェックボックスの要素を取得
var alpha = document.getElementById('alpha');

alpha.addEventListener('change', function(){
  // ノードを複製する
  var newCanvas = canvas.cloneNode(true);

  // WebGLContextAttributes設定用オブジェクトの生成
  var options = {};
  options.alpha = alpha.checked;

  // WebGLRenderingContextの差し替え
  gl = newCanvas.getContext('webgl', options) || newCanvas.getContext('experimental-webgl', options);

  // canvas要素の入れ替え
  canvas.parentNode.replaceChild(newCanvas, canvas);
  canvas = newCanvas;
}, false);

とりあえずいくつかのブラウザで試してみたが,上手く動いているみたい.

他に良い方法があれば知りたい.

以下サンプル.背景のクリアカラーを設定できて,アルファチャンネルの有無を切り替えられる.
















2014年10月7日火曜日

WebGLによるPVRテクスチャの利用

DDSの読み込みを作った勢いで,iOSのWebGL向けにPVRテクスチャを読み込む処理を書いてみた.

動作サンプルはこちら.
iOS 8以降で確認できます.

// 拡張機能の取得
var ct = gl.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc");

// FCCを32bit符号付き整数に変換する
function FourCCToInt32(value){
    return (value.charCodeAt(0) << 0) +
        (value.charCodeAt(1) << 8) +
        (value.charCodeAt(2) << 16) +
        (value.charCodeAt(3) << 24);
}

// 32bit符号付き整数をFCCに変換する
function Int32ToFourCC(value){
    return String.fromCharCode(
        (value >> 0) & 0xff,
        (value >> 8) & 0xff,
        (value >> 16) & 0xff,
        (value >> 24) & 0xff
    );
}

// XMLHttpRequestの生成
var XHR = new XMLHttpRequest();

// 読み込むファイルの指定
XHR.open('GET', './sample4.pvr');

// 読み込み完了時の処理
XHR.addEventListener('load', function(){
    // iOS Developer Libraryより
    // PVRTextureLoaderを参照
    // https://developer.apple.com/library/ios/samplecode/PVRTextureLoader/Introduction/Intro.html
    var header = new Uint32Array(XHR.response, 0, 13);
    var tag = Int32ToFourCC(header[11]);

    if(tag != "PVR!"){
        alert("PVRフォーマットではありません.");
        return ;
    }

    var hasAlpha = header[10] != 0 ? true : false;
    var PVR_TEXTURE_FLAG_TYPE_MASK = 0xff;
    var PVR_TEXTURE_FLAG_TYPE_PVRTC_2 = 24;
    var PVR_TEXTURE_FLAG_TYPE_PVRTC_4 = 25;

    var height = header[1];
    var width  = header[2];

    var format = 0;
    var blockSize = 0;
    var blockWidth = 0;
    var blockHeight = 0;
    // 1ピクセルあたりのビット数
    var bpp = 0;
    switch(header[4] & PVR_TEXTURE_FLAG_TYPE_MASK){
    case PVR_TEXTURE_FLAG_TYPE_PVRTC_2:
        if(hasAlpha){
            format = ct.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
        }else{
            format = ct.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
        }
        blockSize = 8 * 4;
        blockWidth = width / 8;
        blockHeight = height / 4;
        bpp = 2;
        break;
    case PVR_TEXTURE_FLAG_TYPE_PVRTC_4:
        if(hasAlpha){
            format = ct.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
        }else{
            format = ct.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
        }
        blockSize = 4 * 4;
        blockWidth = width / 4;
        blockHeight = height / 4;
        bpp = 4;
        break;
    default:
        alert("未知のフォーマットです.\n");
        return ;
    }

    // 最低限のサイズはあるものとする
    blockWidth = Math.max(2, blockWidth);
    blockHeight = Math.max(2, blockHeight);

    var size = blockWidth * blockHeight * ((blockSize * bpp) / 8);
    var buffer = new Uint8Array(XHR.response, 13 * 4, size);

    // 圧縮テクスチャを渡す
    gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, buffer);

    // テクスチャのパラメータを設定
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

    // テクスチャを設定して有効化
    gl.uniform1i(gl.getUniformLocation(program, 'texture'), texture);
    gl.activeTexture(gl.TEXTURE0);

    // 描画開始
    draw();
});
// バイト配列として結果が帰ってくるようにする
XHR.responseType = 'arraybuffer';

// 読み込み開始
XHR.send();

処理としてはDDSを読み込む場合とほとんど変わらず,違いというと,取得する拡張機能の名前が違うことと,受け取ったバイト列をフォーマットに合わせる部分.

このコード書くの,何が大変だったかってpvrのテクスチャを生成するのが手間だったのと,iOSでチェックするためのデバッグが面倒だった.Safariを使ってiOS上で見ているウェブページをデバッグする機能が無かったら大変だった.

2014年10月2日木曜日

WebGLでDDSテクスチャを表示する

WebGLでDDSテクスチャを表示する方法,調べても日本語ではあまり情報がなかったのでまとめておく.
通常のテクスチャの表示方法については,wgld.orgを参考にする.

まず,現状ではDDSのテクスチャを読み込むには,WebGLのコンテキストからgetExtension()を利用して,拡張機能を取得して利用する必要がある.
// var gl; // WebGLのレンダリングコンテキスト

// Chromeでの拡張機能の取得
var ct = gl.getExtension('WEBKIT_WEBGL_compressed_texture_s3tc');
// FireFoxでの拡張機能の取得
// var ct = gl.getExtension('MOZ_WEBGL_compressed_texture_s3tc');
ブラウザによって取得のために渡す文字列が違うが,取得した後は同じように扱える.このctは,compressedTexImage2Dに渡すフォーマットに関する定数を持ったオブジェクトになっている.
今のところ,次の4つの定数が含まれている.

  • COMPRESSED_RGBA_S3TC_DXT1_EXT
  • COMPRESSED_RGBA_S3TC_DXT3_EXT
  • COMPRESSED_RGBA_S3TC_DXT5_EXT
  • COMPRESSED_RGB_S3TC_DXT1_EXT

テクスチャにデータを渡す準備ができたら,テクスチャのデータをXMLHttpRequestを利用して読み込む.
var XHR = new XMLHttpRequest();
XHR.open('GET', テクスチャのパス);

XHR.addEventListener('load', onLoad, false);

// ロード結果のフォーマットの指定
XHR.responseType = 'arraybuffer';

// 実際の読み込み
XHR.send();

これで,読み込み終わったタイミングでonLoadが呼び出されるので,DDSのテクスチャの解析を行う.

DDSファイルは,Microsoftのドキュメントにあるように,4文字を32bit整数にまとめたFOURCCと,その後に続くヘッダから始まる.

まず,FOURCCと32bit整数を相互に変換する機能が必要なので,関数を作っておく.
// FCCを32bit符号付き整数に変換する
function FourCCToInt32(value){
    return (value.charCodeAt(0) << 0) +
        (value.charCodeAt(1) << 8) +
        (value.charCodeAt(2) << 16) +
        (value.charCodeAt(3) << 24);
}

// 32bit符号付き整数をFCCに変換する
function Int32ToFourCC(value){
    return String.fromCharCode(
        (value >> 0) & 0xff,
        (value >> 8) & 0xff,
        (value >> 16) & 0xff,
        (value >> 24) & 0xff
    );
}


XHRの読み込み結果からマジックナンバーおよびヘッダ(31個の32bit整数で出来ている)を読み込み,ヘッダから必要な情報を取り出し,compressedTexImage2Dに渡す.
function onLoad()
{
    var header = new Int32Array(XHR.response, 0, 32);

    // マジックナンバーのチェック
    if(header[0] !== FourCCToInt32('DDS ')){
        return ;
    }

        // ブロックのバイトサイズ
    var blockBytes;
    // フォーマット
    var format;

    // DDS_PIXELFORMATのdwFourCCを確認し,1ブロックのサイズと
    // フォーマットを確認する
    var FOURCC_DXT1 = FourCCToInt32('DXT1');
    var FOURCC_DXT3 = FourCCToInt32('DXT3');
    var FOURCC_DXT5 = FourCCToInt32('DXT5');
    switch(header[21]){
    case FOURCC_DXT1:
        blockBytes = 8;
        format = ct.COMPRESSED_RGBA_S3TC_DXT1_EXT;
        break;
    case FOURCC_DXT3:
        blockBytes = 16;
        format = ct.COMPRESSED_RGBA_S3TC_DXT3_EXT;
        break;
    case FOURCC_DXT5:
        blockBytes = 16;
        format = ct.COMPRESSED_RGBA_S3TC_DXT5_EXT;
        break;
    }

    // DDS_HEADERのdwHeightを取得 (マジックナンバー分に注意)
    var height = header[3];

    // DDS_HEADERのdwWidthを取得 (マジックナンバー分に注意)
    var width  = header[4];

    // DDS_HEADERのdwSizeを取得し,マジックナンバー分を足して,テクスチャデータの
    // 先頭を求める
    var offset = header[1] + 4;

    // 今のテクスチャのバイトサイズを求める
    var length = Math.max(4, width) / 4 * Math.max(4, height) / 4 * blockBytes;

    // バッファを生成する
    var buffer = new Uint8Array(XHR.response, offset, length);

    // 圧縮テクスチャを渡す
    gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, buffer);

    // 本来はミップマップのために,サイズを変えながら残りのデータを読み込むが省略

    // テクスチャのパラメータを設定
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

    // テクスチャを設定して有効化
    gl.uniform1i(gl.getUniformLocation(program, 'texture'), texture);
    gl.activeTexture(gl.TEXTURE0);

    // 実際の描画
    draw();
}

実際に動作するサンプルを用意してみた.とりあえず適当に丸を書いただけのテクスチャなのでおもしろみは無い.

サンプル

他の拡張機能についても,そのうち調べてみよう.

2014年9月30日火曜日

ブラウザでサポートされているWebGLの拡張機能を確認する方法

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title></title>
</head>
<body>
  <canvas id = "canvas">
  </canvas>

  <script>
  var canvas = document.getElementById('canvas');
  var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');

  var extensions = gl.getSupportedExtensions();
  for(var e in extensions){
    document.write(extensions[e] + ",");
  }
  </script>
</body>
</html>

こんな感じで,gl.getSupportedExtensions()を呼び出せば,拡張機能の一覧が取得できる.


2014年1月4日土曜日

同次座標系でのwの効果を体感する

同次座標系(3次元をx, y, z, wの4つで表すヤツ)では,実際の座標は(x/w, y /w, z / w)となる,
ということを体感するためのページを作ってみた.

同次座標系でのwの効果を体感する (GitHub Pages)

横のスライドバーは1から10まで変更できて,(1, 0, 0, w)のwに反映される.
wが大きくなるほど点のx座標が0 (中心)に近づくのが分かる.