webGL页面翻转生产

2023-09-17

以下示例是关于Html中包含webGL页面翻转生产用法的示例代码,想了解webGL页面翻转生产的具体用法?webGL页面翻转生产怎么用?webGL页面翻转生产使用的例子?那么可以参考以下相关源代码片段来学习它的具体使用方法。

文件名:html[英]:webGL ページめくり演出源码类型:Html
<button class="trigger">スタート</button>
<canvas id="canvas"></canvas>
文件名:scss[英]:webGL ページめくり演出源码类型:Html
#canvas {
  display: block;
  width: 100%;
  height: 100vh;
}
文件名:js[英]:webGL ページめくり演出源码类型:Html
/* ======================================
    マウス、タッチ座標をシェーダー内で使うための準備
 ======================================= */
// マウスストーカー要素の座標
const stalker = {
  x: 0,
  y: 0,
};
// マウスの座標
const mouse = {
  x: 0,
  y: 0,
};
// マウス座標を監視する関数
const mousemove = function (event) {
  mouse.x = event.offsetX;
  mouse.y = event.offsetY;
};

// タッチ座標を監視する関数
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.x = offsetX;
  mouse.y = offsetY;
};
/* ======================================
    WebGLを実行するためのデータを格納する変数を準備
 ======================================= */
let gl; // webGLを実行するためのデータを格納する変数

/* ======================================
  webGLを実行するための全ての処理をまとめた関数
 ======================================= */
