点击上方 前端Q,关注公众号

回复加群,加入前端Q技术交流群

前言

笔者认为Three.js是一个伟大的框架,为什么这样说,因为它可以让我们轻易创造三维世界,甚至好像笔者写这遍教程,可以创造一个太阳系,在这个三维世界里你就是创世主。哈哈!好像说得有点夸!!

三维太阳系完整效果:https://shinewen189.github.io/nigo-vue-planet/

了解一些基本天文知识

学习创造这个三维太阳系之前先了解一下基本的天文知识:太阳系有“八大行星”,按照离太阳的距离从近到远,它们依次为水星、金星、地球、火星、木星、土星、天王星、海王星。八大行星自转方向多数也和公转方向一致。只有金星和天王星两个例外。金星自转方向与公转方向相反。而天王星则是在轨道上“横滚”的。例如地球自转一天是23.9小时,公转一年有365.2天 ,而相邻的火星自转一天是24.6小时 公转一年则有687天,其他行星也有不同的公转和自转信息,有了这些信息就可以定义一些基本规则

image.png

了解Three框架

Three的一些基本概念我在用最简单方式打造Three.js 3D汽车展示厅[2]一文也粗略介绍一下,为了让同学们加深理解,笔者就相对于太阳系来比如一下

  1. 场景 Sence 相当于太阳系,宇宙中有无数星系,比如现在说的太阳系,后续还可以增加其他星系,那不是永远都加不完的呀 o(╥﹏╥)o

  2. 相机 Carma 相当一枚哈勃天文望远镜

  3. 几何体 Geometry 相当于太阳和八大行星

  4. 控制 Controls 相当”创世者“的你

有了这几个概念我们就创建一些函数一一对应

完整效果

屏幕录制2021-07-12 下午2.34.26.gif

进入教程

先引入Three 要用到的对象

  import {Group,Mesh,MeshBasicMaterial,PerspectiveCamera,PointCloud,PointCloudMaterial,Scene,SphereGeometry,TextureLoader,Vector3,WebGLRenderer} from 'three'import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js'复制代码

场景(太阳系),相机(哈勃天文望远镜),控制(创世主)

//场景
const setScene = () => {scene = new Scene()renderer = new WebGLRenderer({antialias: true,})renderer.setSize(innerWidth, innerHeight)document.querySelector('#planet').appendChild(renderer.domElement)}
//相机
const setCamera = () => {camera = new PerspectiveCamera(60, innerWidth / innerHeight, 1, 100000)camera.position.set(0, 500, 2000)camera.lookAt(scene.position)}
//控制    const setControls = () => {controls = new OrbitControls(camera, renderer.domElement)}
复制代码

创建一个太阳系的背景(繁星背景)

这个满天星效果是太阳系的背景,运用到Three的粒子系统,行星密度可自行调整

 const starForge = () => {const starQty = 10000const geometry = new SphereGeometry(10000, 100, 50)const materialOptions = {}const starStuff = new PointCloudMaterial(materialOptions)geometry.vertices = []for (let i = 0; i < starQty; i++) {let starVertex = new Vector3()starVertex.x = Math.random() * 20000 - 10000starVertex.y = Math.random() * 20000 - 10000starVertex.z = Math.random() * 20000 - 10000geometry.vertices.push(starVertex)}const stars = new PointCloud(geometry, starStuff)scene.add(stars)}
复制代码

效果如下图:

image.png

创建太阳和行星前先说说行星自转同时公转的规律

屏幕录制2021-07-12 上午11.23.20.gif

旋转方式:实现旋转功能有三种方式

  1. 旋转照相机

  2. 旋转整个场景(Scene)

  3. 旋转单个元素

因为我们这里每个行星的自转速度,公转速度都不一样。所以设置整体旋转并不可行,所以要给每个元素设置不同的旋转属性。

行星需要让它们围绕着太阳转,就要先给它们自身设置一个位置偏移。以水星为例:mercury.position.x \-= 300,而此时设置mercury.rotation.y属性,它就会实现自转。因为它的Y轴位置已经改变了。

