Используйте 95 строк кода Three.js для реализации прототипа панорамного видеоплеера.

внешний интерфейс three.js
Используйте 95 строк кода Three.js для реализации прототипа панорамного видеоплеера.

причина

Прошлой ночью я не мог уснуть, и меня вдруг вдохновило VR-видео с иностранного видеосайта. Потребовалось 40 минут, чтобы написать это маленькое демо и поделиться им с вами.

визуализация

xy-20210802-153030.gif

принцип

Сначала используем сферу

image.png

Сгенерируйте текстурный блок для каждого кадра видео и расширьте его во фрагментном шейдере в соответствии с uv сферы

Положение камеры помещается в исходное положение

использоватьrAfОбновите средство визуализации

Подготовить материалы

Подготовка видео материала

Первым делом скачиваем панорамное видео научным путем с неизвестного сайта (как отечественного, так и зарубежного). Откройте его с помощью vlc player, это выглядит так:

image.png

Код

Сначала мы представляем три и собственный трековый контроллер

import * as THREE from "three/build/three.module";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

Добавьте следующий DOM в html

 <div class="player">
    <div>
      <button @click="$refs.video.play()">播放</button>
    </div>
    <video
      preload
      ref="video"
      controls
      loop
      style="width: 100%; visibility: hidden; position: absolute"
      :src="src"
    ></video>

    <canvas
      style="width: 80%; height: 823px"
      width="1920"
      height="823"
      ref="canvas"
    ></canvas>
  </div>

Инициализировать текстуру видео

initVideoTexture() {
      this.videoTexture = new THREE.VideoTexture(this.$refs.video);
      this.videoTexture.needsUpdate = true;
      this.videoTexture.updateMatrix();
    }

Здесь мы используем видеотекстуру, поставляемую с Three.js. Вы также можете реализовать ее самостоятельно. Создайте закадровый холст, передайте видео в drawImage и создайте его экземпляр через Texture. Не забудьте установить для свойства needUpdate значение true.

    initScene() {
      this.scene = new THREE.Scene();
    }

    initCamera() {
      this.camera = new THREE.PerspectiveCamera(45, 1024 / 768, 1, 1000);
      this.camera.position.z = 30;
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
      this.controls.maxDistance = 100;

      this.controls.update();
      // const helper = new THREE.CameraHelper(this.camera);
      // this.scene.add(helper);
      this.scene.add(this.camera);
    }

Инициализировать сетку

Обратите внимание, что здесь, чтобы определить материал, который мы хотим использоватьuniformsДа ладно, объект текстуры, который мы только что инициализировали, передается, здесь мы используем tex_0 в качестве имени переменной, которую мы используем.

    initMesh() {
      this.geometry = new THREE.SphereGeometry(100, 32, 16);
      this.material = new THREE.ShaderMaterial({
        wireframe: false,
        side: THREE.DoubleSide,
        map: this.videoTexture,
        uniforms: {
          tex_0: new THREE.Uniform(this.videoTexture),
        },
        vertexShader: require("@/components/v.glsl").default,
        fragmentShader: require("@/components/f.glsl").default,
      });
      this.mesh = new THREE.Mesh(this.geometry, this.material);
    }

тиковая функция

  update() {
      this.renderer.render(this.scene, this.camera);

      requestAnimationFrame(this.update);
    }

смонтированная функция

Поскольку я использую vue SFC, напишите это в смонтированном хуке

 mounted() {
    this.initRenderer();
    this.initScene();
    this.initVideoTexture();
    this.initMesh();
    this.initCamera();
    this.addMeshToScene();
    this.update();
  }

реализация шейдера

вершинный шейдер

Здесь мы используем матрицу MVP, которая поставляется с three.js, для преобразования входящих координат вершин в мир, а затем передаем наш uv фрагментному шейдеру через переменную v_uv, определяемую ключевым словом Variing.

precision highp float;
varying vec2 v_uv;
void main() {
    gl_Position = projectionMatrix *
        modelViewMatrix *
        vec4(position.xyz, 1.0);
    v_uv = uv;
}

фрагментный шейдер

Обратите внимание, что поскольку нормаль сферы по умолчанию направлена ​​​​изнутри наружу, отображение координат нашей UV-текстуры также направлено наружу. происходит переворачивание текстур в шаре, что явно неправильно, поэтому нам нужно сесть и перевернуть координаты uv, что очень просто, достаточно использовать 1.0 - v_uv.x

precision highp float;
varying vec2 v_uv;
uniform sampler2D tex_0;
void main() {
    vec4 texColor = texture2D(tex_0, vec2(1. - v_uv.x, v_uv.y));
    gl_FragColor = texColor;
}

Таким образом, наше панорамное видео реализуется. Я поставил демоgiteeО, какие у вас есть идеи, вы также можете подтвердить меня в частном порядке ~~