二维图片具有3D效果[three.js+vue3]

575 阅读2分钟

效果展示:

image.png

实现思路

利用定位,实现图片的三维分层展示 mesh.position.set(0, 0, i); 加上三维场景的处理实现一些二维图片三维位置展示的效果

1.html部分

<template>
  <div>
    <div class="import_model"></div>
  </div>
</template>

2.js部分

2.1 引入和方法封装

<script setup>
import { reactive, ref, nextTick, onMounted, watch, onUnmounted } from "vue";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";

// 引入图片;
import layer_0 from "@/assets/images/comic/layer_0.png";
import layer_1 from "@/assets/images/comic/layer_1.png";
import layer_2 from "@/assets/images/comic/layer_2.png";
import layer_3 from "@/assets/images/comic/layer_3.png";
import layer_4 from "@/assets/images/comic/layer_4.png";
import layer_5 from "@/assets/images/comic/layer_5.png";
import layer_6 from "@/assets/images/comic/layer_6.png";
import layer_7 from "@/assets/images/comic/layer_7.png";
import background from "@/assets/images/comic/background.png";
import boomImage from "@/assets/images/comic/boom.png";

let scene, light, mesh, camera, renderer, axesHelper, grid, controls;
var width = window.innerWidth; //窗口宽度
var height = window.innerHeight; //窗口高度
const publicPath = process.env.BASE_URL;

let container,
  step = 0,
  animateLayer = null,
  layerGroup = new THREE.Group();
var layers = [
  layer_0,
  layer_1,
  layer_2,
  layer_3,
  layer_4,
  layer_5,
  layer_6,
  layer_7,
];

// 加载材质
let textureLoader = new THREE.TextureLoader();

onMounted(() => {
  init();
});

// 初始化
function init() {
  // 创建场景
  createScene();
  // 创建光源
  createLight();
  // 创建相机
  createCamera(width, height);
  // 设置背景
  createBackground();
  // 创建渲染器
  createRender();
  // 创建控制器
  createControls();
  // createGrid();
  // 辅助坐标系
  createAxesHelper();
  createGroundMain();
  // 创建boom背景
  createBoom();
  // 渲染
  render();
}

</script>

2.2 具体方法

创建场景

function createScene() {
  //创建一个三维场景
  scene = new THREE.Scene();
}

创建光源

function createLight() {
 var point = new THREE.PointLight(0xffffff);
  point.position.set(400, 200, 300); //点光源位置
  scene.add(point); //点光源添加到场景中
  //环境光
  // var ambient = new THREE.AmbientLight(0x444444);
  var ambient = new THREE.AmbientLight("#ffffff", 1);
  scene.add(ambient);
  }

创建相机

function createCamera(width, height) {

  var k = width / height; //窗口宽高比
  var s = 200; //三维场景显示范围控制系数,系数越大,显示的范围越大
  //创建相机对象
  // 正交相机
  camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
  // 透视相机(此处需要全景和透视)
  camera = new THREE.PerspectiveCamera(
    45,
    window.innerWidth / window.innerHeight,
    0.1,
    20000
  );

  // camera.position.set(30, 30, 30); //设置相机位置
  // 旋转照相机的角度可以旋转轴
  camera.position.set(0, 0, 50); //设置相机位置
  camera.lookAt(scene.position); //设置相机方向(指向的场景对象)
  // camera.lookAt(new THREE.Vector3(0, 0, 0));
}

创建背景

function createBackground() {
  const loader = new THREE.TextureLoader();
  const bgTexture = loader.load(background);
  scene.background = bgTexture;
}

创建渲染器

function createRender() {
  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(width, height); //设置渲染区域尺寸
  // renderer.setClearColor(0xffffff, 1); //设置背景颜色
  renderer.setPixelRatio(window.devicePixelRatio);
  // renderer.outputEncoding = THREE.sRGBEncoding; // 模型颜色偏差
  renderer.shadowMap.enabled = true; //开启渲染气阴影效果。
  nextTick(() => {
    container = document.querySelector(".import_model");
    container.appendChild(renderer.domElement);
  });
}

创建控制器

function createControls() {
  //创建控件对象  相机对象camera作为参数   控件可以监听鼠标的变化,改变相机对象的属性
  controls = new OrbitControls(camera, renderer.domElement);
  controls.target.set(0, 0, 0);
  controls.enableDamping = true;
  controls.enablePan = false;
  controls.enableZoom = false;
  // 控制旋转角度
  // 垂直旋转角度限制
  // controls.minPolarAngle = 1.2;
  // controls.maxPolarAngle = 1.8;
  // // 水平旋转角度限制
  // controls.minAzimuthAngle = -0.6;
  // controls.maxAzimuthAngle = 0.6;
  //监听鼠标事件,触发渲染函数,更新canvas画布渲染效果
  controls.addEventListener("change", () => {
    renderer.render(scene, camera);
  });
}

辅助坐标系

function createAxesHelper() {
  axesHelper = new THREE.AxesHelper(250);
  scene.add(axesHelper);
}

搭建基本场景完成,以下都是场景内容

function createGroundMain() {
  /*  首先创建一个 Group,用于添加图层网格,
  然后遍历图层背景图片数组,在循环体中创建每个面的网格,
  该网格使用平面立方体 PlaneGeometry,
  材质使用物理材质 MeshPhysicalMaterial,
  对每个网格位置设置相同的x轴和y轴值和不同的z轴值以创建景深效果。
  最后将 Group 添加到场景 Scene 中。 */
  let aspect = 18;
  for (let i = 0; i < layers.length; i++) {
    let mesh = new THREE.Mesh(
      new THREE.PlaneGeometry(10.41, 16),
      new THREE.MeshPhysicalMaterial({
        map: new THREE.TextureLoader().load(layers[i]),
        transparent: true,
        side: THREE.DoubleSide,
      })
    );
    mesh.position.set(0, 0, i);
    mesh.scale.set(1 - i / aspect, 1 - i / aspect, 1 - i / aspect);
    layerGroup.add(mesh);
    //
    if (i == 5) {
      mesh.material.metalness = 0.6;
      // 字的颜色
      mesh.material.emissive = new THREE.Color("red");
      mesh.material.emissiveIntensity = 1.6;
      mesh.material.opacity = 0.9;
    }
    // 会话框
    if (i === 6) {
      mesh.scale.set(1.5, 1.5, 1.5);
      animateLayer = mesh;
    }
  }
  layerGroup.scale.set(2, 2, 2);
  scene.add(layerGroup);
}

创建boom

function createBoom() {
  const boom = new THREE.Mesh(
    new THREE.PlaneGeometry(36.76, 27.05),
    new THREE.MeshPhongMaterial({
      map: new THREE.TextureLoader().load(boomImage),
      transparent: true,
      shininess: 160,
      specular: new THREE.Color(0xff6d00),
      opacity: 0.7,
    })
  );
  boom.scale.set(0.8, 0.8, 0.8);
  boom.position.set(0, 0, -3);
  layerGroup.add(boom);
  scene.add(layerGroup);
}

创建boom动画,此方法在render方法中加载

function boxAnimation() {
  step += 0.01;
  animateLayer.position.x = 2.4 + Math.cos(step);
  animateLayer.position.y = 0.4 + Math.abs(Math.sin(step));
}