Three.js (三) 模型、材质、纹理、网格

2020-03-05  本文已影响0人  李霖弢

模型 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.SpriteTHREE.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 );
文字
  1. 最简单的文字添加方式是直接用html定位覆盖在canvas上
  2. 将文字绘制到画布中,并通过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);
  1. 在3D软件里创建模型,并导出给three.js
  2. BMFonts (位图字体) ,将字形批处理为单个BufferGeometry
  3. 通过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 );
} );

其余FBXCollada以及OBJ也可以支持,需要加载对应的载入JS。

常见几何模型
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() );
通用属性、方法
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修改每个面的颜色或材料

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);
var geometry = new THREE.Geometry();
mesh.updateMatrix();
geometry.merge(mesh.geometry, mesh.matrix);

材质 Material

表面各可视属性的结合,包括色彩、纹理、光滑度、透明度、反射率、折射率、发光度等。
材质会反射符合自身颜色的色光,当没有任何光源时,不管什么颜色的材质都显示为黑色。

通用属性/方法
key 作用 默认值
transparent 是否允许纹理具有透明度 false
opacity 透明度 1
alphaTest 纹理不透明度低于该值的位置会渲染为透明 0
side 要渲染的面,通常只渲染相机能看到的面,但有时如需看到透明材料的内部等,可以使用THREE.BackSideTHREE.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)

var texture = new THREE.TextureLoader().load( "textures/water.jpg" );
参数
通用属性/方法
key 作用 默认值
offset 0~1之间,表示纹理在单次重复时,从一开始将分别在U、V方向上偏移多少。 (0,0)
repeat 纹理分别在U、V方向重复多少次。如大于1则对应的Wrap应当也被设为THREE.RepeatWrappingTHREE.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实例具有geometrymaterial属性,直接对其进行修改并重新渲染即可体现在该物体上。


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 是一个标记变量
    }
}
上一篇 下一篇

猜你喜欢

热点阅读