·webgl兼容性判断
- if (WebGL.isWebGLAvailable()) {
-
- // Initiate function or other initializations here
-
- animate();
-
- } else {
-
- const warning = WebGL.getWebGLErrorMessage();
-
- document.getElementById('container').appendChild(warning);
-
- }
·gltf-viewer,在线预览gltf模型:
https://gltf-viewer.donmccurdy.com
·threejs论坛
- 论坛:https://discourse.threejs.org
-
- stackoverflow:http://stackoverflow.com/tags/three.js/info
·材质变化时注意事项
材质的texture、fog、vertex、colors、morphing、shadow map、alpha test、transparent改变时需要设置
material.needsUpdate = true
·纹理改变注意事项:
纹理中图像,画布,视频和数据改变需要设置:
texture.needsUpdate = true;
·相机改变注意事项
透视相机模型:
改变相机:fov、aspect、near、far是需要重新计算相机投影矩阵:
- camera.aspect = window.innerWidth / window.innerHeight;
-
- camera.updateProjectionMatrix();
相机的viewport进行多相机设置,可以达到一屏显示多个模型效果
·对象销毁:
- 几何体销毁:Geometry.dispose()
-
- 材质销毁:Material.dispose()
-
- 纹理的销毁:Texture.dispose()
·矩阵更新
静态的object3d对象在改变属性(position,quaternion和scale属性),希望手动更新矩阵时
- object.matrixAutoUpdate = false;
-
- object.updateMatrix();
Three.js使用matrix编码3D变换 —— 平移(位置),旋转和缩放
·立方体贴图:
- const loader = new THREE.CubeTextureLoader();
-
- loader.setPath( 'textures/cube/pisa/' );
-
-
-
- const textureCube = loader.load( [
-
- 'px.png', 'nx.png',
-
- 'py.png', 'ny.png',
-
- 'pz.png', 'nz.png'
-
- ] );
-
-
-
- const material = new THREE.MeshBasicMaterial( { color: 0xffffff, envMap: textureCube } );
-
- renderer = new THREE.WebGLRenderer( { antialias: true } );
·窗口改变适配
- function onWindowResize() {
-
- camera.aspect = window.innerWidth / window.innerHeight;
-
- camera.updateProjectionMatrix();
-
- renderer.setSize( window.innerWidth, window.innerHeight );
-
- }
-
-
-
- function animate() {
-
- requestAnimationFrame( animate );
-
- render();
-
-
-
- }
-
-
-
- function render() {
-
- controls.update( clock.getDelta() );
-
- renderer.render( scene, camera );
-
- }
·材质:
https://zhuanlan.zhihu.com/p/100241366?utm_id=0
MeshBasicMaterial:不考虑光照的影响
alphaMap:透明度贴图。灰度纹理,用于控制表面的不透明度,黑色为完全透明,白色为完全不透明。对于RGB和RGBA使用绿色通道。使用此贴图时需将材质的transparent属性设置为true。
1、MeshBasicMaterial:基础网格材质。 不受光照影响。
2、MeshStandardMaterial:标准网格材质。 受到光照影响。
3、MeshLambertMaterial:非光泽表面,没有高光。
4、MeshPhongMaterial:具有镜面高光的材质。
5、PointsMaterial:点材质。 size属性设置粒子大小。 lights属性控制是否受光照影响,默认不受光照影响。 depthWrite是否开启深度写入,默认开启。 blending属性设置材质的混合模式。 sizeAttenuation属性设置是否受近大远小影响。
https://blog.csdn.net/qq_45902692/article/details/128394703
环境贴图添加
在Three.js中设置环境贴图的方式如下:
- scene.background
-
- = new THREE.CubeTextureLoader().setPath('相对目录文件夹,里边包含6张贴图/').load( [
-
- 'posx.jpg',
-
- 'negx.jpg',
-
- 'posy.jpg',
-
- 'negy.jpg',
-
- 'posz.jpg',
-
- 'negz.jpg'
-
- ] );
图片的排放顺序依次是:x轴正方向-x轴负方向-y轴正方向-y轴负方向-z轴正方向-z轴负方向;
按照Three.js创建的默认坐标系中,图片对应的方位是:右侧-左侧-上边-下边-前边-后边;将背景设置成以上的贴图即可显示效果
·加载管理器
- THREE.DefaultLoadingManager.onStart = function ( url, itemsLoaded, itemsTotal ) {
-
- console.log( 'Started loading file: ' + url + '.\nLoaded ' + itemsLoaded + ' of ' + itemsTotal + ' files.' );
-
-
-
- };
-
-
-
- THREE.DefaultLoadingManager.onLoad = function ( ) {
-
- console.log( 'Loading Complete!');
-
- };
-
-
-
- THREE.DefaultLoadingManager.onProgress = function ( url, itemsLoaded, itemsTotal ) {
-
- console.log( 'Loading file: ' + url + '.\nLoaded ' + itemsLoaded + ' of ' + itemsTotal + ' files.' );
-
- };
-
-
-
- THREE.DefaultLoadingManager.onError = function ( url ) {
-
- console.log( 'There was an error loading ' + url );
-
- };
·启用缓存:
THREE.Cache.enabled = true;
·光源:
- AmbientLight:环境光会均匀的照亮场景中的所有物体。环境光不能用来投射阴影,因为它没有方向。
-
- HemisphereLight:不能投射阴影
·gltf模型颜色空间
- renderer.outputColorSpace = THREE.SRGBColorSpace;
-
-
-
- texture.colorSpace = THREE.SRGBColorSpace;
-
- texture.flipY = false;
-
-
-
- // renderer
-
-
-
- renderer = new THREE.WebGLRenderer( { antialias: true } );
-
- renderer.setPixelRatio( window.devicePixelRatio );
-
- renderer.setSize( window.innerWidth, window.innerHeight );
文档一
7. 三维坐标系-加强三维空间认识 | Three.js中文网
·受光影响的材质:

