基础知识

/*** 创建场景对象Scene*/var scene = new THREE.Scene();/*** 创建网格模型*/// var geometry = new THREE.SphereGeometry(60, 40, 40); //创建一个球体几何对象var geometry = new THREE.BoxGeometry(100, 100, 100); //创建一个立方体几何对象Geometryvar material = new THREE.MeshLambertMaterial({color: 0x0000ff}); //材质对象Materialvar mesh = new THREE.Mesh(geometry, material); //网格模型对象Meshscene.add(mesh); //网格模型添加到场景中/*** 光源设置*///点光源var point = new THREE.PointLight(0xffffff);point.position.set(400, 200, 300); //点光源位置scene.add(point); //点光源添加到场景中//环境光var ambient = new THREE.AmbientLight(0x444444);scene.add(ambient);// console.log(scene)// console.log(scene.children)/*** 相机设置*/var width = window.innerWidth; //窗口宽度var height = window.innerHeight; //窗口高度var k = width / height; //窗口宽高比var s = 200; //三维场景显示范围控制系数,系数越大,显示的范围越大//创建相机对象var camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);camera.position.set(200, 300, 200); //设置相机位置camera.lookAt(scene.position); //设置相机方向(指向的场景对象)/*** 创建渲染器对象*/var renderer = new THREE.WebGLRenderer();renderer.setSize(width, height); //设置渲染区域尺寸renderer.setClearColor(0xb9d3ff, 1); //设置背景颜色document.body.appendChild(renderer.domElement); //body元素中插入canvas对象//执行渲染操作 指定场景、相机作为参数renderer.render(scene, camera);
对于某个3D场景的一个瞬间,是决定于当前场景和拍摄位置的。所以一方面,我们需要设置拍摄的素材(物质材质,光源),另一方面我们需要设置相机的位置(远近,方向),这样我们可以得到一张在两个场景共同作用下的图像。
如图,Mesh为素材的几何(形状)和属性(颜色,大小)共同生成的抽象素材概念,称为网络模型。网络模型收到光源后,共同生成了场景Scene。场景和相机共同作用,经过render,变成了最后的渲染结果,即投影图。
定点概念及基础结构

几何体->多个三角形->多个点
可以这么理解,几何体Geometry标识的是一种3D物体的点形成方式,材质Material标识的是如何根据这些点进行渲染。具体解释,首先Threejs中,绘制一个3d图形是通过很多个三角形来完成轮廓的绘制的,我们可以使用Buffer来手动输入所有点来绘制三角形,也可以通过new BoxGeometry(100,100,100)用封装好的接口生成这样的一系列点的轮廓。一旦获取这些点的轮廓,我们可以用轮廓绘制一个几何体,可以是一个面,可以是几个点,也可以是几条线,这些事由材质Material来赋予的。
// var material = new THREE.MeshBasicMaterial({// color: 0x0000ff, //三角面颜色// side: THREE.DoubleSide, //两面可见// }); //材质对象var material = new THREE.PointsMaterial({color: 0xff0000,size: 10.0, //点对象像素尺寸}); //材质对象var mesh = new THREE.Points(geometry, material); //网格模型对象Meshvar material = new THREE.LineBasicMaterial({color: 0xff0000, //线条颜色}); //材质对象var line = new THREE.Line(geometry, material); //线条模型对象scene.add(line);//线条对象添加到场景中scene.add(mesh); //网格模型添加到场景中

