Newer
Older
AegisforEcosystem / next / AR.js-3.4.0 / three.js / src / threex / threex-armarkercloak.js
@KAOKA Daisuke KAOKA Daisuke on 31 May 2022 8 KB into AR.js
import * as THREE from "three";

/**
 * - videoTexture
 * - cloakWidth
 * - cloakHeight
 * - cloakSegmentsHeight
 * - remove all mentions of cache, for cloak
 */
const ArMarkerCloak = function (videoTexture) {
  var updateInShaderEnabled = true;

  // build cloakMesh
  // TODO if webgl2 use repeat warp, and not multi segment, this will reduce the geometry to draw
  var geometry = new THREE.PlaneGeometry(
    1.3 + 0.25,
    1.85 + 0.25,
    1,
    8
  ).translate(0, -0.3, 0);
  var material = new THREE.ShaderMaterial({
    vertexShader: ArMarkerCloak.vertexShader,
    fragmentShader: ArMarkerCloak.fragmentShader,
    transparent: true,
    uniforms: {
      texture: {
        value: videoTexture,
      },
      opacity: {
        value: 0.5,
      },
    },
    defines: {
      updateInShaderEnabled: updateInShaderEnabled ? 1 : 0,
    },
  });

  var cloakMesh = new THREE.Mesh(geometry, material);
  cloakMesh.rotation.x = -Math.PI / 2;
  this.object3d = cloakMesh;

  //////////////////////////////////////////////////////////////////////////////
  //		Code Separator
  //////////////////////////////////////////////////////////////////////////////

  var xMin = -0.65;
  var xMax = 0.65;
  var yMin = 0.65 + 0.1;
  var yMax = 0.95 + 0.1;

  //////////////////////////////////////////////////////////////////////////////
  //		originalsFaceVertexUvs
  //////////////////////////////////////////////////////////////////////////////
  var originalsFaceVertexUvs = [[]];

  // build originalsFaceVertexUvs array
  for (
    var faceIndex = 0;
    faceIndex < cloakMesh.geometry.faces.length;
    faceIndex++
  ) {
    originalsFaceVertexUvs[0][faceIndex] = [];
    originalsFaceVertexUvs[0][faceIndex][0] = new THREE.Vector2();
    originalsFaceVertexUvs[0][faceIndex][1] = new THREE.Vector2();
    originalsFaceVertexUvs[0][faceIndex][2] = new THREE.Vector2();
  }

  // set values in originalsFaceVertexUvs
  for (var i = 0; i < cloakMesh.geometry.parameters.heightSegments / 2; i++) {
    // one segment height - even row - normale orientation
    originalsFaceVertexUvs[0][i * 4 + 0][0].set(xMin / 2 + 0.5, yMax / 2 + 0.5);
    originalsFaceVertexUvs[0][i * 4 + 0][1].set(xMin / 2 + 0.5, yMin / 2 + 0.5);
    originalsFaceVertexUvs[0][i * 4 + 0][2].set(xMax / 2 + 0.5, yMax / 2 + 0.5);

    originalsFaceVertexUvs[0][i * 4 + 1][0].set(xMin / 2 + 0.5, yMin / 2 + 0.5);
    originalsFaceVertexUvs[0][i * 4 + 1][1].set(xMax / 2 + 0.5, yMin / 2 + 0.5);
    originalsFaceVertexUvs[0][i * 4 + 1][2].set(xMax / 2 + 0.5, yMax / 2 + 0.5);

    // one segment height - odd row - mirror-y orientation
    originalsFaceVertexUvs[0][i * 4 + 2][0].set(xMin / 2 + 0.5, yMin / 2 + 0.5);
    originalsFaceVertexUvs[0][i * 4 + 2][1].set(xMin / 2 + 0.5, yMax / 2 + 0.5);
    originalsFaceVertexUvs[0][i * 4 + 2][2].set(xMax / 2 + 0.5, yMin / 2 + 0.5);

    originalsFaceVertexUvs[0][i * 4 + 3][0].set(xMin / 2 + 0.5, yMax / 2 + 0.5);
    originalsFaceVertexUvs[0][i * 4 + 3][1].set(xMax / 2 + 0.5, yMax / 2 + 0.5);
    originalsFaceVertexUvs[0][i * 4 + 3][2].set(xMax / 2 + 0.5, yMin / 2 + 0.5);
  }

  if (updateInShaderEnabled === true) {
    cloakMesh.geometry.faceVertexUvs = originalsFaceVertexUvs;
    cloakMesh.geometry.uvsNeedUpdate = true;
  }

  //////////////////////////////////////////////////////////////////////////////
  //		Code Separator
  //////////////////////////////////////////////////////////////////////////////

  var originalOrthoVertices = [];
  originalOrthoVertices.push(new THREE.Vector3(xMin, yMax, 0));
  originalOrthoVertices.push(new THREE.Vector3(xMax, yMax, 0));
  originalOrthoVertices.push(new THREE.Vector3(xMin, yMin, 0));
  originalOrthoVertices.push(new THREE.Vector3(xMax, yMin, 0));

  // build debugMesh
  var material = new THREE.MeshNormalMaterial({
    transparent: true,
    opacity: 0.5,
    side: THREE.DoubleSide,
  });
  var geometry = new THREE.PlaneGeometry(1, 1);
  var orthoMesh = new THREE.Mesh(geometry, material);
  this.orthoMesh = orthoMesh;

  //////////////////////////////////////////////////////////////////////////////
  //                Code Separator
  //////////////////////////////////////////////////////////////////////////////

  this.update = function (modelViewMatrix, cameraProjectionMatrix) {
    updateOrtho(modelViewMatrix, cameraProjectionMatrix);

    if (updateInShaderEnabled === false) {
      updateUvs(modelViewMatrix, cameraProjectionMatrix);
    }
  };

  return;

  // update cloakMesh
  function updateUvs(modelViewMatrix, cameraProjectionMatrix) {
    var transformedUv = new THREE.Vector3();
    originalsFaceVertexUvs[0].forEach(function (faceVertexUvs, faceIndex) {
      faceVertexUvs.forEach(function (originalUv, uvIndex) {
        // set transformedUv - from UV coord to clip coord
        transformedUv.x = originalUv.x * 2.0 - 1.0;
        transformedUv.y = originalUv.y * 2.0 - 1.0;
        transformedUv.z = 0;
        // apply modelViewMatrix and projectionMatrix
        transformedUv.applyMatrix4(modelViewMatrix);
        transformedUv.applyMatrix4(cameraProjectionMatrix);
        // apply perspective
        transformedUv.x /= transformedUv.z;
        transformedUv.y /= transformedUv.z;
        // set back from clip coord to Uv coord
        transformedUv.x = transformedUv.x / 2.0 + 0.5;
        transformedUv.y = transformedUv.y / 2.0 + 0.5;
        // copy the trasnformedUv into the geometry
        cloakMesh.geometry.faceVertexUvs[0][faceIndex][uvIndex].set(
          transformedUv.x,
          transformedUv.y
        );
      });
    });

    // cloakMesh.geometry.faceVertexUvs = faceVertexUvs
    cloakMesh.geometry.uvsNeedUpdate = true;
  }

  // update orthoMesh
  function updateOrtho(modelViewMatrix, cameraProjectionMatrix) {
    // compute transformedUvs
    var transformedUvs = [];
    originalOrthoVertices.forEach(function (originalOrthoVertices, index) {
      var transformedUv = originalOrthoVertices.clone();
      // apply modelViewMatrix and projectionMatrix
      transformedUv.applyMatrix4(modelViewMatrix);
      transformedUv.applyMatrix4(cameraProjectionMatrix);
      // apply perspective
      transformedUv.x /= transformedUv.z;
      transformedUv.y /= transformedUv.z;
      // store it
      transformedUvs.push(transformedUv);
    });

    // change orthoMesh vertices
    for (var i = 0; i < transformedUvs.length; i++) {
      orthoMesh.geometry.vertices[i].copy(transformedUvs[i]);
    }
    orthoMesh.geometry.computeBoundingSphere();
    orthoMesh.geometry.verticesNeedUpdate = true;
  }
};