·光源:


·全屏布局注意CSS的设置:
-
- body{
-
- overflow: hidden;
-
- margin: 0px;
-
- }
-
·画布宽度变化:
- // onresize 事件会在窗口被调整大小时发生
-
- window.onresize = function () {
-
- // 重置渲染器输出画布canvas尺寸
-
- renderer.setSize(window.innerWidth, window.innerHeight);
-
- // 全屏情况下:设置观察范围长宽比aspect为窗口宽高比
-
- camera.aspect = window.innerWidth / window.innerHeight;
-
- // 渲染器执行render方法的时候会读取相机对象的投影矩阵属性projectionMatrix
-
- // 但是不会每渲染一帧,就通过相机的属性计算投影矩阵(节约计算资源)
-
- // 如果相机的一些属性发生了变化,需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
-
- camera.updateProjectionMatrix();
-
- };
·渲染器设置:
- 1)抗锯齿设置
-
- const renderer = new THREE.WebGLRenderer({
-
- antialias:true,
-
- });
-
- 2)设备像素比
-
- renderer.setPixelRatio(window.devicePixelRatio)
·旋转、平移、缩放:

·几何体居中:
已经偏移的几何体居中,执行.center(),你可以看到几何体重新与坐标原点重合
geometry.center();
·沿着自定义方向移动:
- //向量Vector3对象表示方向
-
- const axis = new THREE.Vector3(1, 1, 1);
-
- axis.normalize(); //向量归一化
-
- //沿着axis轴表示方向平移100
-
- mesh.translateOnAxis(axis, 100);

