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

模型 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。

常见几何模型
  • 长方体 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.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)

  • 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.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属性,直接对其进行修改并重新渲染即可体现在该物体上。

  • 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);

<span class="token comment">// 将方向向量初始化</span>
<span class="token keyword">var</span> ray <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">THREE<span class="token punctuation">.</span>Raycaster</span><span class="token punctuation">(</span>originPoint<span class="token punctuation">,</span> directionVector<span class="token punctuation">.</span><span class="token keyword">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">normalize</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 检测射线与多个物体的相交情况</span>
<span class="token keyword">var</span> collisionResults <span class="token operator">=</span> ray<span class="token punctuation">.</span><span class="token function">intersectObjects</span><span class="token punctuation">(</span>collideMeshList<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 如果返回结果不为空,且交点与射线起点的距离小于物体中心至顶点的距离,则发生了碰撞</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>collisionResults<span class="token punctuation">.</span>length <span class="token operator">&gt;</span> <span class="token number">0</span> <span class="token operator">&amp;&amp;</span> collisionResults<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>distance <span class="token operator">&lt;</span> directionVector<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    crash <span class="token operator">=</span> <span class="token boolean constant">true</span><span class="token punctuation">;</span>   <span class="token comment">// crash 是一个标记变量</span>
<span class="token punctuation">}</span>

}

转载:https://www.jianshu.com/p/1cbf0e75f176

猜你喜欢

转载自blog.csdn.net/bbsyi/article/details/121379676