Web3D-纹理篇

287 阅读4分钟

加载纹理

请注意three.js r84遗弃了TextureLoader进度事件。对于支持进度事件的TextureLoader , 请参考this thread

  // 纹理
  loadTextures () {
    // -----------------方法一
    // const img = new Image()
    // // 创建纹理
    // const texture = new THREE.Texture(img)

    // img.onload = function () {
    //   console.log(texture);
    //   // 更新纹理
    //   texture.needsUpdate = true
    // }
    // img.src = '/src/assets/textures/Wood_Ceiling_Coffers_003_basecolor.Cu38ry6v.jpg';
    // this.texture = texture;

    // ------------------方法二
    // setCrossOrigin('anonymous') 跨域方法
    // const textLoader = new THREE.TextureLoader()
    // this.texture =  textLoader.setCrossOrigin('anonymous').load(
    //   // this.texture =  textLoader.load(
    //   // 'https://3dbooks.netlify.app/assets/Wood_Ceiling_Coffers_003_basecolor.Cu38ry6v.jpg',
    //   '/src/assets/textures/Wood_Ceiling_Coffers_003_basecolor.Cu38ry6v.jpg',
    //   // onLoad回调
    //   function (texture) {},
    //   null,
    //   // onError回调
    //   (error) => {
    //     console.log('error', error);
    //   }
    // )

    // 方法三
    const manager = new THREE.LoadingManager()
    manager.onStart = function( url, itemsLoaded, itemsTotal) {
      console.log( 'Start loading file: '+url +'.\nLoaded' + itemsLoaded+'of '+ itemsTotal+'files.');
    }

    manager.onLoad = function() {
      console.log('Loading complete !');
    }

    manager.onProgress = function( url, itemsLoaded, itemsTotal ){
      console.log('Loading file: '+ url+ '.\Loaded ' +itemsLoaded+ ' of '+itemsTotal+' files. ');
    }

    manager.onError = (url) =>{
      console.log('There was an error loading '+ url);
    }

    const textureLoader = new THREE.TextureLoader(manager)
    const texture = textureLoader.load('/src/assets/textures/Wood_Ceiling_Coffers_003_basecolor.Cu38ry6v.jpg')

    this.texture = texture
  },

// 创建立方体材质
const material = new THREE.MeshLambertMaterial({
  // color: 0x1890ff,
  map: this.texture
})

加载6个面的纹理图

const cubeTextureLoader = new THREE.CubeTextureLoader().load([
    '1.jpg', '2.jpg', '3.jpg', '4.jpg', '5.jpg', '6.jpg',
])
  • 不是纹理图片尺寸越大越好,必要时对图片进行压缩,如,图片尺寸,图片1M->500kb。
  • 纹理占用计算: 宽度 * 高度 * 4 * 1.33字节的内存,需要考虑压缩。
  • 添加几何体的分段数(即一个面上的添加三角形密度),可在物体上,展现纹理的高度、深度、坡度等细节。

利用mipmap解决纹素问题

mipmap优缺点:

  • 优点:
  1. 质量高;避免在远距离情况下,采样频率低+数据频率高,造成失真和摩尔纹。
  2. 性能好:避免不使用Mimap下,距离远时,采样频率低+数据频率高,造成 texture cache 命中率不高(相邻Pixel 采样 Texel时,uv相差比较大),让性能下降。
  • 缺点:
  1. 占用显存,可使用ue 的纹理流 缓存优化(IO换显存)。

Texture纹理对象中,magFilter 属性,可解决纹理图像素极小,呈现出模糊失真的情况。配置代码:texture.magFilter = THREE.NearestFilter;

附加AO贴图

ao贴图在不打光时,模拟体积感;ao贴图不受任何光照影响,仅计算物体间的距离。计算球体的AO贴图时,每个像素根据物体的法线,发射出光线,这个光碰触到物体时,就会产生反馈,标记附近有物体,即呈现黑色。

  • 在纹理较暗的地方添加阴影,必须设置第二组UV才生效。boxGeometry.setAttribute('uv2', new THREE.BufferAttribute(geometry.attributes.uv.array, 2))

bumpMap凹凸贴图只影响光照效果,不影响几何模型。displacementMap位移贴图取代bumpMap凹凸贴图。

颜色纹理+AO+位移贴图呈现立体感

image.png

