-
-
Save kobitoDevelopment/2c62f69d51d3c68a0a5f7f796329a6b5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| canvas { | |
| display: block; | |
| width: 320px; | |
| aspect-ratio: 16/9; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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