顶点->线->面
以顶点作为颜色计算的根本,线和面都是根据插值进行计算
var geometry = new THREE.BufferGeometry(); //创建一个Buffer类型几何体对象//类型数组创建顶点数据//类型数组创建顶点位置position数据var vertices = new Float32Array([0,0,0, //顶点1坐标50,0,0, //顶点2坐标0,100,0, //顶点3坐标0,0,10, //顶点4坐标0,0,100, //顶点5坐标50,0,10, //顶点6坐标]);// 创建属性缓冲区对象var attribue = new THREE.BufferAttribute(vertices, 3); //3个为一组,作为一个顶点的xyz坐标// 设置几何体attributes属性的位置position属性geometry.attributes.position = attribue;//类型数组创建顶点颜色color数据var colors = new Float32Array([1,0,0, //顶点1颜色0,1,0, //顶点2颜色0,0,1, //顶点3颜色1,1,0, //顶点4颜色0,1,1, //顶点5颜色1,0,1, //顶点6颜色]);// 设置几何体attributes属性的颜色color属性geometry.attributes.color = new THREE.BufferAttribute(colors, 3); //3个为一组,表示一个顶点的颜色数据RGB//材质对象var material = new THREE.PointsMaterial({// 使用顶点颜色数据渲染模型,不需要再定义color属性// color: 0xff0000,vertexColors: THREE.VertexColors, //以顶点颜色为准size: 10.0, //点对象像素尺寸});// 点渲染模式 点模型对象Pointsvar points = new THREE.Points(geometry, material); //点模型对象scene.add(points); //点对象添加到场景var material = new THREE.LineBasicMaterial({vertexColors: THREE.VertexColors, //以顶点颜色为准});// 点渲染模式 点模型对象Pointsvar line = new THREE.Line(geometry, material);scene.add(line);// 辅助坐标系 参数250表示坐标系大小,可以根据场景大小去设置var axisHelper = new THREE.AxisHelper(250);scene.add(axisHelper);
法向量
作为区分2d,3d比如同样一个矩形,如果它左边和右边的光反射方向不同,就会被认为是发生了弯折,这是由光的镜面反射影响造成的3d效果。而这种镜面反射的直接反应是通过点的法向量决定的。
即如果上图为一个平面图,所有法向量同方向。这也是区分2d,3d的重要点。
顶点索引复用顶点数据
一个矩形会用到两个三角形,6个点进行绘图,但是其中两个点是重复的,这样可以通过index来对已有的不重复点变化为全部的要用的重复点。
几何体数据
BoxGeometry、PlaneGeometry、SphereGeometry等几何体类的基类是Geometry,所以访问这些几何体的顶点数据,不知道具体属性名称,可以查问threejs文档Geometry
BoxBufferGeometry、PlaneBufferGeometry、SphereBufferGeometry等几何体类的基类是BufferGeometry,所以访问这些几何体的顶点数据,不知道具体属性名称,可以查问threejs文档BufferGeometry
访问外部模型几何体顶点数据
Threejs加载外部模型的时候,会把几何体解析为缓冲类型几何体BufferGeometry,所以访问外部模型几何体顶点数据,可以查看文档BufferGeometry。关于外部模型加载的讲解可以查看课程第14章。
几何体旋转、缩放、平移变换

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

几何体材质,side属性控制渲染哪一面;transparent控制是否由透明度,配合opacity属性,设置透明度,transprent为false时,opacity设置无效。
点,线模型
要查看模型的三角形渲染形式 可以通过设置wireframe属性为true查看
var geometry = new THREE.BoxGeometry(100, 100, 100);// 三角形面渲染模式var material = new THREE.MeshLambertMaterial({color: 0x0000ff, //三角面颜色wireframe:true,//网格模型以线条的模式渲染});var mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh// computeLineDistances方法 计算LineDashedMaterial所需的距离数组scene.add(mesh)
光源对象

