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上で見ているウェブページをデバッグする機能が無かったら大変だった.

0 件のコメント:

コメントを投稿