threeJs-基于原生WebGL封装运行的三维引擎

3/30/2021 内网穿透ddns

# 构建三维场景

three.js程序结构

场景 - 相机 - 渲染器

  • 场景 Scene
    • 网络模型 Mesh
      • 几何体 Geometry
        • 形状
        • 尺寸
      • 材质 Material
        • 颜色
        • 贴图
        • 透明度
    • 光照 Light
      • 颜色
      • 分类
        • 环境光
        • 点光源
        • 平行光
  • 相机 Camera
    • 位置
    • 视线方向
    • 投影方式
      • 投射投影 PerspectiveCamera
      • 正射投影 OrthographicCamera
  • 渲染器 Renderer
    • 渲染器创建 - WebGLRenderer()
    • 开始渲染 - .render(scene, camera)
    • domElement属性 - canvas对象

场景 THREE.Scene()

相机 THREE.OrthographicCamera()

渲染器 THREE.WebGLRenderer()

render.png

动画旋转

mesh.rotateY(0.01);//每次绕y轴旋转0.01弧度

鼠标操作三维场景

  • 缩放:滚动—鼠标中键
  • 旋转:拖动—鼠标左键
  • 平移:拖动—鼠标右键
var controls = new THREE.OrbitControls(camera,renderer.domElement);//创建控件对象
controls.addEventListener('change', render);//监听鼠标、键盘事件
1
2

# 3D场景中插入新的几何体

几何体代码:

//长方体 参数:长,宽,高
var geometry = new THREE.BoxGeometry(100, 100, 100);
// 球体 参数:半径60  经纬度细分数40,40
var geometry = new THREE.SphereGeometry(60, 40, 40);
// 圆柱  参数:圆柱面顶部、底部直径50,50   高度100  圆周分段数
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);
1
2
3
4
5
6
7
8
9
10
11
12

同时绘制多个:

// 立方体网格模型
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
scene.add(mesh3); 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 设置材质效果

var sphereMaterial=new THREE.MeshLambertMaterial({
    color:0xff0000,
    opacity:0.7,
    transparent:true
});//材质对象
1
2
3
4
5

# 材质常见属性

材质属性 简介
color 材质颜色,比如蓝色0x0000ff
wireframe 将几何图形渲染为线框。 默认值为false
opacity 透明度设置,0表示完全透明,1表示完全不透明
transparent 是否开启透明,默认false

# Threejs加载外部模型文件

由3dmx、blender、substence、Solidworks等软件创建好的三维模型文件。

  • 加载器

    • STL
      • STLLoader
    • obj与mtl
      • OBJLoader
      • MTLLoader
    • FBX
      • FBXLoader
    • Collada
      • ColladaLoader
    • PLY
      • PLYLoader
    • GLTF
      • GLTFLoader

    # Three.js模型数据导入导出

导出几何体信息:

var geometry = new THREE.BoxGeometry(100, 100, 100);
// 控制台查看立方体数据
console.log(geometry);
// 控制台查看geometry.toJSON()结果
console.log(geometry.toJSON());
// JSON对象转化为字符串
console.log(JSON.stringify(geometry.toJSON()));
// JSON.stringify()方法内部会自动调用参数的toJSON()方法
console.log(JSON.stringify(geometry));
1
2
3
4
5
6
7
8
9

导出材质信息:

var material = new THREE.MeshLambertMaterial({
  color: 0x0000ff,
}); //材质对象Material
console.log(material);
console.log(material.toJSON());
console.log(JSON.stringify(material));
1
2
3
4
5
6

导出场景scene信息:

var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
scene.add(mesh); //网格模型添加到场景中
console.log(scene);
console.log(scene.toJSON());
1
2
3
4

# 自定义模型加载器文件

实际开发中,加载一种特定格式的模型文件,Threejs在three.js-master\examples\js\loaders目录下会提供一系列的加载器,这些加载器本质上都是解析模型文件的字符串,通过正则表达式提取相关的顶点、材质等信息转化为Threejs自身的类表示的对象。为了让大家更理解这些常见的加载器,课件中提供了一个源码案例,编写了一个非常非常简易的自定义加载器,然后使用自定义的加载器去加载一个文件,让大家明白这些加载器怎么来的。