- 光照计算算法 计算颜色片
Threejs在渲染的时候网格模型材质的颜色值mesh.material.color和光源的颜色值light.color会进行相乘,简单说就是RGB三个分量分别相乘。
平行光漫反射简单数学模型:漫反射光的颜色 = 网格模型材质颜色值 x 光线颜色 x 光线入射角余弦值
漫反射数学模型RGB分量表示:(R2,G2,B2) = (R1,G1,B1) x (R0,G0,B0) x cosθ
R2 = R1 * R0 * cosθG2 = G1 * G0 * cosθB2 = B1 * B0 * cosθ
举个例子,蓝色几何体在红光照射下会呈现黑色,原因是各个点颜色都正交,算出来必定#000000,即黑色。
投影
Three.js物体投影模拟计算主要设置三部分,一个是设置产生投影的模型对象,一个是设置接收投影效果的模型,最后一个是光源对象本身的设置,光源如何产生投影。
产生投影对象
var geometry = new THREE.BoxGeometry(40, 100, 40);var material = new THREE.MeshLambertMaterial({color: 0x0000ff});var mesh = new THREE.Mesh(geometry, material);// mesh.position.set(0,0,0)scene.add(mesh);// 设置产生投影的网格模型mesh.castShadow = true;
接受投影对象
//创建一个平面几何体作为投影面var planeGeometry = new THREE.PlaneGeometry(300, 200);var planeMaterial = new THREE.MeshLambertMaterial({color: 0x999999});// 平面网格模型作为投影面var planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);scene.add(planeMesh); //网格模型添加到场景中planeMesh.rotateX(-Math.PI / 2); //旋转网格模型planeMesh.position.y = -50; //设置网格模型y坐标// 设置接收阴影的投影面planeMesh.receiveShadow = true;
光源
// 方向光var directionalLight = new THREE.DirectionalLight(0xffffff, 1);// 设置光源位置directionalLight.position.set(60, 100, 40);scene.add(directionalLight);// 设置用于计算阴影的光源对象directionalLight.castShadow = true;
如何投影
// 设置计算阴影的区域,最好刚好紧密包围在对象周围// 计算阴影的区域过大:模糊 过小:看不到或显示不完整directionalLight.shadow.camera.near = 0.5;directionalLight.shadow.camera.far = 300;directionalLight.shadow.camera.left = -50;directionalLight.shadow.camera.right = 50;directionalLight.shadow.camera.top = 200;directionalLight.shadow.camera.bottom = -100;// 设置mapSize属性可以使阴影更清晰,不那么模糊// directionalLight.shadow.mapSize.set(1024,1024)console.log(directionalLight.shadow.camera);
阴影对象基类LightShadow
平行光阴影对象DirectionalLightShadow和聚光源阴影对象SpotLightShadow两个类的基类是LightShadow
LightShadow属性.camera
观察光源的相机对象. 从光的角度来看,以相机对象的观察位置和方向来判断,其他物体背后的物体将处于阴影中。
// 聚光源设置spotLight.shadow.camera.near = 1;spotLight.shadow.camera.far = 300;spotLight.shadow.camera.fov = 20;
层级模型
任何画布概念中,都有层级概念。如画一个机器人,不可能直接完全画出所有组件然后组合在一个对象中,而是分拆为手,脚,头,手又分割为手掌,指头,这样增强了组件的复用性和整体性。即我们很多时候需要去管理的是手这个抽象概念,而非一堆指头,因此层级划分很重要。
add方法创造一个子对象,remove方法移出一个子对象。
对象命名,查找,遍历
见http://www.webgl3d.cn/Three.js/
相对坐标与世界坐标
简而言之,相对坐标为子类与父类的相对位置坐标,世界坐标标识相对于坐标系的坐标位置。比如group相对位置为y=+50,mesh为group的children,相对位置为y=+50,那么mesh的世界坐标为(0, 100, 0)
同样的,除了位置,放缩系数和角度,也有类似机制,使用矩阵进行变换。
var geometry = new THREE.BoxGeometry(20, 20, 20);var material = new THREE.MeshLambertMaterial({color: 0x0000ff});var mesh = new THREE.Mesh(geometry, material);// mesh的本地坐标设置为(50, 0, 0)mesh.position.set(50, 0, 0);var group = new THREE.Group();// group本地坐标设置和mesh一样设置为(50, 0, 0)// mesh父对象设置position会影响得到mesh的世界坐标group.position.set(50, 0, 0);group.add(mesh);scene.add(group);// .position属性获得本地坐标console.log('本地坐标',mesh.position);// getWorldPosition()方法获得世界坐标//该语句默认在threejs渲染的过程中执行,如果渲染之前想获得世界矩阵属性、世界位置属性等属性,需要通过代码更新scene.updateMatrixWorld(true);var worldPosition = new THREE.Vector3();mesh.getWorldPosition(worldPosition);console.log('世界坐标',worldPosition);
纹理图与uv映射
https://www.cnblogs.com/yanan-boke/p/7815018.html
首先,纹理贴图维护uv,即左边的坐标系,根据图片大小,分布为四点(0,0),…,(1,1),而uv映射是指将这样一张2d纹理图映射到Matrial的Faces的三角形上。
除了基本的纹理图,还可以加载法线贴图,阴影贴图,高光贴图等。http://www.webgl3d.cn/Three.js/