// 添加全局光照
const ambientLight = new THREE.AmbientLight(0xffffff, 0.7)

  // 创建立方体对象
  createObjects () {
    // 加载纹理
    const colorTexture = this.textureLoader.load('/src/assets/textures/1.jpg') // 颜色纹理
    const aoTexture = this.textureLoader.load('/src/assets/textures/2.jpg') // ao纹理
    // 凹凸贴图只影响光照效果,不影响几何模型
    const bumpTexture = this.textureLoader.load('/src/assets/textures/3.jpg') // 凹凸纹理

    // 创建立方体的几何体
    const geometry = new THREE.BoxGeometry(2, 2, 2)
    // 创建立方体材质
    const material = new THREE.MeshLambertMaterial({
      map: colorTexture,
    })
    // 创建3D物体对象
    const mesh = new THREE.Mesh(geometry, material);
    mesh.position.x = -2;

    // 立方体
    const boxGeometry = new THREE.BoxGeometry(2, 2, 2, 64, 64, 64)
    const boxMaterial = new THREE.MeshLambertMaterial({
      map: colorTexture,
      aoMap: aoTexture,
      aoMapIntensity: 0.8,
      displacementMap: bumpTexture,
      // displacementBias: 0, // 位移贴图在网格顶点上的偏移量
      // displacementScale: 0, // 位移贴图对网格的影响度(黑色无位移,白色是最大位移)
    })

    const box = new THREE.Mesh(boxGeometry, boxMaterial)
    // 手动添加第二个 uv属性
    box.position.x = 1;

    this.scene.add(mesh, box)
    this.mesh = mesh;
    this.box = box
  },

// datGui调试器
gui.add(_this.box.material, 'aoMapIntensity', 0, 1, 0.1)
gui.add(_this.box.material, 'displacementBias', 0, 1, 0.1)
gui.add(_this.box.material, 'displacementScale', 0, 1, 0.1)

法线贴图(需要光照)

法线贴图不会改变曲面的形状,只会改变光照的效果。阴影细节,使纹理更有立体质感。 image.png

  // 创建光照
  createLights () {
    // 添加全局光照
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.7)
    const directionalLight = new THREE.DirectionalLight(0xffffff, 0.7)

    // 改变光照方向
    directionalLight.position.set(1, 2, 4)

    this.scene.add(ambientLight, directionalLight);
  },
  // 创建立方体对象
  createObjects () {
    // 加载纹理
    const colorTexture = this.textureLoader.load('/src/assets/textures/1.jpg') // 颜色纹理
    const normalTexture = this.textureLoader.load('/src/assets/textures/4.jpg') // 法线纹理
    // 创建立方体的几何体
    const geometry = new THREE.SphereGeometry(2, 64, 64)
    // 创建立方体材质
    const material = new THREE.MeshPhongMaterial({
      map: colorTexture,
    })
    // 创建3D物体对象
    const mesh = new THREE.Mesh(geometry, material);
    mesh.position.x = -3;

    // 立方体
    const boxGeometry = new THREE.SphereGeometry(2, 64, 64)
    const boxMaterial = new THREE.MeshPhongMaterial({
      map: colorTexture,
      normalMap: normalTexture, // 法线贴图
    })

    const box = new THREE.Mesh(boxGeometry, boxMaterial)
    box.position.x = 3;

    this.scene.add(mesh, box)
    this.mesh = mesh;
    this.box = box
  },
  
gui.add(_this.box.material.normalScale, 'x', 0, 1, 0.1).onChange(val =>{
  _this.box.material.normalScale = new THREE.Vector2(val, _this.box.material.normalScale.y)
})
gui.add(_this.box.material.normalScale, 'y', 0, 1, 0.1).onChange(val => {
  _this.box.material.normalScale = new THREE.Vector2(_this.box.material.normalScale.x, val)
})

光滑度贴图

反映材质的光滑程度,可模拟漫反射、镜面反射。roughness属性值越大,光点涣散,材质更粗糙(漫反射);越小,材质更光滑(镜面反射)。使用的是MeshStandardMaterial材质。 image.png image.png

纹理属性

在css中有repeat平铺属性,在three.js中叫纹理回环

请注意:纹理中图像的平铺,仅有当图像大小(以像素为单位)为2的幂(2、4、8、16、32、64、128、256、512、1024、2048、……)时才起作用。 宽度、高度无需相等,但每个维度的长度必须都是2的幂。 这是WebGL中的限制,不是由three.js所限制的。

colorTexture.repeat.set(2, 2) // 纹理回环 
// RepeatWrapping 阵列  ClampToEdgeWrapping 默认值  MirroredRepeatWrapping 镜像
colorTexture.wrapS = THREE.RepeatWrapping // 水平方向上的纹理效果
colorTexture.wrapT = THREE.RepeatWrapping // 垂直方向上的纹理效果
colorTexture.offset = new THREE.Vector2(0.5, 0.5) // 纹理偏移
colorTexture.rotation = Math.PI / 12 // 旋转 正值=逆时针  负值=顺时针
colorTexture.center.set(0.5, 0.5) // 设置纹理的旋转中心,默认(0,0), (0.5, 0.5)是纹理的中心

纹理贴图资源