基础知识

image.png

  1. /**
  2. * 创建场景对象Scene
  3. */
  4. var scene = new THREE.Scene();
  5. /**
  6. * 创建网格模型
  7. */
  8. // var geometry = new THREE.SphereGeometry(60, 40, 40); //创建一个球体几何对象
  9. var geometry = new THREE.BoxGeometry(100, 100, 100); //创建一个立方体几何对象Geometry
  10. var material = new THREE.MeshLambertMaterial({
  11. color: 0x0000ff
  12. }); //材质对象Material
  13. var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
  14. scene.add(mesh); //网格模型添加到场景中
  15. /**
  16. * 光源设置
  17. */
  18. //点光源
  19. var point = new THREE.PointLight(0xffffff);
  20. point.position.set(400, 200, 300); //点光源位置
  21. scene.add(point); //点光源添加到场景中
  22. //环境光
  23. var ambient = new THREE.AmbientLight(0x444444);
  24. scene.add(ambient);
  25. // console.log(scene)
  26. // console.log(scene.children)
  27. /**
  28. * 相机设置
  29. */
  30. var width = window.innerWidth; //窗口宽度
  31. var height = window.innerHeight; //窗口高度
  32. var k = width / height; //窗口宽高比
  33. var s = 200; //三维场景显示范围控制系数,系数越大,显示的范围越大
  34. //创建相机对象
  35. var camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
  36. camera.position.set(200, 300, 200); //设置相机位置
  37. camera.lookAt(scene.position); //设置相机方向(指向的场景对象)
  38. /**
  39. * 创建渲染器对象
  40. */
  41. var renderer = new THREE.WebGLRenderer();
  42. renderer.setSize(width, height); //设置渲染区域尺寸
  43. renderer.setClearColor(0xb9d3ff, 1); //设置背景颜色
  44. document.body.appendChild(renderer.domElement); //body元素中插入canvas对象
  45. //执行渲染操作 指定场景、相机作为参数
  46. renderer.render(scene, camera);

对于某个3D场景的一个瞬间,是决定于当前场景和拍摄位置的。所以一方面,我们需要设置拍摄的素材(物质材质,光源),另一方面我们需要设置相机的位置(远近,方向),这样我们可以得到一张在两个场景共同作用下的图像。
image.png
如图,Mesh为素材的几何(形状)和属性(颜色,大小)共同生成的抽象素材概念,称为网络模型。网络模型收到光源后,共同生成了场景Scene。场景和相机共同作用,经过render,变成了最后的渲染结果,即投影图

定点概念及基础结构

image.png
几何体->多个三角形->多个点

可以这么理解,几何体Geometry标识的是一种3D物体的点形成方式,材质Material标识的是如何根据这些点进行渲染。具体解释,首先Threejs中,绘制一个3d图形是通过很多个三角形来完成轮廓的绘制的,我们可以使用Buffer来手动输入所有点来绘制三角形,也可以通过new BoxGeometry(100,100,100)用封装好的接口生成这样的一系列点的轮廓。一旦获取这些点的轮廓,我们可以用轮廓绘制一个几何体,可以是一个面,可以是几个点,也可以是几条线,这些事由材质Material来赋予的。

  1. // var material = new THREE.MeshBasicMaterial({
  2. // color: 0x0000ff, //三角面颜色
  3. // side: THREE.DoubleSide, //两面可见
  4. // }); //材质对象
  5. var material = new THREE.PointsMaterial({
  6. color: 0xff0000,
  7. size: 10.0, //点对象像素尺寸
  8. }); //材质对象
  9. var mesh = new THREE.Points(geometry, material); //网格模型对象Mesh
  10. var material = new THREE.LineBasicMaterial({
  11. color: 0xff0000, //线条颜色
  12. }); //材质对象
  13. var line = new THREE.Line(geometry, material); //线条模型对象
  14. scene.add(line);//线条对象添加到场景中
  15. scene.add(mesh); //网格模型添加到场景中

