Skip to content

Instantly share code, notes, and snippets.

@kobitoDevelopment
Created November 11, 2025 12:31
Show Gist options
  • Select an option

  • Save kobitoDevelopment/2c62f69d51d3c68a0a5f7f796329a6b5 to your computer and use it in GitHub Desktop.

Select an option

Save kobitoDevelopment/2c62f69d51d3c68a0a5f7f796329a6b5 to your computer and use it in GitHub Desktop.
canvas {
display: block;
width: 320px;
aspect-ratio: 16/9;
}
<button id="createContext">WebGLコンテキスト作成</button>
<input type="number" id="deleteNumber" min="1" value="1" />
<button id="deleteSpecific">番号を指定して削除</button>
<button id="deleteAll">全て削除</button>
<div id="canvasContainer"></div>
// WebGLコンテキスト管理用
const contexts = new Map();
let contextCounter = 0;
// 頂点シェーダー
const vertexShaderSource = `
attribute vec2 aPosition;
attribute vec2 aTexCoord;
varying vec2 vTexCoord;
void main() {
gl_Position = vec4(aPosition, 0.0, 1.0);
vTexCoord = aTexCoord;
}
`;
// フラグメントシェーダー
const fragmentShaderSource = `
precision mediump float;
varying vec2 vTexCoord;
uniform sampler2D uTexture;
void main() {
gl_FragColor = texture2D(uTexture, vTexCoord);
}
`;
// シェーダーコンパイル
function compileShader(gl, source, type) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error('シェーダーコンパイルエラー:', gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
// シェーダープログラム作成
function createShaderProgram(gl, vsSource, fsSource) {
const vertexShader = compileShader(gl, vsSource, gl.VERTEX_SHADER);
const fragmentShader = compileShader(gl, fsSource, gl.FRAGMENT_SHADER);
if (!vertexShader || !fragmentShader) {
return null;
}
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error('シェーダーリンクエラー:', gl.getProgramInfoLog(program));
gl.deleteProgram(program);
return null;
}
gl.deleteShader(vertexShader);
gl.deleteShader(fragmentShader);
return program;
}
// テクスチャ読み込み
function loadTexture(gl, url, callback) {
const texture = gl.createTexture();
const image = new Image();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
new Uint8Array([0, 0, 0, 255]));
image.onload = () => {
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
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);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
if (callback) callback();
};
image.src = url;
return texture;
}
// 描画関数
function render(gl, shaderProgram, vertexBuffer, texCoordBuffer, texture) {
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(shaderProgram);
// 頂点座標の設定
const aPosition = gl.getAttribLocation(shaderProgram, 'aPosition');
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aPosition);
// テクスチャ座標の設定
const aTexCoord = gl.getAttribLocation(shaderProgram, 'aTexCoord');
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.vertexAttribPointer(aTexCoord, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aTexCoord);
// テクスチャの設定
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
const uTexture = gl.getUniformLocation(shaderProgram, 'uTexture');
gl.uniform1i(uTexture, 0);
// 描画
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
// WebGLコンテキスト作成
function createContext() {
contextCounter++;
const contextId = contextCounter;
// canvas要素作成
const container = document.createElement("div");
container.className = "canvas-item";
container.innerHTML = `
<h2>${contextId}</h2>
<canvas style="width: 200px; height: 150px;"></canvas>
`;
document.getElementById("canvasContainer").appendChild(container);
const canvas = container.querySelector("canvas");
// デバイスピクセル比に対応したcanvasサイズ設定
const canvasInfo = canvas.getBoundingClientRect();
const devicePixelRatio = window.devicePixelRatio || 1;
canvas.width = canvasInfo.width * devicePixelRatio;
canvas.height = canvasInfo.height * devicePixelRatio;
// WebGLコンテキスト作成
const gl = canvas.getContext("webgl");
if (!gl) {
console.error(`WebGLコンテキスト #${contextId} の作成に失敗しました`);
container.style.backgroundColor = "#ffeeee";
return;
}
// リソース作成
const shaderProgram = createShaderProgram(gl, vertexShaderSource, fragmentShaderSource);
if (!shaderProgram) {
console.error(`シェーダープログラム #${contextId} の作成に失敗しました`);
return;
}
// 頂点バッファ
const vertices = new Float32Array([
-1.0, -1.0,
1.0, -1.0,
-1.0, 1.0,
1.0, 1.0,
]);
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// テクスチャ座標バッファ
const texCoords = new Float32Array([
0.0, 1.0,
1.0, 1.0,
0.0, 0.0,
1.0, 0.0,
]);
const texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, texCoords, gl.STATIC_DRAW);
// テクスチャ読み込み
const texture = loadTexture(gl, 'assets/images/1.jpg', () => {
gl.viewport(0, 0, canvas.width, canvas.height);
render(gl, shaderProgram, vertexBuffer, texCoordBuffer, texture);
});
// コンテキスト情報を保存
contexts.set(contextId, {
id: contextId,
canvas: canvas,
container: container,
gl: gl,
shaderProgram: shaderProgram,
vertexBuffer: vertexBuffer,
texCoordBuffer: texCoordBuffer,
texture: texture
});
console.log(`WebGLコンテキスト #${contextId} を作成しました`);
console.log(`アクティブなコンテキスト数: ${contexts.size}`);
}
// 指定番号のWebGLコンテキスト削除
function deleteContext(contextId) {
const context = contexts.get(contextId);
if (!context) {
console.log(`コンテキスト #${contextId} は存在しません`);
return;
}
const { gl, shaderProgram, vertexBuffer, texCoordBuffer, texture, container, canvas } = context;
// リソースの削除
if (texture) gl.deleteTexture(texture);
if (vertexBuffer) gl.deleteBuffer(vertexBuffer);
if (texCoordBuffer) gl.deleteBuffer(texCoordBuffer);
if (shaderProgram) gl.deleteProgram(shaderProgram);
// WebGLコンテキストの破棄
const loseContext = gl.getExtension("WEBGL_lose_context");
if (loseContext) {
loseContext.loseContext();
}
// canvas要素を新しいものに置き換えて確実にコンテキストを切断
const newCanvas = document.createElement('canvas');
newCanvas.style.cssText = canvas.style.cssText;
canvas.parentNode.replaceChild(newCanvas, canvas);
// DOM要素を削除
container.remove();
// Mapから削除
contexts.delete(contextId);
console.log(`WebGLコンテキスト #${contextId} を削除しました`);
console.log(`アクティブなコンテキスト数: ${contexts.size}`);
}
// 全てのWebGLコンテキストを削除
function deleteAllContexts() {
const contextIds = Array.from(contexts.keys());
contextIds.forEach(id => {
deleteContext(id);
});
console.log('全てのWebGLコンテキストを削除しました');
}
// イベントリスナー設定
document.addEventListener("DOMContentLoaded", () => {
document.getElementById("createContext").addEventListener("click", createContext);
document.getElementById("deleteSpecific").addEventListener("click", () => {
const deleteNumber = parseInt(document.getElementById("deleteNumber").value);
deleteContext(deleteNumber);
});
document.getElementById("deleteAll").addEventListener("click", deleteAllContexts);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment