Forráskód Böngészése

feature: three util

xiongxt 1 éve
szülő
commit
be4253106a
4 módosított fájl, 911 hozzáadás és 353 törlés
  1. 409 0
      src/views/three/index-1.vue
  2. 271 0
      src/views/three/index-2.vue
  3. 54 353
      src/views/three/index.vue
  4. 177 0
      src/views/three/util.js

+ 409 - 0
src/views/three/index-1.vue

@@ -0,0 +1,409 @@
+<template>
+  <div ref="wrap"></div>
+</template>
+<script setup>
+import * as THREE from "three";
+import * as SceneUtils from "three/examples/jsm/utils/SceneUtils";
+import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
+import Stats from "three/examples/jsm/libs/stats.module";
+import * as dat from "dat.gui";
+import { onMounted, ref } from "vue";
+
+/**
+ * @type {import("vue").Ref<HTMLDivElement>}
+ */
+const wrap = ref();
+
+function getCanvasWidth() {
+  return window.innerWidth - 60;
+}
+
+function getCanvasHeight() {
+  return window.innerHeight - 150;
+}
+
+let renderer;
+function initRender() {
+  renderer = new THREE.WebGLRenderer({ antialias: true });
+  renderer.setSize(getCanvasWidth(), getCanvasHeight());
+  renderer.setPixelRatio(window.devicePixelRatio);
+  renderer.setClearColor(0x444444, 1);
+  //告诉渲染器需要阴影效果
+  renderer.shadowMap.enabled = true;
+  renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 默认的是,没有设置的这个清晰 THREE.PCFShadowMap
+  wrap.value.appendChild(renderer.domElement);
+}
+
+let camera;
+function initCamera() {
+  camera = new THREE.PerspectiveCamera(
+    50,
+    getCanvasWidth() / getCanvasHeight(),
+    0.1,
+    10000
+  );
+  camera.position.set(0, 40, 100);
+  camera.lookAt(new THREE.Vector3(0, 0, 0));
+}
+
+let scene;
+function initScene() {
+  scene = new THREE.Scene();
+}
+
+//初始化dat.GUI简化试验流程
+let gui;
+
+let hemiLight, ambientLight, directionalLight, directionalLightHelper;
+function initGui() {
+  //声明一个保存需求修改的相关数据的对象
+  gui = {
+    directionalLight: "#ffffff", //点光源
+    directionalLightIntensity: 1, //灯光强度
+    visible: true, //是否可见
+    castShadow: true,
+    exponent: 30,
+    target: "plane",
+    debug: true,
+    groundColor: "#00ff00",
+    skyColor: "#0000ff",
+    hemiLightIntensity: 0.3,
+    dLightX: -40,
+    dLightY: 60,
+    dLightZ: -10,
+  };
+  let datGui = new dat.GUI();
+  //将设置属性添加到gui当中,gui.add(对象,属性,最小值,最大值)
+
+  const ambientFolder = datGui.addFolder("环境光");
+
+  ambientFolder.addColor(gui, "skyColor").onChange(function (e) {
+    hemiLight.color = new THREE.Color(e);
+  });
+  ambientFolder.addColor(gui, "groundColor").onChange(function (e) {
+    hemiLight.groundColor = new THREE.Color(e);
+  });
+  ambientFolder.add(gui, "hemiLightIntensity", 0, 1).onChange(function (e) {
+    hemiLight.intensity = e;
+  });
+  ambientFolder.addColor(gui, "directionalLight").onChange(function (e) {
+    directionalLight.color = new THREE.Color(e);
+  });
+
+  datGui
+    .add(gui, "dLightX", {
+      left: -100,
+      center: 0,
+      right: 100,
+      // 左: -100,//可以用中文
+      // 中: 0,
+      // 右: 100
+    })
+    .onChange(function (e) {
+      directionalLight.position.setX(e);
+    });
+
+  datGui.add(gui, "dLightY", -100, 100).onChange(function (e) {
+    directionalLight.position.setY(e);
+  });
+
+  datGui.add(gui, "dLightZ", -100, 100).onChange(function (e) {
+    directionalLight.position.setZ(e);
+  });
+
+  datGui.add(gui, "directionalLightIntensity", 0, 5).onChange(function (e) {
+    directionalLight.intensity = e;
+  });
+  datGui.add(gui, "visible").onChange(function (e) {
+    directionalLight.visible = e;
+  });
+  datGui.add(gui, "castShadow").onChange(function (e) {
+    directionalLight.castShadow = e;
+  });
+  datGui.add(gui, "debug").onChange(function (e) {
+    if (e) {
+      let debug = new THREE.CameraHelper(directionalLight.shadow.camera);
+      debug.name = "debug";
+      scene.add(debug);
+    } else {
+      let debug = scene.getObjectByName("debug");
+      scene.remove(debug);
+    }
+  });
+
+  datGui.add(cube.position, "x").name("cube-x").min(-5).max(5).step(1);
+}
+
+function initLight() {
+  hemiLight = new THREE.HemisphereLight("#bcffb1", "#000000", 1);
+  scene.add(hemiLight);
+  // ambientLight = new THREE.AmbientLight("#111111");
+  // scene.add(ambientLight);
+
+  directionalLight = new THREE.DirectionalLight("#ffffff");
+  directionalLight.position.set(-40, 60, -10);
+
+  directionalLightHelper = new THREE.DirectionalLightHelper(directionalLight);
+
+  directionalLight.shadow.camera.near = 20; //产生阴影的最近距离
+  directionalLight.shadow.camera.far = 200; //产生阴影的最远距离
+  directionalLight.shadow.camera.left = -100; //产生阴影距离位置的最左边位置
+  directionalLight.shadow.camera.right = 100; //最右边
+  directionalLight.shadow.camera.top = 100; //最上边
+  directionalLight.shadow.camera.bottom = -100; //最下面
+
+  //这两个值决定使用多少像素生成阴影 默认512
+  directionalLight.shadow.mapSize.height = 1024;
+  directionalLight.shadow.mapSize.width = 1024;
+
+  //告诉平行光需要开启阴影投射
+  directionalLight.castShadow = true;
+
+  // scene.add(directionalLightHelper);
+
+  scene.add(directionalLight);
+}
+
+let cube, plane;
+function initModel() {
+  //辅助工具
+  let helper = new THREE.AxesHelper(100);
+  scene.add(helper);
+
+  const bufferGeometry = new THREE.BufferGeometry();
+
+  bufferGeometry.attributes.position = new THREE.BufferAttribute(
+    new Float32Array([
+      -10,
+      15,
+      10, // 0
+      10,
+      15,
+      10,
+      0,
+      15,
+      -10,
+
+      -10,
+      15,
+      10, // 0
+      10,
+      15,
+      10,
+      0,
+      40,
+      0,
+
+      0,
+      40,
+      0, // 0
+      10,
+      15,
+      10,
+      0,
+      15,
+      -10,
+
+      -10,
+      15,
+      10, // 0
+      0,
+      40,
+      0, // 0
+      0,
+      15,
+      -10,
+    ]),
+    3
+  );
+
+  const buffer = new THREE.Mesh(
+    bufferGeometry,
+    new THREE.MeshBasicMaterial({
+      color: 0x0000ff, //设置材质颜色
+      transparent: true, //开启透明
+      opacity: 0.5, //设置透明度
+      wireframe: true,
+    })
+  );
+
+  buffer.castShadow = true;
+
+  scene.add(buffer);
+
+  //球体
+  let sphereGeometry = new THREE.SphereGeometry(10, 30, 30);
+
+  let material = new THREE.MeshPhongMaterial({
+    color: 0x0000ff,
+    specular: 0x4488ee,
+    shininess: 12,
+  });
+
+  let sphere = new THREE.Mesh(
+    sphereGeometry,
+    new THREE.MeshPhongMaterial({
+      color: 0xcccccc,
+      shininess: 20,
+      specular: 0xffffff,
+    })
+  );
+  sphere.position.set(-20, 20, 0);
+
+  sphere.castShadow = true;
+
+  scene.add(sphere);
+
+  const cylinderGeometry = new THREE.CylinderGeometry(5, 5, 10);
+  const cylinder = new THREE.Mesh(
+    cylinderGeometry,
+    new THREE.MeshLambertMaterial({
+      color: 0xeeeeee,
+    })
+  );
+  cylinder.castShadow = true;
+
+  cylinder.position.x = 50;
+  cylinder.position.y = 10;
+  cylinder.position.z = -10;
+
+  scene.add(cylinder);
+
+  const cylinderGeometry2 = new THREE.CylinderGeometry(0, 5, 10);
+  const cylinder2 = new THREE.Mesh(
+    cylinderGeometry2,
+    new THREE.MeshPhongMaterial({
+      color: 0xcccccc,
+      shininess: 20,
+      specular: 0xffffff,
+    })
+  );
+  cylinder2.castShadow = true;
+
+  cylinder2.position.x = 50;
+  cylinder2.position.y = 10;
+  cylinder2.position.z = -30;
+
+  scene.add(cylinder2);
+
+  //立方体
+
+  var parentCube = new THREE.Mesh(
+    new THREE.BoxGeometry(20, 20, 20, 2, 2, 2),
+    new THREE.MeshBasicMaterial({
+      color: 0x0000ff, //设置材质颜色
+      transparent: true, //开启透明
+      opacity: 0.5, //设置透明度
+      wireframe: true,
+    })
+  );
+
+  cube = new THREE.Mesh(
+    new THREE.BoxGeometry(10, 10, 10),
+    new THREE.MeshPhongMaterial({
+      color: 0xcccccc,
+      shininess: 20,
+      specular: 0xffffff,
+    })
+  );
+  cube.position.x = 0;
+  cube.position.y = 0;
+  cube.position.z = 0;
+  cube.scale.set(2, 2, 2);
+
+  //告诉立方体需要投射阴影
+  cube.castShadow = true;
+
+  console.log(parentCube);
+
+  scene.add(parentCube);
+
+  //底部平面
+  let planeGeometry = new THREE.PlaneGeometry(5000, 5000, 20, 20);
+  let planeMaterial = new THREE.MeshLambertMaterial({ color: 0xaaaaaa });
+
+  plane = new THREE.Mesh(planeGeometry, planeMaterial);
+  plane.rotation.x = -0.5 * Math.PI;
+  plane.position.y = -0;
+
+  //告诉底部平面需要接收阴影
+  plane.receiveShadow = true;
+
+  scene.add(plane);
+}
+
+//初始化性能插件
+let stats;
+function initStats() {
+  stats = new Stats();
+  document.body.appendChild(stats.dom);
+}
+
+//用户交互插件 鼠标左键按住旋转,右键按住平移,滚轮缩放
+let controls;
+function initControls() {
+  controls = new OrbitControls(camera, renderer.domElement);
+
+  // 如果使用animate方法时,将此函数删除
+  //controls.addEventListener( 'change', render );
+  // 使动画循环使用时阻尼或自转 意思是否有惯性
+  controls.enableDamping = true;
+  //动态阻尼系数 就是鼠标拖拽旋转灵敏度
+  controls.dampingFactor = 0.05;
+  //是否可以缩放
+  controls.enableZoom = true;
+  //是否自动旋转
+  controls.autoRotate = false;
+  //设置相机距离原点的最远距离
+  controls.minDistance = 10;
+  //设置相机距离原点的最远距离
+  controls.maxDistance = 300;
+  //是否开启右键拖拽
+  controls.enablePan = true;
+}
+
+function render() {
+  renderer.render(scene, camera);
+}
+
+//窗口变动触发的函数
+function onWindowResize() {
+  camera.aspect = getCanvasWidth() / getCanvasHeight();
+  camera.updateProjectionMatrix();
+  render();
+  renderer.setSize(getCanvasWidth(), getCanvasHeight());
+}
+
+function animate() {
+  //更新控制器
+  render();
+
+  //更新性能插件
+  stats.update();
+
+  controls.update();
+
+  // cube.rotation.x += 0.1;
+  cube.rotation.y += Math.PI / 100;
+
+  requestAnimationFrame(animate);
+}
+
+function draw() {
+  initRender();
+  initScene();
+  initCamera();
+  initLight();
+  initModel();
+  initControls();
+  initStats();
+
+  animate();
+  window.onresize = onWindowResize;
+}
+
+onMounted(() => {
+  draw();
+  initGui();
+});
+</script>

