一.基础入门

  1. 使用版本r0.153,下载压缩包后执行npm start,即可查看本地教程文档。

  2. 创建项目:npm init vite@latest

  3. 下载three:npm install three

  4. ```js
    // 导入three js
    import * as THREE from ‘three’

    1
    2
    3
    4

    5. ```js
    // 创建场景
    const scene = new THREE.Scene();
  5. ```js
    // 创建透视相机
    const camera = new THREE.PerspectiveCamera(45, // 视角

    window.innerWidth / window.innerHeight, // 宽高比
    0.1, // 近平面
    1000 //远平面
    

    );
    // 设置相机位置
    camera.position.z = 5 // z轴 正对眼睛位置
    camera.position.x = 2
    camera.position.y = 2
    camera.lookAt(0, 0, 0) // 相机看向原点 默认就是原点

    1
    2
    3
    4
    5
    6

    7. ```js
    // 创建渲染器
    let renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight)
    document.body.appendChild(renderer.domElement)
  6. ```js
    // 添加世界坐标辅助器
    let axesHelper = new THREE.AxesHelper(5); // 线段长度. 默认为 1.
    scene.add(axesHelper)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    9. ```js
    // 添加轨道控制器
    let controls = new OrbitControls(camera, renderer.domElement);
    // 设置带阻尼的惯性
    controls.enableDamping = true
    // 设置阻尼系数
    controls.dampingFactor = 0.05
    // 设置自动旋转
    controls.autoRotate = true
  7. ```js
    // 渲染函数
    function animate() {

    controls.update()
    requestAnimationFrame(animate)
    {
        // 旋转
        cube.rotation.x += 0.01
        cube.rotation.y += 0.01
        // 渲染
        renderer.render(scene, camera)
    }
    

    }

    animate()

    1
    2
    3
    4

    11. ```js
    // 设置物体坐标
    cube.position.set(3, 0, 0)
  8. ```js
    // 设置立方体的放大
    // cube.scale.set(2, 2, 2) // 放大两倍
    parentCube.scale.set(2,2,2) // 父元素放大子元素一起放大

    1
    2
    3
    4
    5

    13. ```js
    // 控制绕着x轴旋转
    cube.rotation.x = Math.PI / 4 // 旋转45° (局部旋转会叠加父元素旋转 90°)
    parentCube.rotation.x=Math.PI / 4
  9. ```js
    // 监听窗口变化
    window.addEventListener(“resize”, () => {

    // 重置渲染器宽高比
    renderer.setSize(window.innerWidth, window.innerHeight)
    // 重置相机宽高比
    camera.aspect = window.innerWidth / window.innerHeight
    // 更新相机投影矩阵
    camera.updateProjectionMatrix()
    

    });

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    15. ```js
    // 点击按钮事件实现全屏
    let btn = document.createElement("button"); // 创建按钮元素
    btn.innerHTML = "点击全屏"
    // 设置按钮样式
    btn.style.position = "absolute";
    btn.style.top = '10px'
    btn.style.left = '10px'
    btn.style.zindex = '999' //数值越大显示在最上层
    btn.onclick = function () {
    // 全屏
    renderer.domElement.requestFullscreen();
    }
    document.body.appendChild(btn)
  10. ```js
    // 使用GUI实现全屏
    import {GUI} from “three/addons/libs/lil-gui.module.min.js”;
    let eventObj = {

    // 全屏
    fullscreen: function () {
        document.body.requestFullscreen();
    },
    // 退出全屏
    exitFullscreen: function () {
        document.exitFullscreen();
    }
    

    }
    // 创建GUI对象
    let gui = new GUI();
    // 添加按钮
    gui.add(eventObj,’fullscreen’).name = “全屏”;
    gui.add(eventObj,’exitFullscreen’).name = “退出全屏”;

    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
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39

    17. ```js
    // 线框材质
    parentMaterial.wireframe = true

    // 控制立方体位置
    // gui.add(cube.position, 'x', -5, 5).name('立方体x轴位置')
    // gui.add(cube.position, 'x').min(-10).max(10).step(1).name('立方体x轴位置')
    let folder = gui.addFolder('立方体位置');
    folder.add(cube.position, 'x')
    .min(-10)
    .max(10)
    .step(1)
    .name('立方体x轴位置')
    .onChange((val) => {
    console.log('x轴位置:', val)
    });
    folder.add(cube.position, 'y')
    .min(-10).max(10)
    .step(1)
    .name('立方体y轴位置')
    .onFinishChange((val) => {
    console.log('y轴位置:', val)
    });
    folder.add(cube.position, 'z').min(-10).max(10).step(1).name('立方体z轴位置');

    //控制线框材质变化 单选框
    gui.add(parentMaterial, 'wireframe').name('父元素线框模式')

    // 控制立方体颜色
    let colorParams = {
    cubeColor: '#0x00ff00'
    }
    gui.addColor(colorParams, 'cubeColor')
    .name('立方体颜色')
    .onChange((val) => {
    cube.material.color.set(val)
    })

  11. ```js
    // 创建几何体 所有的面都是由三角形构成的
    let geometry = new THREE.BufferGeometry();
    // 创建顶点数据 顶点是有序的 每三个为一个顶点 逆时针为正面
    const vertices = new Float32Array([

    -1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 1.0, 1.0, 0.0,
    1.0, 1.0, 0.0, -1.0, 1.0, 0.0, -1.0, -1.0, 0.0
    

    ]);// 产生六个顶点
    // 创建顶点属性
    geometry.setAttribute(‘position’, new THREE.BufferAttribute(vertices, 3))
    // 创建材质
    const material = new THREE.MeshBasicMaterial({

    color: 0x00ff00,
    wireframe: true,
    // side: THREE.DoubleSide // 两面都可以看到
    

    });
    console.log(geometry); // count:6 有6个顶点

    // 使用索引绘制共用顶点 正方形四个顶点
    const geometry2 = new THREE.BufferGeometry();
    const vertices2 = new Float32Array([

    -1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 1.0, 1.0, 0.0, -1.0, 1.0, 0.0,
    

    ]); // 平面正方形
    // 创建顶点属性
    geometry2.setAttribute(‘position’, new THREE.BufferAttribute(vertices2, 3))
    // 创建索引
    const indices = new Uint16Array([0, 1, 2, 2, 3, 0]);
    // 创建索引属性
    geometry2.setIndex(new THREE.BufferAttribute(indices, 1))
    console.log(geometry2); // count:4 有4顶点
    // 创建网格
    const mesh = new THREE.Mesh(geometry2, material);
    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

    19. ```js
    // 创建索引属性
    geometry2.setIndex(new THREE.BufferAttribute(indices, 1))
    // 设置两个顶点组 形成两个材质
    geometry2.addGroup(0, 3, 0) // 顶点索引位置 索引0开始,数量3
    geometry2.addGroup(3, 3, 1) // 索引3开始(正方形的第四个点),数量3
    console.log(geometry2); // count:4 有4顶点
    // 创建网格
    const mesh = new THREE.Mesh(geometry2, [material, material2]);
    scene.add(mesh)



    // 创建几合体
    let cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
    // // 创建材质
    let material01 = new THREE.MeshBasicMaterial({color: 0x00ff00}); //
    let material02 = new THREE.MeshBasicMaterial({color: 'blue'}); //
    let material03 = new THREE.MeshBasicMaterial({color: 'red'}); //
    let material04 = new THREE.MeshBasicMaterial({color: 'purple'}); //
    let material05 = new THREE.MeshBasicMaterial({color: 'white'}); //
    let material06 = new THREE.MeshBasicMaterial({color: 'yellow'}); //


    let cube = new THREE.Mesh(cubeGeometry, [material01, material02, material03, material04, material05, material06]);
    cube.position.x = 2
    scene.add(cube)
  12. image-20230829114919999

  13. 贴图下载

    https://www.poliigon.com/
    https://3dtextures.me/
    https://www.arroway-textures.ch/

  14. ```js
    import {DoubleSide} from “three”;
    // 导入hdr加载器
    import {RGBELoader} from “three/examples/jsm/loaders/RGBELoader.js”

    // 创建文理加载器
    let textureLoader = new THREE.TextureLoader();
    // 加载文理效果
    let texture = textureLoader.load(‘./texture/watercover/CityNewYork002_COL_VAR1_1K.png’);
    // 加载ao贴图 明暗程度
    let aoMap = textureLoader.load(‘./texture/watercover/CityNewYork002_COL_VAR1_1K.png’);
    // 透明度贴图 黑:全透明 白:不透明 灰:半透明
    let alphaMap = textureLoader.load(‘./texture/door/height.jpg’);
    // 光照贴图
    let lightMap = textureLoader.load(‘./texture/colors.png’);
    // 高光贴图
    let specularMap = textureLoader.load(‘./texture/watercover/CityNewYork002_GLOSS_1K.jpg’);
    // rgbeLoader加载hdr贴图
    let rgbeLoader = new RGBELoader();
    rgbeLoader.load(“./texture/Alex_Hart-Nature_Lab_Bones_2k.hdr”, (envMap => {

    // 设置球形贴图
    envMap.mapping = THREE.EquirectangularReflectionMapping
    // 设置环境贴图
    scene.background = envMap
    //设置环境贴图-整个场景
    scene.environment = envMap
    // 设置planeMaterial环境贴图
    planeMaterial.envMap = envMap
    

    }))
    // 创建平面
    const planeGeometry = new THREE.PlaneGeometry(1, 1);
    // 创建平面材质
    const planeMaterial = new THREE.MeshBasicMaterial({

    side: DoubleSide,
    color: 0xffffff,
    map: texture,
    // 允许透明
    transparent: true,
    // 设置ao
    aoMap: aoMap,
    aoMapIntensity: 1,
    // 设置透明度贴图 alpha贴图是一张灰度纹理,用于控制整个表面的不透明度。(黑色:完全透明;白色:完全不透明)。 默认值为null。
    // alphaMap: alphaMap,
    // 光照贴图
    // lightMap: lightMap,
    // 设置反射值
    reflectivity: 0.5,
    // 设置高光贴图
    specularMap: specularMap
    

    });
    // planeMaterial.map = texture;
    gui.add(planeMaterial, ‘aoMapIntensity’).min(0).max(1).name(‘ao强度’)

    const cube = new THREE.Mesh(planeGeometry, planeMaterial);
    scene.add(cube);

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    23. ```js
    // 创建文理加载器
    let textureLoader = new THREE.TextureLoader();
    // 加载文理效果
    let texture = textureLoader.load('./texture/watercover/CityNewYork002_COL_VAR1_1K.png');
    texture.colorSpace = THREE.SRGBColorSpace // 更加真实的
    // texture.colorSpace = THREE.LinearSRGBColorSpace // 默认线性空间

    gui.add(texture, 'colorSpace', {
    sRGB: THREE.SRGBColorSpace,
    linear: THREE.LinearSRGBColorSpace
    }).onChange(() => {
    texture.needsUpdate = true //更改颜色空间
    })
  15. ```js
    // 创建几何体
    let boxGeometry = new THREE.BoxGeometry(1, 1, 100);
    // 创建材质
    let basicMaterial = new THREE.MeshBasicMaterial({

    color: 0x00ff00
    

    });
    let mesh = new THREE.Mesh(boxGeometry, basicMaterial);
    scene.add(mesh)

    // 创建场景fog
    // scene.fog = new THREE.Fog(0x999999, 0.1, 50);
    // 创建指数fog
    scene.fog = new THREE.FogExp2(0x999999, 0.1)
    scene.background = new THREE.Color(0x999999)

    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
    29

    <img src="../blog/coderyeah/source/_posts/img/image-20230830113331658.png" alt="image-20230830113331658" style="zoom:50%;" />

    25. #### GLTF加载器(GLTFLoader)

    [GlTF](https://www.khronos.org/gltf)(gl传输格式)是一种开放格式的规范 ([open format specification](https://github.com/KhronosGroup/glTF/tree/master/specification/2.0)), 用于更高效地传输、加载3D内容。该类文件以JSON(.gltf)格式或二进制(.glb)格式提供, 外部文件存储贴图(.jpg、.png)和额外的二进制数据(.bin)。一个glTF组件可传输一个或多个场景, 包括网格、材质、贴图、蒙皮、骨架、变形目标、动画、灯光以及摄像机。

    ```js
    import {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';

    // 实例化加载器gltf
    let gltfLoader = new GLTFLoader();
    gltfLoader.load(
    // 模型路径
    './model/Duck.glb',
    // 加载完成回调
    (gltf => {
    scene.add(gltf.scene)
    console.log(gltf)
    }))

    // 加载环境贴图
    let rgbeLoader = new RGBELoader();
    rgbeLoader.load('./texture/Alex_Hart-Nature_Lab_Bones_2k.hdr', (envMap) => {
    // 设置球形映射
    envMap.mapping = THREE.EquirectangularReflectionMapping
    // 设置环境贴图 四面八方的光照进来 小鸭子亮起来
    scene.environment = envMap
    })

    image-20230830141114526

  16. 加载压缩过的grtl模型

    image-20230830143306961

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import {DRACOLoader} from "three/examples/jsm/loaders/DRACOLoader.js" //解码器
    // 实例化draco加载器
    let dracoLoader = new DRACOLoader();
    // 设置draco路径
    dracoLoader.setDecoderPath('./draco/')
    // 设置gltf加载器draco解码器
    gltfLoader.setDRACOLoader(dracoLoader)

    // 加载城市的glb文件 glb:二进制文件 gltf:json文件
    gltfLoader.load(
    './model/city.glb',
    (gltf) => {
    scene.add(gltf.scene) //No DRACOLoader instance provided 因为是压缩文件 需要Draco解压
    })
  17. 光线投射3d场景交互事件

    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
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    // 创建三个球
    let sphere1 = new THREE.Mesh(
    new THREE.SphereGeometry(1, 32, 32),
    new THREE.MeshBasicMaterial(
    {color: 0x00ff00}
    )
    );
    sphere1.position.x = -4
    scene.add(sphere1);

    let sphere2 = new THREE.Mesh(
    new THREE.SphereGeometry(1, 32, 32),
    new THREE.MeshBasicMaterial({
    color: 0x0000ff
    })
    );
    scene.add(sphere2)

    let sphere3 = new THREE.Mesh(
    new THREE.SphereGeometry(1, 32, 32),
    new THREE.MeshBasicMaterial({
    color: 0xff00ff
    })
    );
    sphere3.position.x = 4
    scene.add(sphere3)

    // 创建射线
    const raycaster = new THREE.Raycaster();
    // 创建鼠标向量
    const mouse = new THREE.Vector2();
    // 监听点击事件
    window.addEventListener('click', (event) => {
    // 设置鼠标向量的x, y值
    mouse.x = (event.clientX / window.innerWidth) * 2 - 1 // 这样鼠标点击的x值范围就是-1到1
    //因为坐标相反 y轴上是正数,下方是负数 所以取相反
    mouse.y = -((event.clientY / window.innerHeight) * 2 - 1)
    // console.log(mouse.x, mouse.y)

    // 通过摄像机和鼠标位置更新射线
    raycaster.setFromCamera(mouse, camera)
    // 计算物体和射线的焦点
    const intersects = raycaster.intersectObjects([sphere1, sphere2, sphere3]);
    console.log(intersects);

    if (intersects.length > 0) { // 如果在一条直线上数值为3个 选择第一个距离相机位置最近
    if (intersects[0].object._isSelected) {
    // 改变为原来的颜色
    intersects[0].object.material.color.set(intersects[0].object._originColor)
    intersects[0].object._isSelected = false
    return
    }

    // originColor自定义的属性值
    intersects[0].object._originColor = intersects[0].object.material.color.getHex(); // 获取圆球形原有的rgb颜色值
    intersects[0].object._isSelected = true // 已经选择
    // 设置颜色为红色
    intersects[0].object.material.color.set(0xff0000)
    }
    });

    image-20230830155012561

  18. 补间动画Tween

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// 导入tween
import * as TWEEN from "three/examples/jsm/libs/tween.module.js";
// 创建一个球
let sphere = new THREE.Mesh(
new THREE.SphereGeometry(1, 32, 32),
new THREE.MeshBasicMaterial(
{color: 0x00ff00}
)
);
sphere.position.x = -4
scene.add(sphere);
// 创建补间对象
const tween = new TWEEN.Tween(sphere.position);
tween.to({x: 4}, 1000)
tween.onUpdate(() => {
console.log(sphere.position.x) // 获取位置
})
// 循环往复
// tween.yoyo(true)
// tween.repeat(Infinity) // 设置循环无数次
// tween.repeat(2) // 设置循环2次
// 延迟运行
// tween.delay(3000)
// 设置缓动函数
tween.easing(TWEEN.Easing.Quadratic.InOut) //Quadratic.InOut

const tween2 = new TWEEN.Tween(sphere.position);
tween2.to({x: -4}, 1000) // yoyo(true)
tween.chain(tween2)
tween2.chain(tween)
// 启动补间动画
tween.start();
// tween的回调函数
tween.onStart(() => {
console.log("开始")
})
tween.onStop(() => {
console.log("暂停")
})
tween.onComplete(() => {
console.log("完成")
})
tween.onUpdate(() => {
console.log("更新")
})

let param = {
stop: function () {
tween.stop()
}
}
gui.add(param, 'stop')

二.Geometry进阶

  1. UV:二维纹理坐标

    uv_grid_opengl

  2. 法向量:垂直于平面,作用计算反射光,可以给四个顶点设置法向量,使物体具有反射光。

  3. 物体转换,顶点转换。

  4. 包围盒,包围球

    image-20230905112629736

    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
    // 加载小鸭子
    // 实例化加载器gltf
    let gltfLoader = new GLTFLoader();
    gltfLoader.load(
    // 模型路径
    './model/Duck.glb',
    // 加载完成回调
    (gltf => {
    scene.add(gltf.scene)
    console.log(gltf)
    const duckMesh = gltf.scene.getObjectByName("LOD3spShape");
    const duckGeometry = duckMesh.geometry
    console.log(duckGeometry)
    // 计算包围盒
    duckGeometry.computeBoundingBox();
    // 获取包围盒
    const duckBox = duckGeometry.boundingBox;
    // 更新世界矩阵
    duckMesh.updateWorldMatrix(true, true)
    // 更新包围盒
    duckBox.applyMatrix4(duckMesh.matrixWorld)
    // 创建包围盒辅助器
    const box3Helper = new THREE.Box3Helper(duckBox, 0xffff00);
    scene.add(box3Helper);
    }))
  5. 几何物体居中

    image-20230905114143368

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 设置几合体剧中
// duckGeometry.center
// 获取包围盒
const duckBox = duckGeometry.boundingBox;
// 获取包围盒中心点
const center = duckBox.getCenter(new THREE.Vector3());
console.log(center);


// 获取包围球
let duckSphere = duckGeometry.boundingSphere;
duckSphere.applyMatrix4(duckMesh.matrixWorld)
// 创建包围球辅助器
let sphereGeometry = new THREE.SphereGeometry(duckSphere.radius, 16, 16);
let sphereMaterial = new THREE.MeshBasicMaterial({
color: 0xff0000,
wireframe: true
});
let sphereMesh = new THREE.Mesh(sphereGeometry,sphereMaterial);
sphereMesh.position.copy(duckSphere.center)
scene.add(sphereMesh);

  1. 获取多个物体包围盒

    image-20230905134609742

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // 获取多个物体包围盒
    let box = new THREE.Box3();
    let sphereArr = [sphere1, sphere2, sphere3]
    for (let i = 0; i < sphereArr.length; i++) {
    // // 后去当前物体包围盒
    // sphereArr[i].geometry.computeBoundingBox()
    // // 获取包围盒
    // let box3 = sphereArr[i].geometry.boundingBox
    // sphereArr[i].updateWorldMatrix(true, true)
    // // 将包围盒转换到世界坐标系
    // box3.applyMatrix4(sphereArr[i].matrixWorld)

    // 第二种方式 更简单
    let box3 = new THREE.Box3().setFromObject(sphereArr[i]);
    // 合并包围盒
    box.union(box3)
    }

    let box3Helper = new THREE.Box3Helper(box, 0xff0000);
    scene.add(box3Helper)
  2. 边缘几何体和线框几合体

image-20230905153442341

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
29
30
31
32
33
34
35
// 实例化draco加载器
let dracoLoader = new DRACOLoader();
// 设置draco路径
dracoLoader.setDecoderPath('./draco/')
// 设置gltf加载器draco解码器
let gltfLoader = new GLTFLoader();
gltfLoader.setDRACOLoader(dracoLoader)

// 加载城市的glb文件 glb:二进制文件 gltf:json文件
gltfLoader.load(
'./model/city.glb',
(gltf) => {
// scene.add(gltf.scene) //No DRACOLoader instance provided 因为是压缩文件 需要Draco解压
// 遍历所有3d物体
gltf.scene.traverse((child) => {
if (child.isMesh) {
let geometry = child.geometry;
// 线框材质
let edgesMaterial = new THREE.LineBasicMaterial({
color: 0xffffff
});
// 线框geometry
let edgesGeometry = new THREE.WireframeGeometry(geometry);
// 创建线段
let edges = new THREE.LineSegments(edgesGeometry, edgesMaterial);

// 更新建筑物世界转换矩阵
child.updateWorldMatrix(true, true)
edges.matrix.copy(child.matrixWorld)
edges.matrix.decompose(edges.position, edges.quaternion, edges.scale)
// 添加到场景
scene.add(edges)
}
})
})

三.材质与纹理

  1. MatCap材质

    MeshMatcapMaterial 由一个材质捕捉(MatCap,或光照球(Lit Sphere))纹理所定义,其编码了材质的颜色与明暗。

    由于mapcap图像文件编码了烘焙过的光照,因此MeshMatcapMaterial 不对灯光作出反应。 它将会投射阴影到一个接受阴影的物体上(and shadow clipping works),但不会产生自身阴影或是接受阴影。

    image-20230908150601068

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // 创建渲染器
    let renderer = new THREE.WebGLRenderer({
    // 开启抗锯齿
    antialias:true
    });
    gltfLoader.load(
    './model/Duck.glb',
    (gltf) => {
    console.log(gltf)
    scene.add(gltf.scene) //No DRACOLoader instance provided 因为是压缩文件 需要Draco解压
    // 获取3D物体
    let duckMesh = gltf.scene.getObjectByName('LOD3spShape');
    // 加载贴图
    let matCapTexture = new THREE.TextureLoader()
    .load('./texture/matcaps/50332C_D98D79_955F52_AA7C6C-512px.png');
    // 获取材质
    let preMaterial = duckMesh.material;
    duckMesh.material = new THREE.MeshMatcapMaterial({
    matcap: matCapTexture,
    map: preMaterial.map
    })
    })
  2. Lambert材质-模拟粗超表面

    image-20230912152758058

    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
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    // 加载环境贴图
    let rgbeLoader = new RGBELoader();
    rgbeLoader.load("./texture/Alex_Hart-Nature_Lab_Bones_2k.hdr", (envMap => {
    // 设置球形贴图
    envMap.mapping = THREE.EquirectangularReflectionMapping
    // 设置环境贴图
    scene.background = envMap
    //设置环境贴图-整个场景
    scene.environment = envMap
    // 反光
    phongMaterial.envMap = envMap
    }))


    // 添加环境光
    let ambientLight = new THREE.AmbientLightProbe(0xffffff, 0.3);
    scene.add(ambientLight)
    // 添加点光源
    let pointLight = new THREE.PointLight(0xffffff, 1);
    pointLight.position.set(0, 5, 0)
    scene.add(pointLight);

    // 添加纹理
    let textureLoader = new THREE.TextureLoader();
    let colorTexture = textureLoader.load('./texture/watercover/CityNewYork002_COL_VAR1_1K.png');
    colorTexture.colorSpace = THREE.SRGBColorSpace
    // 高光贴图
    let specularTexture = textureLoader.load('./texture/watercover/CityNewYork002_GLOSS_1K.jpg');
    // 法线贴图
    let normalTexture = textureLoader.load('./texture/watercover/CityNewYork002_NRM_1K.jpg');
    // 凹凸贴图
    let dumpTexture = textureLoader.load('./texture/watercover/CityNewYork002_DISP_1K.jpg');
    // 环境光遮蔽贴图
    let aoTexture = textureLoader.load('./texture/watercover/CityNewYork002_AO_1K.jpg');

    // 创建平面
    let planeGeometry = new THREE.PlaneGeometry(1, 1, 200, 200);
    // let phongMaterial = new THREE.MeshPhongMaterial({
    // map: colorTexture,
    // transparent: true,
    // // 高光贴图
    // specularMap: specularTexture,
    // // normalMap: normalTexture,
    // bumpMap: dumpTexture,
    // // 置换贴图
    // displacementMap: dumpTexture,
    // // 置换程度
    // displacementScale: 0.02,
    // aoMap: aoTexture
    // });
    let phongMaterial = new THREE.MeshLambertMaterial({
    map: colorTexture,
    transparent: true,
    // 高光贴图
    specularMap: specularTexture,
    normalMap: normalTexture,
    bumpMap: dumpTexture,
    // 置换贴图
    displacementMap: dumpTexture,
    // 置换程度
    displacementScale: 0.02,
    aoMap: aoTexture
    });
    let plane = new THREE.Mesh(planeGeometry, phongMaterial);
    // 旋转90度
    plane.rotation.x = -Math.PI * 0.5
    scene.add(plane);
  3. PhongMaterial用于表面光滑,模拟水晶鸭子

image-20230912154913497

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
29
30
31
32
33
34
35
36
37
// 实例化draco加载器
let dracoLoader = new DRACOLoader();
// 设置draco路径
dracoLoader.setDecoderPath('./draco/')
// 设置gltf加载器draco解码器
let gltfLoader = new GLTFLoader();
gltfLoader.setDRACOLoader(dracoLoader)

// 加载环境贴图
let rgbeLoader = new RGBELoader();
rgbeLoader.load("./texture/Alex_Hart-Nature_Lab_Bones_2k.hdr", (envMap => {
// 设置球形贴图
// envMap.mapping = THREE.EquirectangularReflectionMapping
envMap.mapping = THREE.EquirectangularRefractionMapping
// 设置环境贴图
scene.background = envMap
//设置环境贴图-整个场景
scene.environment = envMap
// 加载城市的glb文件 glb:二进制文件 gltf:json文件
gltfLoader.load(
'./model/Duck.glb',
(gltf) => {
scene.add(gltf.scene) //No DRACOLoader instance provided 因为是压缩文件 需要Draco解压
// 添加环境光
let ambientLight = new THREE.AmbientLight(0xffffff,1);
scene.add(ambientLight);
let duckMesh = gltf.scene.getObjectByName('LOD3spShape');
let preMaterial = duckMesh.material;
duckMesh.material = new THREE.MeshPhongMaterial({
color: 0xffffff,
map: preMaterial.map,
refractionRatio: 0.7, // 折射率
reflectivity: 0.99, // 反射强度 0-1
envMap: envMap
})
})
}))
  1. 标准网格材质-模拟真实物理光照效果

    image-20230912170202299

    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
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    // 实例化draco加载器
    let dracoLoader = new DRACOLoader();
    // 设置draco路径
    dracoLoader.setDecoderPath('./draco/')
    // 设置gltf加载器draco解码器
    let gltfLoader = new GLTFLoader();
    gltfLoader.setDRACOLoader(dracoLoader)

    let params = {
    aoMap: true
    }
    // 加载环境贴图
    let rgbeLoader = new RGBELoader();
    rgbeLoader.load("./texture/Alex_Hart-Nature_Lab_Bones_2k.hdr", (envMap => {
    // 设置球形贴图
    // envMap.mapping = THREE.EquirectangularReflectionMapping
    envMap.mapping = THREE.EquirectangularRefractionMapping
    // 设置环境贴图
    scene.background = envMap
    //设置环境贴图-整个场景
    scene.environment = envMap
    // 加载城市的glb文件 glb:二进制文件 gltf:json文件
    gltfLoader.load(
    './model/sword/sword.gltf',
    (gltf) => {
    // 添加环境光
    let ambientLight = new THREE.AmbientLight(0xffffff, 1);
    scene.add(ambientLight);
    console.log(gltf);
    scene.add(gltf.scene) //No DRACOLoader instance provided 因为是压缩文件 需要Draco解压
    let mesh = gltf.scene.getObjectByName('Pommeau_Plane001');
    let aopMap = mesh.material.aoMap
    gui.add(params, 'aoMap').onChange((value) => {
    mesh.material.aoMap = value ? aopMap : null
    mesh.material.needsUpdate = true
    })
    })
    }))

  2. 物理网格材质