草庐IT

记录--使用Lunchbox 在 vue3 中创建一个 3D 地球

林恒 2023-03-28 原文

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

什么是 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)
为了在 Lunchbox.js 应用程序中创建相同的网格,我们将使用相同的类,除了作为组件,而不是将它们分配给变量并将它们与 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.js 和 Three.js 的语法非常相似,但有一些不同之处需要注意。 例如,每个 <Lunchbox> 组件都使用 args 属性将参数作为 props。 argsprop 接受一个参数数组并将其传递给组件的类构造函数。
new THREE.BoxGeometry(1, 2, 3)
上面的代码将在 Lunchbox 中转换为以下内容:
<boxGeometry :args="[1, 2, 3]" />
其他 Three.js 对象属性,例如 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
运行命令后,从库列表中选择 Vue 并为项目命名。 接下来,cd 进入项目文件夹并运行以下命令:
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]"/>
组件还接受多个 props。 由于它作为 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>

组件接受 srcattach prop。 src 属性接受纹理材质的相对或绝对路径,而 attach 属性接受映射选项。

我们将在这个例子中使用 mapbumpMap 选项。 换句话说,我们必须在网格组件内声明第二个 <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 函数并添加以下代码:
onBeforeRender(() =>{
   rotation.value.y += 0.02
})
在这里,我们在每一帧上为对象的 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>
接下来,使用 ref 创建一个变量,并将布尔值 true 传递给它。 使用 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 视觉效果!

本文转载于:

https://juejin.cn/post/7126059371938709517

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

有关记录--使用Lunchbox 在 vue3 中创建一个 3D 地球的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  5. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  6. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  7. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  8. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  9. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  10. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

随机推荐