three.js教程5-几何体顶点UV坐标、纹理贴图TextureLoader

1、纹理贴图

纹理贴图,是给MeshLambertMaterial等材质一些纹理图片,以达到更好的视觉效果。

使用方法:通过纹理贴图加载器TextureLoaderload()方法加载一张图片可以返回一个纹理对象Texture,纹理对象Texture可以作为模型材质颜色贴图.map属性的值。

const geometry = new THREE.PlaneGeometry(200, 100); 
//纹理贴图加载器TextureLoader
const texLoader = new THREE.TextureLoader();
// .load()方法加载图像,返回一个纹理对象Texture
const texture = texLoader.load('./earth.jpg');
const material = new THREE.MeshLambertMaterial({
    // color: 0x00ffff,color和map颜色值会混合,因此设置map后color一般不用设置
    // 设置纹理贴图:Texture对象作为材质map属性的属性值
    map: texture,//map表示材质的颜色贴图属性
});

color和map颜色值会混合,因此设置map后color一般不用设置

2、顶点uv坐标

顶点uv坐标,是二维顶点坐标,顶点UV坐标geometry.attributes.uv和顶点位置坐标geometry.attributes.position是一一对应的。

作用:纹理贴图上提取像素映射到网格模型Mesh的几何体表面上。

顶点UV坐标,可以在0~1.0之间任意取值(是百分比值,比如0.3,对应是30%的位置),纹理贴图左下角对应的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个为一组,表示一个顶点的纹理坐标

获取纹理贴图左下角四分之一部分:

const uvs = new Float32Array([
    0, 0, 
    0.5, 0, 
    0.5, 0.5, 
    0, 0.5, 
]);

3、实现瓷砖地面效果

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

4、背景透明的png图片贴图-transparent

如果不做处理,直接贴图,透明部分会有颜色填充,因此要设置transparent: true解决

const material = new THREE.MeshBasicMaterial({
    map: textureLoader.load('./指南针.png'),        
    transparent: true, //使用背景透明的png贴图,注意开启透明计算
});

5、创建uv动画(利用offset属性)

通过纹理对象的偏移属性.offset实现一个UV动画效果。offset是Vector2对象。

texture.offset.x +=0.5;//纹理U方向偏移
// 设置.wrapS也就是U方向,纹理映射模式(包裹模式)
texture.wrapS = THREE.RepeatWrapping;//对应offste.x偏移

texture.offset.y +=0.5;//纹理V方向偏移
// 设置.wrapT也就是V方向,纹理映射模式
texture.wrapT = THREE.RepeatWrapping;//对应offste.y偏移

把上面代码放到渲染循环函数中执行即可实现动画。

记住对应关系:X:S:U这3个对应,Y:T:V这3个对应。

下面展示一个示例:

 实例完整代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Three.js中文网:www.webgl3d.cn</title>
    <style>
        body{
            overflow: hidden;
             margin: 0px;
        }
    </style>
</head>
<body>
    <!-- type="importmap"功能:.html文件中也能和nodejs开发环境中一样方式,引入npm安装的js库 -->
    <script type="importmap">
        {
            "imports": {
                "three": "../../../three.js/build/three.module.js",
                "three/addons/": "../../../three.js/examples/jsm/"
            }
        }
    </script>
    <script  type="module">  
    import * as THREE from 'three';
    import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

    // 一个矩形平面几何体用来表示传送带
    const geometry = new THREE.PlaneGeometry(200, 20);
    //纹理贴图加载器TextureLoader
    const texLoader = new THREE.TextureLoader();
    // .load()方法加载图像,返回一个纹理对象Texture
    const texture = texLoader.load('./纹理3.jpg');

    const material = new THREE.MeshLambertMaterial({
        map: texture,//map表示材质的颜色贴图属性
    });
    const mesh = new THREE.Mesh(geometry, material);
    mesh.rotateX(-Math.PI/2);

    // 设置阵列
    texture.wrapS = THREE.RepeatWrapping;
    // uv两个方向纹理重复数量
    texture.repeat.x=50;//注意选择合适的阵列数量

    //场景
    const scene = new THREE.Scene();
    scene.add(mesh); //模型对象添加到场景中

    //辅助观察的坐标系
    const axesHelper = new THREE.AxesHelper(100);
    scene.add(axesHelper);

    //光源设置
    const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
    directionalLight.position.set(100, 60, 50);
    scene.add(directionalLight);
    const ambient = new THREE.AmbientLight(0xffffff, 0.4);
    scene.add(ambient);


    //渲染器和相机
    const width = window.innerWidth;
    const height = window.innerHeight;
    const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);
    camera.position.set(292, 223, 185);
    camera.lookAt(0, 0, 0);

    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(width, height);
    document.body.appendChild(renderer.domElement);



    // 渲染循环
    function render() {
        texture.offset.x +=0.1;//设置纹理动画
        renderer.render(scene, camera);
        requestAnimationFrame(render);
    }
    render();


    const controls = new OrbitControls(camera, renderer.domElement);

    // 画布跟随窗口变化
    window.onresize = function () {
        renderer.setSize(window.innerWidth, window.innerHeight);
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
    };
    </script>
</body>

</html>

 

 文章中部分素材选取自Threejs中文网:http://www.webgl3d.cn/

posted @ 2023-02-09 19:59  JackGIS  阅读(3251)  评论(0编辑  收藏  举报