//////////////////////////////////////////////////////////////////////////////
//                Shaders
//////////////////////////////////////////////////////////////////////////////

ArMarkerCloak.markerSpaceShaderFunction =
  "\n" +
  "        vec2 transformUvToMarkerSpace(vec2 originalUv){\n" +
  "                vec3 transformedUv;\n" +
  "                // set transformedUv - from UV coord to clip coord\n" +
  "                transformedUv.x = originalUv.x * 2.0 - 1.0;\n" +
  "                transformedUv.y = originalUv.y * 2.0 - 1.0;\n" +
  "                transformedUv.z = 0.0;\n" +
  "\n" +
  "		// apply modelViewMatrix and projectionMatrix\n" +
  "                transformedUv = (projectionMatrix * modelViewMatrix * vec4( transformedUv, 1.0 ) ).xyz;\n" +
  "\n" +
  "		// apply perspective\n" +
  "		transformedUv.x /= transformedUv.z;\n" +
  "		transformedUv.y /= transformedUv.z;\n" +
  "\n" +
  "                // set back from clip coord to Uv coord\n" +
  "                transformedUv.x = transformedUv.x / 2.0 + 0.5;\n" +
  "                transformedUv.y = transformedUv.y / 2.0 + 0.5;\n" +
  "\n" +
  "                // return the result\n" +
  "                return transformedUv.xy;\n" +
  "        }";

ArMarkerCloak.vertexShader =
  ArMarkerCloak.markerSpaceShaderFunction +
  "	varying vec2 vUv;\n" +
  "\n" +
  "	void main(){\n" +
  "                // pass the UV to the fragment\n" +
  "                #if (updateInShaderEnabled == 1)\n" +
  "		        vUv = transformUvToMarkerSpace(uv);\n" +
  "                #else\n" +
  "		        vUv = uv;\n" +
  "                #endif\n" +
  "\n" +
  "                // compute gl_Position\n" +
  "		vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n" +
  "		gl_Position = projectionMatrix * mvPosition;\n" +
  "	}";

ArMarkerCloak.fragmentShader =
  "\n" +
  "	varying vec2 vUv;\n" +
  "	uniform sampler2D texture;\n" +
  "	uniform float opacity;\n" +
  "\n" +
  "	void main(void){\n" +
  "		vec3 color = texture2D( texture, vUv ).rgb;\n" +
  "\n" +
  "		gl_FragColor = vec4( color, opacity);\n" +
  "	}";

export default ArMarkerCloak;