+ 271 - 0
src/views/three/index-2.vue

@@ -0,0 +1,271 @@
+<template>
+  <div ref="wrap"></div>
+</template>
+<script setup>
+import * as THREE from "three";
+import * as SceneUtils from "three/examples/jsm/utils/SceneUtils";
+import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
+import Stats from "three/examples/jsm/libs/stats.module";
+import * as dat from "dat.gui";
+import { onMounted, ref } from "vue";
+import { BoxGeometry, BufferAttribute } from "three";
+
+/**
+ * @type {import("vue").Ref<HTMLDivElement>}
+ */
+const wrap = ref();
+
+function getCanvasWidth() {
+  return window.innerWidth - 60;
+}
+
+function getCanvasHeight() {
+  return window.innerHeight - 150;
+}
+
+let renderer;
+function initRender() {
+  renderer = new THREE.WebGLRenderer({ antialias: true });
+  renderer.setSize(getCanvasWidth(), getCanvasHeight());
+  renderer.setPixelRatio(window.devicePixelRatio);
+  renderer.setClearColor(0x444444, 1);
+  //告诉渲染器需要阴影效果
+  renderer.shadowMap.enabled = true;
+  renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 默认的是,没有设置的这个清晰 THREE.PCFShadowMap
+  wrap.value.appendChild(renderer.domElement);
+}
+
+let camera;
+function initCamera() {
+  camera = new THREE.PerspectiveCamera(
+    50,
+    getCanvasWidth() / getCanvasHeight(),
+    0.1,
+    10000
+  );
+  camera.position.set(40, 40, 100);
+  camera.lookAt(new THREE.Vector3(0, 0, 0));
+}
+
+let scene;
+function initScene() {
+  scene = new THREE.Scene();
+}
+
+//初始化dat.GUI简化试验流程
+let gui;
+let datGui = new dat.GUI();
+
+let hemiLight, ambientLight, directionalLight, directionalLightHelper;
+function initGui() {
+  //声明一个保存需求修改的相关数据的对象
+  gui = {
+    directionalLight: "#ffffff", //点光源
+    directionalLightIntensity: 1, //灯光强度
+    visible: true, //是否可见
+    castShadow: true,
+    exponent: 30,
+    target: "plane",
+    debug: false,
+    groundColor: "#00ff00",
+    skyColor: "#0000ff",
+    hemiLightIntensity: 0.3,
+    dLightX: -40,
+    dLightY: 60,
+    dLightZ: -10,
+  };
+
+  //将设置属性添加到gui当中,gui.add(对象,属性,最小值,最大值)
+
+  const ambientFolder = datGui.addFolder("环境光");
+
+  ambientFolder.addColor(gui, "skyColor").onChange(function (e) {
+    hemiLight.color = new THREE.Color(e);
+  });
+  ambientFolder.addColor(gui, "groundColor").onChange(function (e) {
+    hemiLight.groundColor = new THREE.Color(e);
+  });
+  ambientFolder.add(gui, "hemiLightIntensity", 0, 1).onChange(function (e) {
+    hemiLight.intensity = e;
+  });
+  ambientFolder.addColor(gui, "directionalLight").onChange(function (e) {
+    directionalLight.color = new THREE.Color(e);
+  });
+
+  datGui.add(gui, "dLightX", -100, 100).onChange(function (e) {
+    directionalLight.position.setX(e);
+  });
+
+  datGui.add(gui, "dLightY", -100, 100).onChange(function (e) {
+    directionalLight.position.setY(e);
+  });
+
+  datGui.add(gui, "dLightZ", -100, 100).onChange(function (e) {
+    directionalLight.position.setZ(e);
+  });
+
+  datGui.add(gui, "directionalLightIntensity", 0, 5).onChange(function (e) {
+    directionalLight.intensity = e;
+  });
+  datGui.add(gui, "visible").onChange(function (e) {
+    directionalLight.visible = e;
+  });
+  datGui.add(gui, "castShadow").onChange(function (e) {
+    directionalLight.castShadow = e;
+  });
+  datGui.add(gui, "debug").onChange(function (e) {
+    if (e) {
+      let debug = new THREE.CameraHelper(directionalLight.shadow.camera);
+      debug.name = "debug";
+      scene.add(debug);
+    } else {
+      let debug = scene.getObjectByName("debug");
+      scene.remove(debug);
+    }
+  });
+}
+
+function initLight() {
+  hemiLight = new THREE.HemisphereLight("#bcffb1", "#000000", 1);
+  scene.add(hemiLight);
+  // ambientLight = new THREE.AmbientLight("#111111");
+  // scene.add(ambientLight);
+
+  directionalLight = new THREE.DirectionalLight("#ffffff");
+  directionalLight.position.set(-40, 60, -10);
+
+  directionalLightHelper = new THREE.DirectionalLightHelper(directionalLight);
+
+  directionalLight.shadow.camera.near = 20; //产生阴影的最近距离
+  directionalLight.shadow.camera.far = 200; //产生阴影的最远距离
+  directionalLight.shadow.camera.left = -100; //产生阴影距离位置的最左边位置
+  directionalLight.shadow.camera.right = 100; //最右边
+  directionalLight.shadow.camera.top = 100; //最上边
+  directionalLight.shadow.camera.bottom = -100; //最下面
+
+  //这两个值决定使用多少像素生成阴影 默认512
+  directionalLight.shadow.mapSize.height = 1024;
+  directionalLight.shadow.mapSize.width = 1024;
+
+  //告诉平行光需要开启阴影投射
+  directionalLight.castShadow = true;
+
+  // scene.add(directionalLightHelper);
+  scene.add(directionalLight);
+}
+
+let cube, plane;
+function initModel() {
+  //辅助工具
+  let helper = new THREE.AxesHelper(100);
+  scene.add(helper);
+
+  scene.add(
+    new THREE.Mesh(new BoxGeometry(10, 10, 10), [
+      new THREE.MeshBasicMaterial({
+        color: 0x0000ff,
+      }),
+      new THREE.MeshBasicMaterial({
+        color: 0xff00ff,
+      }),
+      new THREE.MeshBasicMaterial({
+        color: 0x00ffff,
+      }),
+      new THREE.MeshBasicMaterial({
+        color: 0xff0000,
+      }),
+      new THREE.MeshBasicMaterial({
+        color: 0x00ff00,
+      }),
+      new THREE.MeshBasicMaterial({
+        color: 0x0f000f,
+      }),
+    ])
+  );
+
+  //底部平面
+  let planeGeometry = new THREE.PlaneGeometry(5000, 5000, 20, 20);
+  let planeMaterial = new THREE.MeshLambertMaterial({ color: 0xaaaaaa });
+
+  plane = new THREE.Mesh(planeGeometry, planeMaterial);
+  plane.rotation.x = -0.5 * Math.PI;
+  plane.position.y = -0;
+
+  //告诉底部平面需要接收阴影
+  plane.receiveShadow = true;
+
+  // scene.add(plane);
+}
+
+//初始化性能插件
+let stats;
+function initStats() {
+  stats = new Stats();
+  document.body.appendChild(stats.dom);
+}
+
+//用户交互插件 鼠标左键按住旋转,右键按住平移,滚轮缩放
+let controls;
+function initControls() {
+  controls = new OrbitControls(camera, renderer.domElement);
+
+  // 如果使用animate方法时,将此函数删除
+  //controls.addEventListener( 'change', render );
+  // 使动画循环使用时阻尼或自转 意思是否有惯性
+  controls.enableDamping = true;
+  //动态阻尼系数 就是鼠标拖拽旋转灵敏度
+  controls.dampingFactor = 0.05;
+  //是否可以缩放
+  controls.enableZoom = true;
+  //是否自动旋转
+  controls.autoRotate = false;
+  //设置相机距离原点的最远距离
+  controls.minDistance = 10;
+  //设置相机距离原点的最远距离
+  controls.maxDistance = 300;
+  //是否开启右键拖拽
+  controls.enablePan = true;
+}
+
+function render() {
+  renderer.render(scene, camera);
+}
+
+//窗口变动触发的函数
+function onWindowResize() {
+  camera.aspect = getCanvasWidth() / getCanvasHeight();
+  camera.updateProjectionMatrix();
+  render();
+  renderer.setSize(getCanvasWidth(), getCanvasHeight());
+}
+
+function animate() {
+  //更新控制器
+  render();
+
+  //更新性能插件
+  stats.update();
+
+  controls.update();
+
+  requestAnimationFrame(animate);
+}
+
+function draw() {
+  initRender();
+  initScene();
+  initCamera();
+  initLight();
+  initModel();
+  initControls();
+  initStats();
+
+  animate();
+  window.onresize = onWindowResize;
+}
+initGui();
+
+onMounted(() => {
+  draw();
+});
+</script>

+ 54 - 353
src/views/three/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <div ref="wrap"></div>
+  <div ref="wrap" class="three-wrap"></div>
 </template>
 <script setup>
 import * as THREE from "three";
@@ -8,375 +8,76 @@ import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
 import Stats from "three/examples/jsm/libs/stats.module";
 import * as dat from "dat.gui";
 import { onMounted, ref } from "vue";
+import {
+  activeShadow,
+  addResizeAdapter,
+  getScence,
+  initAxes,
+  initBoxMesh,
+  initCamera,
+  initControl,
+  initGui,
+  initHemiLight,
+  initLight,
+  initPlaneMesh,
+  initRender,
+  initStatus,
+  startAnimate,
+} from "./util";
 
 /**
  * @type {import("vue").Ref<HTMLDivElement>}
  */
 const wrap = ref();
 
-const [canvasW, canvasH] = [1500, 500];
-
-let renderer;
-function initRender() {
-  renderer = new THREE.WebGLRenderer({ antialias: true });
-  renderer.setSize(window.innerWidth, window.innerHeight);
-  renderer.setPixelRatio(window.devicePixelRatio);
-  renderer.setClearColor(0x444444, 1);
-  //告诉渲染器需要阴影效果
-  renderer.shadowMap.enabled = true;
-  renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 默认的是,没有设置的这个清晰 THREE.PCFShadowMap
-  wrap.value.appendChild(renderer.domElement);
-}
-
-let camera;
-function initCamera() {
-  camera = new THREE.PerspectiveCamera(
-    50,
-    window.innerWidth / window.innerHeight,
-    0.1,
-    10000
-  );
-  camera.position.set(0, 40, 100);
-  camera.lookAt(new THREE.Vector3(0, 0, 0));
-}
-
-let scene;
-function initScene() {
-  scene = new THREE.Scene();
-}
-
-//初始化dat.GUI简化试验流程
-let gui;
-
-let hemiLight, ambientLight, directionalLight, directionalLightHelper;
-function initGui() {
-  //声明一个保存需求修改的相关数据的对象
-  gui = {
-    directionalLight: "#ffffff", //点光源
-    directionalLightIntensity: 1, //灯光强度
-    visible: true, //是否可见
-    castShadow: true,
-    exponent: 30,
-    target: "plane",
-    debug: true,
-    groundColor: "#00ff00",
-    skyColor: "#0000ff",
-    hemiLightIntensity: 0.3,
-    dLightX: -40,
-    dLightY: 60,
-    dLightZ: -10,
-  };
-  let datGui = new dat.GUI();
-  //将设置属性添加到gui当中,gui.add(对象,属性,最小值,最大值)
-
-  const ambientFolder = datGui.addFolder("环境光");
-
-  ambientFolder.addColor(gui, "skyColor").onChange(function (e) {
-    hemiLight.color = new THREE.Color(e);
-  });
-  ambientFolder.addColor(gui, "groundColor").onChange(function (e) {
-    hemiLight.groundColor = new THREE.Color(e);
-  });
-  ambientFolder.add(gui, "hemiLightIntensity", 0, 1).onChange(function (e) {
-    hemiLight.intensity = e;
-  });
-  ambientFolder.addColor(gui, "directionalLight").onChange(function (e) {
-    directionalLight.color = new THREE.Color(e);
-  });
-
-  datGui
-    .add(gui, "dLightX", {
-      left: -100,
-      center: 0,
-      right: 100,
-      // 左: -100,//可以用中文
-      // 中: 0,
-      // 右: 100
-    })
-    .onChange(function (e) {
-      directionalLight.position.setX(e);
-    });
-
-  datGui.add(gui, "dLightY", -100, 100).onChange(function (e) {
-    directionalLight.position.setY(e);
-  });
-
-  datGui.add(gui, "dLightZ", -100, 100).onChange(function (e) {
-    directionalLight.position.setZ(e);
-  });
-
-  datGui.add(gui, "directionalLightIntensity", 0, 5).onChange(function (e) {
-    directionalLight.intensity = e;
-  });
-  datGui.add(gui, "visible").onChange(function (e) {
-    directionalLight.visible = e;
-  });
-  datGui.add(gui, "castShadow").onChange(function (e) {
-    directionalLight.castShadow = e;
-  });
-  datGui.add(gui, "debug").onChange(function (e) {
-    if (e) {
-      let debug = new THREE.CameraHelper(directionalLight.shadow.camera);
-      debug.name = "debug";
-      scene.add(debug);
-    } else {
-      let debug = scene.getObjectByName("debug");
-      scene.remove(debug);
-    }
-  });
-}
-
-function initLight() {
-  hemiLight = new THREE.HemisphereLight("#bcffb1", "#000000", 1);
-  scene.add(hemiLight);
-  // ambientLight = new THREE.AmbientLight("#111111");
-  // scene.add(ambientLight);
-
-  directionalLight = new THREE.DirectionalLight("#ffffff");
-  directionalLight.position.set(-40, 60, -10);
-
-  directionalLightHelper = new THREE.DirectionalLightHelper(directionalLight);
-
-  directionalLight.shadow.camera.near = 20; //产生阴影的最近距离
-  directionalLight.shadow.camera.far = 200; //产生阴影的最远距离
-  directionalLight.shadow.camera.left = -100; //产生阴影距离位置的最左边位置
-  directionalLight.shadow.camera.right = 100; //最右边
-  directionalLight.shadow.camera.top = 100; //最上边
-  directionalLight.shadow.camera.bottom = -100; //最下面
-
-  //这两个值决定使用多少像素生成阴影 默认512
-  directionalLight.shadow.mapSize.height = 1024;
-  directionalLight.shadow.mapSize.width = 1024;
-
-  //告诉平行光需要开启阴影投射
-  directionalLight.castShadow = true;
-
-  // scene.add(directionalLightHelper);
-
-  scene.add(directionalLight);
-}
-
-let cube, plane;
-function initModel() {
-  //辅助工具
-  let helper = new THREE.AxesHelper(100);
-  scene.add(helper);
-
-  const bufferGeometry = new THREE.BufferGeometry();
-
-  bufferGeometry.attributes.position = new THREE.BufferAttribute(
-    new Float32Array([
-      -10,
-      0,
-      0, // 0
-      10,
-      0,
-      0, // 1
-      0,
-      10,
-      0, // 2
-      0,
-      0,
-      5, // 3
-      0,
-      10,
-      5, // 4
-      0,
-      0,
-      15, // 5
-    ]),
-    3
-  );
-
-  scene.add(
-    new THREE.Points(
-      bufferGeometry,
-      new THREE.MeshBasicMaterial({
-        color: 0x00ff00,
-        side: THREE.DoubleSide,
-      })
-    )
-  );
-
-  //球体
-  let sphereGeometry = new THREE.SphereGeometry(10, 30, 30);
-  let sphereMaterial = new THREE.MeshLambertMaterial({
-    color: 0xeeeeee,
-    transparent: true,
-    opacity: 0.5,
-  });
-
-  let material = new THREE.MeshPhongMaterial({
-    color: 0x0000ff,
-    specular: 0x4488ee,
-    shininess: 12,
-  });
-
-  let sphere = new THREE.Mesh(
-    sphereGeometry,
-    new THREE.MeshPhongMaterial({
-      color: 0xcccccc,
-      shininess: 20,
-      specular: 0xffffff,
-    })
-  );
-  sphere.position.set(-20, 20, 0);
-
-  sphere.castShadow = true;
-
-  scene.add(sphere);
-
-  const cylinderGeometry = new THREE.CylinderGeometry(5, 5, 10);
-  const cylinder = new THREE.Mesh(
-    cylinderGeometry,
-    new THREE.MeshLambertMaterial({
-      color: 0xeeeeee,
-    })
-  );
-  cylinder.castShadow = true;
+onMounted(() => {
+  const render = initRender();
+  const scene = getScence();
+  const camera = initCamera();
+  const axes = initAxes();
+  const status = initStatus();
+  const control = initControl(camera, render);
 
-  cylinder.position.x = 50;
-  cylinder.position.y = 10;
-  cylinder.position.z = -10;
+  const plane = initPlaneMesh();
+  const box = initBoxMesh();
+  const light = initLight();
+  const hemiLight = initHemiLight();
 
-  scene.add(cylinder);
+  activeShadow(box, plane, hemiLight);
+  scene.add(box).add(plane).add(light).add(hemiLight);
 
-  const cylinderGeometry2 = new THREE.CylinderGeometry(0, 5, 10);
-  const cylinder2 = new THREE.Mesh(
-    cylinderGeometry2,
-    new THREE.MeshPhongMaterial({
-      color: 0xcccccc,
-      shininess: 20,
-      specular: 0xffffff,
-    })
-  );
-  cylinder2.castShadow = true;
+  // const directionalLight = new THREE.DirectionalLight("#ffffff");
+  // directionalLight.position.set(-40, 60, -10);
 
-  cylinder2.position.x = 50;
-  cylinder2.position.y = 10;
-  cylinder2.position.z = -30;
+  // directionalLight.shadow.camera.near = 20; //产生阴影的最近距离
+  // directionalLight.shadow.camera.far = 200; //产生阴影的最远距离
+  // directionalLight.shadow.camera.left = -100; //产生阴影距离位置的最左边位置
+  // directionalLight.shadow.camera.right = 100; //最右边
+  // directionalLight.shadow.camera.top = 100; //最上边
+  // directionalLight.shadow.camera.bottom = -100; //最下面
 
-  scene.add(cylinder2);
+  // //这两个值决定使用多少像素生成阴影 默认512
+  // directionalLight.shadow.mapSize.height = 1024;
+  // directionalLight.shadow.mapSize.width = 1024;
 
-  //立方体
-  let cubeGeometry = new THREE.BoxGeometry(10, 10, 10);
+  // //告诉平行光需要开启阴影投射
+  // directionalLight.castShadow = true;
 
-  let cubeMaterial = new THREE.MeshLambertMaterial({ color: 0x00ffff });
+  // scene.add(directionalLight);
 
-  cube = new THREE.Mesh(
-    cubeGeometry,
-    new THREE.MeshPhongMaterial({
-      color: 0xcccccc,
-      shininess: 20,
-      specular: 0xffffff,
-    })
-  );
-  cube.position.x = 30;
-  cube.position.y = 5;
-  cube.position.z = -5;
+  const gui = initGui();
 
-  //告诉立方体需要投射阴影
-  cube.castShadow = true;
+  wrap.value.appendChild(render.domElement);
+  wrap.value.appendChild(status.dom);
 
-  scene.add(cube);
+  scene.add(axes);
+  render.render(scene, camera);
+  addResizeAdapter(camera, render);
 
-  const geometry = new THREE.BoxGeometry(5, 5, 5);
-  //材质对象Material
-  const material2 = new THREE.MeshLambertMaterial({
-    color: 0x00ffff, //设置材质颜色
+  startAnimate(() => {
+    status.update();
+    control.update();
+    render.render(scene, camera);
   });
-  for (let i = 0; i < 10; i++) {
-    const mesh = new THREE.Mesh(geometry, material2); //网格模型对象Mesh
-    // 沿着x轴分布
-    mesh.position.set(-10, 5, -i * 10);
-    mesh.castShadow = true;
-
-    scene.add(mesh); //网格模型添加到场景中
-  }
-
-  //底部平面
-  let planeGeometry = new THREE.PlaneGeometry(5000, 5000, 20, 20);
-  let planeMaterial = new THREE.MeshLambertMaterial({ color: 0xaaaaaa });
-
-  plane = new THREE.Mesh(planeGeometry, planeMaterial);
-  plane.rotation.x = -0.5 * Math.PI;
-  plane.position.y = -0;
-
-  //告诉底部平面需要接收阴影
-  plane.receiveShadow = true;
-
-  scene.add(plane);
-}
-
-//初始化性能插件
-let stats;
-function initStats() {
-  stats = new Stats();
-  document.body.appendChild(stats.dom);
-}
-
-//用户交互插件 鼠标左键按住旋转,右键按住平移,滚轮缩放
-let controls;
-function initControls() {
-  controls = new OrbitControls(camera, renderer.domElement);
-
-  // 如果使用animate方法时,将此函数删除
-  //controls.addEventListener( 'change', render );
-  // 使动画循环使用时阻尼或自转 意思是否有惯性
-  controls.enableDamping = true;
-  //动态阻尼系数 就是鼠标拖拽旋转灵敏度
-  //controls.dampingFactor = 0.25;
-  //是否可以缩放
-  controls.enableZoom = true;
-  //是否自动旋转
-  controls.autoRotate = false;
-  //设置相机距离原点的最远距离
-  controls.minDistance = 50;
-  //设置相机距离原点的最远距离
-  controls.maxDistance = 200;
-  //是否开启右键拖拽
-  controls.enablePan = true;
-}
-
-function render() {
-  renderer.render(scene, camera);
-}
-
-//窗口变动触发的函数
-function onWindowResize() {
-  camera.aspect = window.innerWidth / window.innerHeight;
-  camera.updateProjectionMatrix();
-  render();
-  renderer.setSize(window.innerWidth, window.innerHeight);
-}
-
-function animate() {
-  //更新控制器
-  render();
-
-  //更新性能插件
-  stats.update();
-
-  controls.update();
-
-  requestAnimationFrame(animate);
-}
-
-function draw() {
-  initGui();
-  initRender();
-  initScene();
-  initCamera();
-  initLight();
-  initModel();
-  initControls();
-  initStats();
-
-  animate();
-  window.onresize = onWindowResize;
-}
-
-onMounted(() => {
-  draw();
 });
 </script>

+ 177 - 0
src/views/three/util.js

@@ -0,0 +1,177 @@
+import * as THREE from "three";
+import * as SceneUtils from "three/examples/jsm/utils/SceneUtils";
+import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
+import Stats from "three/examples/jsm/libs/stats.module";
+import * as dat from "dat.gui";
+import { onMounted, ref } from "vue";
+
+export function getCanvasSize() {
+  return {
+    width: window.innerWidth - 60,
+    height: window.innerHeight - 150,
+  };
+}
+
+export function initRender() {
+  const render = new THREE.WebGLRenderer({
+    // 开起抗锯齿
+    antialias: true,
+  });
+
+  render.setSize(getCanvasSize().width, getCanvasSize().height);
+  render.setPixelRatio(window.devicePixelRatio);
+  render.setClearColor(0x666666);
+
+  render.shadowMap.enabled = true;
+
+  /**
+   * BasicShadowMap 能够给出没有经过过滤的阴影映射 —— 速度最快,但质量最差。
+   * PCFShadowMap 为默认值,使用Percentage-Closer Filtering (PCF)算法来过滤阴影映射。
+   * PCFSoftShadowMap 和PCFShadowMap一样使用 Percentage-Closer Filtering (PCF) 算法过滤阴影映射,但在使用低分辨率阴影图时具有更好的软阴影。
+   * VSMShadowMap 使用Variance Shadow Map (VSM)算法来过滤阴影映射。当使用VSMShadowMap时,所有阴影接收者也将会投射阴影
+   */
+  render.shadowMap.type = THREE.PCFSoftShadowMap;
+  /**
+   * 真实光照
+   */
+  render.physicallyCorrectLights = true;
+  /**
+   * THREE.LinearEncoding:线性颜色空间
+   * THREE.sRGBEncoding:sRGB (opens new window)颜色空间 这种更真实
+   */
+  render.outputColorSpace = THREE.SRGBColorSpace;
+
+  render.toneMapping = THREE.ACESFilmicToneMapping;
+
+  return render;
+}
+
+/**
+ * @type {THREE.Scene}
+ */
+const scene = new THREE.Scene();
+export function getScence() {
+  return scene;
+}
+
+/**
+ *
+ * @param  {...THREE.Mesh} args
+ */
+export function activeShadow(...args) {
+  args.forEach((mesh) => {
+    mesh.castShadow = true;
+    mesh.receiveShadow = true;
+  });
+}
+
+export function initCamera() {
+  const camera = new THREE.PerspectiveCamera(
+    50,
+    getCanvasSize().width / getCanvasSize().height,
+    0.1,
+    2000
+  );
+  camera.updateProjectionMatrix();
+  camera.position.set(40, 40, 40);
+  camera.lookAt(new THREE.Vector3(0, 0, 0));
+  return camera;
+}
+
+export function initAxes() {
+  const axes = new THREE.AxesHelper(100);
+  return axes;
+}
+
+export function initBoxMesh() {
+  return new THREE.Mesh(
+    new THREE.BoxGeometry(10, 10, 10, 5, 5, 5),
+    new THREE.MeshStandardMaterial({
+      color: 0xffffff,
+    })
+  );
+}
+
+export function initPlaneMesh() {
+  const mesh = new THREE.Mesh(
+    new THREE.PlaneGeometry(100, 100),
+    new THREE.MeshStandardMaterial({
+      color: 0x0000ff,
+      side: THREE.DoubleSide,
+    })
+  );
+
+  mesh.rotation.x = -Math.PI / 2;
+  return mesh;
+}
+
+const hemiLight = new THREE.HemisphereLight("#ffffff", "#000000", 1);
+scene.add(hemiLight);
+
+export function initHemiLight() {
+  return new THREE.HemisphereLight("#ffffff", "#000000", 1);
+}
+
+export function initLight() {
+  const light = new THREE.PointLight(0xffffff, 100);
+  light.castShadow = true;
+  light.position.set(50, 50, 0);
+  return light;
+}
+
+/**
+ *
+ * @param {THREE.Camera} camera
+ * @param {THREE.WebGLRenderer} render
+ */
+export function initControl(camera, render) {
+  const control = new OrbitControls(camera, render.domElement);
+  // 使动画循环使用时阻尼或自转 意思是否有惯性
+  control.enableDamping = true;
+  control.dampingFactor = 0.2;
+
+  control.enableZoom = true;
+  control.enableRotate = true;
+  control.autoRotate = false;
+  // 是否开启右键拖拽
+  control.enablePan = true;
+
+  // 设置相机距离原点的最远距离
+  control.minDistance = 10;
+  // 设置相机距离原点的最远距离
+  control.maxDistance = 300;
+
+  return control;
+}
+
+export function initStatus() {
+  return new Stats();
+}
+
+export function initGui() {
+  return new dat.GUI();
+}
+
+/**
+ *
+ * @param {THREE.Camera} camera
+ * @param {THREE.WebGLRenderer} render
+ */
+export function addResizeAdapter(camera, render) {
+  window.addEventListener("resize", () => {
+    camera.aspect = getCanvasSize().width / getCanvasSize().height;
+    camera.updateProjectionMatrix();
+    render.setSize(getCanvasSize().width, getCanvasSize().height);
+    render.render(scene, camera);
+  });
+}
+
+export function startAnimate(cb) {
+  function run() {
+    cb();
+    requestAnimationFrame(() => {
+      run();
+    });
+  }
+  run();
+}