当我们移动了mercury时,mercuryParent的位置是没有变的,自然它的Y轴也不会变,又因为mercuryParent包含了mercury,所以旋转mercuryParent时,mercury也会绕着初始的默认Y轴旋转。所以设置那么多组,是为了实现每颗行星不同的速度和公转的同时自转。至于设置以下代码数值就根据 行星自转一天、公转一年用多少时间来大概定义一下。

//设置公转函数
const revolution = () => {mercuryParent.rotation.y += 0.015venusParent.rotation.y += 0.0065earthParent.rotation.y += 0.05marsParent.rotation.y += 0.03jupiterParent.rotation.y += 0.001saturnParent.rotation.y += 0.02uranusParent.rotation.y += 0.09neptuneParent.rotation.y += 0.001}//设置自转函数const selfRotation = () => {sun.rotation.y += 0.004mercury.rotation.y += 0.002venus.rotation.y += 0.005earth.rotation.y += 0.01mars.rotation.y += 0.01jupiter.rotation.y += 0.08saturn.rotation.y += 1.5uranus.rotation.y += 1neptune.rotation.y += 0.1}
复制代码

创建太阳和八大行星

创建星系用到几何球体+纹理贴图

首先介绍一下太阳如何创造,利用 SphereGeometry创建球体,利用MeshBasicMaterial添加纹理,太阳是质量是最大的,所以设置球体的时候数值是最大。下图是太阳的纹理贴图

sun.jpg
  // 添加设置太阳let sun, sunParentconst setSun = () => {sun = new Group()//建立一个组sunParent = new Group()scene.add(sunParent) //把组都添加到场景里loader.load('src/assets/universe/sun.jpg', (texture) => {const geometry = new SphereGeometry(500, 20, 20) //球体模型const material = new MeshBasicMaterial({map: texture}) //材质 将图片解构成THREE能理解的材质const mesh = new Mesh(geometry, material)  //网孔对象 第一个参数是几何模型(结构),第二参数是材料(外观)sun.add(mesh)//添加到组里sunParent.add(sun)})}复制代码

image.png

按照离太阳最近一个接一个创建

创建水星