image.png
顶点->线->面
以顶点作为颜色计算的根本,线和面都是根据插值进行计算
image.png

  1. var geometry = new THREE.BufferGeometry(); //创建一个Buffer类型几何体对象
  2. //类型数组创建顶点数据
  3. //类型数组创建顶点位置position数据
  4. var vertices = new Float32Array([
  5. 0,
  6. 0,
  7. 0, //顶点1坐标
  8. 50,
  9. 0,
  10. 0, //顶点2坐标
  11. 0,
  12. 100,
  13. 0, //顶点3坐标
  14. 0,
  15. 0,
  16. 10, //顶点4坐标
  17. 0,
  18. 0,
  19. 100, //顶点5坐标
  20. 50,
  21. 0,
  22. 10, //顶点6坐标
  23. ]);
  24. // 创建属性缓冲区对象
  25. var attribue = new THREE.BufferAttribute(vertices, 3); //3个为一组,作为一个顶点的xyz坐标
  26. // 设置几何体attributes属性的位置position属性
  27. geometry.attributes.position = attribue;
  28. //类型数组创建顶点颜色color数据
  29. var colors = new Float32Array([
  30. 1,
  31. 0,
  32. 0, //顶点1颜色
  33. 0,
  34. 1,
  35. 0, //顶点2颜色
  36. 0,
  37. 0,
  38. 1, //顶点3颜色
  39. 1,
  40. 1,
  41. 0, //顶点4颜色
  42. 0,
  43. 1,
  44. 1, //顶点5颜色
  45. 1,
  46. 0,
  47. 1, //顶点6颜色
  48. ]);
  49. // 设置几何体attributes属性的颜色color属性
  50. geometry.attributes.color = new THREE.BufferAttribute(colors, 3); //3个为一组,表示一个顶点的颜色数据RGB
  51. //材质对象
  52. var material = new THREE.PointsMaterial({
  53. // 使用顶点颜色数据渲染模型,不需要再定义color属性
  54. // color: 0xff0000,
  55. vertexColors: THREE.VertexColors, //以顶点颜色为准
  56. size: 10.0, //点对象像素尺寸
  57. });
  58. // 点渲染模式 点模型对象Points
  59. var points = new THREE.Points(geometry, material); //点模型对象
  60. scene.add(points); //点对象添加到场景
  61. var material = new THREE.LineBasicMaterial({
  62. vertexColors: THREE.VertexColors, //以顶点颜色为准
  63. });
  64. // 点渲染模式 点模型对象Points
  65. var line = new THREE.Line(geometry, material);
  66. scene.add(line);
  67. // 辅助坐标系 参数250表示坐标系大小,可以根据场景大小去设置
  68. var axisHelper = new THREE.AxisHelper(250);
  69. scene.add(axisHelper);

image.png

法向量

作为区分2d,3d比如同样一个矩形,如果它左边和右边的光反射方向不同,就会被认为是发生了弯折,这是由光的镜面反射影响造成的3d效果。而这种镜面反射的直接反应是通过点的法向量决定的。
image.png
即如果上图为一个平面图,所有法向量同方向。这也是区分2d,3d的重要点。

顶点索引复用顶点数据

一个矩形会用到两个三角形,6个点进行绘图,但是其中两个点是重复的,这样可以通过index来对已有的不重复点变化为全部的要用的重复点。
image.png

几何体数据

BoxGeometryPlaneGeometrySphereGeometry等几何体类的基类是Geometry,所以访问这些几何体的顶点数据,不知道具体属性名称,可以查问threejs文档Geometry
image.png
BoxBufferGeometryPlaneBufferGeometrySphereBufferGeometry等几何体类的基类是BufferGeometry,所以访问这些几何体的顶点数据,不知道具体属性名称,可以查问threejs文档BufferGeometry
image.png

访问外部模型几何体顶点数据

Threejs加载外部模型的时候,会把几何体解析为缓冲类型几何体BufferGeometry,所以访问外部模型几何体顶点数据,可以查看文档BufferGeometry。关于外部模型加载的讲解可以查看课程第14章。

几何体旋转、缩放、平移变换

image.png
注意
注意网格模型Mesh进行缩放旋转平移变换和几何体Geometry可以实现相同的渲染效果,但是网格模型Mesh进行这些变换不会影响几何体的顶点位置坐标,网格模型缩放旋转平移变换改变的是模型的本地矩阵、世界矩阵。

材质

image.png
几何体材质,side属性控制渲染哪一面;transparent控制是否由透明度,配合opacity属性,设置透明度,transprent为false时,opacity设置无效。

点,线模型

要查看模型的三角形渲染形式 可以通过设置wireframe属性为true查看

  1. var geometry = new THREE.BoxGeometry(100, 100, 100);
  2. // 三角形面渲染模式
  3. var material = new THREE.MeshLambertMaterial({
  4. color: 0x0000ff, //三角面颜色
  5. wireframe:true,//网格模型以线条的模式渲染
  6. });
  7. var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
  8. // computeLineDistances方法 计算LineDashedMaterial所需的距离数组
  9. scene.add(mesh)

image.png

光源对象

image.png

  • 光照计算算法 计算颜色片

Threejs在渲染的时候网格模型材质的颜色值mesh.material.color和光源的颜色值light.color会进行相乘,简单说就是RGB三个分量分别相乘。
平行光漫反射简单数学模型:漫反射光的颜色 = 网格模型材质颜色值 x 光线颜色 x 光线入射角余弦值
漫反射数学模型RGB分量表示:(R2,G2,B2) = (R1,G1,B1) x (R0,G0,B0) x cosθ

  1. R2 = R1 * R0 * cosθ
  2. G2 = G1 * G0 * cosθ
  3. B2 = B1 * B0 * cosθ

举个例子,蓝色几何体在红光照射下会呈现黑色,原因是各个点颜色都正交,算出来必定#000000,即黑色。

投影

