xiongxt 1 an în urmă
părinte
comite
365c31dc1c

+ 1 - 1
src/components/leftAside.vue

@@ -1,7 +1,7 @@
 <template>
   <ul class="left-aside">
     <li v-for="it in subRoutes" :key="it.name">
-      <RouterLink :to="it.name">{{ it.name }}</RouterLink>
+      <RouterLink :to="it.path">{{ it.name }}</RouterLink>
     </li>
   </ul>
 </template>

+ 12 - 4
src/router/index.js

@@ -79,19 +79,27 @@ const routes = [
     component: DynamicComponent,
     children: [
       {
-        path: "index",
-        name: "index",
+        path: "/three/index",
+        name: "threeIndex",
         component: () =>
           import(/* webpackChunkName: "three" */ "../views/three/index.vue"),
       },
       {
-        path: "light",
-        name: "light",
+        path: "/three/light",
+        name: "threeLight",
         component: () =>
           import(
             /* webpackChunkName: "three" */ "../views/three/index-light.vue"
           ),
       },
+      {
+        path: "/three/material",
+        name: "threeMaterial",
+        component: () =>
+          import(
+            /* webpackChunkName: "three" */ "../views/three/index-material.vue"
+          ),
+      },
     ],
   },
   {

BIN
src/views/three/assets/meadow_2k.hdr


+ 187 - 0
src/views/three/index-material.vue

@@ -0,0 +1,187 @@
+<template>
+  <div ref="wrap" class="three-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 { onBeforeUnmount, onMounted, ref } from "vue";
+
+// 导入HDR加载器
+import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";
+import {
+  activeShadow,
+  addResizeAdapter,
+  getScence,
+  initAxes,
+  initBoxMesh,
+  initCamera,
+  initControl,
+  initGui,
+  initHemiLight,
+  initPointLight,
+  initPlaneMesh,
+  initRender,
+  initSphereMesh,
+  initStatus,
+  startAnimate,
+  makeStar,
+  initSpotLight,
+  getCanvasSize,
+} from "./util";
+
+/**
+ * @type {import("vue").Ref<HTMLDivElement>}
+ */
+const wrap = ref();
+const gui = initGui();
+const status = initStatus();
+
+onBeforeUnmount(() => {
+  gui.destroy();
+});
+let render;
+
+onMounted(() => {
+  if (render) {
+    return;
+  }
+  render = initRender(wrap.value);
+  const camera = initCamera(wrap.value);
+  const scene = getScence();
+
+  const axes = initAxes();
+
+  const control = initControl(camera, render);
+
+  const plane = initPlaneMesh();
+  const box = initBoxMesh();
+  const sphere = initSphereMesh();
+  const plight = initPointLight();
+  const star = makeStar();
+  const slight = initSpotLight();
+
+  // 环境贴图
+  const rgbeloader = new RGBELoader();
+  rgbeloader.load(require("./assets/meadow_2k.hdr"), (envMap) => {
+    envMap.mapping = THREE.EquirectangularReflectionMapping;
+    scene.background = envMap;
+    scene.environment = envMap;
+    plane.material.envMap = envMap;
+  });
+
+  // 纹理贴图
+  const textureLoader = new THREE.TextureLoader();
+  textureLoader.load(
+    require("./assets/Tiles130_1K-PNG/Tiles130_1K-PNG_Color.png"),
+    (texture) => {
+      plane.material.map = texture;
+      sphere.material.map = texture;
+      plane.material.transparent = true;
+      sphere.material.transparent = true;
+    }
+  );
+
+  // aoMap 环境遮挡贴图
+
+  box.position.set(0, 5, 0);
+  sphere.position.set(20, 10, 10);
+  plight.position.set(40, 30, 0);
+  star.position.set(0, 20, 0);
+  slight.position.set(-30, 20, 0);
+  activeShadow(box, plane, sphere, plight, star, slight);
+
+  scene.add(box).add(plane).add(sphere).add(plight).add(star).add(slight);
+
+  wrap.value.appendChild(render.domElement);
+  wrap.value.appendChild(status.dom);
+
+  scene.add(axes);
+  render.render(scene, camera);
+  addResizeAdapter(camera, render, wrap.value);
+
+  const raycaster = new THREE.Raycaster();
+  const mouse = new THREE.Vector2();
+  wrap.value.addEventListener("click", (e) => {
+    const { layerX, layerY } = e;
+
+    mouse.x = layerX / (getCanvasSize(wrap.value).width / 2) - 1;
+    mouse.y = -(layerY / (getCanvasSize(wrap.value).height / 2) - 1);
+
+    // 通过摄像机和鼠标坐标更新射线
+    raycaster.setFromCamera(mouse, camera);
+
+    const intersects = raycaster.intersectObjects(scene.children);
+
+    scene.children.forEach((it) => {
+      if (it.isMesh) {
+        it.material._select = false;
+        if (it.material._originColor) {
+          it.material.color.set(it.material._originColor);
+        }
+      }
+    });
+
+    intersects.forEach((it, index) => {
+      if (it.object.isMesh) {
+        if (index === 0 && it.object.material._select !== true) {
+          it.object.material._select = true;
+          it.object.material._originColor = it.object.material.color.getHex();
+          it.object.material.color.set(0xff0000);
+        }
+      }
+    });
+  });
+
+  const starFolder = gui.addFolder("星星");
+  starFolder.add(star.position, "x").min(-50).max(50).name("坐标X");
+  starFolder.add(star.position, "y").min(20).max(100).name("坐标Y");
+  starFolder
+    .add(star.material, "emissiveIntensity")
+    .min(1)
+    .max(10)
+    .name("自发光强度");
+
+  starFolder
+    .add(star.children[0], "intensity")
+    .min(0)
+    .max(1000)
+    .name("光照强度");
+
+  const slightFolder = gui.addFolder("聚光灯");
+  slightFolder
+    .add(slight.position, "x")
+    .min(-50)
+    .max(50)
+    .name("聚光灯X")
+    .onChange(() => {
+      slight.target.position.set(0, 0, 0);
+    });
+  slightFolder
+    .add(slight.position, "y")
+    .min(0)
+    .max(50)
+    .name("聚光灯Y")
+    .onChange(() => {
+      slight.target.position.set(0, 0, 0);
+    });
+
+  slightFolder.add(slight, "angle").min(0).max(Math.PI).name("angle");
+  slightFolder.add(slight, "penumbra").min(0).max(1).name("penumbra");
+  slightFolder.add(slight, "decay").min(0).max(10).name("decay");
+
+  startAnimate(() => {
+    status.update();
+    control.update();
+    render.render(scene, camera);
+  });
+});
+</script>
+
+<style lang="less" scoped>
+.three-wrap {
+  position: relative;
+}
+</style>

+ 2 - 2
src/views/three/util.js

@@ -199,9 +199,9 @@ export function initGui() {
  */
 export function addResizeAdapter(camera, render, dom) {
   window.addEventListener("resize", () => {
-    camera.aspect = getCanvasSize().width / getCanvasSize().height;
+    camera.aspect = getCanvasSize(dom).width / getCanvasSize(dom).height;
     camera.updateProjectionMatrix();
-    render.setSize(getCanvasSize().width, getCanvasSize().height);
+    render.setSize(getCanvasSize(dom).width, getCanvasSize(dom).height);
     render.render(scene, camera);
   });
 }

+ 3 - 0
vue.config.js

@@ -3,6 +3,9 @@ const { defineConfig } = require("@vue/cli-service");
 module.exports = defineConfig({
   transpileDependencies: true,
   publicPath: "/ludash",
+  chainWebpack(config) {
+    config.module.rule("images").test(/\.(png|jpeg|jpg|hdr)$/);
+  },
   css: {
     loaderOptions: {
       less: {