Official website: https://threejs.org/
Let's understand a few concepts first
- Scenes
- The scene is a stage for display and presentation
- camera
- The content presented on the browser side is all taken by the camera
- rendering
- The renderer determines how content is presented to the screen
- geometry
1. Threejs draws a cube
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>绘制立方体</title>
<script src="./three.min.js"></script>
</head>
<body>
<script>
/**
01 场景
02 相机: 分类 位置
03 渲染器:大小 颜色
04 几何体:
*/
const scene = new THREE.Scene()
/**
*fov — 摄像机视锥体垂直视野角度
aspect — 摄像机视锥体长宽比
near — 摄像机视锥体近端面
far — 摄像机视锥体远端面
*/
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000)
const renderer = new THREE.WebGLRenderer({
antialias: true
})
renderer.setClearColor(0xffffff)
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
const geometry = new THREE.BoxGeometry(1, 1, 1) // x轴、y轴、z轴
const material = new THREE.MeshBasicMaterial({
// 材质
color: 0x285b41,
wireframe: true // 透明
})
const cube = new THREE.Mesh(geometry, material)
scene.add(cube)
camera.position.z = 4
function animate() {
requestAnimationFrame(animate)
cube.rotation.y += 0.01
cube.rotation.x += 0.01
renderer.render(scene, camera)
}
animate()
</script>
</body>
</html>
2. Material and camera control
anywhere
start html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>轨迹球控制与材质</title>
<script src="./three.min.js"></script>
<script src="./TrackballControls.js"></script>
</head>
<body>
<script>
// 定义全局变量
let scene, camera, geometry, mesh, renderer, controls
// 初始化渲染器
function initRenderer() {
renderer = new THREE.WebGLRenderer({
antialias: true })
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setPixelRatio(window.devicePixelRatio)
document.body.appendChild(renderer.domElement)
}
// 初始化场景
function initScene() {
scene = new THREE.Scene()
const axesHelper = new THREE.AxesHelper(100)
scene.add(axesHelper)
}
// 初始化相机
function initCamera() {
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000)
camera.position.set(0, 0, 15)
controls = new THREE.TrackballControls(camera, renderer.domElement)
}
// 初始化模型
function initMesh() {
geometry = new THREE.BoxGeometry(2, 2, 2)
// material = new THREE.MeshNormalMaterial()
const texture = new THREE.TextureLoader().load('img/crate.gif')
material = new THREE.MeshBasicMaterial({
map: texture,
side: THREE.DoubleSide
})
mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)
}
// 初始化动画
function animate() {
requestAnimationFrame(animate)
controls.update()
renderer.render(scene, camera)
}
// 定义初始化方法
function init() {
initRenderer()
initScene()
initCamera()
initMesh()
animate()
}
init()
</script>
</body>
</html>
3. Light source operation
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>设置场景光</title>
<script src="./three.min.js"></script>
<script src="./TrackballControls.js"></script>
</head>
<body>
<script>
// 定义全局变量
let scene, camera, geometry, mesh, renderer, controls
// 初始化渲染器
function initRenderer() {
renderer = new THREE.WebGLRenderer({
antialias: true })
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setPixelRatio(window.devicePixelRatio)
document.body.appendChild(renderer.domElement)
}
// 初始化场景
function initScene() {
scene = new THREE.Scene()
const axesHelper = new THREE.AxesHelper(100)
scene.add(axesHelper)
//添加光源
// const directionalLight = new THREE.DirectionalLight('red') // 平行光
// const ambientLight = new THREE.AmbientLight('orange') // 全局环境光
// const pointLight = new THREE.PointLight('green') // 点光源
// const spotLight = new THREE.SpotLight('lightblue') // 聚光灯
const hemisphereLight = new THREE.HemisphereLight('red') // 半球光
hemisphereLight.position.set(0, 30, 0)
scene.add(hemisphereLight)
}
// 初始化相机
function initCamera() {
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000)
camera.position.set(0, 0, 15)
controls = new THREE.TrackballControls(camera, renderer.domElement)
}
// 初始化模型
function initMesh() {
geometry = new THREE.SphereGeometry(3, 26, 26)
const texture = new THREE.TextureLoader().load('img/crate.gif')
material = new THREE.MeshPhongMaterial({
map: texture,
side: THREE.DoubleSide
})
mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)
}
// 初始化动画
function animate() {
requestAnimationFrame(animate)
controls.update()
renderer.render(scene, camera)
}
// 定义初始化方法
function init() {
initRenderer()
initScene()
initCamera()
initMesh()
animate()
}
init()
</script>
</body>
</html>
4. Sprite material and interaction
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>精灵材质与3D交互</title>
<style>
* {
margin: 0;
padding: 0;
}
canvas {
width: 100%;
height: 100%;
display: block;
}
</style>
<script src="./three.min.js"></script>
<script src="./TrackballControls.js"></script>
</head>
<body>
<script>
// 定义全局变量
let scene, camera, geometry, mesh, renderer, controls
const raycaster = new THREE.Raycaster()
const mouse = new THREE.Vector2()
function onMouseMove(event) {
// 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
}
window.addEventListener('mousemove', onMouseMove, false)
window.addEventListener('click', function () {
// 计算物体和射线的焦点
const intersects = raycaster.intersectObjects([mesh])
if (intersects.length > 0) {
mesh.rotation.x += 01
}
}, false)
// 初始化渲染器
function initRenderer() {
renderer = new THREE.WebGLRenderer({
antialias: true })
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setPixelRatio(window.devicePixelRatio)
document.body.appendChild(renderer.domElement)
}
// 初始化场景
function initScene() {
scene = new THREE.Scene()
const axesHelper = new THREE.AxesHelper(100)
scene.add(axesHelper)
}
// 初始化相机
function initCamera() {
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000)
camera.position.set(0, 0, 15)
controls = new THREE.TrackballControls(camera, renderer.domElement)
}
// 初始化模型
function initMesh() {
// const map = new THREE.TextureLoader().load('img/icon.png')
// const material = new THREE.SpriteMaterial({ map: map, color: 0xffffff })
// const sprite = new THREE.Sprite(material)
// scene.add(sprite)
geometry = new THREE.BoxGeometry(2, 2, 2)
// material = new THREE.MeshNormalMaterial()
const texture = new THREE.TextureLoader().load('img/crate.gif')
material = new THREE.MeshBasicMaterial({
map: texture,
side: THREE.DoubleSide
})
mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)
}
// 初始化动画
function animate() {
requestAnimationFrame(animate)
controls.update()
renderer.render(scene, camera)
}
// 定义初始化方法
function init() {
initRenderer()
initScene()
initCamera()
initMesh()
animate()
}
init()
</script>
</body>
</html>
5. VR panorama assembly
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>轨迹球控制与材质</title>
<style>
* {
margin: 0;
padding: 0;
}
canvas {
display: block;
height: 100%;
width: 100%;
}
</style>
<script src="./three.min.js"></script>
<script src="./TrackballControls.js"></script>
</head>
<body>
<script>
// 定义全局变量
let scene, camera, geometry, mesh, renderer, controls
// 初始化渲染器
function initRenderer() {
renderer = new THREE.WebGLRenderer({
antialias: true })
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setPixelRatio(window.devicePixelRatio)
document.body.appendChild(renderer.domElement)
}
// 初始化场景
function initScene() {
scene = new THREE.Scene()
const axesHelper = new THREE.AxesHelper(100)
scene.add(axesHelper)
}
// 初始化相机
function initCamera() {
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000)
camera.position.set(0, 0, 15)
controls = new THREE.TrackballControls(camera, renderer.domElement)
}
// 初始化模型
function initMesh() {
// 前面
const geometryF = new THREE.PlaneGeometry(4, 4)
const materialF = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load('img/0_f.jpg'),
side: THREE.DoubleSide
})
const meshF = new THREE.Mesh(geometryF, materialF)
meshF.rotation.y = 180 * Math.PI / 180
meshF.position.z = 2
scene.add(meshF)
// 后面
const geometryB = new THREE.PlaneGeometry(4, 4)
const materialB = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load('img/0_b.jpg'),
side: THREE.DoubleSide
})
const meshB = new THREE.Mesh(geometryB, materialB)
// meshB.rotation.y = 180 * Math.PI / 180
meshB.position.z = -2
scene.add(meshB)
// 左侧
const geometryL = new THREE.PlaneGeometry(4, 4)
const materialL = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load('img/0_l.jpg'),
side: THREE.DoubleSide
})
const meshL = new THREE.Mesh(geometryL, materialL)
meshL.rotation.y = (-90) * Math.PI / 180
meshL.position.x = 2
scene.add(meshL)
// 右侧
const geometryR = new THREE.PlaneGeometry(4, 4)
const materialR = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load('img/0_r.jpg'),
side: THREE.DoubleSide
})
const meshR = new THREE.Mesh(geometryR, materialR)
meshR.rotation.y = (90) * Math.PI / 180
meshR.position.x = -2
scene.add(meshR)
// 上面
const geometryU = new THREE.PlaneGeometry(4, 4)
const materialU = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load('img/0_u.jpg'),
side: THREE.DoubleSide
})
const meshU = new THREE.Mesh(geometryU, materialU)
meshU.rotation.x = (90) * Math.PI / 180
meshU.rotation.z = (180) * Math.PI / 180
meshU.position.y = 2
scene.add(meshU)
// 下面
const geometryD = new THREE.PlaneGeometry(4, 4)
const materialD = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load('img/0_d.jpg'),
side: THREE.DoubleSide
})
const meshD = new THREE.Mesh(geometryD, materialD)
meshD.rotation.x = (-90) * Math.PI / 180
meshD.rotation.z = (180) * Math.PI / 180
meshD.position.y = -2
scene.add(meshD)
}
// 初始化动画
function animate() {
requestAnimationFrame(animate)
controls.update()
renderer.render(scene, camera)
}
// 定义初始化方法
function init() {
initRenderer()
initScene()
initCamera()
initMesh()
animate()
}
init()
</script>
</body>
</html>
6. Add landmarks for panoramic inspections
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>轨迹球控制与材质</title>
<style>
* {
margin: 0;
padding: 0;
}
canvas {
display: block;
height: 100%;
width: 100%;
}
</style>
<script src="./three.min.js"></script>
<script src="./TrackballControls.js"></script>
</head>
<body>
<script>
// 定义全局变量
let scene, camera, geometry, mesh, renderer, controls
let sixPlane = []
let spriteArrow = ""
let raycaster = new THREE.Raycaster()
const mouse = new THREE.Vector2()
function onMouseMove(event) {
// 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
}
window.addEventListener('mousemove', onMouseMove, false)
// 鼠标点击
function mouseClickEvent(ev) {
ev.preventDefault();
// 射线捕获
raycaster.setFromCamera(mouse, camera)
const intersects = raycaster.intersectObjects([spriteArrow])
if (intersects.length > 0) {
changeScene()
}
}
window.addEventListener('click', mouseClickEvent, false)
// 初始化渲染器
function initRenderer() {
renderer = new THREE.WebGLRenderer({
antialias: true })
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setPixelRatio(window.devicePixelRatio)
document.body.appendChild(renderer.domElement)
}
// 初始化场景
function initScene() {
scene = new THREE.Scene()
const axesHelper = new THREE.AxesHelper(100)
scene.add(axesHelper)
}
// 初始化相机
function initCamera() {
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000)
camera.position.set(0, 0, 15)
controls = new THREE.TrackballControls(camera, renderer.domElement)
// 控制视觉位置
controls.maxDistance = 2 // 最大距离
controls.minDistance = 0 // 最小距离
}
// 初始化模型
function initMesh() {
// 利用精灵材质引入地面标记
new THREE.TextureLoader().load('img/icon.png', (texture) => {
const spriteMaterial = new THREE.SpriteMaterial({
map: texture
})
spriteArrow = new THREE.Sprite(spriteMaterial) //会固定位置
spriteArrow.scale.set(0.1, 0.1, 0.1)
spriteArrow.position.set(0.5, -1, -1.5)
scene.add(spriteArrow)
})
sixPlane = createPlane(0)
for (let i = 0; i < 6; i++) {
scene.add(sixPlane[i])
}
}
// 初始化动画
function animate() {
requestAnimationFrame(animate)
controls.update()
renderer.render(scene, camera)
}
// 定义初始化方法
function init() {
initRenderer()
initScene()
initCamera()
initMesh()
animate()
}
init()
function createPlane(num) {
const BoxGeometry = []
// 前面
const geometryF = new THREE.PlaneGeometry(4, 4)
const materialF = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load('img/' + num + '_f.jpg'),
side: THREE.DoubleSide
})
const meshF = new THREE.Mesh(geometryF, materialF)
meshF.rotation.y = 180 * Math.PI / 180
meshF.position.z = 2
BoxGeometry.push(meshF)
// 后面
const geometryB = new THREE.PlaneGeometry(4, 4)
const materialB = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load('img/' + num + '_b.jpg'),
side: THREE.DoubleSide
})
const meshB = new THREE.Mesh(geometryB, materialB)
// meshB.rotation.y = 180 * Math.PI / 180
meshB.position.z = -2
BoxGeometry.push(meshB)
// 左侧
const geometryL = new THREE.PlaneGeometry(4, 4)
const materialL = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load('img/' + num + '_l.jpg'),
side: THREE.DoubleSide
})
const meshL = new THREE.Mesh(geometryL, materialL)
meshL.rotation.y = (-90) * Math.PI / 180
meshL.position.x = 2
BoxGeometry.push(meshL)
// 右侧
const geometryR = new THREE.PlaneGeometry(4, 4)
const materialR = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load('img/' + num + '_r.jpg'),
side: THREE.DoubleSide
})
const meshR = new THREE.Mesh(geometryR, materialR)
meshR.rotation.y = (90) * Math.PI / 180
meshR.position.x = -2
BoxGeometry.push(meshR)
// 上面
const geometryU = new THREE.PlaneGeometry(4, 4)
const materialU = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load('img/' + num + '_u.jpg'),
side: THREE.DoubleSide
})
const meshU = new THREE.Mesh(geometryU, materialU)
meshU.rotation.x = (90) * Math.PI / 180
meshU.rotation.z = (180) * Math.PI / 180
meshU.position.y = 2
BoxGeometry.push(meshU)
// 下面
const geometryD = new THREE.PlaneGeometry(4, 4)
const materialD = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load('img/' + num + '_d.jpg'),
side: THREE.DoubleSide
})
const meshD = new THREE.Mesh(geometryD, materialD)
meshD.rotation.x = (-90) * Math.PI / 180
meshD.rotation.z = (180) * Math.PI / 180
meshD.position.y = -2
BoxGeometry.push(meshD)
return BoxGeometry
}
function changeScene() {
// 创建六个面
const sixBox = createPlane(2)
const timer = setInterval(() => {
camera.fov -= 1
camera.updateProjectionMatrix()
if (camera.fov == 20) {
clearInterval(timer)
camera.fov = 45
camera.updateProjectionMatrix()
for (let i = 0; i < 6; i++) {
scene.remove(sixPlane[i])
}
sixPlane = sixBox
for (let i = 0; i < 6; i++) {
scene.add(sixPlane[i])
}
spriteArrow.visible = false
}
}, 50)
}
</script>
</body>
</html>