function startup() {
  let canvas = document.querySelector("#canvas"); // webGLを実行するためのcanvas要素を指定
  gl = createGLContext(canvas); // 上記で取得した要素にwebGL実行環境を実装
  canvas.width = window.innerWidth; // canvasの横幅を仕様できるよう格納
  canvas.height = window.innerHeight; // canvasの縦幅を仕様できるよう格納
  const shaderProgram = setupShaders(); // シェーダーコードをバインドした関数を定数に格納
  const programInfo = {
    program: shaderProgram,
    attribLocations: {
      position: gl.getAttribLocation(shaderProgram, "aVertexPosition"), // getAttribLocationで、シェーダーコードコンパイル時に頂点データが格納された配列のインデックスを取得
      color: gl.getAttribLocation(shaderProgram, "aVertexColor"), // 頂点色の属性変数の場所を取得する
      texCoordLocation: gl.getAttribLocation(shaderProgram, "aTexCoord"), // ここに記述した文字列を使ってshaderコード内でattributeを参照できる
    },
  };
  programInfo.verticeNum = setupBuffers(programInfo); // 頂点バッファーのセットアップと頂点数の取得
  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", "assets/images/noise.png"];
let loadedImageArray = [];

const requests = imagesPath.map(function (imagePath) {
  return loadImage(imagePath);
});

Promise.all(requests).then(function (images) {
  loadedImageArray = images;
  startup();
});
/* ======================================
    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;
}

/* ======================================
    シェーダーコードをWebGLコンテキストにバインド 
 ====================================== */
function setupShaders() {
  /* vertexShaderを記述 */
  const vertexShaderSource = `
    precision mediump float;
    attribute vec2 aVertexPosition;  // aVertexPosition...頂点情報の座標を表すattribute
    attribute vec2 aTexCoord; // 画像を描画するためのテクスチャ座標

    varying vec2 vTexCoord; // テクスチャ座標をfragmentShaderへ渡す
    varying vec2 vVertexPosition; // 頂点座標をfragmentShaderへ渡す

    void main() {
      vTexCoord = aTexCoord;
      vVertexPosition = aVertexPosition;
      gl_Position = vec4(aVertexPosition, 0.0, 1.0);
    }
  `;
  /* fragmentShaderを記述 */
  const fragmentShaderSource = `
  precision mediump float;
  uniform sampler2D uTexture1; // テクスチャ画像1枚目
  uniform sampler2D uTexture2; // テクスチャ画像2枚目
  uniform float uTime; // 経過時間
  uniform float uProgress; // 進捗率
  uniform vec2 uResolution; // 画面幅(w,h)
  varying vec2 vTexCoord; // テクスチャ座標
  varying vec2 vVertexPosition; // 頂点座標
  uniform vec2 uMouse; // マウス座標

  const float MIN_AMOUNT = -0.28;
  const float MAX_AMOUNT = 1.5;
  const float PI = 3.141592653589793;
  const float scale = 512.0;
  const float sharpness = 3.0;
  const float cylinderRadius = 1.0 / PI / 2.0;


  vec4 getToColor(vec2 p) {
    return texture2D(uTexture1, p);
  }
  
  vec4 getFromColor(vec2 p) {
    return texture2D(uTexture2, p);
  }

  vec3 hitPoint(float hitAngle, float yc, vec3 point, mat3 rrotation) {
    float hitPoint = hitAngle / (2.0 * PI);
    point.y = hitPoint;
    return rrotation * point;
  }

  vec4 antiAlias(vec4 color1, vec4 color2, float distanc) {
    distanc *= scale;
    if(distanc < 0.0)
      return color2;
    if(distanc > 2.0)
      return color1;
    float dd = pow(1.0 - distanc / 2.0, sharpness);
    return ((color2 - color1) * dd) + color1;
  }

  float distanceToEdge(vec3 point) {
    float dx = abs(point.x > 0.5 ? 1.0 - point.x : point.x);
    float dy = abs(point.y > 0.5 ? 1.0 - point.y : point.y);
    if(point.x < 0.0)
      dx = -point.x;
    if(point.x > 1.0)
      dx = point.x - 1.0;
    if(point.y < 0.0)
      dy = -point.y;
    if(point.y > 1.0)
      dy = point.y - 1.0;
    if((point.x < 0.0 || point.x > 1.0) && (point.y < 0.0 || point.y > 1.0))
      return sqrt(dx * dx + dy * dy);
    return min(dx, dy);
  }

  vec4 seeThrough(float yc, vec2 p, mat3 rotation, mat3 rrotation, float cylinderAngle) {
    float hitAngle = PI - (acos(yc / cylinderRadius) - cylinderAngle);
    vec3 point = hitPoint(hitAngle, yc, rotation * vec3(p, 1.0), rrotation);
    if(yc <= 0.0 && (point.x < 0.0 || point.y < 0.0 || point.x > 1.0 || point.y > 1.0)) {
      return getToColor(p);
    }

    if(yc > 0.0)
      return getFromColor(p);

    vec4 color = getFromColor(point.xy);
    vec4 tcolor = vec4(0.0);

    return antiAlias(color, tcolor, distanceToEdge(point));
  }

  vec4 seeThroughWithShadow(float yc, vec2 p, vec3 point, mat3 rotation, mat3 rrotation, float cylinderAngle, float amount) {
    float shadow = distanceToEdge(point) * 30.0;
    shadow = (1.0 - shadow) / 3.0;

    if(shadow < 0.0)
      shadow = 0.0;
    else
      shadow *= amount;

    vec4 shadowColor = seeThrough(yc, p, rotation, rrotation, cylinderAngle);
    shadowColor.r -= shadow;
    shadowColor.g -= shadow;
    shadowColor.b -= shadow;

    return shadowColor;
  }

  vec4 backside(float yc, vec3 point) {
    vec4 color = getFromColor(point.xy);
    float gray = (color.r + color.b + color.g) / 15.0;
    gray += (8.0 / 10.0) * (pow(1.0 - abs(yc / cylinderRadius), 2.0 / 10.0) / 2.0 + (5.0 / 10.0));
    color.rgb = vec3(gray);
    return color;
  }

  vec4 behindSurface(vec2 p, float yc, vec3 point, mat3 rrotation, float cylinderAngle, float amount) {
    float shado = (1.0 - ((-cylinderRadius - yc) / amount * 7.0)) / 6.0;
    shado *= 1.0 - abs(point.x - 0.5);

    yc = (-cylinderRadius - cylinderRadius - yc);

    float hitAngle = (acos(yc / cylinderRadius) + cylinderAngle) - PI;
    point = hitPoint(hitAngle, yc, point, rrotation);

    if(yc < 0.0 && point.x >= 0.0 && point.y >= 0.0 && point.x <= 1.0 && point.y <= 1.0 && (hitAngle < PI || amount > 0.5)) {
      shado = 1.0 - (sqrt(pow(point.x - 0.5, 2.0) + pow(point.y - 0.5, 2.0)) / (71.0 / 100.0));
      shado *= pow(-yc / cylinderRadius, 3.0);
      shado *= 0.5;
    } else {
      shado = 0.0;
    }
    return vec4(getToColor(p).rgb - shado, 1.0);
  }

  void main() {
    vec2 newUV = vTexCoord;

    float amount = uProgress * (MAX_AMOUNT - MIN_AMOUNT) + MIN_AMOUNT;
    float cylinderCenter = amount;
        // 360 degrees * amount
    float cylinderAngle = 2.0 * PI * amount;

    const float angle = 100.0 * PI / 180.0;
    float c = cos(-angle);
    float s = sin(-angle);

    mat3 rotation = mat3(c, s, 0, -s, c, 0, -0.801, 0.8900, 1);
    c = cos(angle);
    s = sin(angle);

    mat3 rrotation = mat3(c, s, 0, -s, c, 0, 0.98500, 0.985, 1);

    vec3 point = rotation * vec3(newUV, 1.0);

    float yc = point.y - cylinderCenter;

    if(yc < -cylinderRadius) {
      // Behind surface
      gl_FragColor = behindSurface(newUV, yc, point, rrotation, cylinderAngle, amount);
      return;
    }

    if(yc > cylinderRadius) {
      // Flat surface
      gl_FragColor = getFromColor(newUV);
      return;
    }

    float hitAngle = (acos(yc / cylinderRadius) + cylinderAngle) - PI;

    float hitAngleMod = mod(hitAngle, 2.0 * PI);
    if((hitAngleMod > PI && amount < 0.5) || (hitAngleMod > PI / 2.0 && amount < 0.0)) {
      gl_FragColor = seeThrough(yc, newUV, rotation, rrotation, cylinderAngle);
      return;
    }

    point = hitPoint(hitAngle, yc, point, rrotation);

    if(point.x < 0.0 || point.y < 0.0 || point.x > 1.0 || point.y > 1.0) {
      gl_FragColor = seeThroughWithShadow(yc, newUV, point, rotation, rrotation, cylinderAngle, amount);
      return;
    }

    vec4 color = backside(yc, point);

    vec4 otherColor;
    if(yc < 0.0) {
      float shado = 1.0 - (sqrt(pow(point.x - 0.5, 2.0) + pow(point.y - 0.5, 2.0)) / 0.71);
      shado *= pow(-yc / cylinderRadius, 3.0);
      shado *= 0.5;
      otherColor = vec4(0.0, 0.0, 0.0, shado);
    } else {
      otherColor = getFromColor(newUV);
    }

    color = antiAlias(color, otherColor, cylinderRadius - abs(yc));

    vec4 cl = seeThroughWithShadow(yc, newUV, point, rotation, rrotation, cylinderAngle, amount);
    float dist = distanceToEdge(point);

    gl_FragColor = antiAlias(color, cl, dist);
}
`;

  /* =====================================
    シェーダーを読み込む 開始
 ====================================== */
  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 loadImageAsTexture(image) {
  const texture = gl.createTexture(); // テクスチャオブジェクトを作成する
  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.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); // 属性を有効化する

  // テクスチャ座標のバッファーを作成する
  const imageVertices = [0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 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); // 属性を有効化する

  // 使用するテクスチャをそれぞれロードしてバインドする
  const texture1 = loadImageAsTexture(loadedImageArray[0]);
  const texture2 = loadImageAsTexture(loadedImageArray[1]);
  gl.activeTexture(gl.TEXTURE0);
  gl.bindTexture(gl.TEXTURE_2D, texture1);
  gl.activeTexture(gl.TEXTURE1);
  gl.bindTexture(gl.TEXTURE_2D, texture2);

  return verticeNum; // 頂点数を返す
}
function draw(pInfo) {
  gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight); // WebGLのコンテキストとキャンパスのサイズを同じにする。
  gl.useProgram(pInfo.program); // WebGLのコンテキストが使用するプログラムを指定
  // シェーダー内で使うuniformを設定
  const uProgressLoc = gl.getUniformLocation(pInfo.program, "uProgress");
  const uTimeLoc = gl.getUniformLocation(pInfo.program, "uTime");
  const uResolutionLoc = gl.getUniformLocation(pInfo.program, "uResolution");
  const uMouseLoc = gl.getUniformLocation(pInfo.program, "uMouse");
  // 読み込んだテクスチャをそれぞれuniformに紐づける
  const uTextureLoc1 = gl.getUniformLocation(pInfo.program, "uTexture1");
  const uTextureLoc2 = gl.getUniformLocation(pInfo.program, "uTexture2");
  gl.clearColor(0, 0, 0, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);
  gl.uniform2fv(uResolutionLoc, [gl.canvas.width, gl.canvas.height]); // canvas幅
  gl.uniform2fv(uMouseLoc, [stalker.x, stalker.y]); // マウス座標
  gl.uniform1i(uTextureLoc1, 0); // ユニット番号を指定してテクスチャをバインド
  gl.uniform1i(uTextureLoc2, 1); // ユニット番号を指定してテクスチャをバインド
  gl.drawArrays(gl.TRIANGLES, 0, pInfo.verticeNum);

  /* リサイズ対応 */
  function resize(resizedCanvas) {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    resizedCanvas.viewportWidth = canvas.width;
    resizedCanvas.viewportHeight = canvas.height;
  }

  /* マウスとタッチの座標を監視 */
  document.getElementById("canvas").addEventListener("mousemove", mousemove);
  document.getElementById("canvas").addEventListener("touchmove", touchmove);

  /* ======================================
    requestAnimationFrameを再起実行
 ======================================= */
  /* アニメーション管理位用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;
      }
      console.log(obj.num);
    }
    animationID = requestAnimationFrame(step);
  }

  const trigger = document.querySelector(".trigger");
  trigger.addEventListener("click", function () {
    tween(tweenData, 0, 1.0, 3000, easeInOutQuart);
  });

  function animate(timestamp) {
    resize(gl);
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); // ビューポートの設定
    gl.clearColor(0, 0, 0, 0); // クリアカラーの設定
    gl.clear(gl.COLOR_BUFFER_BIT); // カラーバッファーのクリア
    gl.uniform2fv(uResolutionLoc, [gl.canvas.width, gl.canvas.height]); // 常にcanvasの縦横幅を監視
    gl.uniform1f(uTimeLoc, timestamp * 0.05); // 経過時間を監視
    gl.uniform1f(uProgressLoc, tweenData.num); // 進捗率

    /* マウス座標を監視 */
    stalker.x += (mouse.x - stalker.x) * 0.05; // 慣性をつけるための計算
    stalker.y += (mouse.y - stalker.y) * 0.05; // 慣性をつけるための計算
    gl.uniform2fv(uMouseLoc, [stalker.x, stalker.y]); // マウス座標を監視

    gl.drawArrays(gl.TRIANGLES, 0, pInfo.verticeNum);
    window.requestAnimationFrame(animate);
  }

  animate();
}

本文地址:https://itbaoku.cn/snippets/872563.html