WebGLでの利用方法

拡張機能の取得

WebGLでは圧縮テクスチャーの扱いは拡張機能として提供されているため、使用するにはWebGLRenderingContext.getExtension()で拡張機能を有効にします。たとえばPVRTCの圧縮テクスチャーはWEBGL_compressed_texture_pvrtc拡張ですが、ブラウザによってはベンダープレフィックスをつける必要があります。例として、2018年5月時点のiOS SafariではWEBKIT_WEBGL_compressed_texture_pvrtcでないと取得できません。

// PVRTC圧縮テクスチャーの拡張機能を取得(有効化)する
const extPVRTC = (
  context.getExtension('WEBGL_compressed_texture_pvrtc') ||
  context.getExtension('WEBKIT_WEBGL_compressed_texture_pvrtc')
);

拡張機能がサポートされている環境ではgetExtension()で拡張機能オブジェクトが返り、それ以降は機能が有効になります。サポートされていない場合はnullが返ります。

拡張機能がサポートされている場合でも、デバイスが本当に対象の圧縮テクスチャーフォーマットをサポートしているかを確認する必要があります。拡張機能を有効にした後にWebGLRenderingContext.getParameter(WebGLRenderingContext.COMPRESSED_TEXTURE_FORMATS)を呼んで、サポートしている圧縮テクスチャーフォーマットのリストを取得します。リスト内にその時点で有効な圧縮テクスチャーフォーマットの種類の値が入っているので、使用するフォーマットが該当するか確認します。

たとえば、アルファチャンネルなしPVRTCの場合使用するフォーマットはCOMPRESSED_RGB_PVRTC_4BPPV1_IMGですので、前述の拡張機能オブジェクトから値を取得してリスト内にあるかチェックします。

// サポートされている圧縮テクスチャー形式のリストを取得する
const supportedCompressedTextureFormat = context.getParameter(context.COMPRESSED_TEXTURE_FORMATS);

// PVRTC形式がサポートされているかを確認する
const supportsPVRTC = supportedCompressedTextureFormat.includes(extPVRTC.COMPRESSED_RGB_PVRTC_4BPPV1_IMG);

下記は圧縮テクスチャーのフォーマットと拡張機能およびWebGLのフォーマット(internalformat)の対応です。

圧縮テクスチャーのフォーマット拡張機能WebGLのフォーマット(internalformat)
DXT1WEBGL_compressed_texture_s3tcCOMPRESSED_RGB_S3TC_DXT1_EXT
DXT5WEBGL_compressed_texture_s3tcCOMPRESSED_RGBA_S3TC_DXT5_EXT
PVRTC(アルファチャンネルなし)WEBGL_compressed_texture_pvrtcCOMPRESSED_RGB_PVRTC_4BPPV1_IMG
PVRTC(アルファチャンネルあり)WEBGL_compressed_texture_pvrtcCOMPRESSED_RGBA_PVRTC_4BPPV1_IMG
ETC1WEBGL_compressed_texture_etc1COMPRESSED_RGB_ETC1_WEBGL

テクスチャーの作成

テクスチャーオブジェクトの観点で見ると、WebGLでは圧縮テクスチャーの扱いは非圧縮テクスチャーとほぼ同じです。唯一違う点はGPUにテクスチャーを転送するときに非圧縮テクスチャーではWebGLRenderingContext.texImage2D()を使っていましたが、圧縮テクスチャーではWebGLRenderingContext.compressedTexImage2D()を使うことです。

// テクスチャーオブジェクトを作成する
const compressedTexture = context.createTexture();

// 作成したテクスチャーオブジェクトをバインドし、操作対象にする
context.bindTexture(context.TEXTURE_2D, compressedTexture);

// 圧縮テクスチャーをGPUへ転送する
context.compressedTexImage2D(context.TEXTURE_2D, 0, extPVRTC.COMPRESSED_RGB_PVRTC_4BPPV1_IMG, 512, 512, 0, pvrtcImageRawData);

WebGLRenderingContext.compressedTexImage2D()の引数は、それぞれ下記の通りです。

  1. target: テクスチャーの種類。通常のテクスチャーの場合はTEXTURE_2Dを指定
  2. level: ミップマップレベル。ベースのテクスチャーの場合は0を指定
  3. internalformat: 圧縮テクスチャーのフォーマット。前述の通り拡張機能オブジェクトから取得した値
  4. width: テクスチャーの幅
  5. height: テクスチャーの高さ
  6. border: 0固定
  7. pixels: 圧縮テクスチャーのRAW data

いくつか注意があります。まず、自動でミップマップを作ってくれる機能WebGLRenderingContext.generateMipmap()は圧縮テクスチャーには使えません。ミップマップを使いたい場合は自前でミップマップ画像を用意し、その数だけcompressedTexImage2Dを第二引数のlevelを指定して転送します。ミップマップを転送しない場合、当たり前ですがWebGLRenderingContext.texParameteri()TEXTURE_MIN_FILTERLINEAR_MIPMAP_LINEARなどのミップマップ系フィルタは使えません。

次に、第七引数pixelsに指定するのはあくまで圧縮テクスチャーのRAW dataということです。ファイルのヘッダーなど余計な情報がついていると転送できません。たとえば、DXTCの圧縮テクスチャーは一般的にDDSというファイル形式で扱われますが、このDDSにはRAW dataの他にフォーマットやファイルサイズなどのデータが含まれています。WebGLで自前でDDSファイルをテクスチャーとして扱うためには、ヘッダー部を読み捨ててRAW dataのみ取り出す必要があります。

これで圧縮テクスチャーを使えるようになりました。一度データを転送さえしてしまえば、描画命令時のバインド処理やシェーダー内での扱いは非圧縮テクスチャーと全く同じです。

アルファチャンネルの対応

DXTCとPVRTCはそれぞれアルファチャンネルに対応していますが、ETC1にはアルファチャンネルはありません。ETC1でアルファつきテクスチャーを使いたい場合は、RGB用のETC1圧縮テクスチャーとアルファチャンネル用のETC1圧縮テクスチャーの2枚を作成して、シェーダー内でRGBとAlphaとして使用します。

precision mediump float;

uniform sampler2D texture;
uniform sampler2D alphaTexture;
varying vec2 vUV;

void main(void)
{
  // RGB要素をtextureから読み込む
  vec4 rgb = texture2D(texture, vUV);

  // Alpha要素をalphaTextureから読み込む
  vec4 alpha = texture2D(alphaTexture, vUV);

  // RGB要素とAlpha要素を統合して出力する
  gl_FragColor = vec4(rgb.rgb, alpha.r);
}

次のページではATFテクスチャーについて解説します。