Threejs ShapeGeometry自定义形状贴图

最近项目需要在3D场景中给自定义的楼层区域进行贴图区分,对于普通的的纯色材质,实现比较简单,但是如果要进行纹理贴图的材质,就有点复杂了,这里写篇文章记录下。

首先看看我们的楼层定义,如何实现自定义区域。其实很简单,我们使用有序的点来定义楼层的平面形状,然后根据平面的定义,自动生成3d的平面区域。

var areaPts = [];
for (var idx = 0 ; idx < area.points.length; idx++) {
    var p = area.points[idx];
     var v = new THREE.Vector2(p.px , p.py );
     areaPts.push(v);
}
var areaShape = new THREE.Shape(areaPts);
var geometry = new THREE.ShapeGeometry(areaShape);

如果是纯色的贴图我们怎么做,很简单直接设置颜色即可

var material = new THREE.MeshBasicMaterial({ color: color, side: THREE.DoubleSide, transparent: true, opacity: opacity });
var mesh = new THREE.Mesh(geometry, material);

对于贴图,我们使用同样的方法,看看会的到什么效果呢

var texture = new THREE.CanvasTexture(canvas); 
var material = new THREE.MeshPhongMaterial({ map: texture, side: THREE.DoubleSide });

这里使用canvas作为贴图生成材质,运行后,非常不幸,你不会看到正常的贴图。这是为什么呢?原来我们的模型是根据一个shape生成的ShapeGeometry,所以贴图会采用UV坐标进行贴图,关于UV的解释可以看看这篇文章。所以我们需要计算模型的uv坐标供材质贴图使用。

function assignUVs(geometry) { 
        geometry.computeBoundingBox(); 
        var max = geometry.boundingBox.max,
                min = geometry.boundingBox.min;
        var offset = new THREE.Vector2(0 - min.x, 0 - min.y);
        var range = new THREE.Vector2(max.x - min.x, max.y - min.y);
        var faces = geometry.faces; 
        geometry.faceVertexUvs[0] = []; 
        for (var i = 0; i < faces.length ; i++) { 
            var v1 = geometry.vertices[faces[i].a],
                    v2 = geometry.vertices[faces[i].b],
                    v3 = geometry.vertices[faces[i].c]; 
            geometry.faceVertexUvs[0].push([
                new THREE.Vector2((v1.x + offset.x) / range.x, (v1.y + offset.y) / range.y),
                new THREE.Vector2((v2.x + offset.x) / range.x, (v2.y + offset.y) / range.y),
                new THREE.Vector2((v3.x + offset.x) / range.x, (v3.y + offset.y) / range.y)
            ]);
        }
        geometry.uvsNeedUpdate = true;
    }

算好geomotry的uv坐标后,我们就可以放心大胆的进行贴图了。

function getColRowMaterial(mesh)
    {
        var geometry = mesh.geometry;
        assignUVs(geometry);
        var area = mesh.userData.area;
        geometry.computeBoundingBox(); 
        var canvas = getColRowCanvas(area.rows, area.cols, geometry.boundingBox.size().x, geometry.boundingBox.size().y);
        var texture = new THREE.CanvasTexture(canvas);
        texture.wrapT = THREE.RepeatWrapping;
        texture.repeat.y = -1;
        var material = new THREE.MeshPhongMaterial({ map: texture, side: THREE.DoubleSide });
        mesh.material = material;
    }

需要注意的是我们的图片坐标和uv坐标的Y轴是反的图片Y轴是向下的,UV坐标Y轴是向上的。所以我们需要反向下y。

texture.wrapT = THREE.RepeatWrapping;
texture.repeat.y = -1;

经过以上的代码我们就可以得到正确的贴图了。


在线demo


本文由 Leo's Blog 创作,采用 署名-非商业性使用 2.5 中国大陆 进行许可。
如需转载、引用请署名作者且注明文章出处。
2016年12月28日 2448 浏览 评论 Threejs web3d webgl ShapeGeometry 自定义区域贴图
上一篇:YII2 GridView的Pjax 手动触发与不触发 | 下一篇:Threejs教程——动画