three.js学习之纹理贴图Texture中,我们初步了解了three.js中纹理基础知识,包括怎么使用TextureLoader
加载一张或者多张纹理图片,如何使用回调函数或者loadimgManager
管理纹理图片的加载进度,如何计算纹理图片占用的内存大小。最后,我们提到了使用wrapS
和wrapT
配合rotation
,repeat
,offset
来管理贴图的位置,本篇博文中,我们进一步去探索这些内容及其它一些关于纹理的知识点。
在正式开始之前,还是和以前一样,复制three.js学习之纹理贴图Texture中我们做出来的demo代码,删除除了使用多张纹理贴图的代码,只保留使用我家猫咪代码的那段代码即可。
在保留下来的代码中,先删除掉three.js学习之纹理贴图Texture中所有新添加的代码,只保留基础代码即可。
开始之前,我们先使用PlaneGeometry
做一个平面。
1 | const width = 8; |
然后我们停止公用代码中动画的部分,让平面静下来,方便我们后续观察。
停止动画代码如下
1 | // requestAnimationFrame(render) 注释掉这行代码 |
注释掉render
函数中的这些行代码1
2
3
4
5
6
7// time *= 0.001
// group['children'].forEach((each, index) => {
// each.rotation.x = -time
// each.rotation.y = -time
// })
// requestAnimationFrame(render)
重新认识Texture中的wrapS
和wrapT
在默认情况下,新添加的纹理图片会铺满整个平面,这个时候wrapS
和wrapT
实际上并没有什么作用。它两的用处是当新添加的纹理图片由于某些原因无法填满平面而流出的空白区域应该怎么处理。
wrapS
和wrapT
有三个可选值来决定这些留出的空白区域的处理方式。
THREE.ClampToEdgeWrapping
会将空白区域接触到的第一个像素,也是非空白区域的最后一个像素平铺到空白区域。这个值是默认值,所以当我们不对wrapS
和wrapT
设置的时候,他们的值就是它。THREE.RepeatWrapping
会正常复制重复原有的内容,比如我们原先放的是我家猫的照片,那么空白区域会继续将猫的照片放上去。THREE.MirroredRepeatWrapping
和THREE.RepeatWrapping
一样会复制原有内容进行重复,不过会做一个镜像处理。
wrapS
被用来决定水平方向(x)怎么处理空白区域wrapT
被用来决定垂直方向(y)怎么处理空白区域
wrapS
和wrapT
制定了规则,当规则被触发时,也会执行对应的动作,这些规则,还是需要有人来触发,接下来我们一起了解一下触发这些行为的规则。
重新认识three.js中Texture纹理贴图下的repeat
属性
毫无疑问,repeat
就是用来设置纹理贴图重复用的。同wrapS
和wrapT
一样,repeat
通过x和y决定水平方向和垂直方向纹理贴图的重复方式。
有两种方式去设置x和y的值,第一种是直接设置,像这样texture.repeat.x = 2
,texture.repeat.y = 2
。第二种是使用set
函数来设置,像这样texture.repeat.set(x,y)
,或者这样texture.repeat.setX(2)
,texture.repeat.setY(2)
。
repeat
和wrapS
,wrapT
配合使用时,可以这样理解,当我们在水平或者垂直方向上设置重复n次时,就相当于将平面在对应方向切割成了n块,除了第一块是原来的纹理图片外,其余的都是空白区域,这些空白区域,将会接受wrapS
,wrapT
设置的规则进行改变。
下面进入实战环节
three.js 中
texture
的参数wrapS
,wrapT
的值是THREE.ClampToEdgeWrapping
时repeat
的不同表现设置
repeat
的x参数修改代码
1
2
3
4
5
6
7
8
9
10
11const width = 8;
const height = 8;
cosnt planeGeometry = new THREE.PlaneGeometry(width, height)
const texture = await load('https://picture.wuhoushu.com/blog/mao.png');
texture.repeat.x = 2 // 默认情况下,添加这一行即可,也可以用texture.repeat.setX(2)进行设置
const materail = new THREE.MeshBasicMaterial({
map: texture
})
const plane = new THREE.Mesh(planeGeometry, materail)结果如下
可以看到,小猫头部好像有一簇毛长了起来。这是因为最后一个像素被无限复制了,因为其它地方颜色都一样,所以看不出明显的异常来。设置
repeat
的y参数接下来我们同将垂直方向(y)设置重复看看情况
1
2// texture.repeat.x = 2 注释掉这行
texture.repeat.setY(2) // 等同于texture.repeat.y = 2。这么写只是为了正面两个方法都可以用。这个时候,我们发现小猫的背部也被拉长了。
在
wrapS
和wrapT
的参数是ClampToEdgeWrapping
时,正如我们开头描述的一样,空白区域被最后一个像素填充来,前面的例子都是只单一的改变x
或者y
的情况,接下来我们再看看二者同时改变的情况。同时设置
repeat
的x和y参数将前面注释掉的代码释放出来,如下
1
2texture.repeat.x = 4
texture.repeat.setY(4) // 等同于texture.repeat.y = 2。这么写只是为了正面两个方法都可以用。在当前的示例中,我们将repeat的参数x和y都设置成了4,也就是横向和纵向上平面被分成了4份,猫的身体也缩小到了原来的四分之一,其余四分之三则被最后一个像素所占领。
接下来我们看看相同状况下改变
wrapS
和wrapT
会有什么不一样的情况发生
wrapS
,wrapT
的参数是THREE.RepeatWrapping
时repeat
的不同表现设置
repeat
的x参数修改代码:
1
2
3
4
5
6
7
8
9
10
11
12const width = 8;
const height = 8;
cosnt planeGeometry = new THREE.PlaneGeometry(width, height)
const texture = await load('https://picture.wuhoushu.com/blog/mao.png');
texture.wrapS = THREE.RepeatWrapping
texture.wrapT = THREE.RepeatWrapping
texture.repeat.x = 2 // 默认情况下,添加这一行即可,也可以用texture.repeat.setX(2)进行设置
const materail = new THREE.MeshBasicMaterial({
map: texture
})
const plane = new THREE.Mesh(planeGeometry, materail)可以看到,纹理背景在水平(x)方向上分成了左右两个。
设置
修改代码repeat
的y参数1
2// texture.repeat.x = 2 注释掉这行
texture.repeat.setY(2) // 等同于texture.repeat.y = 2。这么写只是为了正面两个方法都可以用。
可以看到,背景分成了上下(y方向)两个
怎么回事,怎么出现了这么多?其实很简单,我们在横向纵向上都重复了4次,4*4=16。所以一下子在背景上出现了14个猫。之所以在
wrapS
,wrapT
等于ClampToEdgeWrapping
时我们只是复制最后一个像素哦,所以感觉没有那么强烈。
wrapS
,wrapT
的参数是THREE.MirroredRepeatWrapping
时repeat
的不同表现设置
repeat
的x参数修改代码:
1
2
3
4
5
6
7
8
9
10
11
12const width = 8;
const height = 8;
cosnt planeGeometry = new THREE.PlaneGeometry(width, height)
const texture = await load('https://picture.wuhoushu.com/blog/mao.png');
texture.wrapS = THREE.MirroredRepeatWrapping
texture.wrapT = THREE.MirroredRepeatWrapping
texture.repeat.x = 2 // 默认情况下,添加这一行即可,也可以用texture.repeat.setX(2)进行设置
const materail = new THREE.MeshBasicMaterial({
map: texture
})
const plane = new THREE.Mesh(planeGeometry, materail)可以看到,纹理背景在垂直(x)方向上分成了左右两个,而且猫是头对着头,这意味着什么呢?镜像了呀。
设置
repeat
的y参数修改代码
1
2// texture.repeat.x = 2 注释掉这行
texture.repeat.setY(2) // 等同于texture.repeat.y = 2。这么写只是为了正面两个方法都可以用。可以看到,背景分成了上下(y方向)两个,背对背了。
同时设置
repeat
的x和y参数1
2texture.repeat.x = 4
texture.repeat.y = 414只猫,两两背对背,脚对脚了。
到这里,three.js中texture
的参数中wrapS
,wrapT
和repeat
互相配合使用的效果我们就探索完了。接下来我们去探索three.js中texture
的参数wrapS
,wrapT
与rotation
配合使用的情况。
重新认识three.js中Texture纹理贴图的属性rotation
属性
rotation
用来控制纹理背景绕着原点旋转多少弧度。1弧度等于180度,因此1 * Math.PI 就是一弧度。
three.js中texture
的纹理贴图旋转默认是以面的左下角为圆心进行旋转,非矩形类的面,也遵循这个规则,只是这个左下角稍微不好找一点,实战时随时观察即可。如上图,图中红色部分是旋转后剩余的空白区域,绿色部分则是已经被旋转的区域,蓝色线框框起来的部分,是实际显示的区域。在旋转过程中,空白区域将会遵循wrapS
,wrapT
设定的值所规定的规则进行填充。接下来咱们一起去验证一下。
对于rotation
的赋值,只能使用teture.rotation= 0.1 * Math.PI
这样的方式进行。
修改代码:1
2
3
4
5
6
7
8
9
10
11
12const width = 8;
const height = 8;
cosnt planeGeometry = new THREE.PlaneGeometry(width, height)
const texture = await load('https://picture.wuhoushu.com/blog/mao.png')
texture.wrapS = THREE.ClampToEdgeWrapping
texture.wrapT = THREE.ClampToEdgeWrapping
texture.rotation = 0.1 * Math.PI
const materail = new THREE.MeshBasicMaterial({
map: texture
})
const plane = new THREE.Mesh(planeGeometry, materail)
可以看到,小猫不再是横向趴着,从头到嘴这一范围(空白区域)内有明显的单元素延伸迹象。
three.js 中
texture
的参数wrapS
,wrapT
的值是THREE.RepeatWrapping
时rotation
的不同表现修改代码:
将1
2texture.wrapS = THREE.ClampToEdgeWrapping
texture.wrapT = THREE.ClampToEdgeWrapping改为
1
2texture.wrapS = THREE.RepeatWrapping
texture.wrapT = THREE.RepeatWrapping可以看到,空白区域出现了猫身体的一部分内容,这是因为旋转后空白区域被分割成不同的区域,纹理背景重复出现,符合预期。
three.js 中
texture
的参数wrapS
,wrapT
的值是THREE.MirroredRepeatWrapping
时rotation
的不同表现
修改代码:
将
1
2texture.wrapS = THREE.RepeatWrapping
texture.wrapT = THREE.RepeatWrapping
改为
1
2texture.wrapS = THREE.MirroredRepeatWrapping
texture.wrapT = THREE.MirroredRepeatWrapping
小猫背对背,嘴对嘴的再次出现,完美符合预期。
最后,我们再看一下 wrapS
,wrapT
与offset
的配合
重新认识three.js中Texture纹理贴图的属性offset
offset
用来决定纹理贴图朝着某个方向偏移多少距离,它有两个值,分别是x和y。同repeat一样,offset也可以通过两种方法设置,第一种就是直接赋值,第二种是通过set进行设置,取值范围在0到1之间,0是不移动,1是整体完全移动出去。
three.js 中
texture
的参数wrapS
,wrapT
的值是THREE.ClampToEdgeWrapping
时offset
的不同表现设置
offset
的x参数修改代码
1
2
3
4
5
6
7
8
9
10
11const width = 8;
const height = 8;
cosnt planeGeometry = new THREE.PlaneGeometry(width, height)
const texture = await load('https://picture.wuhoushu.com/blog/mao.png');
texture.offset.x = 0.3 // 默认情况下,添加这一行即可,也可以用texture.offset.setX(0.3)进行设置
const materail = new THREE.MeshBasicMaterial({
map: texture
})
const plane = new THREE.Mesh(planeGeometry, materail)结果如下
可以看到,小猫头部好像有一簇毛长了起来。这是因为最后一个像素被无限复制了,因为其它地方颜色都一样,所以看不出明显的异常来。感觉和repeat
非常类似设置
offset
的y参数接下来我们同将垂直方向(y)设置重复看看情况
1
2// texture.offset.x = 0.3 注释掉这行
texture.offset.setY(0.3) // 等同于texture.offset.y = 2。这么写只是为了正面两个方法都可以用。这个时候,我们发现小猫的背部也被拉长了。是不是和repeat的参数等于2时非常像。
在
wrapS
和wrapT
的参数是ClampToEdgeWrapping
时,正如我们开头描述的一样,空白区域被最后一个像素填充来,前面的例子都是只单一的改变x
或者y
的情况,接下来我们再看看二者同时改变的情况。同时设置
offset
的x和y参数将前面注释掉的代码释放出来,如下
1
2texture.offset.x = 0.3
texture.offset.setY(0.3)当前的示例中,小猫的北部和头部都被无限拉长了。
接下来我们看看相同状况下改变
wrapS
和wrapT
会有什么不一样的情况发生
wrapS
,wrapT
的参数是THREE.RepeatWrapping
时offset
的不同表现设置
offset
的x参数修改代码:
1
2
3
4
5
6
7
8
9
10
11
12const width = 8;
const height = 8;
cosnt planeGeometry = new THREE.PlaneGeometry(width, height)
const texture = await load('https://picture.wuhoushu.com/blog/mao.png');
texture.wrapS = THREE.RepeatWrapping
texture.wrapT = THREE.RepeatWrapping
texture.offset.x = 0.3 // 默认情况下,添加这一行即可,也可以用texture.offset.setX(0.3)进行设置
const materail = new THREE.MeshBasicMaterial({
map: texture
})
const plane = new THREE.Mesh(planeGeometry, materail)可以看到,纹理背景在水平(x)方向上分成了左右两个。只不过右边稍微小点,因为咱们移动的距离时0.3嘛
可以看到,背景分成了上下(y方向)两个
小猫出现在了四个不同的地方,如果这些地方足够大,将是四只完整的猫。
wrapS
,wrapT
的参数是THREE.MirroredRepeatWrapping
时offset
的不同表现设置
offset
的x参数修改代码:
1
2
3
4
5
6
7
8
9
10
11
12const width = 8;
const height = 8;
cosnt planeGeometry = new THREE.PlaneGeometry(width, height)
const texture = await load('https://picture.wuhoushu.com/blog/mao.png');
texture.wrapS = THREE.MirroredRepeatWrapping
texture.wrapT = THREE.MirroredRepeatWrapping
texture.offset.x = 0.3
const materail = new THREE.MeshBasicMaterial({
map: texture
})
const plane = new THREE.Mesh(planeGeometry, materail)可以看到,纹理背景在垂直(x)方向上分成了左右两个,而且猫是头对着头,这意味着什么呢?镜像了呀。
设置
offset
的y参数修改代码
1
2// texture.offset.x = 0.3 注释掉这行
texture.repeat.setY(0.3) 。可以看到,背景分成了上下(y方向)两个,背对背了。
同时设置
offset
的x和y参数1
2texture.offset.x = 0.3
texture.offset.y = 0.34只猫,两两背对背,脚对脚了。
到这里,three.js中texture
的参数中wrapS
,wrapT
和offset
互相配合使用的效果我们就探索完了。但是咱们只是做了简单的探索,将纹理图片放到不同的立体图形上时,虽然基本上和现在一致,但因为立体图形的多样性,还是会有很多的差别。这些我们后面都会找时间进行进一步的探索,如果你有时间,也可以在本文的基础上自己去探索一下。
three.js中关于纹理背景移动,旋转,重复布局的问题,我们在这里也探索完了,下面还是一如即往的奉上完整源码,供诸君参考。
1 | <script> |