three.js语法入门
创建一个3D场景
- 创建一个虚拟场景用于存放网络模型
/**
* 创建3D场景对象Scene
*/
const scene = new THREE.Scene();
/**
* 创建网格模型
*/
//创建一个长方体几何对象Geometry【形状】
const geometry = new THREE.BoxGeometry(50, 50, 50);
//材质对象Material【对象材质】
const material = new THREE.MeshBasicMaterial({
color: 0x0000ff, //设置材质颜色
});
// 给【网络模型】赋予【形状】和【材质】
// 给网格模型,物体形状和材质外观
const mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
//设置网格模型在三维空间中的位置坐标,默认是坐标原点
// 设立【网络模型】的【位置】
mesh.position.set(0,10,0);
// 【添加网络模型到虚拟场景中】
scene.add(mesh); //网格模型添加到场景中
- 创建一个相机用于模拟人眼视角去捕获这一模型
// 2. 创建相机用于拍照,捕获这个场景,模拟人眼观察这个场景
// 定义相机输出的画布大小
const width = 800
const height = 500
// 视锥体
// 参数
// fov:视野,即观察多大范围,观察角度,默认值50
// aspect:一般为canvas的宽高比
// near:四棱台的近的面到相机的距离
// far:四棱台的远的面到相机的距离
// PerspectiveCamera【透视投影相机】
const camera = new THREE.PerspectiveCamera(30,width/height,0.1,3000)
// 给相机位置,x,y,z
camera.position.set(200,200,200)
// 相机的视角,观察哪,也可以使用x,y,z
camera.lookAt(mesh.position)//看向网络模型的位置
透视投影相机的四个参数fov, aspect, near, far
构成一个四棱台3D空间,被称为视锥体,只有视锥体之内的物体,才会渲染出来,视锥体范围之外的物体不会显示在Canvas画布上。
- 渲染器
// 3.创建渲染器
const renderer = new THREE.WebGLRenderer()
renderer.setSize(width,height)
// 实际上这一步生成的canvas画布
renderer.render(scene,camera) //执行渲染操作,类比相机的拍照动作,场景使用scene,相机用camera
// 把canvas添加到html上
document.body.appendChild(renderer.domElement)
显示三维坐标轴
光源对物体表面的影响
-
基础网格材质MeshBasicMaterial不受光照影响
-
漫反射网格材质MeshLambertMaterial(opens new window)会受到光照影响,该材质也可以称为Lambert网格材质,音译为兰伯特网格材质。
// 创建光源对象,点光源
const pointLight = new THREE.PointLight(0xffffff,1.0)
// 光源衰减,默认值2.0,设置为0就是光源不随空间而进行强度变化
pointLight.decay = 0.0
// 光源放置位置
pointLight.position.set(400,200,200)
// 可视化点光源
const pointLightHelper = new THREE.PointLightHelper(pointLight,10)
// 把光源添加到场景中
scene.add(pointLight)
scene.add(pointLightHelper)
- 环境光AmbientLight没有特定方向,只是整体改变场景的光照明暗。平行光DirectionalLight就是沿着特定方向发射。
相机控件轨道控制器
- 引入
// 创建相机空间实例化对象
const control = new OrbitControls(camera, renderer.domElement)
control.addEventListener('change',function() {
// 渲染
renderer.render(scene,camera)
})
动画渲染
// 周期性执行,默认状态60次/秒
function render(){
// 每次旋转0.01弧度
mesh.rotateY(0.01)
renderer.render(scene,camera)
requestAnimationFrame(render)
}
render()
// canvas宽高动态变化,需要更新相机和渲染的参数
window.onresize = function(){
// 更新渲染器
renderer.setSize(window.innerWidth,window.innerHeight)
// 更新相机
camera.aspect = window.innerWidth/window.innerHeight
camera.updateProjectionMatrix()
}
阵列立方体和相机适配
lookAt值改变的同时,controls.target.set的值要和lookAt保持一致
WebGL渲染器
// 3.创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias:true, //启用抗锯齿
})
// 设置像素比参考当前屏幕的像素比
renderer.setPixelRatio(window.devicePixelRatio)
GUI.js
import * as THREE from 'three';
import {OrbitControls} from 'three/addons/controls/OrbitControls.js'
import {GUI} from 'three/addons/libs/lil-gui.module.min.js'
const gui = new GUI()
// 创建一个待改变的对象
const obj = {
x:30,
color:0xffffff
}
// 通过gui交互改变obj这个对象里面的属性
// 参数【要改变的对象,要改变对象的某个属性,区间范围(0,100)】
// .step【步长,每次拖动时变化的值】
// .onChange【变化时触发的函数】
gui.add(obj, 'x', 0 ,100).name("新增的名字").step(0.1).onChange(function(val){
console.log(val);
})
// 颜色选择器
gui.addColor(obj,'color').onChange(function(val){
Mesh.material.color.set(val)
console.log(val);
})
// 创建分组
// 创建材质子菜单
// 【父菜单.addFolder('子菜单名称')】
const matFolder = gui.addFolder('材质');
// 材质颜色color
matFolder.addColor(obj, 'color').onChange(function(value){
material.color.set(value);
});
// 材质高光颜色specular
matFolder.addColor(obj, 'specular').onChange(function(value){
material.specular.set(value);
});
gui.close() //收起总菜单
matFolder.close() //收起子菜单
平面几何体
const vertices = new Float32Array([
0, 0, 0, //顶点1坐标
80, 0, 0, //顶点2坐标
80, 80, 0, //顶点3坐标
0, 0, 0, //顶点4坐标 和顶点1位置相同
80, 80, 0, //顶点5坐标 和顶点3位置相同
0, 80, 0, //顶点6坐标
]);
// 以逆时针方向为正面,顺时针为反面
const material = new THREE.MeshBasicMaterial({
side: THREE.FrontSide, //默认只有正面可见
side: THREE.DoubleSide, //两面可见
side: THREE.BackSide, //设置只有背面可见
});
// 简化写法
const vertices = new Float32Array([
0, 0, 0, //顶点1坐标
80, 0, 0, //顶点2坐标
80, 80, 0, //顶点3坐标
0, 80, 0, //顶点4坐标
]);
// Uint16Array类型数组创建顶点索引数据
// 以0,1,2为一个对象,0,2,3为一个对象
const indexes = new Uint16Array([
// 下面索引值对应顶点位置数据中的顶点坐标
0, 1, 2, 0, 2, 3,
])
// 索引数据赋值给几何体的index属性
geometry.index = new THREE.BufferAttribute(indexes, 1); //1个为一组
层级模型
- 创建组对象的作用:要想同时操作多个模型的变化,可以直接对组对象进行操作
const geometry = new THREE.BoxGeometry(20, 20, 20);
const material = new THREE.MeshLambertMaterial({color: 0x00ffff});
const mesh1 = new THREE.Mesh(geometry, material);
const mesh2 = new THREE.Mesh(geometry, material);
mesh2.translateX(50)
// 创建一个组对象
const group = new THREE.Group()
// 把网格模型1,2,作为组对象group的子对象存在
group.add(mesh1)
group.add(mesh2)
// 整个组对象一起动
group.translateX(50)
// 网络模型也可以作为组对象的子对象存在
mesh1.add(mesh2)
- 给组对象加以命名
const group1 = new THREE.Group(); //所有高层楼的父对象
group1.name = '高层'
- 遍历所有节点
model.add(group1, group2);
// 递归遍历所有模型节点
model.traverse(function(obj){
console.log(obj.name);
// obj.isMesh是否是网格模型
})
// 获取对应名称的某个节点,和js一个意思
console.log(model.getObjectByName('1号楼'));
- 获取世界坐标
mesh.position.x = 50
// 创建三维向量表示坐标
const v3 = new THREE.Vector3()
console.log(mesh.getWorldPosition(v3));
- 给网格模型单独加个坐标系
const meshAxesHelper = new THREE.AxesHelper(50)
mesh.add(meshAxesHelper)
- 改变模型相对坐标系的位置
// 默认情况下,模型的几何中心和坐标系的原点是重合的
geometry.translate(40,0,0)
- 隐藏与删除模型
scene.remove(mesh) //删除
mesh.visible = false //显示/隐藏:Boolean
const material = new THREE.MeshLambertMaterial({color: 0x00ffff});
const mesh = new THREE.Mesh(geometry, material);
const mesh2 = new THREE.Mesh(geometry, material);
// 如果给材质设置这个属性,那么所有使用这个材质的网格模型,都会被隐藏,因为他们公用一个材质
material.visible = false
贴图
- 纹理贴图
const geometry = new THREE.PlaneGeometry(100, 100);
// 创建纹理加载器对象
const loadTex = new THREE.TextureLoader()
// 加载图片返回纹理对象
const texture = loadTex.load('earth.jpg')
const material = new THREE.MeshLambertMaterial({
color: 0x00ffff,
// 把图片作为模型的贴图
map:texture
});
const mesh = new THREE.Mesh(geometry, material);
只使用颜色背景和采用图片作为纹理的对比:
类似的,还可以贴在球体/正方体上:
const geometry = new THREE.SphereGeometry(50);
const geometry = new THREE.BoxGeometry(100,100,100);
- UV坐标
import * as THREE from 'three';
const geometry = new THREE.BufferGeometry(); //创建一个几何体对象
//类型数组创建顶点数据
const vertices = new Float32Array([
0, 0, 0, //顶点1坐标
160, 0, 0, //顶点2坐标
160, 80, 0, //顶点3坐标
0, 80, 0, //顶点4坐标
]);
// 创建属性缓冲区对象
const attribue = new THREE.BufferAttribute(vertices, 3); //3个为一组,表示一个顶点的xyz坐标
// 设置几何体attributes属性的位置属性
geometry.attributes.position = attribue;
// Uint16Array类型数组创建顶点索引数据
const indexes = new Uint16Array([
0, 1, 2, 0, 2, 3,
])
// 索引数据赋值给几何体的index属性
geometry.index = new THREE.BufferAttribute(indexes, 1); //1个为一组
// 将纹理图片,通过uv左边,映射一部分到几何体上
const uvs = new Float32Array([
0, 0, //左下
0.5, 0, //右下
1, 1, //右上
0, 1, //左上
]);
geometry.attributes.uv = new THREE.BufferAttribute(uvs, 2);
//纹理贴图加载器TextureLoader
const texLoader = new THREE.TextureLoader();
const texture = texLoader.load('./earth.jpg');
const material = new THREE.MeshBasicMaterial({
map: texture, //map表示材质的颜色贴图属性
});
const mesh = new THREE.Mesh(geometry, material);
export default mesh;
- texture阵列【瓷砖案例】
const geometry = new THREE.PlaneGeometry(2000, 2000);
// 创建纹理加载器对象
const loadTex = new THREE.TextureLoader()
// 加载图片返回纹理对象
const texture = loadTex.load('瓷砖.jpg')
// 设置阵列模式
texture.wrapT = THREE.RepeatWrapping
texture.wrapS = THREE.RepeatWrapping
// 选择阵列模式下的重复数量
texture.repeat.set(12,12)
const material = new THREE.MeshLambertMaterial({
// color: 0x00ffff,
map: texture
});
const mesh = new THREE.Mesh(geometry, material);
// three.js默认显示平面是XOY平面,且背面不可见
mesh.rotateX(-Math.PI/2)
- 透明背景的png贴图
// 添加一个辅助网格地面
// size:坐标格的尺寸,默认为10
// 坐标格细分次数
// 中线颜色
// 坐标格网格线颜色
const gridHelper = new THREE.GridHelper(300, 25, 0x004444, 0x004444);
scene.add(gridHelper);
// 需要设置背景透明的png作为贴图只需要加上
const material = new THREE.MeshLambertMaterial({
map: texture,//map表示材质的颜色贴图属性
transparent:true,//开启透明,这样png贴图的透明部分不显示
});
const mesh = new THREE.Mesh(geometry, material);
mesh.rotateX(-Math.PI/2);
// 让地面适当向下平移1,防止冲突
mesh.position.y = 1;
- UV动画
正常情况下,设置偏移,缺失的部分会显示背景色
const geometry = new THREE.PlaneGeometry(200, 20);
//纹理贴图加载器TextureLoader
const texLoader = new THREE.TextureLoader();
// .load()方法加载图像,返回一个纹理对象Texture
const texture = texLoader.load('./纹理1.jpg');
const material = new THREE.MeshLambertMaterial({
// 图片和颜色会叠加
color:0x00ffff,
map: texture,//map表示材质的颜色贴图属性
});
const mesh = new THREE.Mesh(geometry, material);
mesh.rotateX(-Math.PI/2);
// 纹理对象的偏移属性
// 本质上是修改的uv顶点坐标
texture.offset.x = -0.5 //u方向偏移
如果改变映射方式,缺失的部分会重新绕回,补全
// 改变映射方式
texture.wrapS = THREE.RepeatWrapping
渲染的时候给定偏移量,就可以得出类似于跑马灯的效果
// 渲染循环
function render() {
texture.offset.x += 0.001
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();