• threejs要点你值得学习


    ·webgl兼容性判断

    1. if (WebGL.isWebGLAvailable()) {
    2.     // Initiate function or other initializations here
    3.     animate();
    4. } else {
    5.     const warning = WebGL.getWebGLErrorMessage();
    6.     document.getElementById('container').appendChild(warning);
    7. }

    ·gltf-viewer在线预览gltf模型

    https://gltf-viewer.donmccurdy.com

    ·threejs论坛

    1. 论坛:https://discourse.threejs.org
    2. 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是需要重新计算相机投影矩阵:

    1. camera.aspect = window.innerWidth / window.innerHeight;
    2. camera.updateProjectionMatrix();

    相机的viewport进行多相机设置,可以达到一屏显示多个模型效果

    ·对象销毁:

    1. 几何体销毁:Geometry.dispose()
    2. 材质销毁:Material.dispose()
    3. 纹理的销毁:Texture.dispose()

    ·矩阵更新

    静态的object3d对象在改变属性(position,quaternion和scale属性),希望手动更新矩阵时

    1. object.matrixAutoUpdate = false;
    2. object.updateMatrix();

    Three.js使用matrix编码3D变换 —— 平移(位置),旋转和缩放

    ·立方体贴图:

    1. const loader = new THREE.CubeTextureLoader();
    2. loader.setPath( 'textures/cube/pisa/' );
    3. const textureCube = loader.load( [
    4. 'px.png', 'nx.png',
    5. 'py.png', 'ny.png',
    6. 'pz.png', 'nz.png'
    7. ] );
    8. const material = new THREE.MeshBasicMaterial( { color: 0xffffff, envMap: textureCube } );
    9. renderer = new THREE.WebGLRenderer( { antialias: true } );

    ·窗口改变适配

    1. function onWindowResize() {
    2. camera.aspect = window.innerWidth / window.innerHeight;
    3. camera.updateProjectionMatrix();
    4. renderer.setSize( window.innerWidth, window.innerHeight );
    5. }
    6. function animate() {
    7. requestAnimationFrame( animate );
    8. render();
    9. }
    10. function render() {
    11. controls.update( clock.getDelta() );
    12. renderer.render( scene, camera );
    13. }

    ·材质:

    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中设置环境贴图的方式如下: 

    1. scene.background
    2. = new THREE.CubeTextureLoader().setPath('相对目录文件夹,里边包含6张贴图/').load( [
    3. 'posx.jpg',
    4.  'negx.jpg',
    5.  'posy.jpg',
    6.  'negy.jpg',
    7.  'posz.jpg',
    8.  'negz.jpg'
    9. ] ); 

    图片的排放顺序依次是:x轴正方向-x轴负方向-y轴正方向-y轴负方向-z轴正方向-z轴负方向;

    按照Three.js创建的默认坐标系中,图片对应的方位是:右侧-左侧-上边-下边-前边-后边;将背景设置成以上的贴图即可显示效果

    ·加载管理器

    1. THREE.DefaultLoadingManager.onStart = function ( url, itemsLoaded, itemsTotal ) {
    2. console.log( 'Started loading file: ' + url + '.\nLoaded ' + itemsLoaded + ' of ' + itemsTotal + ' files.' );
    3. };
    4. THREE.DefaultLoadingManager.onLoad = function ( ) {
    5. console.log( 'Loading Complete!');
    6. };
    7. THREE.DefaultLoadingManager.onProgress = function ( url, itemsLoaded, itemsTotal ) {
    8. console.log( 'Loading file: ' + url + '.\nLoaded ' + itemsLoaded + ' of ' + itemsTotal + ' files.' );
    9. };
    10. THREE.DefaultLoadingManager.onError = function ( url ) {
    11. console.log( 'There was an error loading ' + url );
    12. };

    ·启用缓存:

    THREE.Cache.enabled = true;

    ·光源:

    1. AmbientLight:环境光会均匀的照亮场景中的所有物体。环境光不能用来投射阴影,因为它没有方向。
    2. HemisphereLight:不能投射阴影

    ·gltf模型颜色空间

    1. renderer.outputColorSpace = THREE.SRGBColorSpace;
    2. texture.colorSpace = THREE.SRGBColorSpace;
    3. texture.flipY = false;
    4. // renderer
    5. renderer = new THREE.WebGLRenderer( { antialias: true } );
    6. renderer.setPixelRatio( window.devicePixelRatio );
    7. renderer.setSize( window.innerWidth, window.innerHeight );

    文档一

    7. 三维坐标系-加强三维空间认识 | Three.js中文网

    ·受光影响的材质:

    ·光源

    ·全屏布局注意CSS的设置:

    ·画布宽度变化:

    1. // onresize 事件会在窗口被调整大小时发生
    2. window.onresize = function () {
    3.     // 重置渲染器输出画布canvas尺寸
    4.     renderer.setSize(window.innerWidth, window.innerHeight);
    5.     // 全屏情况下:设置观察范围长宽比aspect为窗口宽高比
    6.     camera.aspect = window.innerWidth / window.innerHeight;
    7.     // 渲染器执行render方法的时候会读取相机对象的投影矩阵属性projectionMatrix
    8.     // 但是不会每渲染一帧,就通过相机的属性计算投影矩阵(节约计算资源)
    9.     // 如果相机的一些属性发生了变化,需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
    10.     camera.updateProjectionMatrix();
    11. };

    ·渲染器设置:

    1. 1)抗锯齿设置
    2. const renderer = new THREE.WebGLRenderer({
    3.   antialias:true,
    4. });
    5. 2)设备像素比
    6. renderer.setPixelRatio(window.devicePixelRatio)

    ·旋转、平移、缩放:

    ·几何体居中:

    已经偏移的几何体居中,执行.center(),你可以看到几何体重新与坐标原点重合

    geometry.center();

    ·沿着自定义方向移动:

    1. //向量Vector3对象表示方向
    2. const axis = new THREE.Vector3(1, 1, 1);
    3. axis.normalize(); //向量归一化
    4. //沿着axis轴表示方向平移100
    5. mesh.translateOnAxis(axis, 100);

    ·绕某个方向旋转:

    1. const axis = new THREE.Vector3(0,1,0);//向量axis
    2. mesh.rotateOnAxis(axis,Math.PI/8);//绕axis轴旋转π/8

    ·颜色改变:

    1. // 查看Color对象设置0x00ff00对应的的.r、.g、.b值
    2. const color = new THREE.Color(0x00ff00);
    3. color.setRGB(0,1,0);//RGB方式设置颜色
    4. color.setHex(0x00ff00);//十六进制方式设置颜色
    5. color.setStyle('#00ff00');//前端CSS颜色值设置颜色
    6. color.set(0x00ff00);//十六进制方式设置颜色
    7. color.set('#00ff00');//前端CSS颜色值设置颜色
    8. material.color.set(0x00ffff);

    ·材质透明度设置:

    1. material.transparent = true;//开启透明
    2. material.opacity = 0.5;//设置透明度

    ·材质面:

    1. material.side = THREE.BackSide;//背面可以看到
    2. material.side = THREE.DoubleSide;//双面可见

    ·材质或几何体共享

    1. const mesh = new THREE.Mesh(geometry, material);
    2. const mesh2 = new THREE.Mesh(geometry, material);
    3. mesh2.position.x = 100;
    4. // 两个mesh共享一个材质,改变一个mesh的颜色,另一个mesh2的颜色也会跟着改变
    5. // mesh.material和mesh2.material都指向同一个material
    6. // 三者等价:mesh.material、mesh2.material、material
    7. mesh.material.color.set(0xffff00);
    8. // 三者等价:mesh.geometry、mesh2.geometry、geometry
    9. mesh.geometry.translate(0,100,0);

    ·克隆和复制:

    1. 克隆是复制并创建一个与原对象一样的新对象:
    2. const v1 = new THREE.Vector3(1, 2, 3);
    3. console.log('v1',v1);
    4. //v2是一个新的Vector3对象,和v1的.x、.y、.z属性值一样
    5. const v2 = v1.clone();
    6. console.log('v2',v2);
    7. 复制是把原对象的属性值赋值给另一个对象:
    8. const v1 = new THREE.Vector3(1, 2, 3);
    9. console.log('v1',v1);
    10. //v2是一个新的Vector3对象,和v1的.x、.y、.z属性值一样
    11. const v2 = v1.clone();
    12. console.log('v2',v2);

    克隆位置:

    1. mesh.position.copy(mesh2.position);//1. 第1步位置重合
    2. mesh.position.y += 100;//1. 第2步mesh在原来y的基础上增加100

    克隆几何体:

    1. const mesh2 = mesh.clone();
    2. // 克隆几何体和材质,重新设置mesh2的材质和几何体属性
    3. mesh2.geometry = mesh.geometry.clone();
    4. mesh2.material = mesh.material.clone();
    5. // 改变mesh2颜色,不会改变mesh的颜色
    6. mesh2.material.color.set(0xff0000);

    两个模型的姿态角度始终保持一样:

    // 渲染循环

    1. function render() {
    2.     mesh.rotateY(0.01);// mesh旋转动画
    3.     // 同步mesh2和mesh的姿态角度一样,不管mesh姿态角度怎么变化,mesh2始终保持同步
    4.     mesh2.rotation.copy(mesh.rotation);
    5.     renderer.render(scene, camera);
    6.     requestAnimationFrame(render);
    7. }
    8. render();

    ·分组Group:

    1. //把mesh1型插入到组group中,mesh1作为group的子对象
    2. group.add(mesh1);
    3. //把mesh2型插入到组group中,mesh2作为group的子对象
    4. group.add(mesh2);
    5. group.add(mesh1,mesh2);
    6. //把group插入到场景中作为场景子对象
    7. scene.add(group);
    8. console.log('查看group的子对象',group.children);
    9. 父对象旋转缩放平移变换,子对象跟着变化
    10. //沿着Y轴平移mesh1和mesh2的父对象,mesh1和mesh2跟着平移
    11. group.translateY(100);
    12. //父对象缩放,子对象跟着缩放
    13. group.scale.set(4,4,4);
    14. //父对象旋转,子对象跟着旋转
    15. group.rotateY(Math.PI/6)
    16. mesh也能添加mesh子对象:
    17. mesh1.add(mesh2);

    ·按名字查询模型组件:

    1. / 返回名.name"4号楼"对应的对象
    2. const nameNode = scene.getObjectByName ("4号楼");
    3. nameNode.material.color.set(0xff0000);

    ·本地坐标与世界坐标:

    本地(局部)坐标就是模型的position属性;

    世界坐标是模型自身.position和所有父对象.position累加的坐标,通过getWorldPosition获取。

    读取一个模型的世界坐标,并把读取结果存储到参数Vector3中:

    mesh.getWorldPosition(Vector3)

    给子对象添加一个局部坐标系:

    1. const meshAxesHelper = new THREE.AxesHelper(50);
    2. mesh.add(meshAxesHelper);

    ·移除对象:

    1. // 删除父对象group的子对象网格模型mesh1:
    2. group.remove(mesh1);
    3. scene移除:
    4. scene.remove(ambient);//移除场景中环境光
    5. scene.remove(model);//移除场景中模型对象
    6. 查看移除后的对象:
    7. console.log('查看group的子对象',group.children);
    8. 一次移除多个:
    9. group.remove(mesh1,mesh2);

    ·模型隐藏与显示:

    1. 模型属性.visible隐藏或显示:
    2. mesh.visible =false;// 隐藏一个网格模型,visible的默认值是true
    3. group.visible =false;// 隐藏一个包含多个模型的组对象group
    4. mesh.visible =true;// 使网格模型mesh处于显示状态
    5. 材质属性.visible隐藏或显示:
    6. mesh.material.visible =false;

    ·顶点UV坐标的作用:

    顶点UV坐标的作用是从纹理贴图上提取像素映射到网格模型Mesh的几何体表面上。

    顶点UV坐标可以在0~1.0之间任意取值,纹理贴图左下角对应的UV坐标是(0,0),右上角对应的坐标(1,1)。

    /**纹理坐标0~1之间随意定义*/

    1. const uvs = new Float32Array([
    2.     0, 0, //图片左下角
    3.     1, 0, //图片右下角
    4.     1, 1, //图片右上角
    5.     0, 1, //图片左上角
    6. ]);
    7. // 设置几何体attributes属性的位置normal属性
    8. geometry.attributes.uv = new THREE.BufferAttribute(uvs, 2); //2个为一组,表示一个顶点的纹理坐标

    ·纹理对象Texture的阵列功能:

    1. // .load()方法加载图像,返回一个纹理对象Texture
    2. const texture = texLoader.load('./瓷砖.jpg');
    3. // 设置阵列模式
    4. texture.wrapS = THREE.RepeatWrapping;
    5. texture.wrapT = THREE.RepeatWrapping;
    6. // uv两个方向纹理重复数量
    7. texture.repeat.set(12,12);//注意选择合适的阵列数量

    ·旋转对象影响:

    注意旋转方向影响矩形平面背面还是正面朝上,threejs默认渲染正面,不渲染背面。

    ·png贴图:

    注意选择背景透明的.png图像作为颜色贴图,同时材质设置transparent: true,这样png图片背景完全透明的部分不显示。

    ·各种helper:

    1. 网格地面辅助观察:
    2. // 添加一个辅助网格地面
    3. const gridHelper = new THREE.GridHelper(300, 25, 0x004444, 0x004444);
    4. // AxesHelper:辅助观察的坐标系
    5. const axesHelper = new THREE.AxesHelper(150);
    6. scene.add(axesHelper);
    7. // 骨骼关节可以和普通网格模型一样作为其他模型子对象,添加到场景中
    8. const group = new THREE.Group();
    9. group.add(Bone1);
    10. // SkeletonHelper会可视化参数模型对象所包含的所有骨骼关节
    11. const skeletonHelper = new THREE.SkeletonHelper(group);
    12. group.add(skeletonHelper);
    13. // 聚广源辅助对象,可视化聚广源
    14. const spotLightHelper = new THREE.SpotLightHelper(spotLight,0xffffff)
    15. scene.add(spotLightHelper);
    16. //引入性能监视器stats.js
    17. import Stats from 'three/addons/libs/stats.module.js';
    18. //创建stats对象
    19. const stats = new Stats();
    20. //stats.domElement:web页面上输出计算结果,一个div元素,
    21. document.body.appendChild(stats.domElement);
    22. // 渲染函数
    23. function render() {
    24. //requestAnimationFrame循环调用的函数中调用方法update(),来刷新时间
    25. stats.update();
    26. renderer.render(scene, camera); //执行渲染操作
    27. requestAnimationFrame(render); //请求再次执行渲染函数render,渲染下一帧
    28. }
    29. render();
    30. GUI可视化改变三维场景:
    31. // 引入dat.gui.js的一个类GUI
    32. import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
    33. // 实例化一个gui对象
    34. const gui = new GUI();
    35. //改变交互界面style属性
    36. gui.domElement.style.right = '0px';
    37. gui.domElement.style.width = '300px';
    38. gui.add(mesh.position, 'x', 0, 180);
    39. gui.add(mesh.position, 'y', 0, 180);
    40. gui.add(mesh.position, 'z', 0, 180);
    41. matFolder.add(mesh.material,'metalness',0,1);
    42. matFolder.add(mesh.material,'roughness',0,1);
    43. matFolder.add(mesh.material,'clearcoat',0,1);
    44. matFolder.add(mesh.material,'clearcoatRoughness',0,1);
    45. matFolder.add(mesh.material,'envMapIntensity',0,10);
    46. const obj = {
    47.     color: mesh.material.color, // 材质颜色
    48. };
    49. // 材质颜色color
    50. matFolder.addColor(obj, 'color').onChange(function (value) {
    51.     mesh.material.color.set(value);
    52. });
    53. // 范围可以参考文档
    54. matFolder.add(mesh.material,'metalness',0,1);
    55. matFolder.add(mesh.material,'roughness',0,1);
    56. matFolder.add(mesh.material,'transmission',0,1);
    57. matFolder.add(mesh.material,'ior',0,3);
    58. matFolder.add(mesh.material,'envMapIntensity',0,10);

    ·画布居中:

    1. camera.lookAt(0, 0, 0);
    2. 注意相机控件OrbitControls会影响lookAt设置,注意手动设置OrbitControls的目标参数
    3. camera.lookAt(100, 0, 0);
    4. // 设置相机控件轨道控制器OrbitControls
    5. const controls = new OrbitControls(camera, renderer.domElement);
    6. // 相机控件.target属性在OrbitControls.js内部表示相机目标观察点,默认0,0,0
    7. // console.log('controls.target', controls.target);
    8. controls.target.set(100, 0, 0);
    9. controls.update();//update()函数内会执行camera.lookAt(controls.targe)

    ·纹理贴图颜色偏差解决(旧版):

    1. three.js加载gltf模型的时候,可能会遇到three.js渲染结果颜色偏差,对于这种情况,你只需要修改WebGL渲染器默认的编码方式.outputEncoding即可:
    2. //解决加载gltf格式模型纹理贴图和原图不一样问题
    3. renderer.outputEncoding = THREE.sRGBEncoding;
    4. 单独加载的纹理贴图的.encoding和webgl渲染器的.outputEncoding保持一致:
    5. const texLoader = new THREE.TextureLoader();
    6. const texture = texLoader.load('./黑色.png');// 加载手机mesh另一个颜色贴图
    7. texture.encoding = THREE.sRGBEncoding; //和渲染器.outputEncoding一样值

    新版是正确的不用设置:

    //新版本,加载gltf,不需要执行下面代码解决颜色偏差

    renderer.outputColorSpace = THREE.SRGBColorSpace;//设置为SRGB颜色空间

    注意:如果你直接给gltf模型材质设置.map属性更换贴图,会出现纹理贴图错位的问题,这主要和纹理对象Texture的翻转属性.flipY有关:

    1. 纹理对象Texture翻转属性.flipY默认值是true;
    2. gltf的贴图翻转属性.flipY默认值是false。

    故如果单独更换纹理贴图记得设置

    texture.flipY = false

    ·遍历对象

    //循环遍历

    1. const obj = gltf.scene.getObjectByName('洋房');
    2. console.log('obj', obj); //控制台查看返回结果
    3. console.log('obj.children', obj.children);
    4. // obj.children的所有子对象都是Mesh,改变Mesh对应颜色
    5. obj.children.forEach(function (mesh) {
    6.     mesh.material.color.set(0xffff00);
    7. })

    递归遍历:

    1. gltf.scene.traverse(function(obj) {
    2.     console.log('所有模型节点的名称',obj.name);
    3.     // obj.isMesh:if判断模型对象obj是不是网格模型'Mesh'
    4.     if (obj.isMesh) {//判断条件也可以是obj.type === 'Mesh'
    5.         obj.material.color.set(0xffff00);
    6.     }
    7. });

    ·代码方式解决多个mesh共享材质的问题:

    1. //用代码方式解决mesh共享材质问题
    2. gltf.scene.getObjectByName("小区房子").traverse(function (obj) {
    3.     if (obj.isMesh) {
    4.         // .material.clone()返回一个新材质对象,和原来一样,重新赋值给.material属性
    5.         obj.material = obj.material.clone();
    6.     }
    7. });

    ·PBR材质:

    金属度属性.metalness表示材质像金属的程度, 非金属材料,如木材或石材,使用0.0,金属使用1.0;

    threejs的PBR材质,.metalness默认是0.5,0.0到1.0之间的值可用于生锈的金属外观

    1. new THREE.MeshStandardMaterial({
    2.     metalness: 1.0,//金属度属性
    3. })

    mesh.material.metalness = 1.0;//金属度

    粗糙度roughness

    表示模型表面的光滑或者说粗糙程度,越光滑镜面反射能力越强,越粗糙,表面镜面反射能力越弱,更多地表现为漫反射。生活中例如地面比较粗糙,比如镜子表面就非常非常光滑。

    1. new THREE.MeshStandardMaterial({
    2.     roughness: 0.5,//表面粗糙度
    3. })

    1. mesh.material.roughness = 0.5;//表面粗糙度
    2. obj.material.roughness = 0.0;//完全镜面反射,像镜子一样

    环境贴图.envMap(金属效果):环境贴图对PBR材质渲染效果影响还是比较大,一般渲染PBR材质的模型,最好设置一个合适的环境贴图。

    // 加载环境贴图

    1. const textureCube = new THREE.CubeTextureLoader()
    2.     .setPath('./环境贴图/环境贴图0/')
    3.     .load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']);
    4. new THREE.MeshStandardMaterial({
    5.     metalness: 1.0,
    6.     roughness: 0.5,
    7.     envMap: textureCube, //设置pbr材质环境贴图
    8. })    
    9. obj.material.envMap = textureCube; //设置环境贴图

    MeshStandardMaterial的.envMapIntensity属性相当于环境贴图的系数,环境贴图像素值乘以该系数后,在用于影响模型表面

    // envMapIntensity:控制环境贴图对mesh表面影响程度

    //默认值1, 设置为0.0,相当于没有环境贴图

    obj.material.envMapIntensity = 1.0;

    如果一个gltf模型中所有的Mesh都要设置环境贴图就需要递归遍历gltf模型,给里面每个Mesh的材质设置.envMap:

    1. loader.load("../工厂.glb", function (gltf) {
    2.     // 递归遍历批量设置环境贴图
    3.     gltf.scene.traverse(function (obj) {
    4.         if (obj.isMesh) { //判断是否是网格模型
    5.             obj.material.envMap = textureCube; //设置环境贴图
    6.         }
    7.     });
    8. })

    如果你希望环境贴图影响场景中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。

    1. const material = new THREE.MeshPhysicalMaterial( {
    2. clearcoat: 1.0,//物体表面清漆层或者说透明涂层的厚度
    3. } );
    4. const material = new THREE.MeshPhysicalMaterial( {
    5. clearcoat: 1.0,//物体表面清漆层或者说透明涂层的厚度
    6. clearcoatRoughness: 0.1,//透明涂层表面的粗糙度
    7. } );

    车外壳PBR材质设置:

    1. const mesh = gltf.scene.getObjectByName('外壳01');
    2. mesh.material = new THREE.MeshPhysicalMaterial({
    3.         color: mesh.material.color, //默认颜色
    4.         metalness: 0.9,//车外壳金属度
    5.         roughness: 0.5,//车外壳粗糙度
    6.         envMap: textureCube, //环境贴图
    7.         envMapIntensity: 2.5, //环境贴图对Mesh表面影响程度
    8. })  

    车外壳油漆效果:

    车外壳油漆效果,你可以通过PBR材质的清漆层属性.clearcoat和清漆层粗糙度.clearcoatRoughness属性模拟。

    1. const mesh = gltf.scene.getObjectByName('外壳01');
    2. mesh.material = new THREE.MeshPhysicalMaterial( {
    3. clearcoat: 1.0,//物体表面清漆层或者说透明涂层的厚度
    4. clearcoatRoughness: 0.1,//透明涂层表面的粗糙度
    5. } );

    透光率(透射度).transmission

    模拟玻璃、半透明塑料一类的视觉效果

    1. const mesh = gltf.scene.getObjectByName('玻璃01')
    2. mesh.material = new THREE.MeshPhysicalMaterial({
    3.     transmission: 1.0, //玻璃材质透光率,transmission替代opacity
    4. })

    折射率.ior

    非金属材料的折射率从1.0到2.333。默认值为1.5

    1. new THREE.MeshPhysicalMaterial({
    2.     ior:1.5,//折射率
    3. })

    玻璃效果

    1. const mesh = gltf.scene.getObjectByName('玻璃01')
    2. mesh.material = new THREE.MeshPhysicalMaterial({
    3.     metalness: 0.0,//玻璃非金属
    4.     roughness: 0.0,//玻璃表面光滑
    5.     envMap:textureCube,//环境贴图
    6. envMapIntensity: 1.0, //环境贴图对Mesh表面影响程度
    7. transmission: 1.0, //玻璃材质透光率,transmission替代opacity
    8. ior:1.5,//折射率
    9. })

    ·canvas背景透明度:

    若要canvas画布完全透明,可以透过canvas画布看到画布后面叠加的HTML元素图文,呈现出来一种三维模型悬浮在网页上面的效果

    //改变背景透明度值

    renderer.setClearAlpha(0.8);

    //完全透明

    renderer.setClearAlpha(0.0);

    // 在构造函数参数中设置alpha属性的值

    1. var renderer = new THREE.WebGLRenderer({
    2.   alpha: true
    3. });

    //设置背景颜色和透明度

    renderer.setClearColor(0xb9d3ff, 0.4);

    ·canvas下载为图片:

    1. // 1.WebGL渲染器设置
    2. const renderer = new THREE.WebGLRenderer({
    3.     //想把canvas画布上内容下载到本地,需要设置为true
    4.     preserveDrawingBuffer:true,
    5. });
    1. // 2.鼠标单击id为download的HTML元素,threejs渲染结果以图片形式下载到本地
    2. document.getElementById('download').addEventListener('click',function(){
    3.     // 创建一个超链接元素,用来下载保存数据的文件
    4.     const link = document.createElement('a');
    5.     // 通过超链接herf属性,设置要保存到文件中的数据
    6.     link.href = renderer.domElement.toDataURL("image/png");
    7.     link.download = 'threejs.png'; //下载文件名
    8.     link.click(); //js代码触发超链接元素a的鼠标点击事件,开始下载文件到本地
    9. })

    ·深度冲突(模型闪烁):

    1. // WebGL渲染器设置
    2. const renderer = new THREE.WebGLRenderer({
    3.     // 设置对数深度缓冲区,优化深度冲突问题
    4.     logarithmicDepthBuffer: true
    5. });

    有一点要注意,当两个面间隙过小,或者重合,你设置webgl渲染器对数深度缓冲区也是无效的。

    ·颜色插值用于渐变渲染:

    1. loader.load("../地形.glb", function (gltf) {
    2.     model.add(gltf.scene);
    3.     const mesh = gltf.scene.children[0];
    4.     const pos = mesh.geometry.attributes.position;
    5.     const count = pos.count;
    6.     // 1. 计算模型y坐标高度差
    7.     const yArr = [];//顶点所有y坐标,也就是地形高度
    8.     for (let i = 0; i < count; i++) {
    9.         yArr.push(pos.getY(i));//获取顶点y坐标,也就是地形高度
    10.     }
    11.     yArr.sort();//数组元素排序,从小到大
    12.     const miny = yArr[0];//y最小值
    13.     const maxy = yArr[yArr.length - 1];//y最大值
    14. const height = maxy - miny; //山脉整体高度
    15. // 2. 计算每个顶点的颜色值
    16. const colorsArr = [];
    17. const c1 = new THREE.Color(0x0000ff);//山谷颜色
    18. const c2 = new THREE.Color(0xff0000);//山顶颜色
    19. for (let i = 0; i < count; i++) {
    20.     //当前高度和整体高度比值
    21.     const percent = (pos.getY(i) - miny) / height;
    22.     const c = c1.clone().lerp(c2, percent);//颜色插值计算
    23.     colorsArr.push(c.r, c.g, c.b);
    24. }
    25. const colors = new Float32Array(colorsArr);
    26. // 设置几何体attributes属性的颜色color属性
    27. mesh.geometry.attributes.color = new THREE.BufferAttribute(colors, 3); 
    28. // 3. 设置材质,使用顶点颜色渲染
    29. mesh.material = new THREE.MeshLambertMaterial({
    30.     vertexColors:true
    31. });
    32. })

    ·计算模型最小包围盒:

    1. //包围盒设置
    2. const box3 = new THREE.Box3()
    3. console.log('box3',box3);
    4. box3.min = new THREE.Vector3(-10, -10,0);
    5. box3.max = new THREE.Vector3(100, 20,50);
    6. //计算模型最小包围盒.expandByObject()
    7. const box3 = new THREE.Box3();
    8. box3.expandByObject(mesh); // 计算模型最小包围盒
    9. console.log('查看包围盒',box3);
    10. //包围盒尺寸.getSize()
    11. const scale = new THREE.Vector3()
    12. // getSize()计算包围盒尺寸
    13. // 获得包围盒长宽高尺寸,结果保存在参数三维向量对象scale中
    14. box3.getSize(scale)
    15. console.log('模型包围盒尺寸', scale);
    16. //包围盒几何中心.getCenter()
    17. // 计算包围盒中心坐标
    18. const center = new THREE.Vector3()
    19. box3.getCenter(center)
    20. console.log('模型中心坐标', center);

    ·相机position改变后要重新设置lookAt:

    // 通过UI按钮改变相机观察角度

    1. document.getElementById('x').addEventListener('click', function () {
    2.     camera.position.set(500, 0, 0); //x轴方向观察
    3.     camera.lookAt(0, 0, 0); //重新计算相机视线方向
    4. })

    ·OrbitControls

    1. controls.enablePan = false; //禁止右键拖拽
    2. controls.enableZoom = false;//禁止缩放
    3. controls.enableRotate = false; //禁止旋转
    4. controls.enableDamping false; //将其设置为true以启用阻尼(惯性)
    5. 相机控件OrbitControls.target属性对应的就是相机的.lookAt()观察目标。
    6. 执行controls.update();,相机控件内部会执行camera.lookAt(controls.target)。
    7. // controls.target默认值是坐标原点
    8. controls.target.set(x, y, z);
    9. //update()函数内会执行camera.lookAt(x, y, z)
    10. controls.update();
    11. //相机位置与观察目标点最小值
    12. controls.minDistance = 200;
    13. //相机位置与观察目标点最大值
    14. controls.maxDistance = 500;
    15. //相机位置与目标观察点距离
    16. const dis = controls.getDistance();
    17. console.log('dis',dis);
    18. // 缩放范围(正投影相机)
    19. controls.minZoom = 0.5;
    20. controls.maxZoom = 2;
    21. 辅助计算相机位置与目标观察点距离
    22. controls.addEventListener('change',function(){
    23.     //相机位置与目标观察点距离
    24.     const dis = controls.getDistance();
    25.     console.log('dis',dis);
    26. })
    27. // 上下旋转范围
    28. controls.minPolarAngle = 0;//默认值0
    29. controls.maxPolarAngle = Math.PI;//默认值Math.PI
    30. // 左右旋转范围
    31. controls.minAzimuthAngle = -Math.PI/2;  //范围[-2 * Math.PI,2 * Math.PI]
    32. controls.maxAzimuthAngle = Math.PI/2;  //范围[-2 * Math.PI,2 * Math.PI]
    33. .autoRotateBoolean将其设为true,以自动围绕目标旋转
    34. .autoRotateSpeed : Float围绕目标旋转的速度将有多快,默认值为2.0,相当于在60fps时每旋转一周需要30
    35. .dampingFactor : Float当.enableDamping设置为true的时候,阻尼惯性有多大。 默认0.05,请注意,要使得这一值生效,你必须在你的动画循环里调用.update()。
    36. .panSpeed : Float位移的速度,其默认值为1
    37. .rotateSpeed : Float旋转的速度,其默认值为1

    ·灯光设置:

    1. //1.聚光灯
    2. // 聚光源
    3. // 0xffffff:光源颜色
    4. // 1.0:光照强度intensity
    5. const spotLight = new THREE.SpotLight(0xffffff,1.0);
    6. scene.add(spotLight);//光源添加到场景中
    7. spotLight.intensity = 1.0;//光照强度
    8. // 设置聚光光源发散角度
    9. spotLight.angle = Math.PI / 6;//光锥角度的二分之一
    10. // 设置聚光光源位置
    11. spotLight.position.set(0, 50, 0);
    12. // spotLight.target是一个模型对象Object3D,默认在坐标原点
    13. spotLight.target.position.set(50,0,0);
    14. //spotLight.target添加到场景中.target.position才会起作用
    15. scene.add(spotLight.target);
    16. 平行光DirectionalLight阴影步骤
    17. // 1.设置产生投影的网格模型
    18. mesh.castShadow = true;
    19. // 2.设置产生投影的光源
    20. //平行光
    21. const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
    22. // 平行光设置产生阴影的光源对象,开启光源阴影的计算功能
    23. directionalLight.castShadow = true;
    24. //3.设置接收阴影的投影面
    25. planeMesh.receiveShadow = true;
    26. // 4.设置渲染器,允许光源阴影渲染
    27. renderer.shadowMap.enabled = true;
    28. // 5.设置三维场景计算阴影的范围
    29. directionalLight.shadow.camera.left = -50;
    30. directionalLight.shadow.camera.right = 50;
    31. directionalLight.shadow.camera.top = 200;
    32. directionalLight.shadow.camera.bottom = -100;
    33. directionalLight.shadow.camera.near = 0.5;
    34. directionalLight.shadow.camera.far = 600;
    35. // 如果阴影边缘锯齿感的时候,可以适当提升像素
    36. directionalLight.shadow.mapSize.set(1024,1024);
    37. directionalLight.shadow.mapSize.set(2048,2048);
    38. 适当提升.shadow.radius,你可以感到阴影边缘与非阴影区域是渐变过渡,或者说阴影边缘逐渐弱化或模糊化,没有很明显的边界感。
    39. directionalLight.shadow.radius = 3;
    40. // 模型表面产生条纹影响渲染效果,可以改变.shadowMap.type默认值优化
    41. renderer.shadowMap.type = THREE.VSMShadowMap;

    ·动画:

    1. // 给需要设置关键帧动画的模型命名
    2. mesh.name = "Box";
    3. const times = [0, 3, 6]; //时间轴上,设置三个时刻0、3、6秒
    4. // times中三个不同时间点,物体分别对应values中的三个xyz坐标
    5. const values = [0, 0, 0, 100, 0, 0, 0, 0, 100];
    6. // 0~3秒,物体从(0,0,0)逐渐移动到(100,0,0),3~6秒逐渐从(100,0,0)移动到(0,0,100)
    7. const posKF = new THREE.KeyframeTrack('Box.position', times, values);
    8. // 从2秒到5秒,物体从红色逐渐变化为蓝色
    9. const colorKF = new THREE.KeyframeTrack('Box.material.color', [2, 5], [1, 0, 0, 0, 0, 1]);
    10. // 1.3 基于关键帧数据,创建一个clip关键帧动画对象,命名"test",持续时间6秒。
    11. const clip = new THREE.AnimationClip("test", 6, [posKF, colorKF]);
    12. //包含关键帧动画的模型对象作为AnimationMixer的参数创建一个播放器mixer
    13. const mixer = new THREE.AnimationMixer(mesh);
    14. //AnimationMixer的`.clipAction()`返回一个AnimationAction对象
    15. const clipAction = mixer.clipAction(clip);
    16. //.play()控制动画播放,默认循环播放
    17. clipAction.play();
    18. const clock = new THREE.Clock();
    19. function loop() {
    20.     requestAnimationFrame(loop);
    21.     //clock.getDelta()方法获得loop()两次执行时间间隔
    22. const frameT = clock.getDelta();
    23. // 更新播放器相关的时间
    24. mixer.update(frameT);
    25. }
    26. loop();
    27. 其他播放控制
    28. //不循环播放
    29. clipAction.loop = THREE.LoopOnce;
    30. 上面播放一次后模型会回到关键帧动画开头状态,若想停留在最后一帧执行
    31. // 物体状态停留在动画结束的时候
    32. clipAction.clampWhenFinished = true;
    33. //动画停止结束,回到开始状态
    34. clipAction.stop();
    35. // AnimationAction.paused默认值false,设置为true,可以临时暂停动画
    36.  if (clipAction.paused) {//暂停状态
    37.     clipAction.paused = false;//切换为播放状态
    38.   } else {//播放状态
    39.      clipAction.paused = true;//切换为暂停状态
    40.   }
    41. 倍速播放.timeScale
    42. clipAction.timeScale = 1;//默认
    43. clipAction.timeScale = 2;//2倍速
    44. 关键帧时长
    45. clip.duration
    46. //AnimationAction设置开始播放时间:从1秒时刻对应动画开始播放
    47. clipAction.time = 1;
    48. //AnimationClip设置播放结束时间:到5秒时刻对应的动画状态停止
    49. clip.duration = 5;
    50. //不循环播放
    51. clipAction.loop = THREE.LoopOnce;
    52. // 物体状态停留在动画结束的时候
    53. clipAction.clampWhenFinished=true;

    外部模型动画:

    1. //包含关键帧动画的模型对象作为AnimationMixer的参数创建一个播放器mixer
    2. const mixer = new THREE.AnimationMixer(mesh);
    3. THREE.LoopOnce - 只执行一次
    4. THREE.LoopRepeat - 重复次数为repetitions的值, 且每次循环结束时候将回到起始动作开始下一次循环。
    5. THREE.LoopPingPong - 重复次数为repetitions的值, 且像乒乓球一样在起始点与结束点之间来回循环。
    6. .timeScale : Number
    7. 时间(time)的比例因子. 值为0时会使动画暂停。值为负数时动画会反向执行。默认值是1
    8. const IdleAction = mixer.clipAction(gltf.animations[0]);
    9. const RunAction = mixer.clipAction(gltf.animations[1]);
    10. const WalkAction = mixer.clipAction(gltf.animations[3]);
    11. IdleAction.play();
    12. let ActionState = IdleAction;//当前处于播放状态的动画动作对象
    13. // 通过UI按钮控制,切换动画运动状态
    14. document.getElementById('Idle').addEventListener('click', function () {
    15.     ActionState.stop();//播放状态动画终止
    16.     IdleAction.play();
    17.     ActionState = IdleAction;
    18. })
    19. document.getElementById('Run').addEventListener('click', function () {
    20.     ActionState.stop();//播放状态动画终止
    21.     RunAction.play();
    22.     ActionState = RunAction;
    23. })
    24. document.getElementById('Walk').addEventListener('click', function () {
    25.     ActionState.stop();//播放状态动画终止
    26.     WalkAction.play();
    27.     ActionState = WalkAction;
    28. })
    29. // 向量的x、y、z坐标分别在pos基础上增加30
    30. const pos2 = pos.clone().addScalar(30);
    31. // 切换到设备A预览状态
    32. document.getElementById('A').addEventListener('click', function () {
    33.     const A = model.getObjectByName('设备A标注');
    34.     const pos = new THREE.Vector3();
    35.     A.getWorldPosition(pos); //获取三维场景中某个对象世界坐标
    36.     // 相机飞行到的位置和观察目标拉开一定的距离
    37.     const pos2 = pos.clone().addScalar(30);//向量的x、y、z坐标分别在pos基础上增加30
    38.     // 相机从当前位置camera.position飞行三维场景中某个世界坐标附近
    39.     new TWEEN.Tween({
    40.             // 相机开始坐标
    41.             x: camera.position.x,
    42.             y: camera.position.y,
    43.             z: camera.position.z,
    44.             // 相机开始指向的目标观察点
    45.             tx: 0,
    46.             ty: 0,
    47.             tz: 0,
    48.         })
    49.         .to({
    50.             // 相机结束坐标
    51.             x: pos2.x,
    52.             y: pos2.y,
    53.             z: pos2.z,
    54.             // 相机结束指向的目标观察点
    55.             tx: pos.x,
    56.             ty: pos.y,
    57.             tz: pos.z,
    58.         }, 2000)
    59.         .onUpdate(function (obj) {
    60.             // 动态改变相机位置
    61.             camera.position.set(obj.x, obj.y, obj.z);
    62.             // 动态计算相机视线
    63.             camera.lookAt(obj.tx, obj.ty, obj.tz);
    64.         })
    65.         .start();
    66. })
    67. 动画结束更新OrbitControls
    68. .onUpdate(function (obj) {
    69.     ...
    70.     camera.lookAt(obj.tx, obj.ty, obj.tz);
    71. })
    72. .onComplete(function(obj){
    73.     controls.target.set(obj.tx, obj.ty, obj.tz);
    74.     controls.update();
    75. })

    封装一个相机动画函数

    1. // 相机动画函数,从A点飞行到B点,A点表示相机当前所处状态
    2. // pos: 三维向量Vector3,表示动画结束相机位置
    3. // target: 三维向量Vector3,表示相机动画结束lookAt指向的目标观察点
    4. function createCameraTween(endPos,endTarget){
    5.     new TWEEN.Tween({
    6.         // 不管相机此刻处于什么状态,直接读取当前的位置和目标观察点
    7.         x: camera.position.x,
    8.         y: camera.position.y,
    9.         z: camera.position.z,
    10.         tx: controls.target.x,
    11.         ty: controls.target.y,
    12.         tz: controls.target.z,
    13.     })
    14.     .to({
    15.         // 动画结束相机位置坐标
    16.         x: endPos.x,
    17.         y: endPos.y,
    18.         z: endPos.z,
    19.         // 动画结束相机指向的目标观察点
    20.         tx: endTarget.x,
    21.         ty: endTarget.y,
    22.         tz: endTarget.z,
    23.     }, 2000)
    24.     .onUpdate(function (obj) {
    25.         // 动态改变相机位置
    26.         camera.position.set(obj.x, obj.y, obj.z);
    27.         // 动态计算相机视线
    28.         // camera.lookAt(obj.tx, obj.ty, obj.tz);
    29.         controls.target.set(obj.tx, obj.ty, obj.tz);
    30.         controls.update();//内部会执行.lookAt()
    31.     })
    32.     .start();
    33. }
    34. 设置设备A、设备B、停车场、整体预览四个按钮对应的相机动画,这样你可以在4个按钮之间,随意切换相机的观察状态
    35. // 切换到设备A预览状态
    36. document.getElementById('A').addEventListener('click', function () {
    37.     const A = model.getObjectByName('设备A标注');
    38.     const pos = new THREE.Vector3();
    39.     A.getWorldPosition(pos); //获取三维场景中某个对象世界坐标
    40.     // 相机飞行到的位置和观察目标拉开一定的距离
    41.     const pos2 = pos.clone().addScalar(30);
    42.     createCameraTween(pos2, controls.target)
    43. })
    44. // 切换到设备B的预览状态
    45. document.getElementById('B').addEventListener('click', function () {
    46.     const B = model.getObjectByName('设备B标注');
    47.     const pos = new THREE.Vector3();
    48.     B.getWorldPosition(pos); //获取三维场景中某个对象世界坐标
    49.     // 相机飞行到的位置和观察目标拉开一定的距离
    50.     const pos2 = pos.clone().addScalar(30);
    51.     // 相机从当前位置camera.position飞行三维场景中某个世界坐标附近
    52.     createCameraTween(pos2, controls.target)
    53. })
    54. // 切换到设备停车场的预览状态
    55. document.getElementById('car').addEventListener('click', function () {
    56.     const car = model.getObjectByName('停车场标注');
    57.     const pos = new THREE.Vector3();
    58.     car.getWorldPosition(pos); //获取三维场景中某个对象世界坐标
    59.     // 相机飞行到的位置和观察目标拉开一定的距离
    60.     const pos2 = pos.clone().addScalar(30);
    61.     // 相机从当前位置camera.position飞行三维场景中某个世界坐标附近
    62.     createCameraTween(pos2, pos)
    63. })
    64. // 相机整体预览对应的位置和观察目标
    65. const cameraPos0 = new THREE.Vector3(202, 123, 125)
    66. const target0 = new THREE.Vector3(0, 0, 0);
    67. // 切换整体预览状态
    68. document.getElementById('all').addEventListener('click', function () {
    69.     // 相机从当前位置camera.position回到整体预览状态
    70.     createCameraTween(cameraPos0, target0)
    71. })
    72. // 动画开始缓动方式(类比加速启动)
    73. tween.easing(TWEEN.Easing.Sinusoidal.In);
    74. // 动画结束缓动方式(类比减速刹车)
    75. tween.easing(TWEEN.Easing.Sinusoidal.Out);
    76. // 同时设置In和Out
    77. tween.easing(TWEEN.Easing.Sinusoidal.InOut);

    ·贴图颜色矫正:

    注意贴图颜色矫正:

    materialClouds.map.colorSpace = THREE.SRGBColorSpace;

  • 相关阅读:
    使用C语言+USRP B210从零开始实现无线通信(4) 接收检测与解调
    35 | 如何准备测试数据?
    阿里p8软测专家耗时一个月整理出,从0基础自学到功能测试再到自动化测试超全学习指南
    bat脚本基础
    通关剑指 Offer——剑指 Offer II 010. 和为 k 的子数组
    qt功能自己创作
    JavaScript原生
    JAVA-----集合初解
    spring redis不同配置
    Elastic Stack 环境配置与框架简介
  • 原文地址:https://blog.csdn.net/c5211314963/article/details/134005400