Three.js物体投影模拟计算主要设置三部分,一个是设置产生投影的模型对象,一个是设置接收投影效果的模型,最后一个是光源对象本身的设置,光源如何产生投影。
产生投影对象

  1. var geometry = new THREE.BoxGeometry(40, 100, 40);
  2. var material = new THREE.MeshLambertMaterial({
  3. color: 0x0000ff
  4. });
  5. var mesh = new THREE.Mesh(geometry, material);
  6. // mesh.position.set(0,0,0)
  7. scene.add(mesh);
  8. // 设置产生投影的网格模型
  9. mesh.castShadow = true;

接受投影对象

  1. //创建一个平面几何体作为投影面
  2. var planeGeometry = new THREE.PlaneGeometry(300, 200);
  3. var planeMaterial = new THREE.MeshLambertMaterial({
  4. color: 0x999999
  5. });
  6. // 平面网格模型作为投影面
  7. var planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
  8. scene.add(planeMesh); //网格模型添加到场景中
  9. planeMesh.rotateX(-Math.PI / 2); //旋转网格模型
  10. planeMesh.position.y = -50; //设置网格模型y坐标
  11. // 设置接收阴影的投影面
  12. planeMesh.receiveShadow = true;

光源

  1. // 方向光
  2. var directionalLight = new THREE.DirectionalLight(0xffffff, 1);
  3. // 设置光源位置
  4. directionalLight.position.set(60, 100, 40);
  5. scene.add(directionalLight);
  6. // 设置用于计算阴影的光源对象
  7. directionalLight.castShadow = true;

如何投影

  1. // 设置计算阴影的区域,最好刚好紧密包围在对象周围
  2. // 计算阴影的区域过大:模糊 过小:看不到或显示不完整
  3. directionalLight.shadow.camera.near = 0.5;
  4. directionalLight.shadow.camera.far = 300;
  5. directionalLight.shadow.camera.left = -50;
  6. directionalLight.shadow.camera.right = 50;
  7. directionalLight.shadow.camera.top = 200;
  8. directionalLight.shadow.camera.bottom = -100;
  9. // 设置mapSize属性可以使阴影更清晰,不那么模糊
  10. // directionalLight.shadow.mapSize.set(1024,1024)
  11. console.log(directionalLight.shadow.camera);

阴影对象基类LightShadow

平行光阴影对象DirectionalLightShadow和聚光源阴影对象SpotLightShadow两个类的基类是LightShadow

LightShadow属性.camera

观察光源的相机对象. 从光的角度来看,以相机对象的观察位置和方向来判断,其他物体背后的物体将处于阴影中。

  1. // 聚光源设置
  2. spotLight.shadow.camera.near = 1;
  3. spotLight.shadow.camera.far = 300;
  4. spotLight.shadow.camera.fov = 20;

层级模型

任何画布概念中,都有层级概念。如画一个机器人,不可能直接完全画出所有组件然后组合在一个对象中,而是分拆为手,脚,头,手又分割为手掌,指头,这样增强了组件的复用性和整体性。即我们很多时候需要去管理的是手这个抽象概念,而非一堆指头,因此层级划分很重要。
add方法创造一个子对象,remove方法移出一个子对象。
image.png

对象命名,查找,遍历

http://www.webgl3d.cn/Three.js/

相对坐标与世界坐标

简而言之,相对坐标为子类与父类的相对位置坐标,世界坐标标识相对于坐标系的坐标位置。比如group相对位置为y=+50,mesh为group的children,相对位置为y=+50,那么mesh的世界坐标为(0, 100, 0)
同样的,除了位置,放缩系数和角度,也有类似机制,使用矩阵进行变换。

  1. var geometry = new THREE.BoxGeometry(20, 20, 20);
  2. var material = new THREE.MeshLambertMaterial({color: 0x0000ff});
  3. var mesh = new THREE.Mesh(geometry, material);
  4. // mesh的本地坐标设置为(50, 0, 0)
  5. mesh.position.set(50, 0, 0);
  6. var group = new THREE.Group();
  7. // group本地坐标设置和mesh一样设置为(50, 0, 0)
  8. // mesh父对象设置position会影响得到mesh的世界坐标
  9. group.position.set(50, 0, 0);
  10. group.add(mesh);
  11. scene.add(group);
  12. // .position属性获得本地坐标
  13. console.log('本地坐标',mesh.position);
  14. // getWorldPosition()方法获得世界坐标
  15. //该语句默认在threejs渲染的过程中执行,如果渲染之前想获得世界矩阵属性、世界位置属性等属性,需要通过代码更新
  16. scene.updateMatrixWorld(true);
  17. var worldPosition = new THREE.Vector3();
  18. mesh.getWorldPosition(worldPosition);
  19. console.log('世界坐标',worldPosition);

纹理图与uv映射

https://www.cnblogs.com/yanan-boke/p/7815018.html
image.png
首先,纹理贴图维护uv,即左边的坐标系,根据图片大小,分布为四点(0,0),…,(1,1),而uv映射是指将这样一张2d纹理图映射到Matrial的Faces的三角形上。

除了基本的纹理图,还可以加载法线贴图,阴影贴图,高光贴图等。http://www.webgl3d.cn/Three.js/