·绕某个方向旋转:
- const axis = new THREE.Vector3(0,1,0);//向量axis
-
- mesh.rotateOnAxis(axis,Math.PI/8);//绕axis轴旋转π/8
·颜色改变:
- // 查看Color对象设置0x00ff00对应的的.r、.g、.b值
-
- const color = new THREE.Color(0x00ff00);
-
-
-
- color.setRGB(0,1,0);//RGB方式设置颜色
-
- color.setHex(0x00ff00);//十六进制方式设置颜色
-
- color.setStyle('#00ff00');//前端CSS颜色值设置颜色
-
-
-
- color.set(0x00ff00);//十六进制方式设置颜色
-
- color.set('#00ff00');//前端CSS颜色值设置颜色
-
-
-
- material.color.set(0x00ffff);
·材质透明度设置:
- material.transparent = true;//开启透明
-
- material.opacity = 0.5;//设置透明度
·材质面:
- material.side = THREE.BackSide;//背面可以看到
-
- material.side = THREE.DoubleSide;//双面可见
·材质或几何体共享
- const mesh = new THREE.Mesh(geometry, material);
-
- const mesh2 = new THREE.Mesh(geometry, material);
-
- mesh2.position.x = 100;
-
- // 两个mesh共享一个材质,改变一个mesh的颜色,另一个mesh2的颜色也会跟着改变
-
- // mesh.material和mesh2.material都指向同一个material
-
- // 三者等价:mesh.material、mesh2.material、material
-
- mesh.material.color.set(0xffff00);
-
- // 三者等价:mesh.geometry、mesh2.geometry、geometry
-
- mesh.geometry.translate(0,100,0);
·克隆和复制:
- 克隆是复制并创建一个与原对象一样的新对象:
-
- const v1 = new THREE.Vector3(1, 2, 3);
-
- console.log('v1',v1);
-
- //v2是一个新的Vector3对象,和v1的.x、.y、.z属性值一样
-
- const v2 = v1.clone();
-
- console.log('v2',v2);
-
- 复制是把原对象的属性值赋值给另一个对象:
-
- const v1 = new THREE.Vector3(1, 2, 3);
-
- console.log('v1',v1);
-
- //v2是一个新的Vector3对象,和v1的.x、.y、.z属性值一样
-
- const v2 = v1.clone();
-
- console.log('v2',v2);
克隆位置:
- mesh.position.copy(mesh2.position);//1. 第1步位置重合
-
- mesh.position.y += 100;//1. 第2步mesh在原来y的基础上增加100
克隆几何体:
- const mesh2 = mesh.clone();
-
- // 克隆几何体和材质,重新设置mesh2的材质和几何体属性
-
- mesh2.geometry = mesh.geometry.clone();
-
- mesh2.material = mesh.material.clone();
-
- // 改变mesh2颜色,不会改变mesh的颜色
-
- mesh2.material.color.set(0xff0000);
两个模型的姿态角度始终保持一样:
// 渲染循环
- function render() {
-
- mesh.rotateY(0.01);// mesh旋转动画
-
- // 同步mesh2和mesh的姿态角度一样,不管mesh姿态角度怎么变化,mesh2始终保持同步
-
- mesh2.rotation.copy(mesh.rotation);
-
- renderer.render(scene, camera);
-
- requestAnimationFrame(render);
-
- }
-
- render();
·分组Group:
- //把mesh1型插入到组group中,mesh1作为group的子对象
-
- group.add(mesh1);
-
- //把mesh2型插入到组group中,mesh2作为group的子对象
-
- group.add(mesh2);
-
- 或
-
- group.add(mesh1,mesh2);
-
- //把group插入到场景中作为场景子对象
-
- scene.add(group);
-
-
-
- console.log('查看group的子对象',group.children);
-
-
-
- 父对象旋转缩放平移变换,子对象跟着变化
-
- //沿着Y轴平移mesh1和mesh2的父对象,mesh1和mesh2跟着平移
-
- group.translateY(100);
-
- //父对象缩放,子对象跟着缩放
-
- group.scale.set(4,4,4);
-
- //父对象旋转,子对象跟着旋转
-
- group.rotateY(Math.PI/6)
-
-
-
- mesh也能添加mesh子对象:
-
- mesh1.add(mesh2);
·按名字查询模型组件:
- / 返回名.name为"4号楼"对应的对象
-
- const nameNode = scene.getObjectByName ("4号楼");
-
- nameNode.material.color.set(0xff0000);
·本地坐标与世界坐标:
本地(局部)坐标就是模型的position属性;
世界坐标是模型自身.position和所有父对象.position累加的坐标,通过getWorldPosition获取。
读取一个模型的世界坐标,并把读取结果存储到参数Vector3中:
mesh.getWorldPosition(Vector3)
给子对象添加一个局部坐标系:
- const meshAxesHelper = new THREE.AxesHelper(50);
-
- mesh.add(meshAxesHelper);
·移除对象:
- // 删除父对象group的子对象网格模型mesh1:
-
- group.remove(mesh1);
-
- scene移除:
-
- scene.remove(ambient);//移除场景中环境光
-
- scene.remove(model);//移除场景中模型对象
-
- 查看移除后的对象:
-
- console.log('查看group的子对象',group.children);
-
- 一次移除多个:
-
- group.remove(mesh1,mesh2);
·模型隐藏与显示:
- 模型属性.visible隐藏或显示:
-
- mesh.visible =false;// 隐藏一个网格模型,visible的默认值是true
-
- group.visible =false;// 隐藏一个包含多个模型的组对象group
-
- mesh.visible =true;// 使网格模型mesh处于显示状态
-
- 材质属性.visible隐藏或显示:
-
- mesh.material.visible =false;
·顶点UV坐标的作用:
顶点UV坐标的作用是从纹理贴图上提取像素映射到网格模型Mesh的几何体表面上。
顶点UV坐标可以在0~1.0之间任意取值,纹理贴图左下角对应的UV坐标是(0,0),右上角对应的坐标(1,1)。
/**纹理坐标0~1之间随意定义*/
- const uvs = new Float32Array([
-
- 0, 0, //图片左下角
-
- 1, 0, //图片右下角
-
- 1, 1, //图片右上角
-
- 0, 1, //图片左上角
-
- ]);
-
- // 设置几何体attributes属性的位置normal属性
-
- geometry.attributes.uv = new THREE.BufferAttribute(uvs, 2); //2个为一组,表示一个顶点的纹理坐标
·纹理对象Texture的阵列功能:
- // .load()方法加载图像,返回一个纹理对象Texture
-
- const texture = texLoader.load('./瓷砖.jpg');
-
- // 设置阵列模式
-
- texture.wrapS = THREE.RepeatWrapping;
-
- texture.wrapT = THREE.RepeatWrapping;
-
- // uv两个方向纹理重复数量
-
- texture.repeat.set(12,12);//注意选择合适的阵列数量
·旋转对象影响:
注意旋转方向影响矩形平面背面还是正面朝上,threejs默认渲染正面,不渲染背面。
·png贴图:
注意选择背景透明的.png图像作为颜色贴图,同时材质设置transparent: true,这样png图片背景完全透明的部分不显示。
·各种helper:
- 网格地面辅助观察:
-
- // 添加一个辅助网格地面
-
- const gridHelper = new THREE.GridHelper(300, 25, 0x004444, 0x004444);
-
-
-
- // AxesHelper:辅助观察的坐标系
-
- const axesHelper = new THREE.AxesHelper(150);
-
- scene.add(axesHelper);
-
-
-
- // 骨骼关节可以和普通网格模型一样作为其他模型子对象,添加到场景中
-
- const group = new THREE.Group();
-
- group.add(Bone1);
-
- // SkeletonHelper会可视化参数模型对象所包含的所有骨骼关节
-
- const skeletonHelper = new THREE.SkeletonHelper(group);
-
- group.add(skeletonHelper);
-
-
-
- // 聚广源辅助对象,可视化聚广源
-
- const spotLightHelper = new THREE.SpotLightHelper(spotLight,0xffffff)
-
- scene.add(spotLightHelper);
-
-
-
- //引入性能监视器stats.js
-
- import Stats from 'three/addons/libs/stats.module.js';
-
- //创建stats对象
-
- const stats = new Stats();
-
- //stats.domElement:web页面上输出计算结果,一个div元素,
-
- document.body.appendChild(stats.domElement);
-
- // 渲染函数
-
- function render() {
-
- //requestAnimationFrame循环调用的函数中调用方法update(),来刷新时间
-
- stats.update();
-
- renderer.render(scene, camera); //执行渲染操作
-
- requestAnimationFrame(render); //请求再次执行渲染函数render,渲染下一帧
-
- }
-
- render();
-
-
-
- GUI可视化改变三维场景:
-
- // 引入dat.gui.js的一个类GUI
-
- import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
-
- // 实例化一个gui对象
-
- const gui = new GUI();
-
- //改变交互界面style属性
-
- gui.domElement.style.right = '0px';
-
- gui.domElement.style.width = '300px';
-
- gui.add(mesh.position, 'x', 0, 180);
-
- gui.add(mesh.position, 'y', 0, 180);
-
- gui.add(mesh.position, 'z', 0, 180);
-
- matFolder.add(mesh.material,'metalness',0,1);
-
- matFolder.add(mesh.material,'roughness',0,1);
-
- matFolder.add(mesh.material,'clearcoat',0,1);
-
- matFolder.add(mesh.material,'clearcoatRoughness',0,1);
-
- matFolder.add(mesh.material,'envMapIntensity',0,10);
-
-
-
- const obj = {
-
- color: mesh.material.color, // 材质颜色
-
- };
-
- // 材质颜色color
-
- matFolder.addColor(obj, 'color').onChange(function (value) {
-
- mesh.material.color.set(value);
-
- });
-
- // 范围可以参考文档
-
- matFolder.add(mesh.material,'metalness',0,1);
-
- matFolder.add(mesh.material,'roughness',0,1);
-
- matFolder.add(mesh.material,'transmission',0,1);
-
- matFolder.add(mesh.material,'ior',0,3);
-
- matFolder.add(mesh.material,'envMapIntensity',0,10);
·画布居中:
- camera.lookAt(0, 0, 0);
-
- 注意相机控件OrbitControls会影响lookAt设置,注意手动设置OrbitControls的目标参数
-
-
-
- camera.lookAt(100, 0, 0);
-
- // 设置相机控件轨道控制器OrbitControls
-
- const controls = new OrbitControls(camera, renderer.domElement);
-
- // 相机控件.target属性在OrbitControls.js内部表示相机目标观察点,默认0,0,0
-
- // console.log('controls.target', controls.target);
-
- controls.target.set(100, 0, 0);
-
- controls.update();//update()函数内会执行camera.lookAt(controls.targe)
·纹理贴图颜色偏差解决(旧版):
- three.js加载gltf模型的时候,可能会遇到three.js渲染结果颜色偏差,对于这种情况,你只需要修改WebGL渲染器默认的编码方式.outputEncoding即可:
-
- //解决加载gltf格式模型纹理贴图和原图不一样问题
-
- renderer.outputEncoding = THREE.sRGBEncoding;
-
- 单独加载的纹理贴图的.encoding和webgl渲染器的.outputEncoding保持一致:
-
- const texLoader = new THREE.TextureLoader();
-
- const texture = texLoader.load('./黑色.png');// 加载手机mesh另一个颜色贴图
-
- texture.encoding = THREE.sRGBEncoding; //和渲染器.outputEncoding一样值
新版是正确的不用设置:
//新版本,加载gltf,不需要执行下面代码解决颜色偏差
renderer.outputColorSpace = THREE.SRGBColorSpace;//设置为SRGB颜色空间
注意:如果你直接给gltf模型材质设置.map属性更换贴图,会出现纹理贴图错位的问题,这主要和纹理对象Texture的翻转属性.flipY有关:
故如果单独更换纹理贴图记得设置
texture.flipY = false
·遍历对象:
//循环遍历
- const obj = gltf.scene.getObjectByName('洋房');
-
- console.log('obj', obj); //控制台查看返回结果
-
- console.log('obj.children', obj.children);
-
- // obj.children的所有子对象都是Mesh,改变Mesh对应颜色
-
- obj.children.forEach(function (mesh) {
-
- mesh.material.color.set(0xffff00);
-
- })
递归遍历:
- gltf.scene.traverse(function(obj) {
-
- console.log('所有模型节点的名称',obj.name);
-
- // obj.isMesh:if判断模型对象obj是不是网格模型'Mesh'
-
- if (obj.isMesh) {//判断条件也可以是obj.type === 'Mesh'
-
- obj.material.color.set(0xffff00);
-
- }
-
- });
·代码方式解决多个mesh共享材质的问题:
- //用代码方式解决mesh共享材质问题
-
- gltf.scene.getObjectByName("小区房子").traverse(function (obj) {
-
- if (obj.isMesh) {
-
- // .material.clone()返回一个新材质对象,和原来一样,重新赋值给.material属性
-
- obj.material = obj.material.clone();
-
- }
-
- });
·PBR材质:
金属度属性.metalness表示材质像金属的程度, 非金属材料,如木材或石材,使用0.0,金属使用1.0;
threejs的PBR材质,.metalness默认是0.5,0.0到1.0之间的值可用于生锈的金属外观
- new THREE.MeshStandardMaterial({
-
- metalness: 1.0,//金属度属性
-
- })
或
mesh.material.metalness = 1.0;//金属度
粗糙度roughness
表示模型表面的光滑或者说粗糙程度,越光滑镜面反射能力越强,越粗糙,表面镜面反射能力越弱,更多地表现为漫反射。生活中例如地面比较粗糙,比如镜子表面就非常非常光滑。
- new THREE.MeshStandardMaterial({
-
- roughness: 0.5,//表面粗糙度
-
- })
或
- mesh.material.roughness = 0.5;//表面粗糙度
-
-
-
- obj.material.roughness = 0.0;//完全镜面反射,像镜子一样
环境贴图.envMap(金属效果):环境贴图对PBR材质渲染效果影响还是比较大,一般渲染PBR材质的模型,最好设置一个合适的环境贴图。
// 加载环境贴图
- const textureCube = new THREE.CubeTextureLoader()
-
- .setPath('./环境贴图/环境贴图0/')
-
- .load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']);
-
- new THREE.MeshStandardMaterial({
-
- metalness: 1.0,
-
- roughness: 0.5,
-
- envMap: textureCube, //设置pbr材质环境贴图
-
- })
-
- obj.material.envMap = textureCube; //设置环境贴图
MeshStandardMaterial的.envMapIntensity属性相当于环境贴图的系数,环境贴图像素值乘以该系数后,在用于影响模型表面
// envMapIntensity:控制环境贴图对mesh表面影响程度
//默认值1, 设置为0.0,相当于没有环境贴图
obj.material.envMapIntensity = 1.0;
如果一个gltf模型中所有的Mesh都要设置环境贴图就需要递归遍历gltf模型,给里面每个Mesh的材质设置.envMap:
- loader.load("../工厂.glb", function (gltf) {
-
- // 递归遍历批量设置环境贴图
-
- gltf.scene.traverse(function (obj) {
-
- if (obj.isMesh) { //判断是否是网格模型
-
- obj.material.envMap = textureCube; //设置环境贴图
-
- }
-
- });
-
- })
如果你希望环境贴图影响场景中scene所有Mesh,可以通过Scene的场景环境属性.environment实现,把环境贴图对应纹理对象设置为.environment的属性值即可:
// 环境贴图纹理对象textureCube作为.environment属性值,影响所有模型
scene.environment = textureCube;
环境贴图需要保持一致(旧版)
//如果renderer.outputEncoding=THREE.sRGBEncoding;
textureCube.encoding = THREE.sRGBEncoding;
MeshPhysicalMaterial:
MeshPhysicalMaterial和MeshStandardMaterial都是拥有金属度metalness、粗糙度roughness属性的PBR材质,MeshPhysicalMaterial是在MeshStandardMaterial基础上扩展出来的子类,除了继承了MeshStandardMaterial的金属度、粗糙度等属性,还新增了清漆.clearcoat、透光率.transmission、反射率.reflectivity、光泽.sheen、折射率.ior等等各种用于模拟生活中不同材质的属性
清漆层属性.clearcoat:以用来模拟物体表面一层透明图层,就好比你在物体表面刷了一层透明清漆,喷了点水。.clearcoat的范围0到1,默认0。
- const material = new THREE.MeshPhysicalMaterial( {
-
- clearcoat: 1.0,//物体表面清漆层或者说透明涂层的厚度
-
- } );
-
- const material = new THREE.MeshPhysicalMaterial( {
-
- clearcoat: 1.0,//物体表面清漆层或者说透明涂层的厚度
-
- clearcoatRoughness: 0.1,//透明涂层表面的粗糙度
-
- } );
车外壳PBR材质设置:
- const mesh = gltf.scene.getObjectByName('外壳01');
-
- mesh.material = new THREE.MeshPhysicalMaterial({
-
- color: mesh.material.color, //默认颜色
-
- metalness: 0.9,//车外壳金属度
-
- roughness: 0.5,//车外壳粗糙度
-
- envMap: textureCube, //环境贴图
-
- envMapIntensity: 2.5, //环境贴图对Mesh表面影响程度
-
- })
车外壳油漆效果:
车外壳油漆效果,你可以通过PBR材质的清漆层属性.clearcoat和清漆层粗糙度.clearcoatRoughness属性模拟。
- const mesh = gltf.scene.getObjectByName('外壳01');
-
- mesh.material = new THREE.MeshPhysicalMaterial( {
-
- clearcoat: 1.0,//物体表面清漆层或者说透明涂层的厚度
-
- clearcoatRoughness: 0.1,//透明涂层表面的粗糙度
-
- } );
透光率(透射度).transmission
模拟玻璃、半透明塑料一类的视觉效果
- const mesh = gltf.scene.getObjectByName('玻璃01')
-
- mesh.material = new THREE.MeshPhysicalMaterial({
-
- transmission: 1.0, //玻璃材质透光率,transmission替代opacity
-
- })
折射率.ior
非金属材料的折射率从1.0到2.333。默认值为1.5
- new THREE.MeshPhysicalMaterial({
-
- ior:1.5,//折射率
-
- })
玻璃效果
- const mesh = gltf.scene.getObjectByName('玻璃01')
-
- mesh.material = new THREE.MeshPhysicalMaterial({
-
- metalness: 0.0,//玻璃非金属
-
- roughness: 0.0,//玻璃表面光滑
-
- envMap:textureCube,//环境贴图
-
- envMapIntensity: 1.0, //环境贴图对Mesh表面影响程度
-
- transmission: 1.0, //玻璃材质透光率,transmission替代opacity
-
- ior:1.5,//折射率
-
- })
·canvas背景透明度:
若要canvas画布完全透明,可以透过canvas画布看到画布后面叠加的HTML元素图文,呈现出来一种三维模型悬浮在网页上面的效果
//改变背景透明度值
renderer.setClearAlpha(0.8);
//完全透明
renderer.setClearAlpha(0.0);
// 在构造函数参数中设置alpha属性的值
- var renderer = new THREE.WebGLRenderer({
-
- alpha: true
-
- });
//设置背景颜色和透明度
renderer.setClearColor(0xb9d3ff, 0.4);
·canvas下载为图片:
- // 1.WebGL渲染器设置
-
- const renderer = new THREE.WebGLRenderer({
-
- //想把canvas画布上内容下载到本地,需要设置为true
-
- preserveDrawingBuffer:true,
-
- });
- // 2.鼠标单击id为download的HTML元素,threejs渲染结果以图片形式下载到本地
-
- document.getElementById('download').addEventListener('click',function(){
-
- // 创建一个超链接元素,用来下载保存数据的文件
-
- const link = document.createElement('a');
-
- // 通过超链接herf属性,设置要保存到文件中的数据
-
- link.href = renderer.domElement.toDataURL("image/png");
-
- link.download = 'threejs.png'; //下载文件名
-
- link.click(); //js代码触发超链接元素a的鼠标点击事件,开始下载文件到本地
-
- })
·深度冲突(模型闪烁):
- // WebGL渲染器设置
-
- const renderer = new THREE.WebGLRenderer({
-
- // 设置对数深度缓冲区,优化深度冲突问题
-
- logarithmicDepthBuffer: true
-
- });
有一点要注意,当两个面间隙过小,或者重合,你设置webgl渲染器对数深度缓冲区也是无效的。
·颜色插值用于渐变渲染:
- loader.load("../地形.glb", function (gltf) {
-
- model.add(gltf.scene);
-
- const mesh = gltf.scene.children[0];
-
- const pos = mesh.geometry.attributes.position;
-
- const count = pos.count;
-
-
-
- // 1. 计算模型y坐标高度差
-
- const yArr = [];//顶点所有y坐标,也就是地形高度
-
- for (let i = 0; i < count; i++) {
-
- yArr.push(pos.getY(i));//获取顶点y坐标,也就是地形高度
-
- }
-
- yArr.sort();//数组元素排序,从小到大
-
- const miny = yArr[0];//y最小值
-
- const maxy = yArr[yArr.length - 1];//y最大值
-
- const height = maxy - miny; //山脉整体高度
-
-
-
- // 2. 计算每个顶点的颜色值
-
- const colorsArr = [];
-
- const c1 = new THREE.Color(0x0000ff);//山谷颜色
-
- const c2 = new THREE.Color(0xff0000);//山顶颜色
-
- for (let i = 0; i < count; i++) {
-
- //当前高度和整体高度比值
-
- const percent = (pos.getY(i) - miny) / height;
-
- const c = c1.clone().lerp(c2, percent);//颜色插值计算
-
- colorsArr.push(c.r, c.g, c.b);
-
- }
-
- const colors = new Float32Array(colorsArr);
-
- // 设置几何体attributes属性的颜色color属性
-
- mesh.geometry.attributes.color = new THREE.BufferAttribute(colors, 3);
-
- // 3. 设置材质,使用顶点颜色渲染
-
- mesh.material = new THREE.MeshLambertMaterial({
-
- vertexColors:true
-
- });
-
- })
·计算模型最小包围盒:
- //包围盒设置
-
- const box3 = new THREE.Box3()
-
- console.log('box3',box3);
-
- box3.min = new THREE.Vector3(-10, -10,0);
-
- box3.max = new THREE.Vector3(100, 20,50);
-
-
-
- //计算模型最小包围盒.expandByObject()
-
- const box3 = new THREE.Box3();
-
- box3.expandByObject(mesh); // 计算模型最小包围盒
-
- console.log('查看包围盒',box3);
-
-
-
- //包围盒尺寸.getSize()
-
- const scale = new THREE.Vector3()
-
- // getSize()计算包围盒尺寸
-
- // 获得包围盒长宽高尺寸,结果保存在参数三维向量对象scale中
-
- box3.getSize(scale)
-
- console.log('模型包围盒尺寸', scale);
-
-
-
- //包围盒几何中心.getCenter()
-
- // 计算包围盒中心坐标
-
- const center = new THREE.Vector3()
-
- box3.getCenter(center)
-
- console.log('模型中心坐标', center);
·相机position改变后要重新设置lookAt:
// 通过UI按钮改变相机观察角度
- document.getElementById('x').addEventListener('click', function () {
-
- camera.position.set(500, 0, 0); //x轴方向观察
-
- camera.lookAt(0, 0, 0); //重新计算相机视线方向
-
- })
·OrbitControls:
- controls.enablePan = false; //禁止右键拖拽
-
- controls.enableZoom = false;//禁止缩放
-
- controls.enableRotate = false; //禁止旋转
-
- controls.enableDamping false; //将其设置为true以启用阻尼(惯性)
-
-
-
- 相机控件OrbitControls.target属性对应的就是相机的.lookAt()观察目标。
-
- 执行controls.update();,相机控件内部会执行camera.lookAt(controls.target)。
-
- // controls.target默认值是坐标原点
-
- controls.target.set(x, y, z);
-
- //update()函数内会执行camera.lookAt(x, y, z)
-
- controls.update();
-
-
-
- //相机位置与观察目标点最小值
-
- controls.minDistance = 200;
-
- //相机位置与观察目标点最大值
-
- controls.maxDistance = 500;
-
- //相机位置与目标观察点距离
-
- const dis = controls.getDistance();
-
- console.log('dis',dis);
-
-
-
- // 缩放范围(正投影相机)
-
- controls.minZoom = 0.5;
-
- controls.maxZoom = 2;
-
-
-
- 辅助计算相机位置与目标观察点距离
-
- controls.addEventListener('change',function(){
-
- //相机位置与目标观察点距离
-
- const dis = controls.getDistance();
-
- console.log('dis',dis);
-
- })
-
-
-
- // 上下旋转范围
-
- controls.minPolarAngle = 0;//默认值0
-
- controls.maxPolarAngle = Math.PI;//默认值Math.PI
-
-
-
- // 左右旋转范围
-
- controls.minAzimuthAngle = -Math.PI/2; //范围[-2 * Math.PI,2 * Math.PI]
-
- controls.maxAzimuthAngle = Math.PI/2; //范围[-2 * Math.PI,2 * Math.PI]
-
-
-
- .autoRotate:Boolean将其设为true,以自动围绕目标旋转
-
- .autoRotateSpeed : Float围绕目标旋转的速度将有多快,默认值为2.0,相当于在60fps时每旋转一周需要30秒
-
- .dampingFactor : Float当.enableDamping设置为true的时候,阻尼惯性有多大。 默认0.05,请注意,要使得这一值生效,你必须在你的动画循环里调用.update()。
-
- .panSpeed : Float位移的速度,其默认值为1。
-
- .rotateSpeed : Float旋转的速度,其默认值为1。
·灯光设置:
- //1.聚光灯
-
- // 聚光源
-
- // 0xffffff:光源颜色
-
- // 1.0:光照强度intensity
-
- const spotLight = new THREE.SpotLight(0xffffff,1.0);
-
- scene.add(spotLight);//光源添加到场景中
-
- spotLight.intensity = 1.0;//光照强度
-
- // 设置聚光光源发散角度
-
- spotLight.angle = Math.PI / 6;//光锥角度的二分之一
-
- // 设置聚光光源位置
-
- spotLight.position.set(0, 50, 0);
-
- // spotLight.target是一个模型对象Object3D,默认在坐标原点
-
- spotLight.target.position.set(50,0,0);
-
- //spotLight.target添加到场景中.target.position才会起作用
-
- scene.add(spotLight.target);
-
-
-
-
-
- 平行光DirectionalLight阴影步骤
-
- // 1.设置产生投影的网格模型
-
- mesh.castShadow = true;
-
- // 2.设置产生投影的光源
-
- //平行光
-
- const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
-
- // 平行光设置产生阴影的光源对象,开启光源阴影的计算功能
-
- directionalLight.castShadow = true;
-
- //3.设置接收阴影的投影面
-
- planeMesh.receiveShadow = true;
-
- // 4.设置渲染器,允许光源阴影渲染
-
- renderer.shadowMap.enabled = true;
-
- // 5.设置三维场景计算阴影的范围
-
- directionalLight.shadow.camera.left = -50;
-
- directionalLight.shadow.camera.right = 50;
-
- directionalLight.shadow.camera.top = 200;
-
- directionalLight.shadow.camera.bottom = -100;
-
- directionalLight.shadow.camera.near = 0.5;
-
- directionalLight.shadow.camera.far = 600;
-
-
-
- // 如果阴影边缘锯齿感的时候,可以适当提升像素
-
- directionalLight.shadow.mapSize.set(1024,1024);
-
- directionalLight.shadow.mapSize.set(2048,2048);
-
- 适当提升.shadow.radius,你可以感到阴影边缘与非阴影区域是渐变过渡,或者说阴影边缘逐渐弱化或模糊化,没有很明显的边界感。
-
- directionalLight.shadow.radius = 3;
-
-
-
- // 模型表面产生条纹影响渲染效果,可以改变.shadowMap.type默认值优化
-
- renderer.shadowMap.type = THREE.VSMShadowMap;
·动画:
- // 给需要设置关键帧动画的模型命名
-
- mesh.name = "Box";
-
- const times = [0, 3, 6]; //时间轴上,设置三个时刻0、3、6秒
-
- // times中三个不同时间点,物体分别对应values中的三个xyz坐标
-
- const values = [0, 0, 0, 100, 0, 0, 0, 0, 100];
-
- // 0~3秒,物体从(0,0,0)逐渐移动到(100,0,0),3~6秒逐渐从(100,0,0)移动到(0,0,100)
-
- const posKF = new THREE.KeyframeTrack('Box.position', times, values);
-
- // 从2秒到5秒,物体从红色逐渐变化为蓝色
-
- const colorKF = new THREE.KeyframeTrack('Box.material.color', [2, 5], [1, 0, 0, 0, 0, 1]);
-
- // 1.3 基于关键帧数据,创建一个clip关键帧动画对象,命名"test",持续时间6秒。
-
- const clip = new THREE.AnimationClip("test", 6, [posKF, colorKF]);
-
- //包含关键帧动画的模型对象作为AnimationMixer的参数创建一个播放器mixer
-
- const mixer = new THREE.AnimationMixer(mesh);
-
- //AnimationMixer的`.clipAction()`返回一个AnimationAction对象
-
- const clipAction = mixer.clipAction(clip);
-
- //.play()控制动画播放,默认循环播放
-
- clipAction.play();
-
-
-
- const clock = new THREE.Clock();
-
- function loop() {
-
- requestAnimationFrame(loop);
-
- //clock.getDelta()方法获得loop()两次执行时间间隔
-
- const frameT = clock.getDelta();
-
- // 更新播放器相关的时间
-
- mixer.update(frameT);
-
- }
-
- loop();
-
-
-
- 其他播放控制
-
- //不循环播放
-
- clipAction.loop = THREE.LoopOnce;
-
- 上面播放一次后模型会回到关键帧动画开头状态,若想停留在最后一帧执行
-
- // 物体状态停留在动画结束的时候
-
- clipAction.clampWhenFinished = true;
-
- //动画停止结束,回到开始状态
-
- clipAction.stop();
-
-
-
- // AnimationAction.paused默认值false,设置为true,可以临时暂停动画
-
- if (clipAction.paused) {//暂停状态
-
- clipAction.paused = false;//切换为播放状态
-
- } else {//播放状态
-
- clipAction.paused = true;//切换为暂停状态
-
- }
-
-
-
- 倍速播放.timeScale
-
- clipAction.timeScale = 1;//默认
-
- clipAction.timeScale = 2;//2倍速
-
-
-
- 关键帧时长
-
- clip.duration
-
-
-
- //AnimationAction设置开始播放时间:从1秒时刻对应动画开始播放
-
- clipAction.time = 1;
-
- //AnimationClip设置播放结束时间:到5秒时刻对应的动画状态停止
-
- clip.duration = 5;
-
- //不循环播放
-
- clipAction.loop = THREE.LoopOnce;
-
- // 物体状态停留在动画结束的时候
-
- clipAction.clampWhenFinished=true;
外部模型动画:
- //包含关键帧动画的模型对象作为AnimationMixer的参数创建一个播放器mixer
-
- const mixer = new THREE.AnimationMixer(mesh);
-
-
-
-
-
- THREE.LoopOnce - 只执行一次
-
- THREE.LoopRepeat - 重复次数为repetitions的值, 且每次循环结束时候将回到起始动作开始下一次循环。
-
- THREE.LoopPingPong - 重复次数为repetitions的值, 且像乒乓球一样在起始点与结束点之间来回循环。
-
-
-
- .timeScale : Number
-
- 时间(time)的比例因子. 值为0时会使动画暂停。值为负数时动画会反向执行。默认值是1。
-
-
-
- const IdleAction = mixer.clipAction(gltf.animations[0]);
-
- const RunAction = mixer.clipAction(gltf.animations[1]);
-
- const WalkAction = mixer.clipAction(gltf.animations[3]);
-
- IdleAction.play();
-
- let ActionState = IdleAction;//当前处于播放状态的动画动作对象
-
- // 通过UI按钮控制,切换动画运动状态
-
- document.getElementById('Idle').addEventListener('click', function () {
-
- ActionState.stop();//播放状态动画终止
-
- IdleAction.play();
-
- ActionState = IdleAction;
-
- })
-
- document.getElementById('Run').addEventListener('click', function () {
-
- ActionState.stop();//播放状态动画终止
-
- RunAction.play();
-
- ActionState = RunAction;
-
- })
-
- document.getElementById('Walk').addEventListener('click', function () {
-
- ActionState.stop();//播放状态动画终止
-
- WalkAction.play();
-
- ActionState = WalkAction;
-
- })
-
-
-
- // 向量的x、y、z坐标分别在pos基础上增加30
-
- const pos2 = pos.clone().addScalar(30);
-
-
-
- // 切换到设备A预览状态
-
- document.getElementById('A').addEventListener('click', function () {
-
- const A = model.getObjectByName('设备A标注');
-
- const pos = new THREE.Vector3();
-
- A.getWorldPosition(pos); //获取三维场景中某个对象世界坐标
-
- // 相机飞行到的位置和观察目标拉开一定的距离
-
- const pos2 = pos.clone().addScalar(30);//向量的x、y、z坐标分别在pos基础上增加30
-
- // 相机从当前位置camera.position飞行三维场景中某个世界坐标附近
-
- new TWEEN.Tween({
-
- // 相机开始坐标
-
- x: camera.position.x,
-
- y: camera.position.y,
-
- z: camera.position.z,
-
- // 相机开始指向的目标观察点
-
- tx: 0,
-
- ty: 0,
-
- tz: 0,
-
- })
-
- .to({
-
- // 相机结束坐标
-
- x: pos2.x,
-
- y: pos2.y,
-
- z: pos2.z,
-
- // 相机结束指向的目标观察点
-
- tx: pos.x,
-
- ty: pos.y,
-
- tz: pos.z,
-
- }, 2000)
-
- .onUpdate(function (obj) {
-
- // 动态改变相机位置
-
- camera.position.set(obj.x, obj.y, obj.z);
-
- // 动态计算相机视线
-
- camera.lookAt(obj.tx, obj.ty, obj.tz);
-
- })
-
- .start();
-
- })
-
- 动画结束更新OrbitControls
-
- .onUpdate(function (obj) {
-
- ...
-
- camera.lookAt(obj.tx, obj.ty, obj.tz);
-
- })
-
- .onComplete(function(obj){
-
- controls.target.set(obj.tx, obj.ty, obj.tz);
-
- controls.update();
-
- })
封装一个相机动画函数
- // 相机动画函数,从A点飞行到B点,A点表示相机当前所处状态
-
- // pos: 三维向量Vector3,表示动画结束相机位置
-
- // target: 三维向量Vector3,表示相机动画结束lookAt指向的目标观察点
-
- function createCameraTween(endPos,endTarget){
-
- new TWEEN.Tween({
-
- // 不管相机此刻处于什么状态,直接读取当前的位置和目标观察点
-
- x: camera.position.x,
-
- y: camera.position.y,
-
- z: camera.position.z,
-
- tx: controls.target.x,
-
- ty: controls.target.y,
-
- tz: controls.target.z,
-
- })
-
- .to({
-
- // 动画结束相机位置坐标
-
- x: endPos.x,
-
- y: endPos.y,
-
- z: endPos.z,
-
- // 动画结束相机指向的目标观察点
-
- tx: endTarget.x,
-
- ty: endTarget.y,
-
- tz: endTarget.z,
-
- }, 2000)
-
- .onUpdate(function (obj) {
-
- // 动态改变相机位置
-
- camera.position.set(obj.x, obj.y, obj.z);
-
- // 动态计算相机视线
-
- // camera.lookAt(obj.tx, obj.ty, obj.tz);
-
- controls.target.set(obj.tx, obj.ty, obj.tz);
-
- controls.update();//内部会执行.lookAt()
-
- })
-
- .start();
-
- }
-
-
-
- 设置设备A、设备B、停车场、整体预览四个按钮对应的相机动画,这样你可以在4个按钮之间,随意切换相机的观察状态
-
- // 切换到设备A预览状态
-
- document.getElementById('A').addEventListener('click', function () {
-
- const A = model.getObjectByName('设备A标注');
-
- const pos = new THREE.Vector3();
-
- A.getWorldPosition(pos); //获取三维场景中某个对象世界坐标
-
- // 相机飞行到的位置和观察目标拉开一定的距离
-
- const pos2 = pos.clone().addScalar(30);
-
- createCameraTween(pos2, controls.target)
-
- })
-
- // 切换到设备B的预览状态
-
- document.getElementById('B').addEventListener('click', function () {
-
- const B = model.getObjectByName('设备B标注');
-
- const pos = new THREE.Vector3();
-
- B.getWorldPosition(pos); //获取三维场景中某个对象世界坐标
-
- // 相机飞行到的位置和观察目标拉开一定的距离
-
- const pos2 = pos.clone().addScalar(30);
-
- // 相机从当前位置camera.position飞行三维场景中某个世界坐标附近
-
- createCameraTween(pos2, controls.target)
-
- })
-
-
-
- // 切换到设备停车场的预览状态
-
- document.getElementById('car').addEventListener('click', function () {
-
- const car = model.getObjectByName('停车场标注');
-
- const pos = new THREE.Vector3();
-
- car.getWorldPosition(pos); //获取三维场景中某个对象世界坐标
-
- // 相机飞行到的位置和观察目标拉开一定的距离
-
- const pos2 = pos.clone().addScalar(30);
-
- // 相机从当前位置camera.position飞行三维场景中某个世界坐标附近
-
- createCameraTween(pos2, pos)
-
- })
-
-
-
- // 相机整体预览对应的位置和观察目标
-
- const cameraPos0 = new THREE.Vector3(202, 123, 125)
-
- const target0 = new THREE.Vector3(0, 0, 0);
-
- // 切换整体预览状态
-
- document.getElementById('all').addEventListener('click', function () {
-
- // 相机从当前位置camera.position回到整体预览状态
-
- createCameraTween(cameraPos0, target0)
-
- })
-
-
-
- // 动画开始缓动方式(类比加速启动)
-
- tween.easing(TWEEN.Easing.Sinusoidal.In);
-
- // 动画结束缓动方式(类比减速刹车)
-
- tween.easing(TWEEN.Easing.Sinusoidal.Out);
-
- // 同时设置In和Out
-
- tween.easing(TWEEN.Easing.Sinusoidal.InOut);
·贴图颜色矫正:
注意贴图颜色矫正:
materialClouds.map.colorSpace = THREE.SRGBColorSpace;