Ver código fonte

feature: 材质

xiongxt 1 ano atrás
pai
commit
6b110d9b91

+ 7 - 2
src/App.vue

@@ -2,7 +2,7 @@
   <div class="root">
     <nav-bar />
     <div class="wrap">
-      <left-aside class="menu" v-if="isSdk"></left-aside>
+      <left-aside class="menu" v-if="leftAsideVisible"></left-aside>
       <div class="right"><router-view /></div>
     </div>
   </div>
@@ -14,7 +14,12 @@ import LeftAside from "@/components/leftAside.vue";
 import { computed } from "vue";
 import { useRoute } from "vue-router";
 
-const isSdk = computed(() => useRoute().fullPath.startsWith("/sdk"));
+const leftAsideVisible = computed(() => {
+  return (
+    useRoute().fullPath.startsWith("/sdk") ||
+    useRoute().fullPath.startsWith("/three")
+  );
+});
 </script>
 
 <style lang="less">

+ 10 - 2
src/router/index.js

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

BIN
src/views/three/assets/DayEnvironmentHDRI034_1K-HDR.exr


BIN
src/views/three/assets/TexturesCom_rock_boulder_002_header8.jpg


BIN
src/views/three/assets/Tiles130_1K-PNG/Tiles130.png


+ 42 - 0
src/views/three/assets/Tiles130_1K-PNG/Tiles130_1K-PNG.mtlx

@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<materialx version="1.38" fileprefix="./">
+  <standard_surface ypos="-1.879310" xpos="6.159420" name="Tiles130_1K_PNG_StandardSurface" type="surfaceshader">
+    <input name="specular" type="float" value="0" />
+    <input name="coat" type="float" value="1" />
+    <input name="coat_color" type="color3" value="1, 1, 1" />
+    <input name="base" type="float" value="1" />
+    <input nodename="Tiles130_1K_PNG_Color" name="base_color" type="color3" />
+    <input nodename="normalmap" name="normal" type="vector3" />
+    <input nodename="normalmap" name="coat_normal" type="vector3" />
+    <input nodename="Tiles130_1K_PNG_Roughness" name="specular_roughness" type="float" />
+    <input nodename="Tiles130_1K_PNG_Roughness" name="coat_roughness" type="float" />
+  </standard_surface>
+  <surfacematerial ypos="0.000000" xpos="8.695652" name="Tiles130_1K_PNG" type="material">
+    <input nodename="Tiles130_1K_PNG_StandardSurface" name="surfaceshader" type="surfaceshader" />
+    <input nodename="displacement" name="displacementshader" type="displacementshader" />
+  </surfacematerial>
+  <tiledimage ypos="-3.103448" xpos="3.623188" name="Tiles130_1K_PNG_Color" type="color3">
+    <input colorspace="srgb_texture" name="file" type="filename" value="Tiles130_1K-PNG_Color.png" />
+    <input name="uvtiling" type="vector2" value="1.0, 1.0" />
+  </tiledimage>
+  <tiledimage ypos="5.163793" xpos="3.623188" name="Tiles130_1K_PNG_Displacement" type="float">
+    <input name="file" type="filename" value="Tiles130_1K-PNG_Displacement.png" />
+    <input name="uvtiling" type="vector2" value="1.0, 1.0" />
+  </tiledimage>
+  <displacement ypos="1.879310" xpos="6.159420" name="displacement" type="displacementshader">
+    <input nodename="Tiles130_1K_PNG_Displacement" name="displacement" type="float" />
+    <input name="scale" type="float" value="1.0" />
+  </displacement>
+  <tiledimage ypos="0.879310" xpos="1.086957" name="Tiles130_1K_PNG_NormalGL" type="vector3">
+    <input name="file" type="filename" value="Tiles130_1K-PNG_NormalGL.png" />
+    <input name="uvtiling" type="vector2" value="1.0, 1.0" />
+  </tiledimage>
+  <normalmap ypos="3.586207" xpos="3.623188" name="normalmap" type="vector3">
+    <input nodename="Tiles130_1K_PNG_NormalGL" name="in" type="vector3" />
+    <input name="scale" type="float" value="1.0" />
+  </normalmap>
+  <tiledimage ypos="-0.413793" xpos="3.623188" name="Tiles130_1K_PNG_Roughness" type="float">
+    <input name="file" type="filename" value="Tiles130_1K-PNG_Roughness.png" />
+    <input name="uvtiling" type="vector2" value="1.0, 1.0" />
+  </tiledimage>
+</materialx>

BIN
src/views/three/assets/Tiles130_1K-PNG/Tiles130_1K-PNG.usdc


