记录--使用Lunchbox 在 vue3 中创建一个 3D 地球
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助
什么是 Lunchbox.js
Lunchbox.js 是 Three.js 的 Vue 3 自定义渲染器。 你可以把它想象成 Vue 的 react-three-fiber
。
该库通过组件提供对原始 Three.js 对象和类的访问,例如:
- mesh
- lights
- geometries
- group
- materials
这些组件是 Lunchbox.js 的构建块。
例如,在 Three.js 项目中创建标准网格时,我们会使用像 BoxGeometry()
这样的几何类来创建长方体形状,使用像 MeshBasicMaterial()
这样的材质类对其应用颜色,就像在下面的代码中一样:
// 创建边长为 1 的几何图形 const geometry = new THREE.BoxGeometry(1, 1, 1) // 创建黄色材质 const material = new THREE.MeshBasicMaterial({ color: 0xffff00 }) // 在网格中结合几何图形和材质 const mesh = new THREE.Mesh(geometry, material) // 将该网格添加到场景中 scene.add(mesh)
Mesh()
类组合,我们将它们嵌套在 <mesh>
中 组件,如下所示:<Lunchbox> <mesh> <boxGeometry :args="[1, 1 ,1]" /> <meshBasicMaterial :color="0xffff00" /> </mesh> </Lunchbox>
<Lunchbox>
组件都与对应的 Three.js 类和一个 camelCase
名称相匹配。// three.js new THREE.Mesh() <!-- Lunchbox --> <mesh/> // three.js new THREE.BoxGeometry() <!-- Lunchbox --> <boxGeometry/>
<Lunchbox>
组件都使用 args
属性将参数作为 props
。 argsprop
接受一个参数数组并将其传递给组件的类构造函数。new THREE.BoxGeometry(1, 2, 3)
<boxGeometry :args="[1, 2, 3]" />
MeshBasicMaterial
类的 color
属性,可以作为响应式属性添加到 <Lunchbox>
组件上。 这就像我们在前面的例子中看到的一样。<meshBasicMaterial :color="red" />
<Lunchbox>
组件中使用破折号 - 进行设置,如下所示:<mesh :position-x="3" />
Lunchbox为大多数 Three.js 类提供内置支持; 你可以在此处找到组件列表。
可以使用 Lunchbox的 app.extend
方法添加包含的组件中不可用的类。 例如,下面是我们如何将 orbitControl
类添加到 Lunchbox:
import { createApp } from 'lunchboxjs' import App from 'YourApp.vue' import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' createApp(App) .extend({ OrbitControls }) .mount('#app')
extend()
方法是 Lunchbox 的独特功能之一,使用该方法添加的类继承了 <Lunchbox>
组件的所有功能。
我们已经了解了 Lunchbox.js 的核心概念,接下来我们将了解如何使用 Lunchbox 进行构建。
开始
要设置一个 Lunchbox.js 应用程序,首先使用 Vite 的 CLI 安装 Vue:
npm create vite@latest
npm install lunchboxjs three
此命令将安装 Lunchbox.js 和 Three.js 作为 Vue 应用程序的依赖项。 你可以在根目录的 package.json 文件中查看它们。
接下来清理项目中的样板代码,打开 main.js
文件,将内容替换为如下代码:
import { createApp } from 'lunchboxjs' import App from './App.vue' createApp(App).mount('#app')
在这里,我们从 lunchboxjs 而不是从 vue 导入 createApp
函数。 这会将 Vue 应用程序转换为 Lunchbox 环境。
现在我们可以开始在我们的应用程序中构建和渲染 3D 对象。
创建场景
场景是允许我们设置要渲染的项目的对象。 它就像一个显示区域,可以将对象放在一起并呈现给浏览器。 以下是 Three.js 中设置的场景示例:
const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); const renderer = new THREE.WebGLRenderer(); renderer.setSize( window.innerWidth, window.innerHeight ); document.body.appendChild( renderer.domElement );
在Lunchbox.js 中设置场景更直接且不那么神秘。 该库提供了一个 <Lunchbox>
组件,其中包含用于在 Three.js 中创建渲染器和场景的底层代码。
要进行设置,请转到 App.vue 主组件并将 <Lunchbox>
组件放在模板部分中,如下所示:
<template> <Lunchbox> <!-- ... --> </Lunchbox> </template>
<Lunchbox>
标记中的每个对象都将呈现给浏览器。 如果你保存代码并启动开发服务器,你应该会看到类似于下图的黑屏。这个黑屏是我们应用的渲染器; 我们添加到场景中的任何内容都将显示在这里。 你可以使用背景道具更改背景颜色,如下所示:
<Lunchbox background="indigo"> <!-- ... --> </Lunchbox>
以下是你可以在 <Lunchbox>
组件上使用的一些 props:
- background
- cameraPosition
- camera
- zoom
- shadow
添加网格
几何是用于定义网格形状的 Three.js 类。 在本文前面,我们演示了如何使用 boxGeometry
组件渲染长方体网格。 在这里,我们将看到如何向浏览器渲染更多 3D 形状。
Lunchbox.js 带有几个自动生成的内置几何组件,你可以在此处查看可用组件的列表。
继续并在Lunchbox标签内添加一个 <mesh>
组件,并在其中嵌套你选择的任何几何组件。 不要忘记包含一个带有颜色道具的材质组件。
<Lunchbox> <mesh> <boxGeometry /> <MeshBasicMaterial color="red"/> </mesh> </Lunchbox>
材质组件利用 Three.js Material()
类来定义网格的各种表面属性。
<mesh>
组件只接受一个 Geometry()
和 Material()
组件。 要为场景添加更多形状,我们必须创建更多网格,每个 Geometry()
一个,如下所示。
<Lunchbox> <mesh> <boxGeometry /> <MeshBasicMaterial color="red"/> </mesh> <mesh> <torusKnotBufferGeometry /> <MeshBasicMaterial color="grey"/> </mesh> </Lunchbox>
<script> import {Sphere} from './Sphere.vue' </script> <template> <Lunchbox> <Sphere /> </Lunchbox> </template>
args
属性在 Lunchbox 中设置这些参数。<torusKnotBufferGeometry :args="[1, 0.4, 240, 20]"/>
Geometry()
的容器,它还可以用于操纵位置、旋转等。<Lunchbox> <mesh position-x="4" position-x="4"> ... </mesh> </Lunchbox>
在上面的代码中,我们添加了一个 positoin
属性,它将球体网格沿 x 轴向右移动 4px。
以下是一些可用于操纵形状的网格 props:
- position (x, y, z)
- rotation (x, y, x)
- scale (x, y)
网格也可以使用 <group>
组件进行分组。 该组件用作几何图形的容器,我们可以在每个 <group>
中添加任意数量的 <mesh>
。
<Lunchbox> <group> <mesh> <boxGeometry /> <MeshBasicMaterial color="red"/> </mesh> <mesh> <torusKnotGeometry /> <MeshBasicMaterial color="grey"/> </mesh> </group> </Lunchbox>
添加纹理
目前,我们的网格看起来不是很逼真。 应用的材料使它们看起来有点塑料。 我们可以使用 Lunchbox 中的 <textureLoader>
组件应用纹理,为每个网格赋予更逼真的外观。
<textureLoader>
组件利用 Three.js Texture() 类,它让我们可以将逼真的纹理映射到引擎盖下的网格表面。 为了演示这个过程,我们将创建一个地球的 3D 模型。
要创建地球,请先清除场景,然后使用 <sphereGeometry/>
组件创建具有球面几何形状的新网格。
<Lunchbox> <mesh> <sphereGeometry /> </mesh> </Lunchbox>
<meshPhysicalMaterial>
组件并将 <textureLoader>
嵌套在其中,如下所示:<Lunchbox> <mesh> <sphereGeometry :args="[1, 32, 32]" /> <meshPhysicalMaterial> <textureLoader /> </meshPhysicalMaterial> </mesh> </Lunchbox>
组件接受 src
和 attach
prop。 src
属性接受纹理材质的相对或绝对路径,而 attach
属性接受映射选项。
我们将在这个例子中使用 map
和 bumpMap
选项。 换句话说,我们必须在网格组件内声明第二个 <textureLoader>
。
复制下面的图像并将它们放在项目的 /public
文件夹中:
接下来,将第一个图像的路径添加到第一个 <textureLoader>
组件的 src
属性中,并为附加属性赋予一个 “map”
值。
<meshPhysicalMaterial> <textureLoader src="/first-image" attach="map" /> <textureLoader ... /> </meshPhysicalMaterial>
要解决此问题,请在 <Lunchbox>
组件中添加 <pointLight />
和 <directionalLight />
组件。
<Lunchbox> <directionalLight /> <pointLight /> <mesh> ... </mesh> </Lunchbox>
为了使图像更有趣和视觉上更令人惊叹,我们将使用第二张图像为地球添加逼真的轮廓。
我们将按照我们处理第一张图像的方式进行处理。 将图像的路径添加到 src 道具,但这次给 attach
prop一个 “bumpMap”
值。
添加动画
Lunchbox.js 提供了一个 onBeforeRender
函数,在渲染之前或之后的每一帧都会调用该函数。 使用此函数,我们可以通过在每一帧上为其旋转属性添加一个值来为我们的地球设置动画。
转到 App.vue 组件的脚本部分并从 Vue 导入 ref
和从 Lunchbox 导入 onBeforeRender
函数。
import {ref} from 'vue' import {onBeforeRender} from 'lunchboxjs'
ref
分配给一个 rotation
变量,并将一个具有以下属性的对象传递给它:const rotation = ref({y: 0});
onBeforeRender(() =>{ rotation.value.y += 0.02 })
rotation-y
属性,并将 rotation.y
值传递给它,如下所示:<mesh rotation-y="rotation.y"> <sphereGeometry /> <meshPhysicalMaterial> <textureLoader ... /> <textureLoader ... /> </meshPhysicalMaterial> </mesh>
现在,如果你保存项目,你的地球应该像下面的例子一样很好地动画。
你可以通过将 x 和 z
属性添加到 ref、onBeforeRender
函数和地球的网格来添加更多旋转动画。
添加事件
我们可以像添加 Vue 中的任何其他元素一样向 <Lunchbox>
组件添加事件监听器。 为了演示,我们将添加一个 click
事件,当它被触发时会暂停我们的地球动画。
继续在地球网格上创建一个 onClick
事件,如下所示:
<mesh rotation-y="rotation.y" @click=""> ... </mesh>
if
语句将声明包装在 onBeforeRender
函数中:const active = ref(true) onBeforeRender(() =>{ if(active){ rotation.value.y += 0.02 } })
在这里,我们将活动变量分配为 if 语句的条件。 当该值设置为 false 时,语句中的代码将不会被执行,动画会暂停。
最后,将以下代码添加到 <mesh>
组件的 onClick
事件中:
<mesh rotation-y="rotation.y" @click="active = !active"> ... </mesh>
现在地球的动画在点击时会暂停播放,如下图:
现在,我们已经在 Vue 中成功构建了 3D 视觉效果!