Three.js——基础纹理、凹凸纹理、法向贴图、环境贴图、canvas贴图

发布于:2024-06-12 ⋅ 阅读:(82) ⋅ 点赞:(0)

个人简介

👀个人主页: 前端杂货铺
开源项目: rich-vue3 (基于 Vue3 + TS + Pinia + Element Plus + Spring全家桶 + MySQL)
🙋‍♂️学习方向: 主攻前端方向,正逐渐往全干发展
📃个人状态: 研发工程师,现效力于中国工业软件事业
🚀人生格言: 积跬步至千里,积小流成江海
🥇推荐学习:🍖开源 rich-vue3 🍍前端面试宝典 🍉Vue2 🍋Vue3 🍓Vue2/3项目实战 🥝Node.js实战 🍒Three.js

🌕个人推广:每篇文章最下方都有加入方式,旨在交流学习&资源分享,快加入进来吧

内容 参考链接
WebGL专栏 WebGL 入门
Three.js(一) 创建场景、渲染三维对象、添加灯光、添加阴影、添加雾化
Three.js(二) scene场景、几何体位置旋转缩放、正射投影相机、透视投影相机
Three.js(三) 聚光灯、环境光、点光源、平行光、半球光
Three.js(四) 基础材质、深度材质、法向材质、面材质、朗伯材质、Phong材质、着色器材质、直线和虚线、联合材质
Three.js(五) Three.js——二维平面、二维圆、自定义二维图形、立方体、球体、圆柱体、圆环、扭结、多面体、文字

前言

大家好,这里是前端杂货铺。

上篇文章我们学习了 骨骼动画。接下来,我们继续我们 three.js 的学习!

在学习的过程中,如若需要深入了解或扩展某些知识,可以自行查阅 => three.js官方文档。


一、基础纹理

纹理简单来说就是给几何体设置外表的显示,跟贴图差不多。要想给几何体添加纹理,我们首先得知道纹理加载器。

TextureLoader,加载 texture 的一个类。 内部使用 ImageLoader 来加载文件。

// manager — 加载器使用的 loadingManager,默认值为 THREE.DefaultLoadingManager
new TextureLoader( manager : LoadingManager )

接下来,我们创建纹理加载器,加载一个外部文件(草地),应用到二维平面上。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="../lib/three/three.js"></script>
    <style>* { margin: 0;padding: 0; }
    </style>
</head>

<body>
<script>
    // 创建场景
    const scene = new THREE.Scene();
    // 创建相机 视野角度FOV、长宽比、近截面、远截面
    const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
    // 设置相机位置
    camera.position.set(0, 0, 20);

    // 创建渲染器
    const renderer = new THREE.WebGLRenderer();
    // 设置渲染器尺寸
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    // 添加平面 平面沿着 X 轴的宽度 | 平面沿着 Y 轴的高度 | 平面的宽度分段数 | 平面的高度分段数
    const geometry = new THREE.PlaneBufferGeometry(10, 10, 2, 2);

    // 创建纹理加载器
    const loader = new THREE.TextureLoader();
    loader.load('../assets/grass.png', (texture) => {
        // 创建材质
        const lambert = new THREE.MeshLambertMaterial({
            map: texture
        });
        const mesh = new THREE.Mesh(geometry, lambert);

        // 添加到场景
        scene.add(mesh);
    })

    // 添加灯光
    const spotLight = new THREE.SpotLight(0xffffff);
    spotLight.position.set(-10, 10, 90);
    scene.add(spotLight);

    const animation = () => {
        // 渲染
        renderer.render(scene, camera);
        requestAnimationFrame(animation);
    }

    animation();
</script>
</body>

</html>

在这里插入图片描述


二、凹凸贴图

凹凸贴图使得物体凹凸不平,对于一些场景会显得更加真实。

.bumpMap:用于创建凹凸贴图的纹理。黑色和白色值映射到与光照相关的感知深度。凹凸实际上不会影响对象的几何形状,只影响光照。如果定义了法线贴图,则将忽略该贴图。

接下来,我们针对墙面添加 基础纹理凹凸贴图,来对比一下不同的呈现效果

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="../lib/three/three.js"></script>
    <style>* { margin: 0; padding: 0; }</style>
</head>

