three.js_9-texture

1.什么是纹理?

纹理(Texture)就是覆盖在几何体上的图片,用于给几何体表面增添色彩和细节,不仅仅是简单的贴图,它还包含很多控制渲染表现的参数。而材质(Material)则是用于决定如何将这些纹理与光照等进行计算并呈现在屏幕上的着色算法。

2.纹理贴图种类

2.1 Alpha 贴图

  • Alpha 贴图是一个灰度图,白色的地方代表可见,黑色则代表不可见,如下图

2.2 高度贴图 (HEIGHT)

  • 高度贴图也是一个灰度图,代表高度。
  • 一个像素点的灰度会提高或降低平面的高度。需要提供细分信息(subdivision)如下图

2.3 法线贴图 (NORMAL)

  • 法线贴图需要我们添加一些表面细节,如光照。
  • 无需细分信息相比高度贴图有更高的性能

2.4 环境光遮蔽贴图 (AMBIENT OCCLUSION)

  • 环境光遮蔽贴图也是一个灰度图,作用是增加物体的阴影
  • 但它并不会模拟真实的物理光照效果(假阴影)。通常用来添加反差效果和更多细节

2.5 金属度贴图 (METALNESS)

  • 金属度贴图是一个灰度图。白色地方代表金属光泽,黑色代表无金属光照
  • 大多用来展示光线反射(如镜子)

2.6 粗糙度贴图 (ROUGHNESS)

  • 粗糙度贴图是一个灰度图,通常与金属度贴图配合使用。
  • 白色代表粗糙,黑色代表平滑。
  • 通常用来展示光线漫反射
    例如用它来展示一个地毯(粗糙)或者金属(光滑)

2.7 PBR principles

这些种类的贴图都需要遵循 PBR 规范

PBR 规范

PBR是基于物理表现的渲染,例如光照、金属光泽、粗糙度(实际上大多数技术都倾向于遵循现实世界的物理效果)
PBR定义了一系列的算法用来模拟现实世界物体的渲染效果

3.使用材质

了解了几种纹理贴图类型后就可以开始使用了。首先使用一个由Paulo做的门纹理,这是一个很有名的库

3.1加载纹理静态资源

使用纹理首先需要解决的问题是引入纹理图片,我们有两种方法引入

  1. 可以使用 import 导入
1
2
import imageSource from './color.jpg'
console.log(imageSource) //http://localhost/images/c43f...jpg
  1. 静态资源服务器
    我们可以在项目根目录的static目录下放置图片
然后就能在浏览器中直接访问到图片了
以上方式皆基于webpack
  1. Native Javascript
1
2
3
4
5
6
7
const image = new Image()

image.onload = () => {
console.log("image loaded")
}

image.src = '/textures/door/color.jpg'

缺点:当图片大,宽带小时会花费很长时间加载

3.2初始化纹理

纹理被加载进来后就可以开始初始化,并将纹理应用在材质上了

1
2
3
4
5
6
7
8
const image = new Image()

image.onload = () => {
const texture = new THREE.Texture(image) //初始化纹理
console.log("image loaded")
}

image.src = '/textures/door/color.jpg'

实际上这段代码可以使用 texture.needsUpdate 属性实现纹理的自动更新,这样就可以避免因作用域问题需要手动维护texture的状态。

1
2
3
4
5
6
7
8
const image = new Image()
const texture = new THREE.Texture(image)
image.onload = () => {
texture.needsUpdate = true
console.log("image loaded")
}

image.src = '/textures/door/color.jpg'

TextureLoader

除了使用img初始化纹理以外,还可以使用 TextureLoader 直接加载纹理

1
2
3
4
5
const textureLoader = new THREE.TextureLoader()
const texture = textureLoader.load('/textures/door/color.jpg')
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshBasicMaterial({map:texture}) //使用纹理
const mesh = new THREE.Mesh(geometry, material)

TextureLoader 可以在第一个参数后添加三个function用来处理加载纹理的生命周期

  • load -当纹理成功加载后触发
  • progress -纹理加载中触发
  • error -纹理加载失败触发

LoadingManager

当加载字体、模型、材质时就需要一个全局的加载进度来告知用户哪些被加载完成了,这个功能已在three.js中集成,既LoadingManager

1
2
3
const loadingManager = new THREE.LoadingManager()
const textureLoader = new THREE.TextureLoader(loadingManager)
const texture = textureLoader.load('/textures/door/color.jpg')

LoadingManager提供了许多事件可供监听,例如 onStartonLoadonProgressonError

