模型 Geometry
精灵(Sprite)
精灵是一个总是面朝着摄像机的方形平面,不会投射任何阴影。
精灵的构造函数只接收一个SpriteMaterial
材料对象,其通常使用半透明的纹理。
var spriteMap = new THREE.TextureLoader().load( "sprite.png" );
var spriteMaterial = new THREE.SpriteMaterial( { map: spriteMap, color: 0xffffff } );
var sprite = new THREE.Sprite( spriteMaterial );
scene.add( sprite );
点(Points)
Points( geometry : Geometry, material : PointsMaterial )
Points是点(默认为一方形平面)的集合,其中每个点都总是面朝着摄像机。相较于 THREE.Sprite
,THREE.Points
更适合粒子数量多的情况(因只需要维护单个实例)。
- 通过依次定义点的坐标生成点阵
var geometry = new THREE.Geometry()
var material = new THREE.PointsMaterial({
size: 4,
vertexColors: true, // 是否为几何体的每个顶点应用颜色,默认值是为所有面应用材质的颜色
color: 0xffffff
})
for (var x = -5; x < 5; x++) {
for (var y = -5; y < 5; y++) {
var particle = new THREE.Vector3(x * 10, y * 10, 0)
geometry.vertices.push(particle)
geometry.colors.push(new THREE.Color(Math.random() * 0xffffff))
}
}
var points = new THREE.Points(geometry, material)
scene.add(points)
- 通过传入现成的几何体生成点阵
var points;
var loader = new THREE.OBJLoader();
loader.load('https://res.cloudinary.com/dgnx97ptu/raw/upload/v1506569170/chahu_xlo7pg.obj', function (loadedMesh) {
var material = new THREE.PointsMaterial({
color: 0xffffff,
size: 0.4,
// 设置元素与背景的融合模式
blending: THREE.AdditiveBlending,
// 用于去除纹理的黑色背景,关于 depthTest 和 depthWrite 的详细解释,请查看https://stackoverflow.com/questions/37647853/three-js-depthwrite-vs-depthtest-for-transparent-canvas-texture-map-on-three-p
depthTest: false
})
loadedMesh.children.forEach(function (child) {
points = new THREE.Points(child.geometry, material)
scene.add(points);
})
})
var cubeGeometry = new THREE.BoxGeometry(10, 10, 10);
points = new THREE.Points(cubeGeometry)
scene.add(points);
线
连续点直接连接成线,线的材质只有实线LineBasicMaterial
和虚线LineDashedMaterial
。
var material = new THREE.LineBasicMaterial({
color: 0xffffff,
linewidth: 1,
linecap: 'round', //ignored by WebGLRenderer
linejoin: 'round' //ignored by WebGLRenderer
});
var geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3( -10, 0, 0) );
geometry.vertices.push(new THREE.Vector3( 0, 10, 0) );
geometry.vertices.push(new THREE.Vector3( 10, 0, 0) );
var line = new THREE.Line( geometry, material );
scene.add( line );
renderer.render( scene, camera );
也可以从路径Path对象中获取点以连接成线
var path = new THREE.Path();
path.lineTo( 0, 0.8 );
path.quadraticCurveTo( 0, 1, 0.2, 1 );
path.lineTo( 1, 1 );
var points = path.getPoints();//返回Vector2组成的数组
var geometry = new THREE.BufferGeometry().setFromPoints( points );
var line = new THREE.Line( geometry );
scene.add( line );
文字
- 最简单的文字添加方式是直接用html定位覆盖在canvas上
- 将文字绘制到画布中,并通过
CanvasTexture
将其用作纹理
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
ctx.fillStyle = "#0000ff";
ctx.font = "10px Arial";
ctx.fillText(`hello`, 0, 10);
let texture = new THREE.CanvasTexture(canvas);
let material = new THREE.MeshBasicMaterial({ map: texture,alphaTest: 0.1, transparent: true, opacity:1});
let geometry = new THREE.BoxGeometry(100, 100, 100);
var mesh = new THREE.Mesh(geometry,material);
this.scene.add(mesh);
- 在3D软件里创建模型,并导出给three.js
- BMFonts (位图字体) ,将字形批处理为单个
BufferGeometry
。 - 通过
new THREE.TextGeometry( text, parameters );
引用Typeface字体文件
导入3D模型
推荐使用 glTF(gl传输格式)。.GLB
和.GLTF
是这种格式的两种不同版本, 都可以被很好地支持。
<script src="GLTFLoader.js"></script>
var loader = new THREE.GLTFLoader();
loader.load( 'path/to/model.glb', function ( gltf ) {
scene.add( gltf.scene );
}, undefined, function ( error ) {
console.error( error );
} );
其余FBX
、Collada
以及OBJ
也可以支持,需要加载对应的载入JS。
常见几何模型
长方体 BoxGeometry(width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1)
长方形 PlaneGeometry(width = 1, height = 1, widthSegments = 1, heightSegments = 1)
-
球 SphereGeometry(radius = 1, widthSegments = 8, heightSegments = 6, phiStart = 0, phiLength = 2 * Math.PI, thetaStart = 0, thetaLength = Math.PI)
- phiLength小于默认值时会裂开(类似吃豆人)
- thetaLength小于默认值时会削去一部分,如Math.PI/2时类似一个碗
扇形 CircleGeometry(radius = 1, segments = 8, thetaStart = 0, thetaLength = 2 * Math.PI)
圆锥几何体(ConeGeometry)
圆柱几何体(CylinderGeometry)
四面几何体(TetrahedronGeometry)
八面几何体(OctahedronGeometry)
十二面几何体(DodecahedronGeometry)
挤压几何体(ExtrudeGeometry)
按照指定参数将一个二维图形(形状Shape)沿 z 轴拉伸出一个三维图形
var heartShape = new THREE.Shape();
heartShape.moveTo( 25, 25 );
heartShape.bezierCurveTo( 25, 25, 20, 0, 0, 0 );
heartShape.bezierCurveTo( 30, 0, 30, 35,30,35 );
heartShape.bezierCurveTo( 30, 55, 10, 77, 25, 95 );
heartShape.bezierCurveTo( 60, 77, 80, 55, 80, 35 );
heartShape.bezierCurveTo( 80, 35, 80, 0, 50, 0 );
heartShape.bezierCurveTo( 35, 0, 25, 25, 25, 25 );
var extrudeSettings = { amount: 8, bevelEnabled: true, bevelSegments: 2, steps: 2, bevelSize: 1, bevelThickness: 1 };
var geometry = new THREE.ExtrudeGeometry( heartShape, extrudeSettings );
var mesh = new THREE.Mesh( geometry, new THREE.MeshPhongMaterial() );
二十面几何体(IcosahedronGeometry)
车削几何体(LatheGeometry)
创建具有轴对称性的网格,比如花瓶。车削绕着Y轴来进行旋转。圆环形(RingGeometry)
圆环几何体(TorusGeometry)
通用属性、方法
- vertices
顶点位置信息(Vector3)数组,保存了模型中每个顶点的位置信息。
出于性能的考虑,Three.js 认为几何体在整个生命周期都不会更改,
如果要修改已经加入Mesh的geometry的顶点,verticesNeedUpdate
值需要被设置为true
,否则修改不会生效。
通过调用mergeVertices()
可以移除重复的顶点,并在各顶点间创建出连续的面。
var geomCockpit = new THREE.BoxGeometry(80, 50, 50, 1, 1, 1);
geomCockpit.vertices[4].y -= 10;
geomCockpit.vertices[4].z += 20;
geomCockpit.vertices[5].y -= 10;
geomCockpit.vertices[5].z -= 20;
geomCockpit.vertices[6].y += 30;
geomCockpit.vertices[6].z += 20;
geomCockpit.vertices[7].y += 30;
geomCockpit.vertices[7].z -= 20;
var cockpit = new THREE.Mesh(geomCockpit);
- face,为
Face3
(三角片面)实例组成的数组,具有如下属性:- a、b、c — 顶点ABC的索引
- normal — 矢量展示 Face3 的方向
- color — 面的颜色值,当材质的
vertexColors
值为THREE.FaceColors
时,该属性生效。 - materialIndex — 材质队列中与该面对应的材质的索引,默认为0。
- vertexNormals — 包含三个 vertex normals 的队列。
- vertexColors — 包含 3 个顶点各自颜色值(Color)的数组。当材质的
vertexColors
值为THREE.VertexColors
时,该属性生效。
通过face修改每个面的颜色或材料
for (let i = 0; i < mesh.geometry.faces.length; i++) {
let hex = Math.random() * 0xffffff;
mesh.geometry.faces[i].color.setHex(hex);
}
let material = new THREE.MeshBasicMaterial({
vertexColors: THREE.FaceColors
});
mesh.material = material
scene.add(mesh);
let mats = [];
for (var i = 0; i < geometry.faces.length; i++) {
let material = new THREE.MeshBasicMaterial({ color: new THREE.Color(Math.random() * 0xffffff) });
geometry.faces[i].materialIndex = i;
mats.push(material);
}
let mesh = new THREE.Mesh(geometry, mats);
scene.add(mesh);
- .merge( Geometry, Matrix4, materialIndexOffsetInteger)
调用者(几何体实例)将一个网格中的几何体合并到自身。
var geometry = new THREE.Geometry();
mesh.updateMatrix();
geometry.merge(mesh.geometry, mesh.matrix);
材质 Material
表面各可视属性的结合,包括色彩、纹理、光滑度、透明度、反射率、折射率、发光度等。
材质会反射符合自身颜色的色光,当没有任何光源时,不管什么颜色的材质都显示为黑色。
通用属性/方法
key | 作用 | 默认值 |
---|---|---|
transparent | 是否允许纹理具有透明度 | false |
opacity | 透明度 | 1 |
alphaTest | 纹理不透明度低于该值的位置会渲染为透明 | 0 |
side | 要渲染的面,通常只渲染相机能看到的面,但有时如需看到透明材料的内部等,可以使用THREE.BackSide 和THREE.DoubleSide
|
THREE.FrontSide |
flatShading | ||
visible | 材质是否可见 | true |
fog | 材质是否受雾影响 | true |
vertexColors | 是否使用顶点着色。THREE.NoColors (将材质颜色应用到所有面)THREE.VertexColors (根据每个Face3的顶点颜色vertexColors 着色) THREE.FaceColors (根据每个Face3的color 值着色) |
THREE.NoColors |
polygonOffset | 是否使用多边形偏移 详见Z-Fighting |
false |
polygonOffsetFactor | 多边形偏移系数 | 0 |
polygonOffsetUnits | 多边形偏移单位 | 0 |
.clone ( ) : Material | 返回与此材质相同参数的新材质 | - |
.copy ( material) : Material | 将被传入材质的参数复制到此材质中 | - |
.dispose () : null | 处理材质。材质的纹理不会被处理。 | - |
基础网格材质(MeshBasicMaterial)
简单的平面/线框体,不受光照的影响。
Lambert网格材质(MeshLambertMaterial)
一种非光泽表面的材质,没有镜面高光。可以很好地模拟未经处理的木材或石材等,但不能模拟具有镜面高光的光泽表面(例如涂漆木材)。
Phong网格材质(MeshPhongMaterial)
一种用于具有镜面高光的光泽表面的材质。
阴影材质(ShadowMaterial)
此材质可以接收阴影,但在其他方面完全透明。
纹理 Texture
纹理就是贴图。将纹理以一定的规则映射到几何体的材质上,那么这个几何体就有纹理皮肤了。
THREE.Texture( image,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy, encoding)
- Image:这是一个图片类型,基本上通过
TextureLoader
来加载。(ImageUtils.loadTexture
已废弃)
var texture = new THREE.TextureLoader().load( "textures/water.jpg" );
参数
- mapping:表示图像将如何应用到物体上。默认值是
THREE.UVMapping
对象类型, 即UV坐标将被用于纹理映射。 - wrapS:表示x轴纹理的铺设方式。
- THREE.ClampToEdgeWrapping(默认值),单个纹理拉伸覆盖整个材料。
- THREE.RepeatWrapping,重复
- THREE.MirroredRepeatWrapping,重复且每次重复时镜像
- wrapT:表示y轴纹理的铺设方式。可选值同
wrapS
。 - magFilter:当一个纹素覆盖大于一个像素时,贴图将如何采样。
-
THREE.LinearFilter
(默认值), 它将获取四个最接近的纹素,并在他们之间进行双线性插值。 -
THREE.NearestFilter
,它将使用最接近的纹素的值。
-
- minFilter:当一个纹素覆盖小于一个像素时,贴图将如何采样。
- format:表示加载的图片的格式
- THREE.RGBAFormat(默认值),允许透明
- THREE.RGBFormat,在载入JPG图片时会自动设置为该值,无透明。
- type:表示存储纹理内存的字节格式,默认是
THREE.UnsignedByteType
。 - anisotropy:各向异性过滤,默认为1,通常为2的幂。值越大纹理越清晰但性能越差。
通用属性/方法
key | 作用 | 默认值 |
---|---|---|
offset | 0~1之间,表示纹理在单次重复时,从一开始将分别在U、V方向上偏移多少。 | (0,0) |
repeat | 纹理分别在U、V方向重复多少次。如大于1则对应的Wrap应当也被设为THREE.RepeatWrapping 或THREE.MirroredRepeatWrapping
|
(1,1) |
rotation | 纹理将逆时针转动的弧度 | 0 |
center | 旋转中心点,(0.5, 0.5)对应纹理的正中心,默认为左下角。 | (0,0) |
onUpdate | 纹理被更新后的回调函数 | null |
needsUpdate | 当纹理被使用时是否自动触发更新 | false |
.clone () : Texture | 拷贝纹理。注意这不是“深拷贝”,图像是共用的。 | - |
.dispose () : null | 处理纹理。 | - |
Canvas纹理(CanvasTexture)
从Canvas元素中创建纹理贴图。needsUpdate
直接为true。第一个入参为一个canvas dom对象。
视频纹理(VideoTexture)
创建一个使用视频来作为贴图的纹理对象。needsUpdate
直接为true。第一个入参为一个video dom对象。
网格物体 Mesh
new THREE.Mesh( geometry : Geometry, material : Material )
网格包含一个几何体以及作用在此几何体上的材质,其中material可以为单个或一个数组,如不传则为一个随机颜色的MeshBasicMaterial
。
Mesh实例具有geometry
和material
属性,直接对其进行修改并重新渲染即可体现在该物体上。
- geometry
来自构造函数的第一个入参 - material
由Material
基类或者其数组派生而来的材质实例,定义了物体的外观。默认值是一个具有随机颜色的MeshBasicMaterial
。 - receiveShadow
- castShadow
- position
- rotation
- renderOrder
- scale
为一个Vector3实例,也可以用mesh.scale.set(1,1,1)
设置
Object3D、Group
这两个类几乎是相同的,其目的是使得组中对象在语法上的结构更加清晰。
可以先将mesh加入Object3D(或Group)实例,然后再将该实例加入scene
var obj1 = new THREE.Object3D();
var obj2 = new THREE.Object3D();
var mesh = new THREE.Mesh(geometry);
obj1.add(mesh);
obj2.add(obj1);
scene.add(obj2);
碰撞检测
/**
* 功能:检测 movingCube 是否与数组 collideMeshList 中的元素发生了碰撞
*
*/
var originPoint = movingCube.position.clone();
for (var vertexIndex = 0; vertexIndex < movingCube.geometry.vertices.length; vertexIndex++) {
// 顶点原始坐标
var localVertex = movingCube.geometry.vertices[vertexIndex].clone();
// 顶点经过变换后的坐标
var globalVertex = localVertex.applyMatrix4(movingCube.matrix);
// 获得由中心指向顶点的向量
var directionVector = globalVertex.sub(movingCube.position);
// 将方向向量初始化
var ray = new THREE.Raycaster(originPoint, directionVector.clone().normalize());
// 检测射线与多个物体的相交情况
var collisionResults = ray.intersectObjects(collideMeshList);
// 如果返回结果不为空,且交点与射线起点的距离小于物体中心至顶点的距离,则发生了碰撞
if (collisionResults.length > 0 && collisionResults[0].distance < directionVector.length()) {
crash = true; // crash 是一个标记变量
}
}