// 如果编写通用的材质加载器需要枚举所有的材质,这里没有列举完
var typeAPI = {
  MeshLambertMaterial: THREE.MeshLambertMaterial,
  MeshBasicMaterial: THREE.MeshBasicMaterial,
  MeshPhongMaterial: THREE.MeshPhongMaterial,
  PointsMaterial: THREE.PointsMaterial,
}
// 创建一个文件加载器,该加载器是对异步加载的封装
var loader = new THREE.FileLoader();
loader.load('material.json', function(elem) {
  console.log(elem);// 查看加载返回的内容
  var obj = JSON.parse(elem);// 字符串转JSON对象
  console.log(obj);// 查看转化结果
  var geometry = new THREE.BoxGeometry(100, 100, 100);
  /**
   * 解析材质数据
   */
  // 根据type的值判断调用threejs的哪一个API
  var material = new typeAPI[obj.type]();
  // 从obj.color中提取颜色
  // 16711935对应颜色0xFF00FF  255对应颜色0x0000FF
  material.color.r = (obj.color >> 16 & 255) / 255; //获取颜色值R部分
  material.color.g = (obj.color >> 8 & 255) / 255; //获取颜色值G部分
  material.color.b = (obj.color & 255) / 255; //获取颜色值B部分

  var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
  scene.add(mesh); //网格模型添加到场景中
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 加载Three.js导出的模型数据

缓冲几何体数据加载器

/**
 *
 */
var loader = new THREE.BufferGeometryLoader();
loader.load('bufferGeometry.json',function (geometry) {
  // 控制台查看加载放回的threejs对象结构
  console.log(geometry);
  var material = new THREE.MeshLambertMaterial({
    color: 0x0000ff,
  }); //材质对象Material
  var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
  scene.add(mesh); //网格模型添加到场景中
})
1
2
3
4
5
6
7
8
9
10
11
12
13

网格模型Mesh加载,包含几何体Geometry和材质Material

var loader = new THREE.ObjectLoader();
loader.load('model.json',function (obj) {
  console.log(obj);
console.log(obj.type);
  obj.scale.set(100,100,100)
  scene.add(obj)
})
1
2
3
4
5
6
7

加载组Group对象,模型对象构成的树结构

loader.load('group.json', function(obj) {
  console.log(obj);
  console.log(obj.type);
  scene.add(obj)
})
1
2
3
4
5

加载场景对象,场景对象不仅包含模型,还包括光源对象

loader.load('scene.json',function (obj) {
  console.log(obj);
  console.log(obj.type);
  obj.scale.set(100,100,100)
  scene.add(obj)
})
1
2
3
4
5
6

# 加载.obj模型文件

使用三维软件导出.obj模型文件的时候,会同时导出一个材质文件.mtl.obj.stl文件包含的信息一样都是几何体顶点相关数据,材质文件.mtl包含的是模型的材质信息,比如颜色、贴图路径等。

加载.obj三维模型的时候,可以只加载.obj文件,然后借助three.js引擎自定义材质Material (opens new window),也可以同时加载.obj.mtl文件。

# 只加载obj文件

<!-- 引入obj模型加载库OBJLoader.js -->
<script src="../../three.js-master/examples/js/loaders/OBJLoader.js"></script>
/**
 * OBJ文件加载  只加载obj文件中的几何信息,不加载材质文件.mtl
 */
var loader = new THREE.OBJLoader();
// 没有材质文件,系统自动设置Phong网格材质
loader.load('./立方体/box.obj',function (obj) {
  // 控制台查看返回结构:包含一个网格模型Mesh的组Group
  console.log(obj);
  // 查看加载器生成的材质对象:MeshPhongMaterial
  console.log(obj.children[0].material);
  scene.add(obj);
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14

加载文件返回的对象插入场景中后,你也可以做一些自定的设置,比如缩放、居中等操作。

// 加载后的一些编辑操作
obj.children[0].scale.set(20,20,20);//网格模型缩放
obj.children[0].geometry.center();//网格模型的几何体居中
obj.children[0].material.color.set(0xff0000);//设置材质颜色
1
2
3
4

# 同时加载obj文件和mtl文件

mtl文件包含了模型的材质信息,比如模型颜色、透明度等信息,还有纹理贴图的路径,比如颜色贴图、法线贴图、高光贴图等等。

<!-- 引入obj模型加载库OBJLoader.js -->
<script src="../../three.js-master/examples/js/loaders/OBJLoader.js"></script>
<!-- 引入obj模型材质加载库MTLLoader.js -->
<script src="../../three.js-master/examples/js/loaders/MTLLoader.js"></script>

/**
 * OBJ和材质文件mtl加载
 */
var OBJLoader = new THREE.OBJLoader();//obj加载器
var MTLLoader = new THREE.MTLLoader();//材质文件加载器
MTLLoader.load('./立方体/box.mtl', function(materials) {
  // 返回一个包含材质的对象MaterialCreator
  console.log(materials);
  //obj的模型会和MaterialCreator包含的材质对应起来
  OBJLoader.setMaterials(materials);
  OBJLoader.load('./立方体/box.obj', function(obj) {
    console.log(obj);
    obj.scale.set(10, 10, 10); //放大obj组对象
    scene.add(obj);//返回的组对象插入场景中
  })
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 导出.obj.mtl的名称、路径问题

3dmax导出的obj和mtl模型文件有时候需要修改一下个别位置字符,比如.obj.mtl文件的名称可能是乱码mtllib �����.mtl.mtl文件中贴图的路径要设置正确,比如导出的是绝对路径还是相对路径,可以打开看下。

# .obj文件不包含信息

.obj文件不包含场景的相机Camera (opens new window)、光源Light (opens new window)等信息,不能导出骨骼动画、变形动画,如果希望导出光照信息、相机信息、骨骼动画信息、变形动画信息,可以选择.fbx.gltf等格式。

# Three.js全屏/局部渲染

通过Three.js发开Web3D应用的时候,渲染窗口可能是全屏效果占满web页面整个body区域,也可能是web页面上一个特定位置特定区域,比如渲染区域是一个特定位置、特定宽高的div元素所在区域。

# 全屏渲染

直接通过Three.js的WebGL渲染器WebGLRenderer (opens new window).setSize()方法设置渲染尺寸为浏览器body区域宽高度。

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);
//透视投影相机设置
// var camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000);

var renderer = new THREE.WebGLRenderer();
//设置渲染区域尺寸
renderer.setSize(width, height);
// body元素中插入Threejs渲染结果:canvas对象
document.body.appendChild(renderer.domElement);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 局部渲染

下面代码实现的功能是Threejs渲染区域尺寸是300x300px,渲染结果.domElement(Canvas元素)插入到一个div元素中。

<!-- 把Three.js渲染结果一个canvas元素插入到div中 -->
<div id="pos"></div>

<script>
// 注意相机参数设置
var width = 300; //窗口宽度
var height = 300; //窗口高度
var k = width / height; //窗口宽高比
var s = 150; //三维场景显示范围控制系数,系数越大,显示的范围越大
//正投影相机设置
var camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
//透视投影相机设置
// var camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000);
...
var renderer = new THREE.WebGLRenderer();
// 设置渲染区域尺寸,本质就是设置输出canvas的尺寸
renderer.setSize(300, 300);
// 把渲染器的渲染结果canvas对象插入到'pos'对应的div元素中
document.getElementById('pos').appendChild(renderer.domElement);

</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

可以通过渲染结果.domElement(Canvas元素)的父元素div来控制渲染区域在浏览器上的位置。

<!-- 控制div位置 绝对定位  距离上方100px  距离左侧30px -->
<div id="pos" style="position: absolute;left: 30px;top: 100px;"></div>
1
2

# .domElement属性

Three.js渲染器WebGLRenderer.domElement属性是Three.js执行渲染方法.render()的渲染结果,本质上就是一个HTML元素Canvas,如果你对WebGL稍微有些了解的话,更容易理解.domElement属性,如果通过原生WebGL渲染一个场景,需要手动创建一个Cnavas画布获得WebGL上下文,如果使用Three.js框架,Threejs系统会自动创建一个canvas对象,然后把渲染结果呈现在该Canvas画布上。

.domElement属性值是一个HTML元素canvas,那么如何使用 .domElement属性,就变成前端问题,你可以把.domElement表示的canvas画布插入到Web页面中任何一个HTML元素中。

# .setSize()方法

Threejs渲染器的.setSize()方法简单解释就是设置渲染结果的尺寸范围,本质上就是设置.domElement表示的canvas画布的宽高尺寸。

# 相机控件OrbitControls作用范围

如果渲染区域变化了,不仅仅要改变WebGL渲染器相关参数,如果使用的其它扩展库与Threejs渲染范围有关,比如OrbitControls.js,,也需要设置该控件的鼠标作用范围。通过OrbitControls创建一个相机控件对象的时候,默认情况下,在浏览器的窗口整个内容区域body发生鼠标事件都会旋转、平移或缩放三维场景,如果局部渲染,注意设置构造函数THREE.OrbitControls的参数2。

// 局部渲染设置OrbitControls空间作用范围
var controls = new THREE.OrbitControls(camera, renderer.domElement);
1
2
上次更新: 2024/09/27 10:53:32
最近更新
01
跨域的几种常见解决方案
04-03
02
react教程
03-01
03
前端抓包神器whistle
09-01