BIN
src/views/three/assets/Tiles130_1K-PNG/Tiles130_1K-PNG_AmbientOcclusion.png


BIN
src/views/three/assets/Tiles130_1K-PNG/Tiles130_1K-PNG_Color.png


BIN
src/views/three/assets/Tiles130_1K-PNG/Tiles130_1K-PNG_Displacement.png


BIN
src/views/three/assets/Tiles130_1K-PNG/Tiles130_1K-PNG_NormalDX.png


BIN
src/views/three/assets/Tiles130_1K-PNG/Tiles130_1K-PNG_NormalGL.png


BIN
src/views/three/assets/Tiles130_1K-PNG/Tiles130_1K-PNG_Roughness.png


+ 157 - 0
src/views/three/index-light.vue

@@ -0,0 +1,157 @@
+<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";
+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();
+});
+
+onMounted(() => {
+  const 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();
+
+  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>

+ 20 - 41
src/views/three/index.vue

@@ -7,7 +7,7 @@ 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 { onBeforeUnmount, onMounted, ref } from "vue";
 import {
   activeShadow,
   addResizeAdapter,
@@ -26,19 +26,27 @@ import {
   startAnimate,
   makeStar,
   initSpotLight,
+  getCanvasSize,
 } from "./util";
 
 /**
  * @type {import("vue").Ref<HTMLDivElement>}
  */
 const wrap = ref();
+const gui = initGui();
+const status = initStatus();
+
+onBeforeUnmount(() => {
+  gui.destroy();
+});
 
 onMounted(() => {
-  const render = initRender();
+  const render = initRender(wrap.value);
+  const camera = initCamera(wrap.value);
   const scene = getScence();
-  const camera = initCamera();
+
   const axes = initAxes();
-  const status = initStatus();
+
   const control = initControl(camera, render);
 
   const plane = initPlaneMesh();
@@ -57,50 +65,15 @@ onMounted(() => {
 
   scene.add(box).add(plane).add(sphere).add(plight).add(star).add(slight);
 
-  const gui = initGui();
-
   wrap.value.appendChild(render.domElement);
   wrap.value.appendChild(status.dom);
 
   scene.add(axes);
   render.render(scene, camera);
-  addResizeAdapter(camera, render);
+  addResizeAdapter(camera, render, wrap.value);
+
   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();
@@ -109,3 +82,9 @@ onMounted(() => {
   });
 });
 </script>
+
+<style lang="less" scoped>
+.three-wrap {
+  position: relative;
+}
+</style>

+ 21 - 10
src/views/three/util.js

@@ -5,20 +5,27 @@ import Stats from "three/examples/jsm/libs/stats.module";
 import * as dat from "dat.gui";
 import { onMounted, ref } from "vue";
 
-export function getCanvasSize() {
+/**
+ * @param {HTMLDivElement} dom
+ */
+export function getCanvasSize(dom) {
   return {
-    width: window.innerWidth - 60,
-    height: window.innerHeight - 150,
+    width: window.innerWidth - dom.getBoundingClientRect().left,
+    height: window.innerHeight - dom.getBoundingClientRect().top,
   };
 }
 
-export function initRender() {
+/**
+ * @param {HTMLDivElement} dom
+ * @returns
+ */
+export function initRender(dom) {
   const render = new THREE.WebGLRenderer({
     // 开起抗锯齿
     antialias: true,
   });
 
-  render.setSize(getCanvasSize().width, getCanvasSize().height);
+  render.setSize(getCanvasSize(dom).width, getCanvasSize(dom).height);
   render.setPixelRatio(window.devicePixelRatio);
   render.setClearColor(0x666666);
 
@@ -64,11 +71,13 @@ export function activeShadow(...args) {
     mesh.receiveShadow = true;
   });
 }
-
-export function initCamera() {
+/**
+ * @param {HTMLDivElement} dom
+ */
+export function initCamera(dom) {
   const camera = new THREE.PerspectiveCamera(
     50,
-    getCanvasSize().width / getCanvasSize().height,
+    getCanvasSize(dom).width / getCanvasSize(dom).height,
     0.1,
     2000
   );
@@ -178,15 +187,17 @@ export function initStatus() {
 }
 
 export function initGui() {
-  return new dat.GUI();
+  const gui = new dat.GUI();
+  return gui;
 }
 
 /**
  *
  * @param {THREE.Camera} camera
  * @param {THREE.WebGLRenderer} render
+ * @param {HTMLDivElement} dom
  */
-export function addResizeAdapter(camera, render) {
+export function addResizeAdapter(camera, render, dom) {
   window.addEventListener("resize", () => {
     camera.aspect = getCanvasSize().width / getCanvasSize().height;
     camera.updateProjectionMatrix();