1
2
3
4
5
6
7
8
9
10
11
12
const loadingManager = new THREE.LoadingManager()
loadingManager.onStart = ()=>{
console.log("onStart")
}
loadingManager.onLoad = ()=>{
console.log("onLoad")
}
loadingManager.onProgress = ()=>{
console.log("onProgress")
}
const textureLoader = new THREE.TextureLoader(loadingManager)
const texture = textureLoader.load('/textures/door/color.jpg')

实际上,在写完这些代码后你会发现仅仅触发 “onStart”和”onProgress” ,Loaded事件并未被触发/

UV UNWRAPPER

了解 UV Unwrapping 之前先来了解以下 UV map(贴图)UV mapping(映射)

UV Map 是包裹在3D模型上的材质,创建UV 贴图的过程就叫做UV Unwrapping(拆图)

U和V指的是2D空间的水平和垂直轴,因为X、Y、Z已经在3D空间中使用

UV mapping即是将 3D网格转换为2D信息,用于指定材质如何”贴在几何体上”

类似Three.js 内置的 BoxGeometry UV 坐标已经被指定过,可以用 geometry.attributes.uv 访问

可以看到在这个 Float32ArrayitemSize = 2,因为 UV 坐标是 2D 的

如果你创建了自己的几何体,你就需要自己指定 UV coordinates(坐标),当你使用 3D 建模软件建模时,UV Unwrapping 也是必不可少的一步

4.材质变换

4.1 重复

我们可以用 repeat 属性让材质重复,它是一个 Vector2 的属性

1
2
3
4
const colorTexture = textureLoader.load('...')

colorTexture.repeat.x = 2
colorTexture.repeat.y = 3

注意默认情况下,材质不会重复,采用最后一个材质像素拉伸适应几何体,这也是为什么上面设置 repet 属性后的怪异行为。我们可以给 wrapSwrapT 属性设置 Three.RepeatWrapping改变重复方式

1
2
colorTexture.wrapS = THREE.RepeatWrapping
colorTexture.wrapT = THREE.RepeatWrapping

我们也可以使用 THREE.MirroredRepeatWrapping 达到重复的目的

1
2
colorTexture.wrapS = THREE.MirroredRepeatWrapping
colorTexture.wrapT = THREE.MirroredRepeatWrapping

4.2 偏移

要偏移一个材质使用 offset 属性设置,这个属性也是一个 Vector2 的类型

1
2
colorTexture.offset.x = 0.5
colorTexture.offset.y = 0.5

4.3 旋转

使用 rotation 属性旋转材质。注意单位是 rad(弧度)

1
colorTexture.rotation = 1

旋转时是按照 pivot point (轴心) 旋转的。可以使用 center 属性修改轴心,这个属性是 Vector2 类型的

1
2
colorTexture.center.x = 0.5
colorTexture.center.y = 0.5

4.4 filtering and mipmapping

当你把 camera 移动至立方体的顶端至几乎看不见的时候就会发现材质变得模糊,这是因为 filteringmipmapping(多级渐远纹理) 特性

mipmapping 是把一个材质宽高不断减半细分的技术,直到得到 1x1 像素的材质分量。这些材质分量会被送到 GPU 内部,并由 GPU 决定最适合显示的材质分量

这些过程会被 Three.js 和 GPU 自动接管,不过我们任然能改变两种算法来达到合适的显示效果

  1. minification filtering
    当camera与纹理表面距离变的更远时就会发生这种情况,我们可以使用材质的 minFilter 属性来改变 filter. 它有下面六个取值

    • THREE.NearestFilter
    • THREE.LinearFilter
    • THREE.NearestMipmapNearestFilter
    • THREE.NearestMipmapLinearFilter
    • THREE.LinearMipmapNearestFilter
    • THREE.LinearMipmapLinearFilter(default)
  2. Magnification filtering
    当camera与纹理表面距离变得更近(纹理被放大)时就会发生这种情况,我们可以使用纹理的 magFilter 属性改变 filter. 它有两种取值

    • THREE.NearestFilter
    • THREE.LinearFilter (default)

当我们在 minFilter 使用 THREE.NearestFilter 时就不需要 mipmaps 了, 使用 colorTexture.generateMipmaps = false 来禁止生成 mipmaps

5.材质优化

材质图片的大小可能很大,所以我们可以使用 TinyPNG 做一个压缩


three.js_9-texture
https://nanxfu.github.io/2023/02/19/three-js-9-texture/
Beitragsautor
nanxfu
Veröffentlicht am
February 19, 2023
Urheberrechtshinweis