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

小咪咪 2022-09-02 04:04 198阅读 0赞

点击上方 前端瓶子君,关注公众号

回复算法,加入前端编程面试算法每日一题群

5bb41fd5256443ad75e2a87d27dd06ce.png

前言

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

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

了解一些基本天文知识

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

5a2b7a393ccaf629c195b54e097d86bf.png image.png

了解Three框架

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

  1. 场景 Sence 相当于太阳系,宇宙中有无数星系,比如现在说的太阳系,后续还可以增加其他星系,那不是永远都加不完的呀 o(╥﹏╥)o
  2. 相机 Carma 相当一枚哈勃天文望远镜
  3. 几何体 Geometry 相当于太阳和八大行星
  4. 控制 Controls 相当”创世者“的你

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

完整效果

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

进入教程

先引入Three 要用到的对象

  1. import {
  2. Group,
  3. Mesh,
  4. MeshBasicMaterial,
  5. PerspectiveCamera,
  6. PointCloud,
  7. PointCloudMaterial,
  8. Scene,
  9. SphereGeometry,
  10. TextureLoader,
  11. Vector3,
  12. WebGLRenderer
  13. } from 'three'
  14. import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js'
  15. 复制代码

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

  1. //场景
  2. const setScene = () => {
  3. scene = new Scene()
  4. renderer = new WebGLRenderer({
  5. antialias: true,
  6. })
  7. renderer.setSize(innerWidth, innerHeight)
  8. document.querySelector('#planet').appendChild(renderer.domElement)
  9. }
  10. //相机
  11. const setCamera = () => {
  12. camera = new PerspectiveCamera(60, innerWidth / innerHeight, 1, 100000)
  13. camera.position.set(0, 500, 2000)
  14. camera.lookAt(scene.position)
  15. }
  16. //控制
  17. const setControls = () => {
  18. controls = new OrbitControls(camera, renderer.domElement)
  19. }
  20. 复制代码

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

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

  1. const starForge = () => {
  2. const starQty = 10000
  3. const geometry = new SphereGeometry(10000, 100, 50)
  4. const materialOptions = {}
  5. const starStuff = new PointCloudMaterial(materialOptions)
  6. geometry.vertices = []
  7. for (let i = 0; i < starQty; i++) {
  8. let starVertex = new Vector3()
  9. starVertex.x = Math.random() * 20000 - 10000
  10. starVertex.y = Math.random() * 20000 - 10000
  11. starVertex.z = Math.random() * 20000 - 10000
  12. geometry.vertices.push(starVertex)
  13. }
  14. const stars = new PointCloud(geometry, starStuff)
  15. scene.add(stars)
  16. }
  17. 复制代码

效果如下图:

4a36cec9a3cdcd9d57f07507d35913f6.png image.png

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

dec00fe5ac9bf8cce884b3c08fb97267.gif 屏幕录制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轴旋转。所以设置那么多组,是为了实现每颗行星不同的速度和公转的同时自转。至于设置以下代码数值就根据 行星自转一天、公转一年用多少时间来大概定义一下。

  1. //设置公转函数
  2. const revolution = () => {
  3. mercuryParent.rotation.y += 0.015
  4. venusParent.rotation.y += 0.0065
  5. earthParent.rotation.y += 0.05
  6. marsParent.rotation.y += 0.03
  7. jupiterParent.rotation.y += 0.001
  8. saturnParent.rotation.y += 0.02
  9. uranusParent.rotation.y += 0.09
  10. neptuneParent.rotation.y += 0.001
  11. }
  12. //设置自转函数
  13. const selfRotation = () => {
  14. sun.rotation.y += 0.004
  15. mercury.rotation.y += 0.002
  16. venus.rotation.y += 0.005
  17. earth.rotation.y += 0.01
  18. mars.rotation.y += 0.01
  19. jupiter.rotation.y += 0.08
  20. saturn.rotation.y += 1.5
  21. uranus.rotation.y += 1
  22. neptune.rotation.y += 0.1
  23. }
  24. 复制代码

创建太阳和八大行星

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

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

ae503203f1d63efa18cf9357a3bec94a.png sun.jpg

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

129be114470dac9ce1a4b746dccf5491.png image.png

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

创建水星

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

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

2c86f66073a730a217a0e7bc1e7bddcf.png image.png

创建金星

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

cc10af83f7295dab83122926d08cb6a6.png venus.jpg

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

c2c03a4bae122fa8c3b156907d742091.png image.png

地球

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

d80f824a0e7c08043e3e4395c240acbc.png earth.jpg

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

5a7047968e0aab363228b6d29da16c4c.png image.png

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

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

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

火星的纹理贴图

01b88101ab015cfce9d57c142e6f5643.png mars.jpg

木星的纹理贴图

ec274cdaea7886c2cb297ef6a1d67044.png jupiter.jpg

土星的纹理贴图

c5ca2af687edcab4b392393081f3cba3.png saturn.jpg

天王星的纹理贴图

07107aec8aa28d34712187073124c73a.png uranus.jpg

海王星的纹理贴图1ebbfc66744a957672a5ef0c57870236.png

最后

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

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

上完整代码

  1. <template>
  2. <div id="planet">
  3. </div>
  4. </template>
  5. <script setup>
  6. import {onMounted} from 'vue'
  7. import {
  8. Group,
  9. Mesh,
  10. MeshBasicMaterial,
  11. PerspectiveCamera,
  12. PointCloud,
  13. PointCloudMaterial,
  14. Scene,
  15. SphereGeometry,
  16. TextureLoader,
  17. Vector3,
  18. WebGLRenderer
  19. } from 'three'
  20. import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js'
  21. const loader = new TextureLoader() //引入模型的loader实例
  22. let scene, camera, renderer, group, controls // 定义所有three实例变量
  23. // 创建场景
  24. const setScene = () => {
  25. scene = new Scene()
  26. renderer = new WebGLRenderer({
  27. antialias: true,
  28. })
  29. renderer.setSize(innerWidth, innerHeight)
  30. document.querySelector('#planet').appendChild(renderer.domElement)
  31. }
  32. // 创建相机
  33. const setCamera = () => {
  34. camera = new PerspectiveCamera(60, innerWidth / innerHeight, 1, 100000)
  35. camera.position.set(0, 500, 2000)
  36. camera.lookAt(scene.position)
  37. }
  38. // 设置模型控制
  39. const setControls = () => {
  40. controls = new OrbitControls(camera, renderer.domElement)
  41. }
  42. // 添加设置太阳
  43. let sun, sunParent
  44. const setSun = () => {
  45. sun = new Group()//建立一个组
  46. sunParent = new Group()
  47. scene.add(sunParent) //把组都添加到场景里
  48. loader.load('src/assets/universe/sun.jpg', (texture) => {
  49. const geometry = new SphereGeometry(500, 20, 20) //球体模型
  50. const material = new MeshBasicMaterial({map: texture}) //材质 将图片解构成THREE能理解的材质
  51. const mesh = new Mesh(geometry, material) //网孔对象 第一个参数是几何模型(结构),第二参数是材料(外观)
  52. sun.add(mesh)//添加到组里
  53. sunParent.add(sun)
  54. })
  55. }
  56. // 设置水星
  57. let mercury, mercuryParent
  58. const setMercury = () => {
  59. mercury = new Group()//建立一个组
  60. mercuryParent = new Group()
  61. scene.add(mercuryParent)
  62. loader.load('src/assets/universe/mercury.jpg', (texture) => {
  63. const geometry = new SphereGeometry(25, 20, 20) //球体模型
  64. const material = new MeshBasicMaterial({map: texture}) //材质 将图片解构成THREE能理解的材质
  65. const mesh = new Mesh(geometry, material) //网孔对象 第一个参数是几何模型(结构),第二参数是材料(外观)
  66. mercury.position.x -= 600
  67. mercury.add(mesh)//添加到组里
  68. mercuryParent.add(mercury)
  69. })
  70. }
  71. //设置金星
  72. let venus, venusParent
  73. const setVenus = () => {
  74. venus = new Group()//建立一个组
  75. venusParent = new Group()
  76. scene.add(venusParent)
  77. loader.load('src/assets/universe/venus.jpg', (texture) => {
  78. const geometry = new SphereGeometry(100, 20, 20) //球体模型
  79. const material = new MeshBasicMaterial({map: texture}) //材质 将图片解构成THREE能理解的材质
  80. const mesh = new Mesh(geometry, material) //网孔对象 第一个参数是几何模型(结构),第二参数是材料(外观)
  81. venus.position.x -= 700
  82. venus.add(mesh)//添加到组里
  83. venusParent.add(venus)
  84. })
  85. }
  86. //设置地球
  87. let earth, earthParent
  88. const setEarth = () => {
  89. earth = new Group()//建立一个组
  90. earthParent = new Group()
  91. scene.add(earthParent)
  92. loader.load('src/assets/universe/earth.jpg', (texture) => {
  93. const geometry = new SphereGeometry(100, 20, 20) //球体模型
  94. const material = new MeshBasicMaterial({map: texture}) //材质 将图片解构成THREE能理解的材质
  95. const mesh = new Mesh(geometry, material) //网孔对象 第一个参数是几何模型(结构),第二参数是材料(外观)
  96. earth.position.x -= 900
  97. earth.add(mesh)//添加到组里
  98. earthParent.add(earth)
  99. })
  100. }
  101. //设置火星
  102. let mars, marsParent
  103. const setMars = () => {
  104. mars = new Group()//建立一个组
  105. marsParent = new Group()
  106. scene.add(marsParent)
  107. loader.load('src/assets/universe/mars.jpg', (texture) => {
  108. const geometry = new SphereGeometry(85, 20, 20) //球体模型
  109. const material = new MeshBasicMaterial({map: texture}) //材质 将图片解构成THREE能理解的材质
  110. const mesh = new Mesh(geometry, material) //网孔对象 第一个参数是几何模型(结构),第二参数是材料(外观)
  111. mars.position.x -= 1200
  112. mars.add(mesh)//添加到组里
  113. marsParent.add(mars)
  114. })
  115. }
  116. // 设置木星
  117. let jupiter, jupiterParent
  118. const setJupiter = () => {
  119. jupiter = new Group()//建立一个组
  120. jupiterParent = new Group()
  121. scene.add(jupiterParent)
  122. loader.load('src/assets/universe/jupiter.jpg', (texture) => {
  123. const geometry = new SphereGeometry(150, 20, 20) //球体模型
  124. const material = new MeshBasicMaterial({map: texture}) //材质 将图片解构成THREE能理解的材质
  125. const mesh = new Mesh(geometry, material) //网孔对象 第一个参数是几何模型(结构),第二参数是材料(外观)
  126. jupiter.position.x -= 1500
  127. jupiter.add(mesh)//添加到组里
  128. jupiterParent.add(jupiter)
  129. })
  130. }
  131. // 设置土星
  132. let saturn, saturnParent
  133. const setSaturn = () => {
  134. saturn = new Group()//建立一个组
  135. saturnParent = new Group()
  136. scene.add(saturnParent)
  137. loader.load('src/assets/universe/saturn.jpg', (texture) => {
  138. const geometry = new SphereGeometry(120, 20, 20) //球体模型
  139. const material = new MeshBasicMaterial({map: texture}) //材质 将图片解构成THREE能理解的材质
  140. const mesh = new Mesh(geometry, material) //网孔对象 第一个参数是几何模型(结构),第二参数是材料(外观)
  141. saturn.position.x -= 1800
  142. saturn.add(mesh)//添加到组里
  143. saturnParent.add(saturn)
  144. })
  145. }
  146. //设置天王星
  147. let uranus, uranusParent
  148. const setUranus = () => {
  149. uranus = new Group()
  150. uranusParent = new Group()
  151. scene.add(uranusParent)
  152. loader.load('src/assets/universe/uranus.jpg', (texture) => {
  153. const geometry = new SphereGeometry(50, 20, 20) //球体模型
  154. const material = new MeshBasicMaterial({map: texture}) //材质 将图片解构成THREE能理解的材质
  155. const mesh = new Mesh(geometry, material) //网孔对象 第一个参数是几何模型(结构),第二参数是材料(外观)
  156. uranus.position.x -= 2100
  157. uranus.add(mesh)//添加到组里
  158. saturnParent.add(uranus)
  159. })
  160. }
  161. //设置海王星
  162. let neptune, neptuneParent
  163. const setNeptune = () => {
  164. neptune = new Group()
  165. neptuneParent = new Group()
  166. scene.add(neptuneParent)
  167. loader.load('src/assets/universe/neptune.jpg', (texture) => {
  168. const geometry = new SphereGeometry(50, 20, 20) //球体模型
  169. const material = new MeshBasicMaterial({map: texture}) //材质 将图片解构成THREE能理解的材质
  170. const mesh = new Mesh(geometry, material) //网孔对象 第一个参数是几何模型(结构),第二参数是材料(外观)
  171. neptune.position.x -= 2300
  172. neptune.add(mesh)//添加到组里
  173. neptuneParent.add(neptune)
  174. })
  175. }
  176. //监听浏览器改变大小时重新渲染
  177. function onWindowResize() {
  178. const WIDTH = window.innerWidth,
  179. HEIGHT = window.innerHeight
  180. camera.aspect = WIDTH / HEIGHT
  181. camera.updateProjectionMatrix()
  182. renderer.setSize(WIDTH, HEIGHT)
  183. }
  184. //设置公转函数
  185. const revolution = () => {
  186. mercuryParent.rotation.y += 0.015
  187. venusParent.rotation.y += 0.0065
  188. earthParent.rotation.y += 0.05
  189. marsParent.rotation.y += 0.03
  190. jupiterParent.rotation.y += 0.01
  191. saturnParent.rotation.y += 0.02
  192. uranusParent.rotation.y += 0.09
  193. neptuneParent.rotation.y += 0.01
  194. }
  195. //设置自转
  196. const selfRotation = () => {
  197. sun.rotation.y += 0.004
  198. mercury.rotation.y += 0.002
  199. venus.rotation.y += 0.005
  200. earth.rotation.y += 0.01
  201. mars.rotation.y += 0.01
  202. jupiter.rotation.y += 0.08
  203. saturn.rotation.y += 1.5
  204. uranus.rotation.y += 1
  205. neptune.rotation.y += 0.1
  206. }
  207. // 设置太阳系背景
  208. const starForge = () => {
  209. const starQty = 10000
  210. const geometry = new SphereGeometry(10000, 100, 50)
  211. const materialOptions = {}
  212. const starStuff = new PointCloudMaterial(materialOptions)
  213. geometry.vertices = []
  214. for (let i = 0; i < starQty; i++) {
  215. let starVertex = new Vector3()
  216. starVertex.x = Math.random() * 20000 - 10000
  217. starVertex.y = Math.random() * 20000 - 10000
  218. starVertex.z = Math.random() * 20000 - 10000
  219. geometry.vertices.push(starVertex)
  220. }
  221. const stars = new PointCloud(geometry, starStuff)
  222. scene.add(stars)
  223. }
  224. // 循环场景 、相机、 位置更新
  225. const loop = () => {
  226. requestAnimationFrame(loop)
  227. revolution()
  228. selfRotation()
  229. renderer.render(scene, camera)
  230. camera.lookAt(scene.position)
  231. }
  232. //初始化所有函数
  233. const init = () => {
  234. setScene() //设置场景
  235. setCamera() //设置相机
  236. setSun() // 设置太阳
  237. setMercury() //设置水星
  238. setVenus() //设置金星
  239. setEarth() // 地球
  240. setMars() //火星
  241. setJupiter() // 木星
  242. setSaturn() // 土星
  243. setUranus()// 天王星
  244. setNeptune()//海王星
  245. starForge()//设置满天星背景
  246. setControls() //设置可旋转控制
  247. loop() // 循环动画
  248. }
  249. onMounted(init)
  250. window.addEventListener('resize', onWindowResize)
  251. </script>
  252. 复制代码

63a22a799b3d379ab02da51a26291f78.png image.png

关于本文

来源:lizhenwen

https://juejin.cn/post/6983938127911976990

最后

欢迎关注【前端瓶子君】✿✿ヽ(°▽°)ノ✿

回复「算法」,加入前端编程源码算法群,每日一道面试题(工作日),第二天瓶子君都会很认真的解答哟!

回复「交流」,吹吹水、聊聊技术、吐吐槽!

回复「阅读」,每日刷刷高质量好文!

如果这篇文章对你有帮助,「在看」是最大的支持

》》面试官也在看的算法资料《《

“在看和转发”就是最大的支持

发表评论

表情:
评论列表 (有 0 条评论,198人围观)

还没有评论,来说两句吧...

相关阅读

    相关 matlab 太阳系仿真,三维仿真太阳系

    三维仿真太阳系是一款三维太阳系软件,展示的是3d太阳系模型,是学习的非常好的软件,虽然界面是e文的,有个人教教,对小孩学习太阳系系统知识是非常好的。体积小,但功能强大。界面非常