水星离太阳最近,质量是所有行星中最小,所以球体数值也给一个最小的数值。下图水星纹理贴图

 let mercury, mercuryParentconst setMercury = () => {mercury = new Group()mercuryParent = new Group()scene.add(mercuryParent)loader.load('src/assets/universe/mercury.jpg', (texture) => {const geometry = new SphereGeometry(25, 20, 20) //球体模型const material = new MeshBasicMaterial({map: texture}) //材质 将图片解构成THREE能理解的材质const mesh = new Mesh(geometry, material)  //网孔对象 第一个参数是几何模型(结构),第二参数是材料(外观)mercury.position.x -= 600mercury.add(mesh)//添加到组里mercuryParent.add(mercury)})}
复制代码

image.png

创建金星

O(∩_∩)O哈哈~ 应该是下图,这张才是金星行星的纹理贴图,千万不要用错哟!!

venus.jpg
  let venus, venusParentconst setVenus = () => {venus = new Group()//建立一个组venusParent = new Group()scene.add(venusParent)loader.load('src/assets/universe/venus.jpg', (texture) => {const geometry = new SphereGeometry(100, 20, 20) //球体模型const material = new MeshBasicMaterial({map: texture}) //材质 将图片解构成THREE能理解的材质const mesh = new Mesh(geometry, material)  //网孔对象 第一个参数是几何模型(结构),第二参数是材料(外观)venus.position.x -= 700venus.add(mesh)//添加到组里venusParent.add(venus)})}复制代码

image.png

地球

怎可以没有我们的家园呢,这么美丽的家园要好好保护它啊!!

earth.jpg
let earth, earthParentconst setEarth = () => {earth = new Group()//建立一个组earthParent = new Group()scene.add(earthParent)loader.load('src/assets/universe/earth.jpg', (texture) => {const geometry = new SphereGeometry(100, 20, 20) //球体模型const material = new MeshBasicMaterial({map: texture}) //材质 将图片解构成THREE能理解的材质const mesh = new Mesh(geometry, material)  //网孔对象 第一个参数是几何模型(结构),第二参数是材料(外观)earth.position.x -= 900earth.add(mesh)//添加到组里earthParent.add(earth)})}
复制代码

image.png

火星、木星、土星、天王星、海王星

接下来的行星设置都是大同小异、只是公转、自转、和行星大小的设置不同。

接着对应行星的纹理贴图也一一发给大家

火星的纹理贴图

mars.jpg

木星的纹理贴图

jupiter.jpg

土星的纹理贴图

saturn.jpg

天王星的纹理贴图

uranus.jpg

海王星的纹理贴图

最后

一个三维太阳系就创造出来啦,这个例子也是很适合刚入门three.js的同学,目的也是提高对三维的兴趣,提高自身成就感。当然在这列子上我们还可以增加一些功能,比如定位标注一些行星的信息,点击行星可以进入星球内部,利用天空盒子做一个VR全景效果,等等。另外小弟找这些行星纹理贴图也不易,特别找金星的时候????,希望大家如果喜欢这篇文章能给个赞小弟,当鼓励一下。以后小弟必定为大家创作更多好文,谢谢啦!!^_^

屏幕录制2021-07-12 下午2.34.26.gif

上完整代码

<template><div id="planet"></div>
</template>
<script setup>import {onMounted} from 'vue'import {Group,Mesh,MeshBasicMaterial,PerspectiveCamera,PointCloud,PointCloudMaterial,Scene,SphereGeometry,TextureLoader,Vector3,WebGLRenderer} from 'three'import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js'const loader = new TextureLoader() //引入模型的loader实例let scene, camera, renderer, group, controls // 定义所有three实例变量// 创建场景const setScene = () => {scene = new Scene()renderer = new WebGLRenderer({antialias: true,})renderer.setSize(innerWidth, innerHeight)document.querySelector('#planet').appendChild(renderer.domElement)}// 创建相机const setCamera = () => {camera = new PerspectiveCamera(60, innerWidth / innerHeight, 1, 100000)camera.position.set(0, 500, 2000)camera.lookAt(scene.position)}// 设置模型控制const setControls = () => {controls = new OrbitControls(camera, renderer.domElement)}// 添加设置太阳let sun, sunParentconst setSun = () => {sun = new Group()//建立一个组sunParent = new Group()scene.add(sunParent) //把组都添加到场景里loader.load('src/assets/universe/sun.jpg', (texture) => {const geometry = new SphereGeometry(500, 20, 20) //球体模型const material = new MeshBasicMaterial({map: texture}) //材质 将图片解构成THREE能理解的材质const mesh = new Mesh(geometry, material)  //网孔对象 第一个参数是几何模型(结构),第二参数是材料(外观)sun.add(mesh)//添加到组里sunParent.add(sun)})}// 设置水星let mercury, mercuryParentconst setMercury = () => {mercury = new Group()//建立一个组mercuryParent = new Group()scene.add(mercuryParent)loader.load('src/assets/universe/mercury.jpg', (texture) => {const geometry = new SphereGeometry(25, 20, 20) //球体模型const material = new MeshBasicMaterial({map: texture}) //材质 将图片解构成THREE能理解的材质const mesh = new Mesh(geometry, material)  //网孔对象 第一个参数是几何模型(结构),第二参数是材料(外观)mercury.position.x -= 600mercury.add(mesh)//添加到组里mercuryParent.add(mercury)})}//设置金星let venus, venusParentconst setVenus = () => {venus = new Group()//建立一个组venusParent = new Group()scene.add(venusParent)loader.load('src/assets/universe/venus.jpg', (texture) => {const geometry = new SphereGeometry(100, 20, 20) //球体模型const material = new MeshBasicMaterial({map: texture}) //材质 将图片解构成THREE能理解的材质const mesh = new Mesh(geometry, material)  //网孔对象 第一个参数是几何模型(结构),第二参数是材料(外观)venus.position.x -= 700venus.add(mesh)//添加到组里venusParent.add(venus)})}//设置地球let earth, earthParentconst setEarth = () => {earth = new Group()//建立一个组earthParent = new Group()scene.add(earthParent)loader.load('src/assets/universe/earth.jpg', (texture) => {const geometry = new SphereGeometry(100, 20, 20) //球体模型const material = new MeshBasicMaterial({map: texture}) //材质 将图片解构成THREE能理解的材质const mesh = new Mesh(geometry, material)  //网孔对象 第一个参数是几何模型(结构),第二参数是材料(外观)earth.position.x -= 900earth.add(mesh)//添加到组里earthParent.add(earth)})}
//设置火星let mars, marsParentconst setMars = () => {mars = new Group()//建立一个组marsParent = new Group()scene.add(marsParent)loader.load('src/assets/universe/mars.jpg', (texture) => {const geometry = new SphereGeometry(85, 20, 20) //球体模型const material = new MeshBasicMaterial({map: texture}) //材质 将图片解构成THREE能理解的材质const mesh = new Mesh(geometry, material)  //网孔对象 第一个参数是几何模型(结构),第二参数是材料(外观)mars.position.x -= 1200mars.add(mesh)//添加到组里marsParent.add(mars)})}// 设置木星let jupiter, jupiterParentconst setJupiter = () => {jupiter = new Group()//建立一个组jupiterParent = new Group()scene.add(jupiterParent)loader.load('src/assets/universe/jupiter.jpg', (texture) => {const geometry = new SphereGeometry(150, 20, 20) //球体模型const material = new MeshBasicMaterial({map: texture}) //材质 将图片解构成THREE能理解的材质const mesh = new Mesh(geometry, material)  //网孔对象 第一个参数是几何模型(结构),第二参数是材料(外观)jupiter.position.x -= 1500jupiter.add(mesh)//添加到组里jupiterParent.add(jupiter)})}// 设置土星let saturn, saturnParentconst setSaturn = () => {saturn = new Group()//建立一个组saturnParent = new Group()scene.add(saturnParent)loader.load('src/assets/universe/saturn.jpg', (texture) => {const geometry = new SphereGeometry(120, 20, 20) //球体模型const material = new MeshBasicMaterial({map: texture}) //材质 将图片解构成THREE能理解的材质const mesh = new Mesh(geometry, material)  //网孔对象 第一个参数是几何模型(结构),第二参数是材料(外观)saturn.position.x -= 1800saturn.add(mesh)//添加到组里saturnParent.add(saturn)})}//设置天王星let uranus, uranusParentconst setUranus = () => {uranus = new Group()uranusParent = new Group()scene.add(uranusParent)loader.load('src/assets/universe/uranus.jpg', (texture) => {const geometry = new SphereGeometry(50, 20, 20) //球体模型const material = new MeshBasicMaterial({map: texture}) //材质 将图片解构成THREE能理解的材质const mesh = new Mesh(geometry, material)  //网孔对象 第一个参数是几何模型(结构),第二参数是材料(外观)uranus.position.x -= 2100uranus.add(mesh)//添加到组里saturnParent.add(uranus)})}//设置海王星let neptune, neptuneParentconst setNeptune = () => {neptune = new Group()neptuneParent = new Group()scene.add(neptuneParent)loader.load('src/assets/universe/neptune.jpg', (texture) => {const geometry = new SphereGeometry(50, 20, 20) //球体模型const material = new MeshBasicMaterial({map: texture}) //材质 将图片解构成THREE能理解的材质const mesh = new Mesh(geometry, material)  //网孔对象 第一个参数是几何模型(结构),第二参数是材料(外观)neptune.position.x -= 2300neptune.add(mesh)//添加到组里neptuneParent.add(neptune)})}//监听浏览器改变大小时重新渲染function onWindowResize() {const WIDTH = window.innerWidth,HEIGHT = window.innerHeightcamera.aspect = WIDTH / HEIGHTcamera.updateProjectionMatrix()renderer.setSize(WIDTH, HEIGHT)}//设置公转函数const revolution = () => {mercuryParent.rotation.y += 0.015venusParent.rotation.y += 0.0065earthParent.rotation.y += 0.05marsParent.rotation.y += 0.03jupiterParent.rotation.y += 0.01saturnParent.rotation.y += 0.02uranusParent.rotation.y += 0.09neptuneParent.rotation.y += 0.01}//设置自转const selfRotation = () => {sun.rotation.y += 0.004mercury.rotation.y += 0.002venus.rotation.y += 0.005earth.rotation.y += 0.01mars.rotation.y += 0.01jupiter.rotation.y += 0.08saturn.rotation.y += 1.5uranus.rotation.y += 1neptune.rotation.y += 0.1}// 设置太阳系背景const starForge = () => {const starQty = 10000const geometry = new SphereGeometry(10000, 100, 50)const materialOptions = {}const starStuff = new PointCloudMaterial(materialOptions)geometry.vertices = []for (let i = 0; i < starQty; i++) {let starVertex = new Vector3()starVertex.x = Math.random() * 20000 - 10000starVertex.y = Math.random() * 20000 - 10000starVertex.z = Math.random() * 20000 - 10000geometry.vertices.push(starVertex)}const stars = new PointCloud(geometry, starStuff)scene.add(stars)}// 循环场景 、相机、 位置更新const loop = () => {requestAnimationFrame(loop)revolution()selfRotation()renderer.render(scene, camera)camera.lookAt(scene.position)}//初始化所有函数const init = () => {setScene() //设置场景setCamera() //设置相机setSun() // 设置太阳setMercury() //设置水星setVenus() //设置金星setEarth() // 地球setMars() //火星setJupiter() // 木星setSaturn() // 土星setUranus()// 天王星setNeptune()//海王星starForge()//设置满天星背景setControls() //设置可旋转控制loop() // 循环动画}onMounted(init)window.addEventListener('resize', onWindowResize)</script>复制代码

image.png

关于本文

来源:lizhenwen

https://juejin.cn/post/6983938127911976990

声明:文章著作权归作者所有,如有侵权,请联系小编删除。

内推社群

我组建了一个氛围特别好的腾讯内推社群,如果你对加入腾讯感兴趣的话(后续有计划也可以),我们可以一起进行面试相关的答疑、聊聊面试的故事、并且在你准备好的时候随时帮你内推。下方加 winty 好友回复「面试」即可。

教你如何用Three.js创造一个三维太阳系相关推荐

  1. 如何用 Node.js 实现一个简单的 Websocket 服务?

    最近正在研究 Websocket 相关的知识,想着如何能自己实现 Websocket 协议.到网上搜罗了一番资料后用 Node.js 实现该协议,倒也没有想象中那么复杂,除去注释语句和 console ...

  2. 教你如何用 Python 来实现一个大数据搜索引擎

    搜索是大数据领域里常见的需求.Splunk和ELK分别是该领域在非开源和开源领域里的领导者.本文利用很少的Python代码实现了一个基本的数据搜索功能,试图让大家理解大数据搜索的基本原理. 布隆过滤器 ...

  3. 超详细AI二维码制作教程:手把手教你如何用Stable Diffusion 生成一个创意二维码?

    AI已来,未来已来! 来势汹汹的人工智能,如同创世纪的洪水,正在全世界的范围内引发一场史无前例的科技革命.AI正在改变世界!而我们正是这场巨变的见证者. 今天我们要介绍的内容就是:如何利用AI工具St ...

  4. 教你如何用node.js开发微信公众号(一)

    内容简介 本文章简单介绍了如何用node.js开发微信公众号.主要内容包括微信公众平台的配置,signature的验证,以及用node.js去处理用户主动发起的行为. 准备工作 开发前需要安装好nod ...

  5. 前端每日实战:164# 视频演示如何用原生 JS 创作一个数独训练小游戏(内含 4 个视频)...

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/mQYobz 可交互视频 此视频是可 ...

  6. 前端每日实战:163# 视频演示如何用原生 JS 创作一个多选一场景的交互游戏(内含 3 个视频)...

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/LXMzRX 可交互视频 此视频是可 ...

  7. 干货!如何用Node.js实现一个精巧的P2P网络

    加密货币都是去中心化的应用,去中心化的基础就是P2P网络,其作用和地位不言而喻,无可替代.当然,对于一个不开源的所谓私链(私有区块链),是否必要,尚无定论. 事实上,P2P网络不是什么新技术.但是,使 ...

  8. 手把手教你挖掘数据:怎样创造一个“尿布与啤酒”的都市传奇?

    导读:大数据相关行业的研究者和从业者都知道这样一个"都市传奇": 美国中西部的一家连锁超市的数据挖掘团队发现,周四下午5点-7点,男人们频繁地购买尿布和啤酒.该商店将一个小的尿布陈 ...

  9. 【圣诞节限定】今天教你如何用Html+JS+CSS绘制3D动画圣诞树

    一.前言 应CSDN的邀请,这次给大家展示一波,如何用H5技术绘制3D圣诞树. 二.创意名 只采用简单的Html+JS+CSS 技术绘制. 三.效果展示 圣诞树修过如下: 四.编码实现 将源码复制保存 ...

  10. 倒计时css和js html代码,手把手教你利用CSS和JS创建一个倒数计时器

    倒计时功能,在很多地方都会用到,我们平时都习惯去用一些插件来应用,会减少不少的工作量,并且效果也能达到预期. 我今天并不是想分享什么倒计时插件,而是自己写一个简单的倒数计时器,有兴趣的同学可以往下看看 ...

最新文章

  1. 数字图像处理2018-10-9
  2. android radiooptions简介
  3. poj 1061 青蛙的约会
  4. 操作系统原理:操作系统的启动 中断/异常/系统调用
  5. BindingException: Parameter 'XXX' not found. Available parameters are [collection, list]
  6. android 快捷方式 未安装该应用程序,android,解决手动创建的桌面快捷方式无法跳转到制定的activity的问题,提示未安装应用程序...
  7. Objective-C Memory Management Being Exceptional 异常处理与内存
  8. Ubuntu 16.04 安装 CUDA10.1 (解决循环登陆的问题)
  9. RMAN Crosscheck 和 Delete 命令的2个实例
  10. AndroidHttpCapture抓包工具
  11. Ultra Edit中编辑并一键运行Ansys命令流
  12. java开发html如何转换为word,Java怎么将html转换成word
  13. 2017春招实习-Android开发面经-bat,td等公司
  14. 弗洛伊德学说中的本我、自我和超我
  15. 冬季下肢静脉曲张如何治疗
  16. 如何得到满意的好答案
  17. 动态jenkins slave
  18. 5G无用时代的终结—从iPhone 12系列到vivo 6G应用场景的展望
  19. LayoutInflate: Avoid passing null as the view root
  20. mail = imaplib.IMAP4_SSL('k20gslf-0kF')

热门文章

  1. 如何在宝塔面板中屏蔽垃圾蜘蛛?
  2. @Aspect 注解使用详解
  3. Quartz是什么?
  4. excel提取身份证出生日期_Excel如何从身份证号码中提取年龄
  5. 电脑安全证书错误怎么处理比较好
  6. JAVA 之父高斯林:廉颇老矣,尚能饭否?
  7. empty怎么发音_empty怎么读?empty是什么意思?
  8. Python描述 LeetCode 8. 字符串转换整数 (atoi)
  9. CentOS 7超详细安装与网络配置
  10. 谷歌发布研究人口流动性的新方法【智能快讯】