webgl-expert について
WebGL Expertは、WebGL 1.0と2.0でブラウザー向けグラフィックスを開発する人のためのリファレンススキルです。描画の概念、性能改善、デバッグ、実装時の注意点を扱います。
主な機能
- 01WebGL 1.0と2.0のガイド
- 02シェーダー、バッファー、テクスチャの手順
- 03描画と行列の概念
- 04性能最適化の実践
- 05コンテキスト管理とデバッグ
- 06APIリファレンスとサンプル
活用シーン
- 01ブラウザーでの2D・3D描画の実装
- 02WebGLのバージョンや拡張機能の選択
- 03シェーダーや実行時エラーの調査
- 04描画性能の改善
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ ronnycoding/ .claude --skill webgl-expertインストール後、Claude Code で/webgl-expertを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
reference.mdexamples/basic-triangle.mdtextured-cube.mdtemplates/shader-template.glsl詳細
WebGL エキスパート
WebGL(Web Graphics Library)API 開発の専門ガイドです。Web ブラウザーで高性能な 2D および 3D グラフィックスを描画するための、WebGL 1.0 と WebGL 2.0 の両方を扱います。
概要
WebGL は、プラグインを必要とせずに HTML canvas 要素内でハードウェアアクセラレーションによる 3D グラフィックス描画を可能にする JavaScript API です。OpenGL ES 2.0(WebGL 1.0)および OpenGL ES 3.0(WebGL 2.0)の標準に厳密に準拠しています。
主な機能:
- ハードウェアアクセラレーションによる 2D および 3D 描画
- プログラム可能なシェーダーパイプライン(GLSL)
- テクスチャマッピングと高度なマテリアル
- ライティングと変換システム
- ゲームや可視化向けの高性能なグラフィックス
- クロスプラットフォーム対応(すべての最新ブラウザー)
コアインターフェース
WebGLRenderingContext(WebGL 1.0)
WebGL 操作の基本インターフェースです。canvas のコンテキストを介して取得します。
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
if (!gl) {
console.error('WebGL not supported');
}
WebGL2RenderingContext(WebGL 2.0)
高度な機能を備えた拡張インターフェースです。
const gl = canvas.getContext('webgl2');
if (!gl) {
console.log('WebGL 2 not supported, falling back to WebGL 1');
gl = canvas.getContext('webgl');
}
WebGL 2 専用機能:
- 3D テクスチャ
- サンプラーオブジェクト
- Uniform Buffer Objects(UBO)
- Transform Feedback
- Vertex Array Objects(VAO)— コア機能
- インスタンス描画
- 複数のレンダーターゲット
- 整数テクスチャと属性
- クエリーオブジェクト
- オクルージョンクエリー
描画パイプライン
1. シェーダーの作成とコンパイル
シェーダーは GLSL(OpenGL Shading Language)で記述されたプログラムで、GPU 上で実行されます。
頂点シェーダー — 各頂点を処理します。
attribute vec3 aPosition;
attribute vec2 aTexCoord;
uniform mat4 uModelViewProjection;
varying vec2 vTexCoord;
void main() {
gl_Position = uModelViewProjection * vec4(aPosition, 1.0);
vTexCoord = aTexCoord;
}
フラグメントシェーダー — ピクセルの色を決定します。
precision mediump float;
varying vec2 vTexCoord;
uniform sampler2D uTexture;
void main() {
gl_FragColor = texture2D(uTexture, vTexCoord);
}
JavaScript によるシェーダーのセットアップ:
function createShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error('Shader compilation error:', gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
function createProgram(gl, vertexShader, fragmentShader) {
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error('Program linking error:', gl.getProgramInfoLog(program));
gl.deleteProgram(program);
return null;
}
return program;
}
2. バッファー管理
バッファーには頂点データ(位置、色、法線、テクスチャ座標)を格納します。
// Create buffer
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Upload data
const positions = new Float32Array([
-1.0, -1.0, 0.0,
1.0, -1.0, 0.0,
0.0, 1.0, 0.0
]);
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
// Set up attribute pointer
const positionLocation = gl.getAttribLocation(program, 'aPosition');
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);
バッファーの使用パターン:
gl.STATIC_DRAW— データは変更されませんgl.DYNAMIC_DRAW— データはときどき変更されますgl.STREAM_DRAW— データはフレームごとに変更されます
3. テクスチャの処理
function loadTexture(gl, url) {
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
// Placeholder until image loads
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
new Uint8Array([255, 0, 255, 255]));
const image = new Image();
image.onload = () => {
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
// Generate mipmaps if power of 2
if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
gl.generateMipmap(gl.TEXTURE_2D);
} else {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
}
};
image.src = url;
return texture;
}
function isPowerOf2(value) {
return (value & (value - 1)) === 0;
}
4. 描画ループ
function render(gl, program) {
// Clear canvas
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// Enable depth testing
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
// Use program
gl.useProgram(program);
// Set uniforms
const projectionMatrix = mat4.create();
mat4.perspective(projectionMatrix, Math.PI / 4, canvas.width / canvas.height, 0.1, 100.0);
const uniformLocation = gl.getUniformLocation(program, 'uModelViewProjection');
gl.uniformMatrix4fv(uniformLocation, false, projectionMatrix);
// Draw
gl.drawArrays(gl.TRIANGLES, 0, 3);
// Animation loop
requestAnimationFrame(() => render(gl, program));
}
行列演算
WebGL では、変換に列優先の行列を使用します。推奨ライブラリー:
- glMatrix — 高速な行列・ベクトル演算
- three.js — 数学機能を内蔵した高水準の 3D ライブラリー
一般的な変換:
// Model matrix (object transform)
const modelMatrix = mat4.create();
mat4.translate(modelMatrix, modelMatrix, [x, y, z]);
mat4.rotate(modelMatrix, modelMatrix, angle, [0, 1, 0]);
mat4.scale(modelMatrix, modelMatrix, [sx, sy, sz]);
// View matrix (camera)
const viewMatrix = mat4.create();
mat4.lookAt(viewMatrix, eyePosition, targetPosition, upVector);
// Projection matrix
const projectionMatrix = mat4.create();
mat4.perspective(projectionMatrix, fov, aspect, near, far);
// Combined MVP matrix
const mvpMatrix = mat4.create();
mat4.multiply(mvpMatrix, projectionMatrix, viewMatrix);
mat4.multiply(mvpMatrix, mvpMatrix, modelMatrix);
パフォーマンスの最適化
ベストプラクティス
- 状態変更を最小限にする — 状態が似た描画呼び出しをまとめます
- Vertex Array Objects(VAO)を使用する — 属性セットアップのオーバーヘッドを削減します
- テクスチャアトラス — 複数のテクスチャを 1 つにまとめます
- インスタンス描画 — 類似したオブジェクトを効率よく多数描画します
- 視錐台カリング — 視野の外にあるオブジェクトは描画しません
- 詳細度(LOD) — 距離がある場合は、より単純なモデルを使用します
- テクスチャ圧縮 — 圧縮テクスチャ形式(DXT、ETC、ASTC)を使用します
- シェーダーの複雑さを抑える — フラグメントシェーダーをシンプルに保ちます
- ユニフォームバッファーを使用する(WebGL 2)— ユニフォームデータを効率よく共有します
- CPU と GPU の同期を避ける — データを頻繁に読み戻さないようにします
インスタンス描画(WebGL 2)
const ext = gl.getExtension('ANGLE_instanced_arrays'); // WebGL 1
// or use gl.drawArraysInstanced directly in WebGL 2
// Set up per-instance attribute
const instanceOffsetBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, instanceOffsetBuffer);
gl.bufferData(gl.ARRAY_BUFFER, offsetData, gl.STATIC_DRAW);
const offsetLocation = gl.getAttribLocation(program, 'aInstanceOffset');
gl.enableVertexAttribArray(offsetLocation);
gl.vertexAttribPointer(offsetLocation, 3, gl.FLOAT, false, 0, 0);
gl.vertexAttribDivisor(offsetLocation, 1); // Advance per instance
// Draw multiple instances
gl.drawArraysInstanced(gl.TRIANGLES, 0, vertexCount, instanceCount);
拡張機能システム
高度な機能を利用するには、拡張機能の有無を確認して使用します。
function getExtension(gl, name) {
const ext = gl.getExtension(name);
if (!ext) {
console.warn(`Extension ${name} not supported`);
}
return ext;
}
// Common extensions
const anisotropic = getExtension(gl, 'EXT_texture_filter_anisotropic');
const floatTextures = getExtension(gl, 'OES_texture_float');
const depthTexture = getExtension(gl, 'WEBGL_depth_texture');
const drawBuffers = getExtension(gl, 'WEBGL_draw_buffers');
const loseContext = getExtension(gl, 'WEBGL_lose_context'); // for testing
重要な拡張機能のカテゴリ:
- テクスチャ形式: WEBGL_compressed_texture_s3tc、WEBGL_compressed_texture_etc
- 描画: WEBGL_draw_buffers、EXT_blend_minmax、EXT_frag_depth
- 精度: OES_texture_float、OES_texture_half_float
- インスタンス描画: ANGLE_instanced_arrays(WebGL 1)
- デバッグ: WEBGL_debug_renderer_info、WEBGL_debug_shaders
コンテキスト管理
コンテキスト喪失への対応
canvas.addEventListener('webglcontextlost', (event) => {
event.preventDefault();
console.log('WebGL context lost');
cancelAnimationFrame(animationId);
}, false);
canvas.addEventListener('webglcontextrestored', () => {
console.log('WebGL context restored');
initWebGL(); // Recreate all resources
render();
}, false);
コンテキスト作成オプション
const gl = canvas.getContext('webgl2', {
alpha: false, // No alpha channel (better performance)
antialias: true, // Antialiasing (performance cost)
depth: true, // Depth buffer
stencil: false, // Stencil buffer
premultipliedAlpha: true, // Alpha premultiplication
preserveDrawingBuffer: false, // Keep buffer after render
powerPreference: 'high-performance', // GPU preference
failIfMajorPerformanceCaveat: false // Fallback to software
});
一般的なパターン
フレームバッファー描画(テクスチャへの描画)
const framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
const targetTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, targetTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, targetTexture, 0);
// Render to framebuffer
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.viewport(0, 0, width, height);
// ... render scene ...
// Render to canvas
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, canvas.width, canvas.height);
複数のレンダーターゲット(WebGL 2)
const ext = gl.getExtension('WEBGL_draw_buffers'); // WebGL 1
// Fragment shader outputs to multiple targets
gl.drawBuffers([
gl.COLOR_ATTACHMENT0,
gl.COLOR_ATTACHMENT1,
gl.COLOR_ATTACHMENT2
]);
よくある落とし穴
- コンパイル/リンクエラーを確認しない - 常にシェーダーのステータスを確認してください
- 属性の有効化を忘れる -
gl.enableVertexAttribArray()を呼び出してください - データ型が正しくない -
Float32Array、Uint16Arrayなどを使用してください - コンテキスト喪失に対処しない - イベントリスナーを追加してください
- WebGL 1 と 2 の API を混在させる - バージョンの互換性を確認してください
- 2 の累乗テクスチャを前提にする - POT 以外のテクスチャを正しく処理してください
- Z ファイティング - 深度バッファの精度が不十分です
- 座標系の混同 - WebGL ではクリップ空間 [-1, 1] を使用します
- 時期尚早な最適化 - 最適化の前にプロファイリングしてください
- バッファをクリアしない - 毎フレーム
gl.clear()を呼び出してください
デバッグツール
- ブラウザーの DevTools - コンソールで WebGL エラーを確認します
- WebGL Inspector - フレームキャプチャ用のブラウザー拡張機能
- Spector.js - WebGL デバッグライブラリー
- gl.getError() - 実行時エラーを確認します
- WEBGL_debug_shaders - 変換後のシェーダーソースを取得します
// Error checking
const error = gl.getError();
if (error !== gl.NO_ERROR) {
console.error('WebGL error:', error);
}
人気のライブラリーとフレームワーク
- three.js - シーングラフを備えた包括的な 3D ライブラリー
- Babylon.js - 物理演算と VR に対応したゲームエンジン
- PlayCanvas - クラウドベースのゲームエンジン
- Pixi.js - 高速な 2D WebGL レンダラー
- Phaser - 2D ゲームフレームワーク
- regl - 関数型の WebGL ラッパー
- twgl - 小型の WebGL ヘルパーライブラリー
- glMatrix - 高性能な行列/ベクトルライブラリー
学習リソース
- MDN WebGL チュートリアル
- WebGL の基礎
- シェーダーの本
- Shadertoy - シェーダーの例
- WebGL2 の基礎
クイックリファレンス
reference.md を参照してください:
- 定数の完全なリファレンス
- すべての WebGL メソッド
- GLSL 組み込み関数
- 拡張機能の互換性マトリックス
examples を参照してください:
- 基本的な三角形のレンダリング
- テクスチャマッピング
- ライティングモデル
- 高度なテクニック
バージョンの互換性
WebGL 1 と 2 の両方に対応する場合:
function initWebGL(canvas) {
const gl = canvas.getContext('webgl2');
let version = 2;
if (!gl) {
gl = canvas.getContext('webgl');
version = 1;
console.log('Using WebGL 1');
}
// Feature detection
const hasVAO = version === 2 || gl.getExtension('OES_vertex_array_object');
const hasInstancing = version === 2 || gl.getExtension('ANGLE_instanced_arrays');
return { gl, version, hasVAO, hasInstancing };
}
セキュリティに関する考慮事項
- クロスオリジンテクスチャ - CORS を適切に使用してください
- シェーダーの検証 - ユーザーが提供するシェーダーコードを検証してください
- リソース制限 - クライアントが報告する機能を信頼しないでください
- タイミング攻撃 - シェーダーのコンパイル時間に注意してください
- コンテキストフィンガープリンティング - プライバシー保護のために WebGL をブロックするユーザーもいます
WebGL についてユーザーを支援する際は:
- バージョンを特定する - WebGL 1 と 2 のどちらが必要か確認してください
- 要件を確認する - ブラウザーのサポートと必要な拡張機能を確認してください
- シンプルに始める - 高度な機能の前に、基本的なレンダリングから始めてください
- 体系的にデバッグする - シェーダー、バッファ、状態の順に確認してください
- パフォーマンスをプロファイリングする - ブラウザーツールを使ってボトルネックを特定してください
- ライブラリーを検討する - 複雑なプロジェクトには three.js/Babylon.js を推奨してください
- 入力を検証する - null のコンテキストやコンパイルエラーがないか確認してください
- コンテキスト喪失に対処する - 必ず復旧処理を実装してください
- 適切に最適化する - 初期段階で過剰に最適化しないでください
- さまざまなデバイスでテストする - GPU の機能は大きく異なります