THREE JS 贴图之UV纹理映射
介绍
Threejs贴图即将图片或canvas作为贴图贴到物体表面,让模型图片表现物体的纹理。通过贴图来使不同的模型展示不同的样式,不仅仅可以实现‘换装’,也可以做一些炫酷的效果。本文主要通过设置geometry的uv坐标来实现道路流光效果。
#简单实现
思路:抛开真实道路情况的弯曲、倾斜的效果,首先创建个长方形的mesh,给mesh添加纹理,再通过animation方法每帧设置纹理的offset属性移动 -0.036。想法很好,说干就干。效果如图所示:
#查看mesh
实现直线道路的过程很轻松,以为很快就结束了,只要在场景中选取几个点位,通过Catmull-Rom算法将点解析成平滑的曲线,再生成面,其余的步骤就轻车熟路了。万万没想到出现的不是流光效果,而是闪烁效果!!!
苦思冥想N天后发现是贴图的问题,泪奔了,原因是通过顶点创建的面在geometry的faceVertexUvs属性中为空的,如图。(具体发生的原因还没有仔细研究,既然知道走到这了,手动映射一下UV就可以了吧)。
UV映射
材质贴图又称纹理贴图,是将图片包裹到物体的表面,可以理解为给书包书皮,这样可以减少计算机的计算量。UV映射是将2D图形投影到3D模型表面,U、V表示纹理贴图的坐标,范围在0~1之间(即图1的坐标范围为(0,0)~(1,1))。如下图所示图1中0~7表示我们要添加的坐标点,根据这7个点来生成平面,THREE JS中进行UV映射时应按照逆时针构建三角网,正确的顺序应为(0,1,3)、(0,3,2)……一共要构建6个三角网,当然如果要按照顺时针构建的话显示图像就会在背面,也可以设置显示方式为THREE.DOUBLE,可以正反面都显示。假设图二表示我们要映射的图片,图片的左下角对应0坐标,右下角对应1坐标,2坐标对应图片的1/3位置。以此类推即可。
UV映射理解通了很简单,如果做复杂模型的话多一些工作量,大体思路是这样的。
模拟弯曲道路流光效果-完结撒花
有了以上的知识基础,再实现流光道路就轻车熟路了。
/**
* 弯曲道路 流光效果
*/
function initBlendRoad() {
//texture
var textureLoader = new THREE.TextureLoader();
texture = textureLoader.load("../../static/img/road/newRoad.png");
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.magFilter = THREE.LinearFilter;
texture.offset.z = 0.5;
texture.repeat.x = 2;
texture.repeat.y = 3;
//vertices
var points1 = [
new THREE.Vector3(50,-100,0),
new THREE.Vector3(43,0,3),
new THREE.Vector3(-20,50,0),
];
var vertices1 = new THREE.CatmullRomCurve3(points1);
var points2 = [
new THREE.Vector3(60,-100,0),
new THREE.Vector3(50,0,5),
new THREE.Vector3(-19.5,59.4,0),
];
var vertices2 = new THREE.CatmullRomCurve3(points2);
//mesh
var initgeometry = new THREE.INITGEOMETRY(vertices1.getPoints(99), vertices2.getPoints(99));
this.initFaceVertexUvs(initgeometry);
var initMaterial = new THREE.INITMATERIAL({texture:texture});
var mesh = new THREE.Mesh(initgeometry, initMaterial);
// console.log('curve road',mesh);
scene.add(mesh);
}
/**
* 传入geometry,根据 faces属性生成 faceVertexUvs
* 默认逆时针加载,从(0,0)点开始
*/
function initFaceVertexUvs(geometry){
let len = geometry.faces.length+2;
let part = Math.floor((1/(len/2 -1)) * 100) /100;
for (let i = 2; i < len; i++) {
if (i % 2 == 0){
// 偶数
geometry.faceVertexUvs[0].push(
[
new THREE.Vector2(0,((i/2)-1) * part),
new THREE.Vector2(1,((i/2)-1) * part),
new THREE.Vector2(0,(i/2)* part),
]
);
}else if (i%2 == 1){
// 奇数
geometry.faceVertexUvs[0].push(
[
new THREE.Vector2(1,(((i-1)/2)-1) * part),
new THREE.Vector2(1,((i-1)/2) * part),
new THREE.Vector2(0,((i-1)/2) * part),
]
);
}
}
}
想要什么类型的效果可以自己用PS制作个图片或从网上下载一个即可,下面附上最终效果图: