three.js海滩小岛示例

598 阅读5分钟

用three.js从零做一个海滩小岛

1.初始化

初始化呢在我之前的文章里有详细的描述,跟着步骤走就好啦,特别快就能弄好:three.js学习(1)

2.加入场景、相机

这里我们先把css代码改一点:

 * {
     margin: 0;
     padding: 0;
 }
 body {
     background-color: #000000; 
 }
 ::-webkit-scrollbar {
     display: none;
 }

一切的3D的画面都来自场景,想要看见物体需要相机。

 import * as THREE from 'three'
 ​
 //初始化场景
 const scene = new THREE.Scene()
 ​
 //初始化相机,老样子:角度 宽高比 近端距离 远端距离
 const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 2000)
 ​
 //设置相机位置
 camera.position.set(-50, 50, 130)
 //更新摄像头宽高比例
 camera.aspect = window.innerWidth / window.innerHeight
 //更新摄像头的投影矩阵 因为显示的大小变化了,所以要重新计算
 camera.updateProjectionMatrix()
 //将相机放入场景中
 scene.add(camera)

3.创建渲染器

最终要将它们渲染出来,所以需要一个渲染器renderer。

 //初始化渲染器
 const renderer = new THREE.WebGLRenderer({
     //为了更好的效果,设置为 抗锯齿
     antialias: true,
 })
 ​
 //设置渲染器输出环境的编码 为了好看一些
 renderer.outputEncoding = THREE.sRGBEncoding
 ​
 //设置渲染器宽高
 renderer.setSize(window.innerWidth, window.innerHeight)
 ​
 //监听屏幕的大小改变,修改渲染器的宽高,相机的比例
 window.addEventListener("resize",()=>{
     camera.aspect = window.innerWidth / window.innerHeight
     camera.updateProjectionMatrix()
     renderer.setSize(window.innerWidth, window.innerHeight)
 })
 ​
 //渲染器最终会渲染出一个canvas画布 在renderer.domElement
 //将渲染器的画布添加到页面上
 document.body.appendChild(renderer.domElement)
 ​
 //我们需要不断地渲染画面,才能让画面动起来
 //因此这里写一个渲染函数
 const render = ()=>{
     //渲染场景,相机
     renderer.render(scene,camera)
     //引擎自动更新渲染器 通过调用‘请求动画帧requestAnimationFrame()’不断渲染
     requestAnimationFrame(render)
 }
 ​
 //调用渲染函数
 render()

4.添加控制器

想要实现拖动我们的3D场景的话,我们需要添加控制器。

 //导入控制器
 import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls'
 ​
 //实例化控制器 导入我们的相机和渲染的画布
 const controls = new OrbitControls(camera,renderer.domElement)

5.添加内容

1.天空

这里呢,我们要先跑一下程序yarn dev,这样他会自己打包出一个dist文件夹来。接着我们在dist文件夹中创建一个textures文件夹来放材质。

模拟我们的地球呢,我们也创造出一个大球来。然后将贴图贴上去。这样就好像置身于内部。

 //创建一个巨大的天空球体 参数:半径 细分程度(相当于我们看地图的时候 经度 纬度)
 const skyGeometry = new THREE.SphereGeometry(1000, 60, 60) //创建几何体
 const skyMaterial = new THREE.MeshBasicMaterial({ //创建材质
         //使用纹理加载器加载纹理 也就是一张天空的图片
         map: new THREE.TextureLoader().load('./textures/sky.jpg')
     })
     //由于正常的创建,一个几何体只有外围是能看见的(为了节省资源)
     //所以当我们置身于球的内部,是看不见东西的,因此需要将几何体的z轴(我们眼睛看的方向)颠倒
 skyGeometry.scale(1, 1, -1)
 ​
 //创建天空球 几何体+材质
 const sky = new THREE.Mesh(skyGeometry, skyMaterial)
     //加入场景中
 scene.add(sky)

不过嘞,像我们在网上找的图片大部分情况下,最左端和最右端是不相接的,所以会有一个缝合的边线哈哈。如果想要360°的效果的话,可以去找或者自己去用那种 鱼眼摄像机 拍一个360°的天空。(试了一下好像也是怪怪的QAQ)

当然,如果有一段会动的天空的视频的话,加进来效果也是杠杠的。最好是找一个跟图片一样的视频这样就很连贯,而且记住视频是有声音的哦 连声音都会播放。

 //视频纹理
 const video = document.createElement('video') //创建视频
 video.src = './textures/sky.mp4'
 video.loop = true //循环播放
     //由于浏览器会禁止我们直接进行循环播放视频,所以我们可以加一点交互
 window.addEventListener('mousemove', (e) => {
     //当鼠标移动的时候,播放视频
     if (video.paused) { //判断是否暂停状态
         video.play()
         skyMaterial.map = new THREE.VideoTexture(video) //将天空材质的纹理从图片变为视频
         skyMaterial.map.needsUpdate = true //自动更新
     }
 })

2.水面

three.js呢,有给我们提供一个水面的设置嘿嘿。

 //导入水面
 import {Water} from 'three/examples/jsm/objects/Water2'
 ​
 //创建水面
 const waterGeometry = new THREE.CircleGeometry(300, 64) //圆形几何体
 const water = new Water(waterGeometry, {
         textureHeight: 1024,
         textureWidth: 1024,
         color: 0x0080ff,
         flowDirection: new THREE.Vector2(1, 1), //流动方向
         scale: 1, //水面波纹的大小
     })
     //将水面旋转90度,把它放平
     // water.rotation.x = -Math.PI / 2
 scene.add(water)

我试了超久!找不到资料,我的水面是没有波纹的QAQ 哭死 不知道为啥,可能是更新了,我询问了一位博主,他说是没有加贴图,但我查了很多资料都是直接flowDirection就可以了,而且flowDirection和flowMap不能同时存在。我也尝试了加贴图,不过好像还是没有效果。有没有大佬帮忙解答疑难TAT

这个问题还待解决,目前我不确定是我电脑的问题还是其他问题,解决的话会更新文章,先继续学习加入模型的部分

3.小岛

那么小岛的模型它是一种GLB类型的文件。这是一个压缩过的模型,不然会太大了。所以呢一会儿得导入一个用来解压的库。在dist中呢创建一个draco文件夹,放入解压库。建立一个model文件夹来放.glb模型。

Draco是谷歌团队在2017年1月发布的一个Draco是用于压缩和解压缩3D几何网格和点云的库。

 //导入gltf载入库
 import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
 //导入解压库
 import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'
 ​
 //添加小岛模型
 //实例化gltf载入库
 const loader = new GLTFLoader()
 //实例化解压库
 const dracoLoader = new DRACOLoader()
 //添加draco载入库
 dracoLoader.setDecoderPath('./draco/')
 loader.setDRACOLoader(dracoLoader)
 //添加模型
 loader.load("./model/island2.glb", (gltf) => {
     scene.add(gltf.scene);
 });

ok 那么这么加进来的模型呢,我们会发现是黑的,为什么呢,因为我们的天空不是一个hdr的图片只是一个jpg,它是没有光照属性的,因此需要引入一个hdr的环境纹理。

 //RGBELoader用于导入HDR图
 import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader'
 ​
 //载入环境纹理hdr
 const hdrLoader = new RGBELoader()
 hdrLoader.loadAsync("./assets/050.hdr").then((texture) => {
     //纹理映射 球面映射
     texture.mapping = THREE.EquirectangularReflectionMapping
     scene.background = texture
     scene.environment = texture
 })
 ​
 //添加平行光 提高亮度
 const light = new THREE.DirectionalLight(0xffffff, 1)
 light.position.set(-100, 100, 10)
 scene.add(light)

当然也可以不加上面的hdr,只加个平行光也行,只不过就没那么真实。