<body>
<script>
    // 创建场景
    const scene = new THREE.Scene();
    // 创建相机 视野角度FOV、长宽比、近截面、远截面
    const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
    // 设置相机位置
    camera.position.set(0, 0, 20);

    // 创建渲染器
    const renderer = new THREE.WebGLRenderer();
    // 设置渲染器尺寸
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    const geometry = new THREE.BoxGeometry(6, 6, 3);

    // 创建纹理加载器
    const loader = new THREE.TextureLoader();
    loader.load('../assets/wall.jpg', (texture) => {
        // 创建材质
        const lambert = new THREE.MeshPhongMaterial({
            map: texture // 颜色贴图
        });

        const mesh = new THREE.Mesh(geometry, lambert);
        mesh.rotation.y = -0.1;
        mesh.position.x = -4;

        // 添加到场景
        scene.add(mesh);
    })

    renderer.setClearColor(0xffffff);

    // 凹凸贴图
    loader.load('../assets/wall.jpg', (texture) => {
        loader.load('../assets/wall-bump.jpg', (bump) => {
            const lambert = new THREE.MeshPhongMaterial({
                map: texture, // 基础纹理
                bumpMap: bump, // 凹凸贴图
                bumpScale: 0.2 // 凹凸贴图的高度
            })

            const mesh = new THREE.Mesh(geometry, lambert);
            mesh.rotation.y = -0.5;
            mesh.position.x = 4;

            scene.add(mesh);
        })
    })

    // 添加灯光
    const spotLight = new THREE.SpotLight(0xffffff);
    spotLight.position.set(-10, 10, 90);
    scene.add(spotLight);

    const animation = () => {
        // 渲染
        renderer.render(scene, camera);
        requestAnimationFrame(animation);
    }

    animation();
</script>
</body>

</html>

在这里插入图片描述


三、法向纹理

.normalMap : 用于创建法线贴图的纹理。RGB值会影响每个像素片段的曲面法线,并更改颜色照亮的方式。法线贴图不会改变曲面的实际形状,只会改变光照。

接下来,我们针对玉石雕刻添加 基础纹理法向贴图,来对比一下不同的呈现效果

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="../lib/three/three.js"></script>
    <style>* { margin: 0;padding: 0; }</style>
</head>

<body>
<script>
    // 创建场景
    const scene = new THREE.Scene();
    // 创建相机 视野角度FOV、长宽比、近截面、远截面
    const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
    // 设置相机位置
    camera.position.set(0, 0, 40);

    // 创建渲染器
    const renderer = new THREE.WebGLRenderer();
    // 设置渲染器尺寸
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    const geometry = new THREE.BoxGeometry(10, 10, 10);

    // 创建纹理加载器
    const loader = new THREE.TextureLoader();

    // 创建材质
    const lambert = new THREE.MeshPhongMaterial({
        map: THREE.ImageUtils.loadTexture('../assets/plaster.jpg'),
    });

    const mesh = new THREE.Mesh(geometry, lambert);
    mesh.rotation.y = 0.5;
    mesh.position.x = -12;

    // 添加到场景
    scene.add(mesh);

    renderer.setClearColor(0xffffff);
    
    const lambert2 = new THREE.MeshPhongMaterial({
        map: THREE.ImageUtils.loadTexture('../assets/plaster.jpg'),
        normalMap: THREE.ImageUtils.loadTexture('../assets/plaster-normal.jpg') // 法向贴图
    })

    const mesh2 = new THREE.Mesh(geometry, lambert2);
    mesh2.rotation.y = -0.5;
    mesh2.position.x = 12;

    scene.add(mesh2);

    // 添加灯光
    const spotLight = new THREE.SpotLight(0xffffff);
    spotLight.position.set(-10, 10, 90);
    scene.add(spotLight);

    const animation = () => {
        // 渲染
        renderer.render(scene, camera);
        requestAnimationFrame(animation);
    }

    animation();
</script>
</body>

</html>

在这里插入图片描述


四、环境贴图

立方相机(CubeCamera),创建 6个 渲染到 WebGLCubeRenderTarget 的摄像机, 并将其拍摄的场景渲染到一个 WebGLCubeRenderTarget 上。

new THREE.CubeCamera( near : Number, far : Number, renderTarget : WebGLCubeRenderTarget )
参数名称 描述
near 近剪切面的距离
far 远剪切面的距离
renderTarget 目标多维数据集渲染目标
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>*{ margin: 0; padding: 0;}</style>
    <script src="../lib/three/three.js"></script>

    <!--  轨道控件  -->
    <script src="../lib/three/OrbitControls.js"></script>
</head>

