一、快速入门
1. Threejs第一个3D场景
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>第一个three.js文件_WebGL三维场景</title>
<style>
body {
margin: 0;
overflow: hidden;
/* 隐藏body窗口区域滚动条 */
}
</style>
<!--引入three.js三维引擎-->
<script src="http://www.yanhuangxueyuan.com/versions/threejsR92/build/three.js"></script>
<!-- <script src="./three.js"></script> -->
</head>
<body>
<script>
/**
* 创建场景对象Scene
*/
var scene = new THREE.Scene();
/**
* 创建网格模型
*/
// var geometry = new THREE.SphereGeometry(60, 40, 40); //创建一个球体几何对象
var geometry = new THREE.BoxGeometry(100, 100, 100); //创建一个立方体几何对象Geometry:长宽高
var material = new THREE.MeshLambertMaterial({
// color: 0x0000ff // 蓝色 16进制RGB三原色模型
color: 0x00ff00 // 绿色
}); //材质对象Material:颜色、透明度等
var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
scene.add(mesh); //网格模型添加到场景中
/**
* 光源设置
*/
//点光源
var point = new THREE.PointLight(0xffffff); // 0xffffff 定义 光照强度
// var point = new THREE.PointLight(0x444444);
point.position.set(400, 200, 300); //点光源位置
scene.add(point); //点光源添加到场景中
//环境光,加上环境光更亮
var ambient = new THREE.AmbientLight(0x444444);
scene.add(ambient);
// console.log(scene)
// console.log(scene.children)
/**
* 相机设置
*/
var width = window.innerWidth; //窗口宽度
var height = window.innerHeight; //窗口高度
var k = width / height; //窗口宽高比
var s = 300; //三维场景显示范围控制系数,系数越大,显示的范围越大
// 创建相机对象,控制显示的场景范围
var camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);// 正交相机(正射投影相机)
// 相机角度,宽高比,近裁截面,远裁截面
// var camera = new THREE.PerspectiveCamera(45, k, 1, 1000);// 透视投影相机
camera.position.set(200, 300, 200); //设置相机位置
camera.lookAt(scene.position); //设置相机方向(指向的场景对象)
/**
* 创建渲染器对象
*/
var renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);//设置渲染区域尺寸
renderer.setClearColor(0xb9d3ff, 1); //设置背景颜色
document.body.appendChild(renderer.domElement); //body元素中插入canvas对象
//执行渲染操作,渲染出一帧图像并显示在Web页面上 指定场景、相机作为参数
renderer.render(scene, camera);
</script>
</body>
</html>
three.js程序结构:
并非所有功能都可以通过 three 模块来直接访问。three.js 中其它较为流行的部分 —— 如控制器(control)、加载器(loader)以及后期处理效果(post-processing effect) —— 必须从 examples/jsm 子目录下导入。
2. 旋转动画、requestAnimationFrame周期性渲染
在上一节第一个3D场景已绘制好的立方体代码基础上进行更改。
每执行一次渲染器对象WebGLRenderer的渲染方法.render(),浏览器就会渲染出一帧图像并显示在Web页面上,这就是说按照一定的周期不停地调用渲染方法.render()就可以不停地生成新的图像覆盖原来的图像。这也就是说只要一边旋转立方体,一边执行渲染方法.render()重新渲染,就可以实现立方体的旋转效果。
按照一定周期执行渲染器的
.render()方法,更新显示结果,产生动画效果。一般调用渲染方法
.render()进行渲染的渲染频率控制在每秒30-60次(时间间隔16.67~33.33ms),人的视觉效果都很正常,也可以兼顾渲染性能
为了实现立方体旋转动画效果,直接使用下面的代码代替上一节中代码renderer.render(scene,camera);即可。
- setInterval 渲染
function render() {
renderer.render(scene,camera);//执行渲染操作
mesh.rotateY(0.01);//每次绕y轴旋转0.01弧度
}
// 间隔20ms调用函数,20ms也就是刷新频率是50FPS(1s/20ms),每秒渲染50次
setInterval("render()",20);
- requestAnimationFrame
function render() {
renderer.render(scene,camera);//执行渲染操作
mesh.rotateY(0.01);//每次绕y轴旋转0.01弧度
requestAnimationFrame(render);//请求再次执行渲染函数render
}
render();
requestAnimationFrame向浏览器发起一个执行某函数的请求, 什么时候会执行由浏览器决定,一般默认保持60FPS的频率,大约每16.7ms调用一次requestAnimationFrame()方法指定的函数,60FPS是理想的情况下,如果渲染的场景比较复杂或者说硬件性能有限可能会低于这个频率。
- requestAnimationFrame 均匀旋转
// 在实际执行程序的时候,可能requestAnimationFrame(render)请求的函数并不一定能按照理想的60FPS频率执行,两次执行渲染函数的时间间隔也不一定相同,如果执行旋转命令的rotateY的时间间隔不同,旋转运动就不均匀,为了解决这个问题需要记录两次执行绘制函数的时间间隔
let T0 = new Date();//上次时间
function render() {
let T1 = new Date();//本次时间
let t = T1-T0;//两次调用渲染函数的时间差
T0 = T1;//把本次时间赋值给上次时间
requestAnimationFrame(render);
renderer.render(scene,camera);//执行渲染操作
mesh.rotateY(0.001*t);//旋转角速度0.001弧度每毫秒
}
render();
不同的three.js应用场景,执行一帧的时间是不确定的,three.js一般选择
requestAnimationFrame,而不是setInterval渲染动画。
3. 鼠标操作三维场景旋转
OrbitControls.js控件支持鼠标左中右键操作和键盘方向键操作。
执行构造函数THREE.OrbitControls()浏览器会同时干两件事,一是给浏览器定义了一个鼠标、键盘事件,自动检测鼠标键盘的变化,如果变化了就会自动更新相机的数据, 执行该构造函数同时会返回一个对象,可以给该对象添加一个监听事件,只要鼠标或键盘发生了变化,就会触发渲染函数。
场景操作:
- 缩放:滚动—鼠标中键
- 平移:拖动—鼠标右键
- 旋转:拖动—鼠标左键
- 添加监听事件,更新渲染效果:
renderer.render(scene, camera);
// 创建控件对象 相机对象camera作为参数 控件可以监听鼠标的变化,改变相机对象的属性
var controls = new THREE.OrbitControls(camera,renderer.domElement);
// 监听鼠标事件,触发渲染函数,更新canvas画布渲染效果
controls.addEventListener('change', render);
- 使用requestAnimationFrame更新渲染
如果threejs代码中通过requestAnimationFrame()实现渲染器渲染方法render()的周期性调用,当通过OrbitControls操作改变相机状态的时候,没必要在通过controls.addEventListener('change', render)监听鼠标事件调用渲染函数,因为requestAnimationFrame()就会不停的调用渲染函数。
function render() {
renderer.render(scene,camera);
requestAnimationFrame(render); // OrbitControls改变相机对象的属性,再次渲染
}
render();
// 创建控件对象 相机对象camera作为参数 控件可以监听鼠标的变化,改变相机对象的属性
var controls = new THREE.OrbitControls(camera,renderer.domElement);
4. 插入多个几何体并偏移
几何体类型:
//长方体 参数:长,宽,高
// var geometry = new THREE.BoxGeometry(100, 100, 100);
// 球体 参数:半径60 经纬度细分数40,40
// var geometry = new THREE.SphereGeometry(60, 40, 40);
// 圆柱 参数:圆柱面顶部、底部直径、高度、圆周分段数
// var geometry = new THREE.CylinderGeometry( 50, 50, 100, 25 );
// 正八面体
// var geometry = new THREE.OctahedronGeometry(50);
// 正十二面体
// var geometry = new THREE.DodecahedronGeometry(50);
// 正二十面体
var geometry = new THREE.IcosahedronGeometry(50);
threejs的几何体默认位于场景世界坐标的原点(0,0,0),所以绘制多个几何体的时候,主要它们的位置设置。插入多个几何体并偏移:
// 立方体网格模型
var geometry1 = new THREE.BoxGeometry(100, 100, 100);
var material1 = new THREE.MeshLambertMaterial({
color: 0x0000ff
}); //材质对象Material
var mesh1 = new THREE.Mesh(geometry1, material1); //网格模型对象Mesh
scene.add(mesh1); //网格模型添加到场景中
// 球体网格模型
var geometry2 = new THREE.SphereGeometry(60, 40, 40);
var material2 = new THREE.MeshLambertMaterial({
color: 0xff00ff
});
var mesh2 = new THREE.Mesh(geometry2, material2); //网格模型对象Mesh
mesh2.translateY(120); // 球体网格模型沿Y轴正方向平移120
scene.add(mesh2);
// 圆柱网格模型
var geometry3 = new THREE.CylinderGeometry(50, 50, 100, 25);
var material3 = new THREE.MeshLambertMaterial({
color: 0xffff00
});
var mesh3 = new THREE.Mesh(geometry3, material3); //网格模型对象Mesh
// mesh3.translateX(120); // 球体网格模型沿Y轴正方向平移120
mesh3.position.set(120,0,0);// 设置mesh3模型对象的xyz坐标为120,0,0 position属性是Vector3对象
scene.add(mesh3); //
// 辅助坐标系
var axesHelper = new THREE.AxesHelper(250);
scene.add(axesHelper);
threejs三维坐标系老版本名称是
AxisHelper,新版本名称AxesHelper。
5. 设置材质效果
材质常见属性:
- color 材质颜色
- wireframe 线框模式渲染,默认值为false
- opacity 透明度,0~1
- transparent 是否开启透明度设置,默认值为false
材质类型:
- MeshBasicMaterial 基础网格材质,不受光照影响
// 基础网格材质 不受光照影响 没有棱角感
var material = new THREE.MeshBasicMaterial({
color: 0x0000ff,
// transparent:true, // 开启透明度
// opacity:0.5, // 设置透明度具体值
// wireframe:true, // 线框模式
});
// material.opacity = 0.5; // 属性设置
- MeshLambertMaterial Lambert网格材质,与光照有反应(漫反射)
// Lambert网格材质 与光照计算 漫反射 产生棱角感
var material = new THREE.MeshLambertMaterial({
color: 0x00ff00,
});
- MeshPhongMaterial 高光Phong材质,与光照有反应(镜面反射)
// 与光照计算 高光效果(镜面反射) 产生棱角感
var material = new THREE.MeshPhongMaterial({
color: 0xff0000,
specular: 0x444444, // 高光颜色
shininess: 30, // 光照强度系数
});
- MeshStandardMaterial PBR物理材质,相比较高光Phong材质可以更好的模拟金属、玻璃等效果
var material = new THREE.MeshStandardMaterial({
color: 0xff0000,
specular: 0x444444,
shininess: 30,
});
ps:与上面例子一样设置了点光源和环境光。
处在光照条件下的物体表面会发生光的反射现象,不同的表面粗糙度不同,宏观上来看对光的综合反射效果,可以使用两个反射模型来概括,一个是漫反射,一个是镜面反射。
6. 光照对象创建
常见光源类型:
- AmbientLight 环境光
- PointLight 点光源
- DirectionalLight 平行光,比如太阳光
- SpotLight 聚光源
- 单一点光源+环境光,立方体有明暗面
只设置一个点光源的情况下,通过鼠标旋转操作整个三维场景,立方体点光源无法照射的地方相对其他位置会比较暗。
//点光源
var point = new THREE.PointLight(0xffffff);
point.position.set(400, 200, 300); //点光源位置
// 通过add方法插入场景中,不插入的话,渲染的时候不会获取光源的信息进行光照计算
scene.add(point); //点光源添加到场景中
// 环境光 环境光颜色与网格模型的颜色进行RGB进行乘法运算
var ambient = new THREE.AmbientLight(0x444444);
scene.add(ambient);
- 只有点光源,整体偏暗
//点光源
var point = new THREE.PointLight(0xffffff);
point.position.set(400, 200, 300); //点光源位置
scene.add(point); //点光源添加到场景中
- 只有环境光,统一的颜色,没有明暗面
仅仅使用环境光的情况下,你会发现整个立方体没有任何棱角感,这是因为环境光只是设置整个空间的明暗效果。如果需要立方体渲染要想有立体效果,需要使用具有方向性的点光源、平行光源等。
通过光源构造函数的参数可以设置光源的颜色,一般设置明暗程度不同的白光RGB三个分量值是一样的。如果把THREE.AmbientLight(0x444444);的光照参数0x444444改为0xffffff,你会发现场景中的立方体渲染效果更明亮。
// 环境光
var ambient = new THREE.AmbientLight(0x444444);
scene.add(ambient);
- 添加对称点光源,暗面也有灯光
//点光源
var point = new THREE.PointLight(0xffffff);
point.position.set(400, 200, 300); //点光源位置
scene.add(point); //点光源添加到场景中
// 点光源2 位置和point关于原点对称
var point2 = new THREE.PointLight(0xffffff);
point2.position.set(-400, -200, -300); //点光源位置
scene.add(point2); //点光源添加到场景中
//环境光 环境光颜色与网格模型的颜色进行RGB进行乘法运算
var ambient = new THREE.AmbientLight(0x444444);
scene.add(ambient);
单一点光源暗面:
对称点光源暗面:
二、顶点概念、几何体结构
1. 顶点位置数据解析渲染
- 立方体顶点
var geometry = new THREE.BoxBufferGeometry(100, 100, 100);
console.log('立方体', geometry)
var material = new THREE.MeshBasicMaterial({
color: 0x0000ff, //三角面颜色
side: THREE.DoubleSide //内外面可见
}); //材质对象
var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
scene.add(mesh); //网格模型添加到场景中
- 未设置顶点的Buffer类型几何体
var geometry = new THREE.BufferGeometry(); //创建一个Buffer类型几何体对象
console.log('空的几何体', geometry)
- 为Buffer类型几何体设置顶点(自定义几何体)
var geometry = new THREE.BufferGeometry(); //创建一个Buffer类型几何体对象
//类型数组创建顶点数据
var vertices = new Float32Array([
0, 0, 0, //顶点1坐标
50, 0, 0, //顶点2坐标
0, 100, 0, //顶点3坐标
0, 0, 10, //顶点4坐标
0, 0, 100, //顶点5坐标
50, 0, 10, //顶点6坐标
]);
// 创建属性缓冲区对象
var attribue = new THREE.BufferAttribute(vertices, 3); //3个为一组,表示一个顶点的xyz坐标
// 设置几何体attributes属性的位置属性
geometry.attributes.position = attribue;
console.log('三角面', geometry)
- 面渲染模式
// 三角面(网格)渲染模式
var material = new THREE.MeshBasicMaterial({
color: 0x0000ff, //三角面颜色
side: THREE.DoubleSide //内外面可见
}); //材质对象
var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
scene.add(mesh); //网格模型添加到场景中
- 点渲染模式
var material = new THREE.PointsMaterial({
color: 0xff0000,
size: 5.0 //点对象像素尺寸
}); //材质对象
var points = new THREE.Points(geometry, material); //点模型对象
scene.add(points); //点对象添加到场景中
- 线渲染模式
// 线条渲染模式
var material=new THREE.LineBasicMaterial({
color:0xff0000 //线条颜色
});//材质对象
var line=new THREE.Line(geometry,material);//线条模型对象
scene.add(line);//线条对象添加到场景中
几何体本质
立方体网格模型Mesh是由立方体几何体geometry和材质material两部分构成,立方体几何体BoxGeometry本质上就是一系列的顶点构成,只是Threejs的APIBoxGeometry把顶点的生成细节封装了,用户可以直接使用。比如一个立方体网格模型,有6个面,每个面至少两个三角形拼成一个矩形平面,每个三角形三个顶点构成,对于球体网格模型而言,同样是通过三角形拼出来一个球面,三角形数量越多,网格模型表面越接近于球形。
var geometry = new THREE.BoxGeometry(100, 100, 100); //创建一个立方体几何对象Geometry
var material = new THREE.MeshLambertMaterial({ color: 0x0000ff }); //材质对象Material
var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
2. 顶点颜色数据插值计算
- 点渲染模式:每个点一种颜色,不会插值计算
- 线line、网格mesh渲染模式:颜色插值计算
顶点位置和顶点颜色一一对应。
材质属性vertexColors
THREE.NoColors将材质的颜色应用于所有面(默认值)->取决于材质属性.colorTHREE.VertexColors通过顶点颜色进行插值计算渲染->取决于几何体的顶点颜色geometry.attributes.colorTHREE.FaceColors根据每个Face3 Color值对面部进行着色
属性缓冲区对象BufferAttribute:
Threejs提供的接口BufferAttribute目的是为了创建各种各样顶点数据,比如顶点颜色数据,顶点位置数据等等,然后作为几何体BufferGeometry的顶点位置坐标属性BufferGeometry.attributes.position、顶点颜色属性BufferGeometry.attributes.color的值。关于BufferGeometry更多属性和方法可以查看文档BufferGeometry。
点渲染模式
var geometry = new THREE.BufferGeometry(); //声明一个缓冲几何体对象
// 顶点位置position数据
var vertices = new Float32Array([
0, 0, 0, //顶点1坐标
50, 0, 0, //顶点2坐标
0, 100, 0, //顶点3坐标
0, 0, 10, //顶点4坐标
0, 0, 100, //顶点5坐标
50, 0, 10, //顶点6坐标
]);
// 创建属性缓冲区对象
var attribue = new THREE.BufferAttribute(vertices, 3); //3个为一组,作为一个顶点的xyz坐标
// 设置几何体attributes属性的位置position属性
geometry.attributes.position = attribue;
// 顶点颜色color数据
var colors = new Float32Array([
1, 0, 0, //顶点1颜色 1=rgb中的255
0, 1, 0, //顶点2颜色
0, 0, 1, //顶点3颜色
1, 1, 0, //顶点4颜色
0, 1, 1, //顶点5颜色
1, 0, 1, //顶点6颜色
]);
// 设置几何体attributes属性的颜色color属性
geometry.attributes.color = new THREE.BufferAttribute(colors, 3); //3个为一组,表示一个顶点的颜色数据RGB
//材质对象
var material = new THREE.PointsMaterial({
// 使用顶点颜色数据渲染模型,不需要再定义color属性
// color: 0xff0000,
vertexColors: THREE.VertexColors, //以顶点颜色为准
size: 10.0 //点对象像素尺寸
});
// 点渲染模式 点模型对象Points
var points = new THREE.Points(geometry, material); //点模型对象
scene.add(points); //点对象添加到场景中
线line、网格mesh渲染模式(插值计算)
//材质对象
var material = new THREE.LineBasicMaterial({
// color: 0xff0000, // 线为红色的
vertexColors: THREE.VertexColors, //以顶点颜色为准,插值计算
});
// 线条渲染模式 线模型对象Line
var line = new THREE.Line(geometry, material); //点模型对象
scene.add(line); //点对象添加到场景中
两个顶点的颜色还是红色和绿色,但是由于设置了vertexColors: THREE.VertexColors,顶点之间的颜色为插值计算,于是线为彩色的:
var material = new THREE.MeshBasicMaterial({
// color: 0xff0000,
vertexColors: THREE.VertexColors, //以顶点颜色为准
});
// 网格模型 三角面渲染模式
var mesh = new THREE.Mesh(geometry, material); //网格模型
scene.add(mesh); //点对象添加到场景中
之所以出现渐变是因为Threejs通过底层WebGL进行渲染的时候会对顶点的颜色数据进行插值计算。颜色插值计算简单点说,比如一条直线的端点1设置为红色,端点2设置为蓝色,整条直线就会呈现出从点1到红色点2的蓝色颜色渐变,对于网格模型Mesh而言,就是三角形的三个顶点分别设置一个颜色,三角形内部的区域像素会根据三个顶点的颜色进行插值计算。
插值计算示意图:
3. 顶点法向量数据光照计算
WebGL中为了计算光线与物体表面入射角,你首先要计算物体表面每个位置的法线方向,在Threejs中表示物体的网格模型Mesh的曲面是由一个一个三角形构成,所以为了表示物体表面各个位置的法线方向,可以给几何体的每个顶点定义一个方向向量。
- 不设置顶点法向量
没有法向量数据,点光源、平行光等带有方向性的光源不会起作用,三角形平面整个渲染效果相对暗淡,而且两个三角形分界位置没有棱角感。
var geometry = new THREE.BufferGeometry(); //声明一个空几何体对象
//类型数组创建顶点位置position数据
var vertices = new Float32Array([
0, 0, 0, //顶点1坐标
50, 0, 0, //顶点2坐标
0, 100, 0, //顶点3坐标
0, 0, 0, //顶点4坐标
0, 0, 100, //顶点5坐标
50, 0, 0, //顶点6坐标
]);
// 创建属性缓冲区对象
var attribue = new THREE.BufferAttribute(vertices, 3); //3个为一组
// 设置几何体attributes属性的位置position属性
geometry.attributes.position = attribue
- 设置顶点法向量
在上面顶点位置数据基础上定义顶点法向量数据,这时候除了环境光以外,点光源也会参与光照计算,三角形整个表面比较明亮,同时两个三角形表面法线不同,即使光线方向相同,明暗自然不同,在分界位置有棱角感。
...
var normals = new Float32Array([
0, 0, 1, //顶点1法向量
0, 0, 1, //顶点2法向量
0, 0, 1, //顶点3法向量
0, 1, 0, //顶点4法向量
0, 1, 0, //顶点5法向量
0, 1, 0, //顶点6法向量
]);
// 设置几何体attributes属性的位置normal属性
geometry.attributes.normal = new THREE.BufferAttribute(normals, 3); //3个为一组,表示一个顶点的法向量数据
4. 顶点索引复用顶点数据
索引方式绘制好处:节约数据量。
对于顶点索引而言选择整型类型数组Uint16Array、Uint32Array等,对于非索引的顶点数据,需要使用浮点类型数组Float32Array等。
- 不使用顶点索引
var geometry = new THREE.BufferGeometry(); //声明一个空几何体对象
//类型数组创建顶点位置position数据
var 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坐标
]);
// 创建属性缓冲区对象
var attribue = new THREE.BufferAttribute(vertices, 3); //3个为一组
// 设置几何体attributes属性的位置position属性
geometry.attributes.position = attribue
var normals = new Float32Array([
0, 0, 1, //顶点1法向量
0, 0, 1, //顶点2法向量
0, 0, 1, //顶点3法向量
0, 0, 1, //顶点4法向量
0, 0, 1, //顶点5法向量
0, 0, 1, //顶点6法向量
]);
// 设置几何体attributes属性的位置normal属性
geometry.attributes.normal = new THREE.BufferAttribute(normals, 3); //3个为一组,表示一个顶点的xyz坐标
- 使用顶点索引
var geometry = new THREE.BufferGeometry(); //声明一个空几何体对象
//类型数组创建顶点位置position数据
var vertices = new Float32Array([
0, 0, 0, //顶点1坐标
80, 0, 0, //顶点2坐标
80, 80, 0, //顶点3坐标
0, 80, 0, //顶点4坐标
]);
// 创建属性缓冲区对象
var attribue = new THREE.BufferAttribute(vertices, 3); //3个为一组
// 设置几何体attributes属性的位置position属性
geometry.attributes.position = attribue
var normals = new Float32Array([
0, 0, 1, //顶点1法向量
0, 0, 1, //顶点2法向量
0, 0, 1, //顶点3法向量
0, 0, 1, //顶点4法向量
]);
// 设置几何体attributes属性的位置normal属性
geometry.attributes.normal = new THREE.BufferAttribute(normals, 3); //3个为一组,表示一个顶点的xyz坐标
// Uint16Array类型数组创建顶点索引数据
var indexes = new Uint16Array([
0, 1, 2, 0, 2, 3,
])
// 索引数据赋值给几何体的index属性
geometry.index = new THREE.BufferAttribute(indexes, 1); //1个为一组
BufferGeometry总结
BufferGeometry
geometry.attributesgeometry.attributes.position顶点位置geometry.attributes.color顶点颜色geometry.attributes.normal顶点法向量geometry.attributes.uv纹理贴图UV坐标geometry.attributes.uv2光照贴图lightMapUV2坐标
geometry.index顶点索引数据
5. 设置Geometry顶点位置、顶点颜色数据
几何体Geometry和缓冲类型几何体BufferGeometry表达的含义相同,只是对象的结构不同,Threejs渲染的时候会先把Geometry转化为BufferGeometry再解析几何体顶点数据进行渲染。
几何体Geometry的geometry.vertices、geometry.colors 和缓冲类型几何体BufferGeometry的geometry.attributes.position、geometry.attributes.color是对应的。
Vector3定义顶点位置坐标数据
Vector3是threejs的三维向量对象,可以通过Vector3对象表示一个顶点的xyz坐标,顶点的法线向量。
var geometry = new THREE.Geometry(); //声明一个几何体对象Geometry
var p1 = new THREE.Vector3(50, 0, 0); //顶点1坐标
var p2 = new THREE.Vector3(0, 70, 0); //顶点2坐标
var p3 = new THREE.Vector3(80, 70, 0); //顶点3坐标
//顶点坐标添加到geometry对象
geometry.vertices.push(p1, p2, p3);
Color定义顶点颜色数据
// Color对象表示顶点颜色数据
var color1 = new THREE.Color(0x00ff00); //顶点1颜色——绿色
var color2 = new THREE.Color(0xff0000); //顶点2颜色——红色
var color3 = new THREE.Color(0x0000ff); //顶点3颜色——蓝色
//顶点颜色数据添加到geometry对象
geometry.colors.push(color1, color2, color3);
设置几何体
Geometry顶点颜色属性geometry.colors,对网格模型Mesh是无效的,对于点模型Points、线模型Line是有效的。
6. Face3对象定义Geometry的三角面
var geometry = new THREE.Geometry(); //声明一个几何体对象Geometry
// 两个三角形有6个顶点,但是两个顶点位置重合的,可以设置4个顶点即可。
var p1 = new THREE.Vector3(0, 0, 0); //顶点1坐标
var p2 = new THREE.Vector3(0, 100, 0); //顶点2坐标
var p3 = new THREE.Vector3(50, 0, 0); //顶点3坐标
var p4 = new THREE.Vector3(0, 0, 100); //顶点4坐标
//顶点坐标添加到geometry对象
geometry.vertices.push(p1, p2, p3,p4);
// Color对象表示顶点颜色数据
var color1 = new THREE.Color(0x00ff00); //顶点1颜色——绿色
var color2 = new THREE.Color(0xff0000); //顶点2颜色——红色
var color3 = new THREE.Color(0x0000ff); //顶点3颜色——蓝色
var color4 = new THREE.Color(0xffff00); //顶点3颜色——黄色
//顶点颜色数据添加到geometry对象
geometry.colors.push(color1, color2, color3, color4);
/* 三角面1 */
// 通过Face3构建一个三角面,不要设置顶点位置坐标数据,只需要通过数组索引值从geometry.vertices数组中获得顶点位置坐标数据
var face1 = new THREE.Face3(0, 1, 2); // 使用顶点索引数组三角面
//设置三角面face1每个顶点的法向量
var n1 = new THREE.Vector3(0, 0, -1);
var n2 = new THREE.Vector3(0, 0, -1);
var n3 = new THREE.Vector3(0, 0, -1);
// 设置三角面Face3三个顶点的法向量
face1.vertexNormals.push(n1, n2, n3);
// 设置三角面face1三个顶点的颜色
face1.vertexColors = [
new THREE.Color(0xffff00),
new THREE.Color(0xff00ff),
new THREE.Color(0x00ffff),
]
/* 三角面2 */
var face2 = new THREE.Face3(0, 2, 3);
// 设置三角面法向量
face2.normal = new THREE.Vector3(0, -1, 0);
// 设置三角面颜色
face2.color = new THREE.Color(0x00ff00);
// 三角面face1、face2添加到几何体中
geometry.faces.push(face1, face2);
//材质对象
var material = new THREE.MeshLambertMaterial({
vertexColors: THREE.VertexColors, //以顶点颜色为准
// vertexColors: THREE.FaceColors,
side: THREE.DoubleSide, // 内外面可见
});
//网格模型对象
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh); //网格模型对象添加到场景中
Geometry总结
7. 访问几何体对象的数据Geometry和BufferGeometry
- 几何体
Geometry
var geometry = new THREE.BoxGeometry(100, 100, 100); //创建一个立方体几何对象Geometry
console.log(geometry);
- 缓冲类型几何体
BufferGeometry
var geometry = new THREE.BoxBufferGeometry(100, 100, 100); //创建一个立方体几何对象Geometry
console.log(geometry);
几何体Geometry和缓冲类型几何体BufferGeometry的编码差别
- 几何体
Geometry
var geometry = new THREE.Geometry(); //声明一个几何体对象Geometry
// 两个三角形有6个顶点,但是两个顶点位置重合的,可以设置4个顶点即可。
var p1 = new THREE.Vector3(0, 0, 0); //顶点1坐标
var p2 = new THREE.Vector3(80, 0, 0); //顶点2坐标
var p3 = new THREE.Vector3(80, 80, 0); //顶点3坐标
var p4 = new THREE.Vector3(0, 80, 0); //顶点4坐标
//顶点坐标添加到geometry对象
geometry.vertices.push(p1, p2, p3, p4);
// Color对象表示顶点颜色数据
var color1 = new THREE.Color(1, 0, 0); //顶点1颜色
var color2 = new THREE.Color(0, 1, 0); //顶点2颜色
var color3 = new THREE.Color(0, 0, 1); //顶点3颜色
var color4 = new THREE.Color(1, 1, 0); //顶点4颜色
//顶点颜色数据添加到geometry对象
geometry.colors.push(color1, color2, color3, color4);
// 三角面1
var face1 = new THREE.Face3(0, 1, 2); // 使用顶点索引 geometry.vertices
face1.normal = new THREE.Vector3(0, 0, 1);
face1.vertexColors = [color1, color2, color3]
// 三角面2
var face2 = new THREE.Face3(0, 2, 3);
face2.normal = new THREE.Vector3(0, 0, 1);
face2.vertexColors = [color1, color3, color4]
// 三角面face1、face2添加到几何体中
geometry.faces.push(face1, face2);
// 材质对象
var material = new THREE.MeshLambertMaterial({
vertexColors: THREE.VertexColors // 根据顶点颜色插值计算
});
//网格模型对象
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh); //网格模型对象添加到场景中
- 缓冲类型几何体
BufferGeometry
var geometry = new THREE.BufferGeometry(); //声明一个空几何体对象
//类型数组创建顶点位置position数据
var vertices = new Float32Array([
0, 0, 0, //顶点1坐标
80, 0, 0, //顶点2坐标
80, 80, 0, //顶点3坐标
0, 80, 0, //顶点4坐标
]);
// 创建属性缓冲区对象
var attribue = new THREE.BufferAttribute(vertices, 3); //3个为一组
// 设置几何体attributes属性的位置position属性
geometry.attributes.position = attribue
var normals = new Float32Array([
0, 0, 1, //顶点1法向量
0, 0, 1, //顶点2法向量
0, 0, 1, //顶点3法向量
0, 0, 1, //顶点4法向量
]);
// 设置几何体attributes属性的位置normal属性
geometry.attributes.normal = new THREE.BufferAttribute(normals, 3); //3个为一组,表示一个顶点的xyz坐标
// Uint16Array类型数组创建顶点索引数据
var indexes = new Uint16Array([
0, 1, 2, 0, 2, 3,
])
// 索引数据赋值给几何体的index属性
geometry.index = new THREE.BufferAttribute(indexes, 1); //1个为一组
//类型数组创建顶点颜色color数据
var colors = new Float32Array([
1, 0, 0, //顶点1颜色
0, 1, 0, //顶点2颜色
0, 0, 1, //顶点3颜色
1, 1, 0, //顶点4颜色
]);
// 设置几何体attributes属性的颜色color属性
geometry.attributes.color = new THREE.BufferAttribute(colors, 3); //3个为一组,表示一个顶点的颜色数据RGB
var material = new THREE.MeshBasicMaterial({
vertexColors: THREE.VertexColors, // 根据顶点颜色插值计算
});
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
删除几何体面
var geometry = new THREE.BoxGeometry(100, 100, 100); //创建一个立方体几何对象Geometry
// pop():删除数组的最后一个元素 shift:删除数组的第一个元素
geometry.faces.pop();
// geometry.faces.shift();
var material = new THREE.MeshLambertMaterial({
color: 0x0000ff,
// side: THREE.DoubleSide, //内外面可见
}); //材质对象Material
var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
scene.add(mesh); //网格模型添加到场景中
8. 几何体旋转、缩放、平移
- 缩放
scale( x : Float, y : Float, z : Float )
var geometry = new THREE.BoxGeometry(100, 100, 100); //创建一个立方体几何对象Geometry
geometry.scale(0.5, 2, 2);
- 平移
translate( x : Float, y : Float, z : Float )
var geometry = new THREE.BoxGeometry(100, 100, 100); // 默认在原点
geometry.translate(0, 0, 100);
- 旋转,参数为弧度
rotateXrotateYrotateZ
var geometry = new THREE.BoxGeometry(100, 100, 100);
geometry.rotateX(Math.PI / 4); // 绕x轴旋转45度
- 局中
.center()
var geometry = new THREE.BoxGeometry(100, 100, 100); //创建一个立方体几何对象Geometry
geometry.translate(0, 0, 100);
geometry.center(); // 居中:偏移的几何体居中
变换的本质:改变几何体的顶点位置数据
三、材质对象
1. 常用材质对象
材质的本质:顶点着色器、片元着色器代码、unifomrs数据。
点材质
- PointsMaterial
var geometry = new THREE.SphereGeometry(100, 25, 25); //创建一个球体几何对象
// 创建一个点材质对象
var material = new THREE.PointsMaterial({
color: 0x0000ff, //颜色
size: 3, //点渲染尺寸
});
//点模型对象 参数:几何体 点材质
var point = new THREE.Points(geometry, material);
scene.add(point); //网格模型添加到场景中
线材质
- LineBasicMaterial 线基础材质
var geometry = new THREE.BoxGeometry(100, 100, 100);//立方体几何体
// 直线基础材质对象
var material = new THREE.LineBasicMaterial({
color: 0x0000ff
});
var line = new THREE.Line(geometry, material); //线模型对象
scene.add(line); //线模型添加到场景中
- LineDashedMaterial 虚线材质
var geometry = new THREE.BoxGeometry(100, 100, 100);//立方体几何体
// 虚线材质对象:产生虚线效果
var material = new THREE.LineDashedMaterial({
color: 0x0000ff,
dashSize: 10,//显示线段的大小。默认为3。
gapSize: 5,//间隙的大小。默认为1
});
var line = new THREE.Line(geometry, material); //线模型对象
// computeLineDistances方法 计算LineDashedMaterial所需的距离数组
line.computeLineDistances(); // 不调用无法形成虚线
scene.add(line); //线模型添加到场景中
网格材质
MeshBasicMaterial基础网格材质,不受光照影响
// 基础网格材质 不受光照影响 没有棱角感
var material = new THREE.MeshBasicMaterial({
color: 0x0000ff,
// transparent:true, // 开启透明度
// opacity:0.5, // 设置透明度具体值
// wireframe:true, // 线框模式
});
// material.opacity = 0.5; // 属性设置
MeshLambertMaterialLambert网格材质,暗淡,与光照有反应(漫反射)
// Lambert网格材质 与光照计算 漫反射 产生棱角感
var material = new THREE.MeshLambertMaterial({
color: 0x00ff00,
});
MeshPhongMaterial高光Phong材质,高亮表面,与光照有反应(镜面反射)
// 与光照计算 高光效果(镜面反射) 产生棱角感
var material = new THREE.MeshPhongMaterial({
color: 0xff0000,
specular: 0x444444, // 高光颜色
shininess: 30, // 光照强度系数
});
MeshToonMaterial卡通材质。与MeshPhongMaterial高亮材质类似,但它不是平滑地着色,而是使用一个渐变图(一个X乘1的纹理)来决定如何着色。默认使用的渐变图是前70%的部分使用70%的亮度,之后的部分使用100%的亮度,当然,你可以定义你自己的渐变图。这最终会给人一种2色调的感觉,看起来就像卡通一样。
const colors = new Uint8Array(3);
for ( let c = 0; c <= colors.length; c ++ ) {
colors[ c ] = ( c / colors.length ) * 256;
}
const gradientMap = new THREE.DataTexture( colors, colors.length, 1, THREE.RedFormat );
const toonMaterial = new THREE.MeshToonMaterial( {
color: 0x00ffff,
gradientMap: gradientMap // 卡通着色的渐变贴图
} );
MeshStandardMaterialPBR物理材质,相比较高光Phong材质可以更好的模拟金属、玻璃等效果roughness粗糙度,0-1,0:平滑的镜面反射(有光泽),1:完全漫反射(无强烈反光)。metalness金属度,0:非金属,1:金属,通常没有中间值。
const standardMaterial = new THREE.MeshStandardMaterial({
color: 0xff8c00,
specular: 0x444444, // 高光颜色
shininess: 30, // 镜面高光的光泽度
roughness: 0.5, // 材质的粗糙程度。默认值为1.0
metalness: 0, // 材质与金属的相似度。默认值为0.0
});
这里是一个快速示例,从左至右看,粗糙度从0到1,从上至下看,金属度从0到1。
MeshPhysicalMaterial与MeshStandardMaterial相同,但它增加了2个参数,指定光泽层。clearcoat光泽层,当需要在表面加一层薄薄的半透明材质的时候,可以使用。范围0-1,有些类似于车漆,碳纤,被水打湿的表面的材质clearCoatRoughness光泽层的粗糙程度
const physicalMaterial = new THREE.MeshPhysicalMaterial({
color: 0xff8c00,
specular: 0x444444, // 高光颜色
shininess: 30, // 镜面高光的光泽度
roughness: 0.5, // 材质的粗糙程度。0.0表示平滑的镜面反射,1.0表示完全漫反射。默认值为1.0
// metalness: 1, // 材质与金属的相似度。非金属材质,如木材或石材,使用0.0,金属使用1.0,通常没有中间值。 默认值为0.0
clearcoat: 0.5, // 光泽层的亮度
clearCoatRoughness: 0 // 光泽层的粗糙程度
});
这里是和上面一样的按 metalness 划分的 roughness 网格,但可以设置 clearcoat 和 clearCoatRoughness 。
MeshDepthMaterial网格深度材质,渲染的效果和模型像素对应顶点距离相机的位置远近有关。MeshDepthMaterial渲染每个像素的深度,其中处在摄像机负近端面的像素其深度为0,处在摄像机负远端面的像素其深度为1。
MeshNormalMaterial网格法向量材质,会绘制视图空间法线(相对于摄像机的法线)。x 是红色, y 是绿色, 以及 z 是蓝色,所以朝向右边的东西是粉红色,朝向左边的是水蓝色,朝上的是浅绿色,朝下的是紫色,朝向屏幕的是淡紫色。
ShadowMaterial阴影材质
更复杂的材质会消耗更多的GPU功耗。如果你不需要额外的功能,那就使用最简单的材质。
各种标准材质的构建速度从最快到最慢:
MeshBasicMaterial➡MeshLambertMaterial➡MeshPhongMaterial➡MeshToonMaterial➡MeshStandardMaterial➡MeshPhysicalMaterial。
精灵Sprite材质
SpriteMaterial
var spriteMaterial = new THREE.SpriteMaterial({
color:0xff00ff,//设置精灵矩形区域颜色
rotation: Math.PI/4,//绕垂直屏幕方向旋转45度,弧度值
lights: false, // 受光照影响
// map: texture,//设置精灵纹理贴图
});
自定义着色器材质
-
ShaderMaterial通过three.js的着色器系统来制作自定义材质。 -
RawShaderMaterial可以用来制作完全自定义的着色器,不需要three.js的帮助。
2. 模型构成
- 点模型:Points
var geometry = new THREE.SphereGeometry(100, 25, 25); //创建一个球体几何对象
// 创建一个点材质对象
var material = new THREE.PointsMaterial({
color: 0x0000ff, //颜色
size: 3, //点渲染尺寸
});
//点模型对象 参数:几何体 点材质
var point = new THREE.Points(geometry, material);
scene.add(point); //网格模型添加到场景中
-
线模型
- Line 直线模型
var geometry = new THREE.BoxGeometry(100, 100, 100); //创建一个立方体几何对象Geometry // 线条渲染模式 var material=new THREE.LineBasicMaterial({ color:0xff0000 //线条颜色 });//材质对象 var line=THREE.Line(geometry,material) // 直线模型对象- LineLoop 闭环线模型
var line=new THREE.LineLoop(geometry,material);//闭环线模型对象 scene.add(line);//线条对象添加到场景中- LineSegments 间断线模型
var line=new THREE.LineSegments(geometry,material);//间断线模型对象 scene.add(line);//线条对象添加到场景中 -
网格材质
- 网格模型Mesh
var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh scene.add(mesh)- 骨骼类网格模型SkinnedMesh
var material = new THREE.MeshPhongMaterial({ skinning: true, //允许蒙皮动画 }); var SkinnedMesh = new THREE.SkinnedMesh(geometry, material); scene.add(SkinnedMesh); //网格模型添加到场景中 ... var skeleton = new THREE.Skeleton([Bone1, Bone2, Bone3]); //创建骨骼系统 SkinnedMesh.add(Bone1); //根骨头关节添加到网格模型 SkinnedMesh.bind(skeleton); //网格模型绑定到骨骼系统 skeleton.bones[1].rotation.x = 0.5; skeleton.bones[2].rotation.x = 0.5; -
精灵模型Sprite
// 创建精灵模型对象,不需要几何体geometry参数
var sprite = new THREE.Sprite(spriteMaterial);
scene.add(sprite);
比较线模型的线条绘制模式和网格模型的线条绘制模式
- 网格模型的线框绘制模式
var geometry = new THREE.BoxGeometry(100, 100, 100); //创建一个立方体几何对象Geometry
// 三角面(网格)渲染模式 MeshLambertMaterial MeshBasicMaterial
var material = new THREE.MeshBasicMaterial({
color: 0x0000ff, //三角面颜色
wireframe:true,//网格模型以线条的模式渲染
}); //材质对象
var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
scene.add(mesh); //网格模型添加到场景中
- 线模型的线条绘制模式
var geometry = new THREE.BoxGeometry(100, 100, 100);
var material = new THREE.LineBasicMaterial({
color: 0x0000ff,
});
var mesh = new THREE.Line(geometry, material);
scene.add(mesh);
两个模式绘制时使用的顶点不同:
3. 材质共有属性、私有属性
材质共有属性:
opacity透明度,0~1transparent是否开启透明度设置,默认值为falsevertexColorsTHREE.NoColors将材质的颜色应用于所有面(默认值)->取决于材质属性.colorTHREE.VertexColors通过顶点颜色进行插值计算渲染->取决于几何体的顶点颜色geometry.attributes.colorTHREE.FaceColors根据每个Face3 Color值对面部进行着色->取决于三角面的顶点颜色
sideTHREE.FrontSide仅前面可见(默认值)THREE.BackSide仅背面可见THREE.DoubleSide双面可见
材质私有属性:
color:材质颜色。点、线、网格材质具有,自定义着色器材质ShaderMaterial、RawShaderMaterial不具有;wireframe:线框模式渲染,默认值为false。网格材质具有。
// 基础网格材质 不受光照影响 没有棱角感
var material = new THREE.MeshBasicMaterial({
color: 0x0000ff,
// transparent:true, // 开启透明度
// opacity:0.5, // 设置透明度具体值
// wireframe:true, // 线框模式
// side:THREE.DoubleSide, // 双面可见
});
// material.opacity = 0.5; // 属性设置
四、模型对象
1. 点、线、网格模型介绍
参照“材质对象-模型构成”。
2. 模型对象旋转、平移、缩放
- 缩放:
.scale
mesh.scale.set(0.5, 1.5, 2) // 网格模型xyz方向分别缩放0.5,1.5,2倍
mesh.scale.x = 2.0; // x轴方向放大2倍
- 旋转:
.rotateX(angle)、.rotateY(angle)、.rotateZ(angle).rotateOnAxis(axis, angle)- axis 方向 Vector3
- angle 角度,单位弧度
mesh.rotateY(Math.PI / 2);// 绕着Y轴旋转90度
var axis = new THREE.Vector3(1, 1, 1); // 向量Vector3对象表示方向
axis.normalize(); // 向量归一化
mesh.rotateOnAxis(axis, Math.PI / 2) // 沿着axis轴表示方向旋转90度
console.log(mesh.rotation); // 旋转方法,改变了rotation属性
- 平移:
.translateX(distance)、.translateY(distance)、.translateZ(distance).translateOnAxis(axis, angle)- axis 方向 Vector3
- distance 平移距离
mesh.translateX(100); // 网格模型沿着x轴方向平移100
var axis = new THREE.Vector3(1, 1, 1);
axis.normalize(); //向量归一化
mesh.translateOnAxis(axis, 100); // 沿着axis轴表示方向平移100
console.log(mesh.position); // 平移方法,改变了position属性
几何变换的区别
- 网格模型Mesh对象
- 不会改变顶点位置数据
- 旋转平移缩放方法改变的是模型对象的
scale、rotation、position属性
- 几何体Geometry对象
- 改变顶点位置数据
3. 对象克隆clone、复制copy
Threejs大多数对象都有克隆.clone()和复制.copy()两个方法,点模型Points、线模型Line、网格网格模型Mesh一样具有这两个方法。
- 克隆clone
mesh2 = mesh1.clone()- 对象mesh1执行克隆方法,返回一个新的对象mesh2
- mesh1和mesh2共享几何体geometry和材质material对象,复制的是geometry和material对象的引用。几何体和材质属性的属性值改变,mesh1和mesh2的颜色都会改变。(浅拷贝)
- mesh2会获得mesh1的位置、角度、矩阵等属性,复制的是具体的值,而不是引用。mesh2和mesh1的位置、角度等属性改变互不影响。(深拷贝)
var geometry = new THREE.BoxGeometry(20, 20, 20);
var material = new THREE.MeshLambertMaterial({
color: 0x0000ff
});
var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
for (let i = 0; i < 10; i++) {
// 执行clone方法克隆mesh;
var newMesh = mesh.clone();
// 等同于
// var mesh = new THREE.Mesh(geometry, material);
newMesh.translateY(i * 25);
scene.add(newMesh)
}
- 复制copy
mesh2 .copy(mesh1)- 对象从另一个对象复制数据
- 从一个网格模型对象复制非几何体、材质对象。mesh2复制mesh1的位置、旋转、矩阵等属性(不包含geometry和material属性)
var geometry = new THREE.BoxGeometry(20, 20, 20);
var material = new THREE.MeshLambertMaterial({
color: 0x0000ff
});
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
// mesh经过一系列变换
mesh.translateX(50);
mesh.rotateX(Math.PI / 4);
mesh.translateZ(50);
var geometry2 = new THREE.BoxGeometry(30, 30, 30);
var material2 = new THREE.MeshLambertMaterial({
color: 0xff00ff
});
var newMesh = new THREE.Mesh(geometry2, material2);
// 复制mesh的位置、旋转、矩阵等属性(不包含geometry和material属性)
newMesh.copy(mesh);
// 相比mesh而言,在平移
newMesh.translateX(-50);
scene.add(newMesh)
五、光源对象
1. 光照原理和常见光源类型
常见光源类型:
-
环境光
-
AmbientLight环境光。没有特定方向,只有颜色
环境光:显示颜色 = 环境光颜色 * 材质颜色
-
-
方向光
PointLight点光源DirectionalLight平行光,比如太阳光SpotLight聚光灯光源
方向光:漫反射光的颜色 = 几何体表面基色 x 光线颜色 x 光线入射角余弦值
Threejs在渲染的时候网格模型材质的颜色值
mesh.material.color和光源的颜色值light.color会进行相乘,简单说就是RGB三个分量分别相乘。漫反射数学模型RGB分量表示:
(R2,G2,B2) = (R1,G1,B1) x (R0,G0,B0) x cosθR2 = R1 * R0 * cosθ G2 = G1 * G0 * cosθ B2 = B1 * B0 * cosθ
光源辅助对象:
- SpotLightHelper 聚光灯光源辅助对象
- PointLightHelper 点光源辅助对象
- DirectionalLightHelper 平行光源辅助对象
光源辅助对象,就像
AxesHelper可视化显示三维坐标轴一样显示光源对象,通过这些辅助对象可以方便调试代码,查看位置、方向。
- 环境光,设置整个空间的明暗效果,整个立方体没有任何棱角感。
// 环境光
var ambient = new THREE.AmbientLight(0x444444);
scene.add(ambient);
ps:一般设置明暗程度不同的白光RGB三个分量值是一样的。
- 点光源,平面不同区域呈现出不同的明暗效果(棱角感),点光源无法照射的地方相对其他位置会比较暗。需要设置光源位置。
//点光源
var point = new THREE.PointLight(0xffffff);
point.position.set(400, 200, 300); //点光源位置
scene.add(point); //点光源添加到场景中
- 聚光灯光源,沿着特定方逐渐发散。需要设置光源位置、照射对象、发散角度。
// 聚光光源
var spotLight = new THREE.SpotLight(0xffffff);
// 设置聚光光源位置
spotLight.position.set(200, 200, 200);
// 聚光灯光源指向网格模型mesh2
spotLight.target = mesh2;
// 设置聚光光源发散角度
spotLight.angle = Math.PI / 6
scene.add(spotLight);//光对象添加到scene场景中
只照射mesh2,mesh1没有光照是黑色的:
- 平行光源,平面不同区域接收到平行光的入射角一样。主要是确定光线方向。
光源的方向:通过 光源position属性 和 照射对象的position属性 计算,仅仅设置光线位置是不起作用的。
// 平行光
var directionalLight = new THREE.DirectionalLight(0xffffff, 1);
// 注意:位置属性在这里不代表方向光的位置,仅用于计算平行光的方向。你可以认为方向光没有位置
directionalLight.position.set(80, 100, 50);
// 方向光指向对象,可以不设置,默认的位置是0,0,0
directionalLight.target = mesh2;
scene.add(directionalLight);
聚光光源、平行光源的target属性设置
target属性是一个Object3d类型的对象。可以自定义光照照射对象,记得把目标对象添加到scene场景中。
// 聚光光源
var spotLight = new THREE.SpotLight(0xffffff);
// 设置聚光光源位置
spotLight.position.set(200, 200, 200);
// 设置聚光光源发散角度
spotLight.angle = Math.PI / 6
// 1. 聚光灯光源指向网格模型mesh2
// spotLight.target = mesh2;
// 2. 直接设置target属性 记得把目标对象添加到scene场景中
// spotLight.target.position.set(150,0,0);
// scene.add(spotLight.target)
// 3. 创建一个用来设置target的Object3D对象
var targetObject = new THREE.Object3D();
targetObject.translateX(150);
scene.add(targetObject);
spotLight.target = targetObject;
scene.add(spotLight);
2. 阴影投影计算
-
设置产生投影的模型对象
mesh.castShadow = true; -
设置接收投影效果的模型
planeMesh.receiveShadow = true; -
设置用于计算阴影的光源对象
light.castShadow = true; -
设置计算阴影的区域,最好刚好紧密包围在对象周围,过大会模糊(阴影纹理贴图出现严重锯齿),过小阴影看不到或显示不完整
- 平行光:长方体区域,
shadow.camera为OrthographicCamera正交投影
directionalLight.shadow.camera.near = 0.5; directionalLight.shadow.camera.far = 300; directionalLight.shadow.camera.left = -50; directionalLight.shadow.camera.right = 50; directionalLight.shadow.camera.top = 200; directionalLight.shadow.camera.bottom = -100; // 设置mapSize属性可以使阴影更清晰,不那么模糊,但是会消耗更多的性能,建议设置shadow.camera // directionalLight.shadow.mapSize.set(1024,1024) // 默认 512,512- 聚光灯:锥形区域,
shadow.camera为PerspectiveCamera透视投影
spotLight.shadow.camera.near = 1; spotLight.shadow.camera.far = 300; spotLight.shadow.camera.fov = 20; - 平行光:长方体区域,
-
设置webgl渲染器
renderer.shadowMap.enabled = true;
完整代码:
var scene = new THREE.Scene();
var geometry = new THREE.BoxGeometry(40, 100, 40);
var material = new THREE.MeshLambertMaterial({
color: 0x0000ff
});
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
// 1. 设置产生投影的网格模型
mesh.castShadow = true;
var planeGeometry = new THREE.PlaneGeometry(300, 200);
var planeMaterial = new THREE.MeshLambertMaterial({
color: 0x999999
});
var planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
scene.add(planeMesh);
planeMesh.rotateX(-Math.PI / 2);
planeMesh.position.y = -50;
// 2. 设置接收阴影的投影面
planeMesh.receiveShadow = true;
// 环境光
var ambient = new THREE.AmbientLight(0x444444);
scene.add(ambient);
// 一、平行光
// var directionalLight = new THREE.DirectionalLight(0xffffff, 1);
// directionalLight.position.set(60, 100, 40);
// scene.add(directionalLight);
// 3. 设置用于计算阴影的光源对象
// directionalLight.castShadow = true;
// 4. 设置计算阴影的区域,最好刚好紧密包围在对象周围
// 计算阴影的区域过大:模糊 过小:看不到或显示不完整
// directionalLight.shadow.camera.near = 0.5;
// directionalLight.shadow.camera.far = 300;
// directionalLight.shadow.camera.left = -50;
// directionalLight.shadow.camera.right = 50;
// directionalLight.shadow.camera.top = 200;
// directionalLight.shadow.camera.bottom = -100;
// 设置mapSize属性可以使阴影更清晰,不那么模糊,但是会消耗更多的性能,建议设置shadow.camera
// directionalLight.shadow.mapSize.set(1024,1024)
// 二、聚光光源
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(50, 90, 50);
spotLight.angle = Math.PI /6
scene.add(spotLight);
// 3. 设置用于计算阴影的光源对象
spotLight.castShadow = true;
// 4. 设置计算阴影的区域,注意包裹对象的周围
spotLight.shadow.camera.near = 1;
spotLight.shadow.camera.far = 300;
spotLight.shadow.camera.fov = 20;
// 聚光光源辅助显示
var spotLightHelper = new THREE.SpotLightHelper(spotLight);
scene.add(spotLightHelper);
// 相机
var width = window.innerWidth;
var height = window.innerHeight;
var k = width / height;
var s = 150;
var camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
camera.position.set(200, 300, 200);
camera.lookAt(scene.position);
// 渲染器
var renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
renderer.setClearColor(0xb9d3ff, 1);
document.body.appendChild(renderer.domElement);
// 5. 设置渲染器,允许场景中使用阴影贴图
renderer.shadowMap.enabled = true;
function render() {
renderer.render(scene, camera);
}
render();
var controls = new THREE.OrbitControls(camera,renderer.domElement);
controls.addEventListener('change', render);
3. 基类Light和Object3D
查看文档SpotLight、DirectionalLight、环境光AmbientLight等光源对象都有一个共同的基类Light,光源Light也有一个基类Object3D。也就是说Threejs环境光、点光源等子类光源可以继承Light和Object3D两个父类的属性和方法。
- 继承基类Light的属性:光源颜色
.color和光源强度.intensity
// 环境光:颜色设置为`0xffffff`,强度系数设置为0.5
var ambient = new THREE.AmbientLight(0xffffff,0.5); scene.add(ambient);
// 等同于
var ambient = new THREE.AmbientLight(0xffffff); // 设置光照颜色
ambient.intensity = 0.5; // 设置光照强度属性
- 继承Light的基类Object3D的属性:光源位置
.position
var point = new THREE.PointLight(0xffffff);//点光源
point.position.set(400, 200, 300); // 设置点光源位置
scene.add(point); // 继承父类Object3D
六、层级模型、树结构
1. 组对象Group、层级模型
层级模型:
- 根节点 scene
- 中间节点 group1
- 叶子节点 baseMesh
父对象group进行旋转、缩放、平移变换,子对象同样跟着变换
var geometry = new THREE.BoxGeometry(20, 20, 20);
var material = new THREE.MeshLambertMaterial({
color: 0x0000ff
});
// 网格模型mesh沿着x轴方向阵列
var group1 = new THREE.Group();
// 共享材质和几何体数据,批量创建mesh
var baseMesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
for (let i = 0; i < 10; i++) {
var mesh = baseMesh.clone()
mesh.translateX(i * 25); // 平移该网格模型
group1.add(mesh); // 把网格模型插入到组group1中
}
// group1沿着y轴方向阵列
for (let i = 0; i < 10; i++) {
var newGroup = group1.clone(); // 克隆组group1
newGroup.translateY(i * 25); //沿着z轴平移
scene.add(newGroup); //场景中插入组group1克隆的对象
}
console.log(scene)
子对象.children
Threejs场景对象Scene、组对象Group都有一个子对象属性.children,通过该属性可以访问父对象的子对象。
.add()方法给父对象添加子对象。可以插入一个/多个子对象。.remove()方法是删除父对象中的子对象。同样可以插入一个/多个子对象。
2. 对象节点命名、查找、遍历
-
对象节点属性
id唯一的,系统自动分配,id号和创建对象的顺序有关name字符串,手动命名,可能重名
var headMesh = sphereMesh(10, 0, 0, 0); headMesh.name = "脑壳" var leftEyeMesh = sphereMesh(1, 8, 5, 4); leftEyeMesh.name = "左眼" var rightEyeMesh = sphereMesh(1, 8, 5, -4); rightEyeMesh.name = "右眼" var headGroup = new THREE.Group(); headGroup.name = "头部"type构造函数类型children子节点数组parent父节点
-
递归遍历方法
.traverse()
// 递归遍历场景对象scene obj:每次遍历的对象
scene.traverse(function(obj) {
if (obj.type === "Group") {
console.log(obj.name);
}
if (obj.type === "Mesh") {
console.log(' ' + obj.name);
obj.material.color.set(0xffff00);
}
if (obj.name === "左眼" | obj.name === "右眼") {
obj.material.color.set(0x000000)
}
console.log(obj.id); // 打印id属性
console.log(obj.parent); // 打印该对象的父对象
console.log(obj.children); // 打印该对象的子对象
})
-
查找某个具体的对象节点
.getObjectByName(name)遍历查找对象的子对象,返回name对应的对象(重名返回第一个)
var nameNode = scene.getObjectByName ( "左腿" ); nameNode.material.color.set(0xff0000);.getObjectById(id)遍历查找对象的子对象,并返回id对应的对象
var idNode = scene.getObjectById ( 4 ); console.log(idNode);
3. 本地位置坐标、世界位置坐标
访问模型的位置属性.position获得模型在本地坐标系或者说模型坐标系下的三维坐标,通过模型的.getWorldPosition()方法获得该模型在世界坐标下的三维坐标。
网格模型的世界坐标是 网格模型的位置属性.position 和 网格模型父对象group的位置属性.position 的累加。
.position本地坐标.scale本地缩放参数.getWorldPosition()返回世界坐标.getWorldScale()获得世界缩放参数
var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
mesh.position.set(50, 0, 0)
var group = new THREE.Group();
group.add(mesh); //网格模型添加到组中
group.position.set(50, 0, 0)
scene.add(group); //组添加到场景中
/* 获得世界坐标 */
// 该语句默认在threejs渲染的过程中执行 如果想获得世界矩阵属性、世界位置属性等属性,需要手动更新
scene.updateMatrixWorld(true);
// 声明一个三维向量用来保存世界坐标
var worldPosition = new THREE.Vector3();
// 执行getWorldPosition方法把模型的世界坐标保存到参数worldPosition中
mesh.getWorldPosition(worldPosition)
// mesh的世界坐标是mesh位置属性`.position`和mesh父对象group位置属性`.position`的累加
console.log('世界坐标', worldPosition); // {x: 100, y: 0, z: 0}
console.log('本地坐标', mesh.position);// {x: 50, y: 0, z: 0}
七、几何体对象、曲线、三维建模
1. 常见几何体和曲线API介绍
- 几何体:
几何体本质上就是threejs生成顶点的算法,所有几何体的基类分为Geometry和BufferGeometry两大类,两类几何体直接可以相互转化。
- 曲线:
曲线和几何体同样本质上都是用来生成顶点的算法,曲线主要是按照一定的规则生成一系列沿着某条轨迹线分布的顶点。
圆弧线 ArcCurve
new THREE.ArcCurve( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise )
aX,aY圆弧圆心坐标aRadius圆弧半径aStartAngle,aEndAngle起始角度aClockwise是否顺时针绘制,默认值为false
绘制圆弧步骤:
- 声明几何体对象Geometry/缓冲几何体
var geometry = new THREE.Geometry();
// var geometry = new THREE.BufferGeometry();
- 创建圆弧
// 参数:0, 0圆弧坐标原点x,y 100:圆弧半径 0, 2 * Math.PI:圆弧起始角度
var arc = new THREE.ArcCurve(0, 0, 100, 0, 2 * Math.PI);
- 获取圆弧绘制顶点
.getPoints()
// getPoints是基类Curve的方法,返回一个vector2/vector3对象作为元素组成的数组
// 将曲线等分为几部分,每隔一定距离取一个顶点
var points = arc.getPoints(50); // 分段数50,返回51个顶点
- 将圆弧绘制顶点作为几何体的顶点数据
.setFromPoints()
// setFromPoints方法从points中提取数据改变几何体的顶点属性
// 如果几何体是Geometry,改变的是.vertices
// 如果几何体是BufferGeometry,改变的是.attributes.position
geometry.setFromPoints(points);
- 绘制几何体(圆弧)
var material = new THREE.LineBasicMaterial({
color: 0x000000
});
var line = new THREE.Line(geometry, material);
scene.add(line);
- 修改圆弧起始角度,绘制弧线
var arc = new THREE.ArcCurve(0, 0, 100, Math.PI, 2 * Math.PI);
直线 LineCurve
- 三维直线
LineCurve3
var geometry = new THREE.Geometry();
var p1 = new THREE.Vector3(50, 0, 0); //顶点1坐标
var p2 = new THREE.Vector3(0, 70, 0); //顶点2坐标
// 三维直线LineCurve3
var LineCurve = new THREE.LineCurve3(p1, p2);
var pointArr = LineCurve.getPoints(10); // 10分段,共11个点
geometry.setFromPoints(pointArr);
var material = new THREE.LineBasicMaterial({
color: 0xffff00,
});
var line = new THREE.Line(geometry, material);
scene.add(line); //线条对象添加到场景中
- 二维直线
LineCurve
var geometry = new THREE.Geometry();
// 二维直线LineCurve
var LineCurve = new THREE.LineCurve(new THREE.Vector2(50, 0), new THREE.Vector2(0, 70));
var pointArr = LineCurve.getPoints(10); // 10分段,共11个点
geometry.setFromPoints(pointArr);
var material = new THREE.LineBasicMaterial({
color: 0xffff00,
});
var line = new THREE.Line(geometry, material);
scene.add(line);
3. 样条曲线、贝塞尔曲线
规则的曲线比如圆、椭圆、抛物线都可以用一个函数去描述,对于不规则的曲线无法使用一个特定的函数去描述,这也就是样条曲线和贝塞尔曲线出现的原因。
- 三维样条曲线
CatmullRomCurve3:经过一系列点创建平滑的样条曲线
/* 在三维空间中设置5个顶点,输入三维样条曲线CatmullRomCurve3作为参数,返回更多个顶点,通过返回的顶点数据,构建一个几何体,通过`Line`可以绘制出来一条沿着5个顶点的光滑样条曲线。 */
var geometry = new THREE.Geometry(); //声明一个几何体对象Geometry
// 三维样条曲线 Catmull-Rom算法
var curve = new THREE.CatmullRomCurve3([
new THREE.Vector3(-50, 20, 90),
new THREE.Vector3(-10, 40, 40),
new THREE.Vector3(0, 0, 0),
new THREE.Vector3(60, -60, 0),
new THREE.Vector3(70, 0, 80)
]);
var points = curve.getPoints(100);
geometry.setFromPoints(points);
var material = new THREE.LineBasicMaterial({
color: 0x000000
});
var line = new THREE.Line(geometry, material);
scene.add(line);
- 二次贝塞尔曲线:起点、终点和1个控制点定义
- 2D:
QuadraticBezierCurveVector2 - 3D:
QuadraticBezierCurve3Vector3
- 2D:
var geometry = new THREE.Geometry(); //声明一个几何体对象Geometry
var p1 = new THREE.Vector3(-80, 0, 0);
var p2 = new THREE.Vector3(20, 100, 0);
var p3 = new THREE.Vector3(80, 0, 0);
// 三维二次贝赛尔曲线
var curve = new THREE.QuadraticBezierCurve3(p1, p2, p3);
var points = curve.getPoints(100);
geometry.setFromPoints(points);
var material = new THREE.LineBasicMaterial({
color: 0x000000
});
var line = new THREE.Line(geometry, material);
scene.add(line);
// 辅助线/点
var geometry2 = new THREE.Geometry();
geometry2.vertices.push(p1, p2, p3)
var material2 = new THREE.PointsMaterial({
color: 0xff00ff,
size: 10,
});
var points = new THREE.Points(geometry2, material2);
scene.add(points); // 辅助点
scene.add(new THREE.Line(geometry2, material2)); // 辅助线
- 三次贝塞尔曲线:起点、终点和2个控制点定义
- 2D:
CubicBezierCurve - 3D:
CubicBezierCurve3
- 2D:
var geometry = new THREE.Geometry();
var p1 = new THREE.Vector3(-80, 0, 0);
var p2 = new THREE.Vector3(-40, 100, 0);
var p3 = new THREE.Vector3(40, 100, 0);
var p4 = new THREE.Vector3(80, 0, 0);
// 三维三次贝赛尔曲线
var curve = new THREE.CubicBezierCurve3(p1, p2, p3, p4);
var points = curve.getPoints(100);
geometry.setFromPoints(points);
var material = new THREE.LineBasicMaterial({
color: 0x000000
});
var line = new THREE.Line(geometry, material);
scene.add(line);
设置控制点相对应:
var p3 = new THREE.Vector3(40, -100, 0);
4. 多个线条组合曲线CurvePath
通过组合曲线CurvePath可以把多个圆弧线、样条曲线、直线等多个曲线(基类Curve)合并成一个曲线。
var geometry = new THREE.Geometry(); //声明一个几何体对象Geometry
// 绘制一个U型轮廓
var R = 80; // 圆弧半径
var arc = new THREE.ArcCurve(0, 0, R, 0, Math.PI, true);
// 半圆弧的一个端点作为直线的一个端点
var line1 = new THREE.LineCurve(new THREE.Vector2(R, 200, 0), new THREE.Vector2(R, 0, 0));
var line2 = new THREE.LineCurve(new THREE.Vector2(-R, 0, 0), new THREE.Vector2(-R, 200, 0));
// 创建组合曲线对象CurvePath
var CurvePath = new THREE.CurvePath();
// 把多个线条插入到CurvePath中
CurvePath.curves.push(line1, arc, line2);
var points = CurvePath.getPoints(200); // 分段数200
geometry.setFromPoints(points); // 设置几何体顶点
var material = new THREE.LineBasicMaterial({
color: 0x000000
});
var line = new THREE.Line(geometry, material);
scene.add(line);
5. 曲线路径管道 TubeGeometry
TubeGeometry的功能就是通过一条曲线的顶点生成一个管道几何体geometry的顶点数据、face3数据。它的本质就是以曲线上顶点为基准,生成一系列曲线等径分布的顶点数据。
TubeGeometry(path, tubularSegments, radius, radiusSegments, closed)
path扫描路径,基类Curve的路径构造函数tubularSegments管道轨迹细分数,默认64radius管道半径,默认1radiusSegments管道截面圆弧细分数,默认8closed管道是否闭合
//创建管道成型的路径(3D样条曲线)
var path = new THREE.CatmullRomCurve3([
new THREE.Vector3(-10, -50, -50),
new THREE.Vector3(10, 0, 0),
new THREE.Vector3(8, 50, 50),
new THREE.Vector3(-5, 0, 100)
]);
// path:路径 40:管道轨迹细分数 5:管道半径 25:管道截面圆弧细分数
var geometry = new THREE.TubeGeometry(path, 40, 5, 25);
var material = new THREE.MeshPhongMaterial({
color: 0xE6A23C,
side: THREE.DoubleSide //两面可见
});
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
线条模式渲染,查看细分数:
// path:路径 10:管道轨迹细分数 5:管道半径 5:管道截面圆弧细分数
var geometry = new THREE.TubeGeometry(path, 10, 5, 5);
...
material.wireframe = true;
...
管道轨迹细分数:
管道截面圆弧细分数:
多段曲线路径创建生成管道 curvePath.curves
// 创建多段线条的顶点数据
var p1 = new THREE.Vector3(-85.35, -35.36)
var p2 = new THREE.Vector3(-50, 0, 0);
var p3 = new THREE.Vector3(0, 50, 0);
var p4 = new THREE.Vector3(50, 0, 0);
var p5 = new THREE.Vector3(85.35, -35.36);
// 创建线条一:直线
let line1 = new THREE.LineCurve3(p1,p2);
// 重建线条2:三维样条曲线
var curve = new THREE.CatmullRomCurve3([p2, p3, p4]);
// 创建线条3:直线
let line2 = new THREE.LineCurve3(p4,p5);
var curvePath = new THREE.CurvePath();// 创建CurvePath对象
curvePath.curves.push(line1, curve, line2);// 插入多段线条
//通过多段曲线路径创建生成管道
var geometry = new THREE.TubeGeometry(curvePath, 100, 5, 25, false);
var material = new THREE.MeshPhongMaterial({
color: 0xE6A23C,
side: THREE.DoubleSide,//双面可见
});
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
6. 旋转成型 LatheGeometry
一组顶点(Vector2)以y轴为旋转轴进行旋转生成旋转轮廓。
顶点数据可以通过二维向量对象
Vector2定义,也可以通过3D曲线或2D线条轮廓生成。
LatheGeometry(points, segments, phiStart, phiLength)
pointsVector2表示的坐标数据组成的数组segments圆周方向细分数,默认12phiStart开始角度,默认0phiLength旋转角度,默认2π
// 三个顶点构成的轮廓(理解为两条相连的直线段)
var points = [
new THREE.Vector2(50,60),
new THREE.Vector2(25,0),
new THREE.Vector2(50,-60)
];
// 30:旋转方向细分数 0,2*Math.PI:旋转起始角度设置
var geometry = new THREE.LatheGeometry(points,30,0,2*Math.PI);
var material=new THREE.MeshPhongMaterial({
color:0x0000ff,
side:THREE.DoubleSide
});
// material.wireframe = true;//线条模式渲染(查看细分数)
var mesh=new THREE.Mesh(geometry,material);
scene.add(mesh);
样条曲线插值计算生成旋转轮廓
var points = [
new THREE.Vector2(50,60),
new THREE.Vector2(25,0),
new THREE.Vector2(50,-60)
];
// SplineCurve:二维样条曲线
var splineCurve = new THREE.SplineCurve(points);
var splinePoints = splineCurve.getPoints(50); // 分段数50,返回51个顶点
var geometry = new THREE.LatheGeometry(splinePoints,30);
...
7. Shape对象和轮廓填充
Shape绘制二维形状- 内轮廓
.holes属性(Array)
- 内轮廓
ShapeGeometry轮廓填充:根据轮廓的顶点使用三角面Face3自动填充中间区域
var points = [
new THREE.Vector2(-50, -50),
new THREE.Vector2(-60, 0),
new THREE.Vector2(0, 50),
new THREE.Vector2(60, 0),
new THREE.Vector2(50, -50),
new THREE.Vector2(-50, -50),
]
// 通过顶点定义轮廓
var shape = new THREE.Shape(points);
// 所谓填充:ShapeGeometry算法利用顶点计算出三角面face3数据填充轮廓
var geometry = new THREE.ShapeGeometry(shape, 25);
var material = new THREE.MeshPhongMaterial({
color: 0x0000ff,
side: THREE.DoubleSide, //两面可见
});
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
通过shpae基类path的方法绘制轮廓
// 通过shpae基类path的方法绘制轮廓(本质也是生成顶点)
var shape = new THREE.Shape();
// shape.absarc(0,0,100,0,2*Math.PI);//圆弧轮廓
// 四条直线绘制一个矩形轮廓
shape.moveTo(0,0);//起点
shape.lineTo(0,100);//第2点
shape.lineTo(100,100);//第3点
shape.lineTo(100,0);//第4点
shape.lineTo(0,0);//第5点
var geometry = new THREE.ShapeGeometry(shape, 25);
var material = new THREE.MeshPhongMaterial({
color: 0x0000ff,
side: THREE.DoubleSide, //两面可见
});
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
shape外轮廓和内轮廓
shape可以用来绘制外轮廓,也可以用来绘制内轮廓,ShapeGeometry会使用三角形自动填充shape内轮廓和外轮廓中间的中部。
下面给出了几个通过shape绘制的轮廓图案。
- u形面
// 圆弧与直线连接
var shape = new THREE.Shape(); //Shape对象
var R = 50;
// 绘制一个半径为R、圆心坐标(0, 0)的半圆弧
shape.absarc(0, 0, R, 0, Math.PI);
//从圆弧的一个端点(-R, 0)到(-R, -200)绘制一条直线
shape.lineTo(-R, -200);
// 绘制一个半径为R、圆心坐标(0, -200)的半圆弧
shape.absarc(0, -200, R, Math.PI, 2 * Math.PI);
//从圆弧的一个端点(R, -200)到(-R, -200)绘制一条直线
shape.lineTo(R, 0);
var geometry = new THREE.ShapeGeometry(shape, 30);
...
- 圆形挖孔
// 一个外轮廓圆弧嵌套三个内圆弧轮廓
var shape = new THREE.Shape(); //Shape对象
//外轮廓
shape.arc(0, 0, 100, 0, 2 * Math.PI);
// 内轮廓1
var path1 = new THREE.Path();
path1.arc(0, 0, 40, 0, 2 * Math.PI);
// 内轮廓2
var path2 = new THREE.Path();
path2.arc(80, 0, 10, 0, 2 * Math.PI);
// 内轮廓3
var path3 = new THREE.Path();
path3.arc(-80, 0, 10, 0, 2 * Math.PI);
//三个内轮廓分别插入到holes属性中
shape.holes.push(path1, path2, path3);
var geometry = new THREE.ShapeGeometry(shape, 30);
...
多个轮廓同时填充
// 轮廓对象1
var shape=new THREE.Shape();
shape.arc(-50,0,30,0,2*Math.PI);
// 轮廓对象2
var shape2=new THREE.Shape();
shape2.arc(50,0,30,0,2*Math.PI);
// 轮廓对象3
var shape3=new THREE.Shape();
shape3.arc(0,50,30,0,2*Math.PI);
// 多个shape作为元素组成数组,每一个shpae可以理解为一个要填充的轮廓
var geometry = new THREE.ShapeGeometry([shape,shape2,shape3], 30);
var material = new THREE.MeshPhongMaterial({
color: 0x0000ff,
side: THREE.DoubleSide, //两面可见
// wireframe: true,
}); //材质对象
var mesh = new THREE.Mesh(geometry, material); //网格模型对象
scene.add(mesh);
8. 拉伸扫描成型 ExtrudeGeometry
构造函数ExtrudeGeometry和ShapeGeometry一样是利用Shape对象生成几何体对象,区别在于ExtrudeGeometry可以利用2D轮廓生成3D模型。
new THREE.ExtrudeGeometry(shape, object)
shape拉伸对象(二维轮廓),多个轮廓可以用数组。object拉伸参数amount拉伸长度,默认100bevelEnabled是否使用倒角bevelSegments倒角细分数,默认3bevelThickness倒角尺寸(经向)curveSegments拉伸轮廓细分数steps拉伸方向细分数extrudePath扫描路径THREE.CurvePath,默认Z轴方向material前后面材质索引号extrudeMaterial拉伸面、倒角面材质索引号bevelSize倒角尺寸(拉伸方向)
拉伸
var shape = new THREE.Shape();
// shape.absarc(50,50,40,0,2*Math.PI);//圆弧
shape.moveTo(0, 0);
shape.lineTo(0, 100);
shape.lineTo(100, 100);
shape.lineTo(100, 0);
shape.lineTo(0, 0);
var geometry = new THREE.ExtrudeGeometry( //拉伸造型
shape, //二维轮廓
//拉伸参数
{
amount: 120, //拉伸长度
// curveSegments: 35, //拉伸轮廓细分数
// steps: 12, //拉伸方向的细分数
// bevelEnabled: false, //无倒角
// bevelSegments:1,//倒直角:设置为1 倒圆角:越大越光滑
// bevelThickness: 30,//拉伸方向尺寸
// bevelSize: 4,//径向尺寸
}
);
扫描
对于扫描而言不需要定义amount属性设置拉伸距离,设置扫描路径即可(属性extrudePath)。
var shape = new THREE.Shape();
/**四条直线绘制一个矩形轮廓*/
shape.moveTo(0,0);//起点
shape.lineTo(0,10);//第2点
shape.lineTo(10,10);//第3点
shape.lineTo(10,0);//第4点
shape.lineTo(0,0);//第5点
/**创建轮廓的扫描轨迹(3D样条曲线)*/
var curve = new THREE.SplineCurve3([
new THREE.Vector3( -10, -50, -50 ),
new THREE.Vector3( 10, 0, 0 ),
new THREE.Vector3( 8, 50, 50 ),
new THREE.Vector3( -5, 0, 100)
]);
var geometry = new THREE.ExtrudeGeometry(//拉伸造型
shape,//二维轮廓
//拉伸参数
{
bevelEnabled:false,//无倒角
extrudePath:curve,//选择扫描轨迹
steps:50//沿着路径细分数
}
);