Skip to content

Instantly share code, notes, and snippets.

@kobitoDevelopment
Last active September 12, 2025 08:27
Show Gist options
  • Select an option

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

Select an option

Save kobitoDevelopment/d2022be2d32b0b49aadd33bf9c16f50c to your computer and use it in GitHub Desktop.
#canvas {
display: block;
width: 100%;
height: 100vh;
}
<div>
<span>進捗率</span>
<span class="progress"></span>
</div>
<div>
<span>マウス座標</span>
<span class="mousePosition"></span>
</div>
<button class="trigger">スタート</button>
<canvas id="canvas"></canvas>
/* ======================================
線形補完用の関数
======================================= */
// (加減, 上限, 割合)を渡して、下限と上限の間の割合%にあたる数値がどの値にあたるかを返す
function lerp(x, y, a) {
return (1 - a) * x + a * y;
}
// (加減, 上限, 現在の値)を渡して、現在の値が下限と上限の間を0~1とした場合にどの値にあたるかを返す
function norm(v, a, b) {
return (v - a) / (b - a);
}
/* ======================================
マウス、タッチ座標をシェーダー内で使うための準備
======================================= */
// マウスの座標
const mouse = {
current: {
x: 0,
y: 0,
},
last: {
x: 0,
y: 0,
},
inertia: {
x: 0,
y: 0,
},
velocity: {
x: 0,
y: 0,
},
};
// マウス座標を監視する関数
const mousemove = function (event) {
mouse.last.x = mouse.current.x;
mouse.last.y = mouse.current.y;
mouse.current.x = event.offsetX;
mouse.current.y = event.offsetY;
mouse.velocity.x = (mouse.current.x - mouse.last.x) / 16;
mouse.velocity.y = (mouse.current.y - mouse.last.y) / 16;
// マウス移動時にも波紋を追加
if (mouse.current.x !== 0 || mouse.current.y !== 0) {
addRippleAt(mouse.current.x, mouse.current.y);
}
};
// タッチ座標を監視する関数
const touchmove = function (event) {
const rect = event.target.getBoundingClientRect();
const offsetX = event.touches[0].clientX - window.pageXOffset - rect.left;
const offsetY = event.touches[0].clientY - window.pageYOffset - rect.top;
mouse.last.x = mouse.current.x;
mouse.last.y = mouse.current.y;
mouse.current.x = offsetX;
mouse.current.y = offsetY;
mouse.velocity.x = (mouse.current.x - mouse.last.x) / 16;
mouse.velocity.y = (mouse.current.y - mouse.last.y) / 16;
};
/* マウスとタッチの座標を監視 */
document.getElementById("canvas").addEventListener("mousemove", mousemove);
document.getElementById("canvas").addEventListener("touchmove", touchmove);
/* 波紋を追加する関数 */
function addRippleAt(x, y) {
if (!gl || !programInfoRippleDrop) return;
// 現在の読み込み用バッファの内容を書き込み用バッファにコピー
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffers[bufferWriteIndex].framebuffer);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.useProgram(programInfoRippleDrop.program);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, framebuffers[bufferReadIndex].texture);
const uTexturePrevFrameDrop = gl.getUniformLocation(programInfoRippleDrop.program, "uTexturePrevFrame");
const uDropCenter = gl.getUniformLocation(programInfoRippleDrop.program, "uDropCenter");
const uDropRadius = gl.getUniformLocation(programInfoRippleDrop.program, "uDropRadius");
const uDropStrength = gl.getUniformLocation(programInfoRippleDrop.program, "uDropStrength");
// 座標正規化(-1から1の範囲に変換)
const elWidth = gl.canvas.width;
const elHeight = gl.canvas.height;
const normalizedX = (2 * x) / elWidth - 1.0;
const normalizedY = 1.0 - (2 * y) / elHeight;
const radius = 20;
const longestSide = Math.max(elWidth, elHeight);
const normalizedRadius = radius / longestSide;
gl.uniform1i(uTexturePrevFrameDrop, 0);
gl.uniform2fv(uDropCenter, [normalizedX, normalizedY]);
gl.uniform1f(uDropRadius, normalizedRadius);
gl.uniform1f(uDropStrength, 0.01);
gl.drawArrays(gl.TRIANGLES, 0, programInfoRippleDrop.verticeNum);
// バッファを切り替え
bufferWriteIndex = 1 - bufferWriteIndex;
bufferReadIndex = 1 - bufferReadIndex;
}
document.getElementById("canvas").addEventListener("mousedown", function (event) {
addRippleAt(event.offsetX, event.offsetY);
});
document.getElementById("canvas").addEventListener("touchstart", function (event) {
const rect = event.target.getBoundingClientRect();
const offsetX = event.touches[0].clientX - window.pageXOffset - rect.left;
const offsetY = event.touches[0].clientY - window.pageYOffset - rect.top;
addRippleAt(offsetX, offsetY);
});
/* ======================================
リサイズ対応
======================================= */
function resize(resizedCanvas) {
canvasInfo = canvas.getBoundingClientRect();
canvas.width = canvasInfo.width;
canvas.height = canvasInfo.height;
resizedCanvas.viewportWidth = canvas.width;
resizedCanvas.viewportHeight = canvas.height;
}
/* ======================================
再帰実行するアニメーション用の準備
======================================= */
/* アニメーション管理位用ID */
let animationID;
/* tweenさせたいデータを格納 */
const tweenData = {
num: 0,
};
/* tweenのイージング関数(linearにあたる関数で等速) */
function easeInOutQuart(x) {
return x < 0.5 ? 8 * x * x * x * x : 1 - Math.pow(-2 * x + 2, 4) / 2;
}
function tween(obj, from, value, duration, easing) {
let start = null;
function step(timestamp) {
if (!start) {
start = timestamp;
}
let progress = timestamp - start;
obj.num = from + easing(progress / duration) * (value - from);
if (progress < duration) {
animationID = requestAnimationFrame(step);
} else {
obj.num = value;
}
}
animationID = requestAnimationFrame(step);
}
const trigger = document.querySelector(".trigger");
trigger.addEventListener("click", function () {
tween(tweenData, 0, 1.0, 2000, easeInOutQuart);
});
/* ======================================
WebGLを実行するためのデータを格納する変数を準備
======================================= */
let gl; // webGLを実行するためのデータを格納する変数
let canvasInfo;
/* フレームバッファ関連の変数 */
let framebuffers = []; // 2つのフレームバッファを格納
let bufferWriteIndex = 0; // 書き込み用バッファのインデックス
let bufferReadIndex = 1; // 読み込み用バッファのインデックス
let programInfoRipple; // ripple更新用プログラム情報
let programInfoRippleDrop; // ripple追加用プログラム情報
/* ======================================
webGLを実行するための全ての処理をまとめた関数
======================================= */
function startup() {
let canvas = document.querySelector("#canvas"); // webGLを実行するためのcanvas要素を指定
canvasInfo = canvas.getBoundingClientRect(); // canvas要素の位置とサイズを取得
gl = createGLContext(canvas); // 上記で取得した要素にwebGL実行環境を実装
canvas.width = canvasInfo.width; // canvasの横幅を使用できるよう格納
canvas.height = canvasInfo.height; // canvasの縦幅を使用できるよう格納
/* 2つのフレームバッファを作成 */
for (let i = 0; i < 2; i++) {
framebuffers.push(createFramebuffer(gl, gl.canvas.width, gl.canvas.height));
}
/* フレームバッファを初期化(透明な黒でクリア) */
for (let i = 0; i < 2; i++) {
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffers[i].framebuffer);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
}
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
/* メインのシェーダーを準備 */
const shaderProgram = setupShaders(vertexShaderMain, fragmentShaderMain);
const programInfo = {
program: shaderProgram,
attribLocations: {
position: gl.getAttribLocation(shaderProgram, "aVertexPosition"),
color: gl.getAttribLocation(shaderProgram, "aVertexColor"),
texCoordLocation: gl.getAttribLocation(shaderProgram, "aTexCoord"),
},
};
/* ripple追加用のシェーダーを準備 */
const shaderProgramRippleDrop = setupShaders(vertexShaderMain, fragmentShaderRippleDrop);
programInfoRippleDrop = {
program: shaderProgramRippleDrop,
attribLocations: {
position: gl.getAttribLocation(shaderProgramRippleDrop, "aVertexPosition"),
color: gl.getAttribLocation(shaderProgramRippleDrop, "aVertexColor"),
texCoordLocation: gl.getAttribLocation(shaderProgramRippleDrop, "aTexCoord"),
},
};
programInfoRippleDrop.verticeNum = setupBuffers(programInfoRippleDrop);
/* ripple更新用のシェーダーを準備 */
const shaderProgramRipple = setupShaders(vertexShaderMain, fragmentShaderRippleUpdate);
programInfoRipple = {
program: shaderProgramRipple,
attribLocations: {
position: gl.getAttribLocation(shaderProgramRipple, "aVertexPosition"),
color: gl.getAttribLocation(shaderProgramRipple, "aVertexColor"),
texCoordLocation: gl.getAttribLocation(shaderProgramRipple, "aTexCoord"),
},
};
programInfoRipple.verticeNum = setupBuffers(programInfoRipple);
programInfo.verticeNum = setupBuffers(programInfo); // 頂点バッファーのセットアップと頂点数の取得
// 使用するテクスチャをそれぞれロードしてバインドする
texture1 = loadTexture(loadedImageArray[0]);
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight); // WebGLのコンテキストとキャンパスのサイズを同じにする。
gl.useProgram(programInfo.program); // WebGLのコンテキストが使用するプログラムを指定
draw(programInfo);
}
/* ======================================
画像の読み込みを待機、ロード完了後にPromise を返却する関数
======================================= */
function loadImage(imagePath) {
return new Promise((resolve) => {
const img = new Image();
img.src = imagePath;
img.addEventListener("load", function () {
return resolve(img);
});
});
}
/* ======================================
テクスチャとして使用する全ての画像のロードが完了したら実行
======================================= */
const imagesPath = ["assets/images/1.jpg"];
let loadedImageArray = [];
/* 使用するテクスチャの数だけ変数を準備 */
let texture1;
const requestsImages = imagesPath.map(function (imagePath) {
return loadImage(imagePath);
});
async function startWebGL() {
await Promise.all(requestsImages).then(function (images) {
loadedImageArray = images;
});
startup();
}
startWebGL();
/* ======================================
WebGLを実行する場所(コンテキスト)を作成する関数
======================================= */
function createGLContext(canvas) {
let ctx = canvas.getContext("webgl"); // webGLの実行コンテキストを取得
if (ctx) {
ctx.viewportWidth = canvas.width; // コンテキストの横幅をcanvasの横幅と一致させる
ctx.viewportHeight = canvas.height; // コンテキストの縦幅をcanvasの横幅と一致させる
} else {
console.log("webGLを実行できません。");
}
return ctx;
}
/* ======================================
シェーダーコードを読み込む関数 開始
====================================== */
// fragment、またはvertexのシェーダータイプを指定して呼び出す
function loadShader(type, shaderSource) {
const shader = gl.createShader(type);
gl.shaderSource(shader, shaderSource); // シェーダーコードをバイナリコードにコンパイル(解析の際、シェーダーコードにindexが付与される)
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert("シェーダーの作成に失敗しました。\n" + gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
/* =====================================
オフスクリーンレンダリング用にフレームバッファを作成する関数
====================================== */
function createFramebuffer(gl, width, height) {
const framebuffer = gl.createFramebuffer(); // フレームバッファ
const depthRenderBuffer = gl.createRenderbuffer(); // レンダーバッファ
const texture = gl.createTexture(); // テクスチャ
// フレームバッファをバインド
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
// レンダーバッファをバインド
gl.bindRenderbuffer(gl.RENDERBUFFER, depthRenderBuffer);
// レンダーバッファを深度バッファとして設定する
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
// フレームバッファにレンダーバッファを関連付けする
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthRenderBuffer);
// テクスチャをユニット0にバインド
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
// テクスチャにサイズなどを設定する(ただし中身は null)
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
// テクスチャパラメータを設定
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
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.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
// すべてのオブジェクトは念の為バインドを解除しておく
gl.bindTexture(gl.TEXTURE_2D, null);
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
// 各オブジェクトを、JavaScript のオブジェクトに格納して返す
return {
framebuffer: framebuffer,
depthRenderbuffer: depthRenderBuffer,
texture: texture,
};
}
/* ======================================
vertexShaderとfragmentShaderを記述
====================================== */
/* メインのvertexShader */
const vertexShaderMain = `
precision mediump float; // precision...精度修飾子。どの程度の精度でデータを扱うか(law mideun high があり、highpにすると負荷が高くなる)
/*
attribute... 頂点ごとに異なるデータを受け取る変数(バッファから座標などの情報を取得)
uniform...全頂点で共通の値を持つデータを宣言(取得)
*/
attribute vec2 aVertexPosition; // aVertexPosition...頂点情報の座標を表すattribute
attribute vec3 aVertexColor; // aVertextColor...頂点情報の色を表すattribute
attribute vec2 aTexCoord; // 画像を描画するためのテクスチャ座標
/*
varying... vertexShaderからfragmentShaderへ変数を渡す
*/
varying vec2 vTexCoord; // テクスチャ座標をfragmentShaderへ渡す
varying vec2 vVertexPosition; // 頂点座標をfragmentShaderへ渡す
varying vec3 vVertexColor; // 頂点に設定した色をfragmentShaderへ渡す
/*
void main(){}に書いた処理が実行される(これ以前に、main内で使用する関数・変数の定義は可能)
*/
void main() {
vTexCoord = aTexCoord;
vVertexPosition = aVertexPosition;
vVertexColor = aVertexColor;
gl_Position = vec4(aVertexPosition, 0.0, 1.0);
}
`;
/* メインのfragmentShader. */
const fragmentShaderMain = `
precision highp float;
uniform sampler2D uTexture1; // テクスチャ画像1枚目
uniform sampler2D uTexturePrevFrame; // 前フレームの描画結果を保持するテクスチャ
uniform vec2 uResolution; // 画面幅(w,h)
varying vec2 vTexCoord; // テクスチャ座標
void main() {
/* 1枚目のテクスチャ(1.jpg)にripple効果を適用 */
vec4 rippleInfo = texture2D(uTexturePrevFrame, vTexCoord);
// rippleの高さ情報から歪み計算
float height = rippleInfo.r;
vec2 delta = vec2(1.0 / uResolution.x, 1.0 / uResolution.y);
float heightX = texture2D(uTexturePrevFrame, vTexCoord + vec2(delta.x, 0.0)).r;
float heightY = texture2D(uTexturePrevFrame, vTexCoord + vec2(0.0, delta.y)).r;
// 法線ベクトルを計算
vec3 dx = vec3(delta.x, heightX - height, 0.0);
vec3 dy = vec3(0.0, heightY - height, delta.y);
vec2 offset = -normalize(cross(dy, dx)).xz;
// 歪み強度
float perturbance = 0.03;
vec2 distortedCoord = vTexCoord + offset * perturbance;
// 1.jpgに歪み効果を適用(Y軸反転)
vec2 flippedDistortedCoord = vec2(distortedCoord.x, 1.0 - distortedCoord.y);
gl_FragColor = texture2D(uTexture1, flippedDistortedCoord);
// スペキュラーハイライトを追加
float specular = pow(max(0.0, dot(offset, normalize(vec2(-0.6, 1.0)))), 4.0);
gl_FragColor.rgb += specular;
}
`;
/* 波紋追加用のfragmentShader */
const fragmentShaderRippleDrop = `
precision highp float;
const float PI = 3.141592653589793;
uniform sampler2D uTexturePrevFrame;
uniform vec2 uDropCenter;
uniform float uDropRadius;
uniform float uDropStrength;
varying vec2 vTexCoord;
void main() {
vec4 info = texture2D(uTexturePrevFrame, vTexCoord);
float drop = max(0.0, 1.0 - length(uDropCenter * 0.5 + 0.5 - vTexCoord) / uDropRadius);
drop = 0.5 - cos(drop * PI) * 0.5;
info.r += drop * uDropStrength;
gl_FragColor = info;
}
`;
/* 波紋更新用のfragmentShader */
const fragmentShaderRippleUpdate = `
precision highp float;
uniform sampler2D uTexturePrevFrame;
uniform vec2 uResolution;
varying vec2 vTexCoord;
void main() {
vec4 info = texture2D(uTexturePrevFrame, vTexCoord);
vec2 delta = vec2(1.0 / uResolution.x, 1.0 / uResolution.y);
// 隣接ピクセルの高さを取得
float average = (
texture2D(uTexturePrevFrame, vTexCoord - vec2(delta.x, 0.0)).r +
texture2D(uTexturePrevFrame, vTexCoord - vec2(0.0, delta.y)).r +
texture2D(uTexturePrevFrame, vTexCoord + vec2(delta.x, 0.0)).r +
texture2D(uTexturePrevFrame, vTexCoord + vec2(0.0, delta.y)).r
) * 0.25;
// 波動方程式
info.g += (average - info.r) * 2.0;
info.g *= 0.995; // 減衰
info.r += info.g;
gl_FragColor = info;
}
`;
/* ======================================
シェーダーコードをWebGLコンテキストにバインド
====================================== */
function setupShaders(vertexShaderSource, fragmentShaderSource) {
/* =====================================
シェーダーを読み込む 開始
====================================== */
const vertexShader = loadShader(gl.VERTEX_SHADER, vertexShaderSource); // vertexShaderの読み込み(第一引数にシェーダータイプ、第二引数にシェーダーコード)
const fragmentShader = loadShader(gl.FRAGMENT_SHADER, fragmentShaderSource); // fragmentShaderの読み込み(第一引数にシェーダータイプ、第二引数にシェーダーコード)
/* =====================================
webGLプログラムオブジェクトを作成 開始
====================================== */
const shaderProgram = gl.createProgram(); // シェーダープログラムを作成
gl.attachShader(shaderProgram, vertexShader); // プログラムにシェーダーをバインド(追加)
gl.attachShader(shaderProgram, fragmentShader); // vertexシェーダーとfragmentシェーダーをリンク(varying等でデータを受け渡せるように)
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert("シェーダーセットアップに失敗しました。\n環境による問題が発生しています。");
}
return shaderProgram;
}
/* =====================================
テクスチャを読み込む関数
====================================== */
function loadTexture(info) {
const texture = gl.createTexture(); // テクスチャオブジェクトを作成する
gl.bindTexture(gl.TEXTURE_2D, texture); // テクスチャオブジェクトをバインドする
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, info); // 画像データをテクスチャに設定する
// テクスチャのラップモードを設定する
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.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
// ピクセルの格納方法を設定する(画像のY軸の反転を無効化する)
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
return texture;
}
/* =====================================
バッファーのセットアップとテクスチャのロード
====================================== */
function setupBuffers(pInfo) {
/* 頂点を設定 */
const vertexPositionBuffer = gl.createBuffer(); // 頂点座標のバッファーを作成する
const verticeNum = 6; // 頂点の数
const triangleVertices = [-1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1]; // 頂点座標(-1 から 1の間、原点は中心(0,0))
gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer); // ARRAY_BUFFERに頂点データを格納するバッファを紐づける
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(triangleVertices), gl.STATIC_DRAW); // ARRAY_BUFFERバッファに頂点データをロードする
gl.vertexAttribPointer(pInfo.attribLocations.position, 2, gl.FLOAT, false, 0, 0); // シェーダーコード内から抽出された属性(attribute)と上記でアップした頂点データを紐づける
gl.enableVertexAttribArray(pInfo.attribLocations.position); // 属性を有効化する
// テクスチャ座標のバッファーを作成する(フレームバッファ用にY軸を統一)
const imageVertices = [0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0]; // テクスチャ座標(フレームバッファ用)
const texCoordBuffer = gl.createBuffer();
// テクスチャ座標バッファーをバインドし、データを設定する
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer); // ARRAY_BUFFERにテクスチャ座標データを格納するバッファを紐づける
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(imageVertices), gl.STATIC_DRAW); // 画像を表示させる領域を頂点で囲む
gl.enableVertexAttribArray(pInfo.attribLocations.texCoordLocation); // 属性を有効化する
gl.vertexAttribPointer(pInfo.attribLocations.texCoordLocation, 2, gl.FLOAT, false, 0, 0); //シェーダーコード内から抽出された属性(attribute)と上記でアップしたテクスチャ座秒データを紐づける
/* 頂点の色を設定する場合 */
// 頂点の色
const colorVertices = [1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1];
const vertexColorBuffer = gl.createBuffer(); // 位置を管理する頂点の入れ物(バッファ)を作成
gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer); // ARRAY_BUFFERに頂点データを格納するバッファを紐づける
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colorVertices), gl.STATIC_DRAW); // ARRAY_BUFFERバッファに頂点データをロードする
gl.vertexAttribPointer(pInfo.attribLocations.color, colorVertices.length / verticeNum, gl.FLOAT, false, 0, 0); // シェーダーコード内から抽出された属性(attribute)と上記でアップした頂点データを紐づける
gl.enableVertexAttribArray(pInfo.attribLocations.color); // 属性を有効化する
return verticeNum; // 頂点数を返す
}
function draw(pInfo) {
// シェーダー内で使うuniformを設定
const uResolutionLoc = gl.getUniformLocation(pInfo.program, "uResolution");
const uTextureLoc1 = gl.getUniformLocation(pInfo.program, "uTexture1");
const uTexturePrevFrame = gl.getUniformLocation(pInfo.program, "uTexturePrevFrame");
/* ======================================
requestAnimationFrameを再起実行
======================================= */
function animate(timestamp) {
/* リサイズ対応 */
resize(gl);
/* ステップ2: 波紋更新シェーダーで波紋を拡散(毎フレーム実行) */
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffers[bufferWriteIndex].framebuffer);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.useProgram(programInfoRipple.program);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, framebuffers[bufferReadIndex].texture);
const uTexturePrevFrameRipple = gl.getUniformLocation(programInfoRipple.program, "uTexturePrevFrame");
const uResolutionRipple = gl.getUniformLocation(programInfoRipple.program, "uResolution");
gl.uniform1i(uTexturePrevFrameRipple, 0);
gl.uniform2fv(uResolutionRipple, [gl.canvas.width, gl.canvas.height]);
gl.drawArrays(gl.TRIANGLES, 0, programInfoRipple.verticeNum);
// 波紋更新後にバッファを入れ替え(sample2と同じタイミング)
bufferWriteIndex = 1 - bufferWriteIndex;
bufferReadIndex = 1 - bufferReadIndex;
/* ステップ3: メインシェーダーで画面に最終描画 */
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(pInfo.program);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture1);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, framebuffers[bufferReadIndex].texture);
gl.uniform1i(uTextureLoc1, 0);
gl.uniform1i(uTexturePrevFrame, 1);
gl.uniform2fv(uResolutionLoc, [gl.canvas.width, gl.canvas.height]);
gl.drawArrays(gl.TRIANGLES, 0, pInfo.verticeNum);
window.requestAnimationFrame(animate);
}
animate();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment