最近指导新入职的前端同事进行threejs的学习,一开始找了threejs的文档,还有一些网上较好的教程和例子给他们看,一段时间后,发现学习进展不是很理想。可能是因为网上的教程比较零散,有些概念对于新手来说比较高深,不太容易掌握和运用。
前两天的中午,我决定从零开始构建一个自己喜欢的场景,这样学习起来也比较有趣,也足够有挑战,对于前端来说,用代码实现它,也足够有成就感。
这将是一个长期更新的文章,我尽量以浅显的思路和一些简单的方法来实现,同时也兼顾运用到threejs的各种基础类,循序渐进,一步步尝试、学习和完善。
我不愿意称之为教程,因为也是对自己学习threejs大半年的心得和记录,文中的错误、更好的实现方法,希望和大家一起探讨。在实现的过程中,能够对初次接触threejs的朋友有一些帮助,用代码写出自己的梦幻场景。
制作前准备
我喜欢的场景之一是海岛,之前玩过很长时间的海岛奇兵。
中午回到办公室,找到了一些可供参考的素材。
01-02.jpeg
01-01.jpeg
01-03.jpeg
我的梦幻海岛的框架应该是这样的:
大海、海岛、岩石、沙滩、树林、椰子树、螃蟹……想想就流口水~
开始制作基础场景
- 引入必要的库
- 构建three场景
// html
<div id="map"></div>
// javascript
function init () {
const HEIGHT = window.innerHeight
const WIDTH = window.innerWidth
const mainCanvas = document.getElementById('map')
let scene
let renderer
let camera
let controls
// 创建Scene
function createScene () {
scene = new THREE.Scene()
camera = new THREE.PerspectiveCamera(75, WIDTH / HEIGHT, 0.1, 10000)
camera.position.set(5, 5, 5)
camera.lookAt(0, 0, 0)
renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true
})
renderer.setSize(WIDTH, HEIGHT)
mainCanvas.appendChild(renderer.domElement)
renderer.setAnimationLoop(() => {
renderer.render(scene, camera)
})
}
// 创建灯光
function createLight () {
const AmbientLight = new THREE.AmbientLight(0xcccccc)
scene.add(AmbientLight)
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.08)
directionalLight.position.set(10, 10, 0)
scene.add(directionalLight)
}
// 创建控制器
function createControl () {
controls = new THREE.OrbitControls(camera, mainCanvas)
controls.target.set(0, 0, 0)
controls.update()
}
// 屏幕调整事件
function onWindowResize () {
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth, window.innerHeight)
}
// 创建窗口调整事件
function createEvent () {
window.addEventListener('resize', onWindowResize, false)
}
// 创建海岛场景
function createIsland () {
}
// 初始化
createScene()
createLight()
createControl()
createEvent()
createIsland()
}
init()
- 创建海水
在function init () {} 中加入以下代码
// 海面
function sea (width, height, segments) {
const sea = {
width: width,
height: height,
segments: segments,
mesh: null,
init: function() {
const geometry = new THREE.PlaneBufferGeometry(this.width, this.height, this.segments
, this.segments);
const material = new THREE.MeshBasicMaterial({
color: 0xcccccc,
side: THREE.DoubleSide,
wireframe: true
});
this.mesh = new THREE.Mesh(geometry, material);
}
}
sea.init();
return sea;
}
修改function createIsland () {}
function createIsland () {
const mySea = new sea(90, 90, 100);
mySea.mesh.rotation.x = Math.PI / 2;
scene.add(mySea.mesh);
}
- 创建小岛
// 岛
function island (width, height, segments) {
const island = {
width: width,
height: height,
segments: segments,
mesh: null,
init: function() {
const geometry = new THREE.PlaneGeometry(this.width, this.height, this.segments, this.segments);
const material = new THREE.MeshPhongMaterial({
color: 0xcc6699,
side: THREE.DoubleSide,
wireframe: true
});
this.mesh = new THREE.Mesh(geometry, material);
}
}
island.init();
return island;
}
继续修改function createIsland () {}
function createIsland () {
const mySea = new sea(90, 90, 100);
mySea.mesh.rotation.x = Math.PI / 2;
scene.add(mySea.mesh);
const myIsland = new island(8, 8, 8);
myIsland.mesh.rotation.x = Math.PI / 2;
scene.add(myIsland.mesh);
}
第一课完成,虽然有点丑,但我们有了海和海岛:
01-06.png