<body>
<script>
    const clock = new THREE.Clock();
    // 创建场景
    const scene = new THREE.Scene();
    // 创建相机 视野角度FOV、长宽比、近截面、远截面
    const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
    // 设置相机位置
    camera.position.set(100, 100, 0);
    camera.lookAt(new THREE.Vector3(0, 0, 0));

    // 轨道控制器
    const controls = new THREE.OrbitControls(camera);
    controls.minDistance = 1; // 将相机向内移动
    controls.maxDistance = 50; // 将相机向外移动

    // 创建渲染器
    const renderer = new THREE.WebGLRenderer();
    // 设置渲染器尺寸
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    // 添加灯光
    const spotLight = new THREE.SpotLight(0xffffff);
    scene.add(spotLight);

    // 创建虚拟的场景(天空盒子)
    const imgs = [
        '../assets/sky/right.jpg',
        '../assets/sky/left.jpg',
        '../assets/sky/top.jpg',
        '../assets/sky/bottom.jpg',
        '../assets/sky/front.jpg',
        '../assets/sky/back.jpg',
    ]

    // 材质数组
    const mats = [];
    // 用于每个面加载不同的材质
    for (let i = 0; i < imgs.length; i++) {
        mats.push(new THREE.MeshBasicMaterial({
            map: THREE.ImageUtils.loadTexture(imgs[i]),
            side: THREE.DoubleSide, // 双面-里和外
        }))
    }

    // 天空盒子,当作场景
    const skybox = new THREE.Mesh(new THREE.BoxGeometry(100, 100, 100), new THREE.MeshFaceMaterial(mats));
    scene.add(skybox);

    // 创建一个球体和一个立方体
    const sphereGeometry = new THREE.SphereGeometry(4, 15, 15);
    const cubeGeometry = new THREE.BoxGeometry(5, 5, 5);

    // 立方体贴图和环境一致,球体是跟随当前环境
    const cubeMaterial = new THREE.MeshBasicMaterial({
        envMap: THREE.ImageUtils.loadTextureCube(imgs) // 环境贴图-六张图片
    })

    // 通过立方相机实现(创建 6 个渲染到 WebGLCubeRenderTarget 的摄像机)
    const cubeCamera = new THREE.CubeCamera(0.1, 2000, 256);
    scene.add(cubeCamera);

    const sphereMaterial = new THREE.MeshBasicMaterial({
        envMap: cubeCamera.renderTarget, // 环境贴图-渲染目标对象
    })

    const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
    const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);

    sphere.position.x = 5;
    cube.position.x = -5;

    scene.add(sphere);
    scene.add(cube);

    const animation = () => {
        cube.rotation.x += 0.01;
        cube.rotation.y += 0.01;
        // 更新轨道控制器
        controls.update(clock.getDelta());
        // 渲染
        renderer.render(scene, camera);
        requestAnimationFrame(animation);

        // 更新立方相机
        cubeCamera.updateCubeMap(renderer, scene);
    }

    animation();
</script>
</body>

</html>

环境贴图


五、canvas 贴图

要想实现 canvas 贴图,我们首先得创建 canvas 的内容,然后把绘制好的 canvas 当作参数传到纹理中,供网格材质使用。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>*{ margin: 0; padding: 0;}</style>
    <script src="../lib/three/three.js"></script>
</head>

<body>
<script>
    // 创建场景
    const scene = new THREE.Scene();
    // 创建相机 视野角度FOV、长宽比、近截面、远截面
    const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
    // 设置相机位置
    camera.position.set(100, 100, 0);
    camera.lookAt(new THREE.Vector3(0, 0, 0));

    // 创建渲染器
    const renderer = new THREE.WebGLRenderer();
    // 设置渲染器尺寸
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    renderer.setClearColor(0xffffff);

    // 添加灯光
    const spotLight = new THREE.SpotLight(0xffffff);
    scene.add(spotLight);

    function getSprite() {
        const canvas = document.createElement('canvas');
        canvas.width = 160;
        canvas.height = 160;

        const c = canvas.getContext('2d');
        c.fillStyle = 'red';
        // 绘制圆形
        c.arc(80, 80, 32, 0, Math.PI * 2);
        c.fill();

        // canvas 纹理,返回出去
        const texture = new THREE.Texture(canvas);
        // 更新纹理
        texture.needsUpdate = true;
        return texture;
    }

    // 创建一个立方体
    const cubeGeometry = new THREE.BoxGeometry(25, 25, 25);

    // canvas 贴图
    const cubeMaterial = new THREE.MeshBasicMaterial({ map: getSprite() })

    const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);

    cube.position.x = -5;

    scene.add(cube);

    const animation = () => {
        cube.rotation.x += 0.01;
        cube.rotation.y += 0.01;
        // 渲染
        renderer.render(scene, camera);
        requestAnimationFrame(animation);
    }

    animation();
</script>
</body>

</html>

在这里插入图片描述


总结

本篇文章我们讲解了几种常见纹理的基本使用,包括 基础纹理、凹凸纹理、法向贴图、环境贴图、canvas贴图

更多内容扩展请大家自行查阅 => three.js官方文档,真心推荐读一读!!

好啦,本篇文章到这里就要和大家说再见啦,祝你这篇文章阅读愉快,你下篇文章的阅读愉快留着我下篇文章再祝!


参考资料:

  1. Three.js 官方文档
  2. WebGL+Three.js 入门与实战【作者:慕课网_yancy】

在这里插入图片描述