Threejs ShapeGeometry自定义形状贴图

您所在的位置:网站首页 手机自定义贴图 Threejs ShapeGeometry自定义形状贴图

Threejs ShapeGeometry自定义形状贴图

2024-03-02 02:50| 来源: 网络整理| 查看: 265

最近项目需要在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

 

 

    Three.js Geometry Texture             body {             margin: 0;         }

        canvas {             width: 100%;             height: 100%         }                

    var floorData;     var resizeRatio = 50;     var scene = new THREE.Scene();     var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 10000);     camera.position.set(0, 100, 100);

    var renderer = new THREE.WebGLRenderer({antialias: true});     renderer.setSize(window.innerWidth, window.innerHeight);     renderer.setClearColor(0x177FB3, 1);     document.body.appendChild(renderer.domElement);

    var lightDirect = new THREE.DirectionalLight(0xf2f2f2, 0.8);//设置平行光源     lightDirect.position.set(0, 200, 0);//设置光源向量     scene.add(lightDirect);

    var controls = new THREE.OrbitControls(camera, renderer.domElement);

    addFloor();

    render();

    function render() {         requestAnimationFrame(render);         renderer.render(scene, camera);     }     ;

    function addFloor() {         $.getJSON("data/floorshape.json", function (result) {             floorData = result.data;             showAreas();             showWall();         });     }

    function showWall() {

        var wallgeometry = new THREE.BoxGeometry(1, 1, 1);         for (var i = 0; i < this.floorData.wall_segments.length; i++) {             var seg = this.floorData.wall_segments[i];             var start = this.floorData.wall_points[seg.start_idx];             var end = this.floorData.wall_points[seg.end_idx];

            var a = new THREE.Vector2(start.px, start.py);             var b = new THREE.Vector2(end.px, end.py);             var v = b.sub(a);

            var wall_width = v.length() * resizeRatio, wall_height = seg.height * resizeRatio, wall_depth = 2;             var material = new THREE.MeshBasicMaterial({color: 0x898989, transparent: true, opacity: 0.6});             var wall = new THREE.Mesh(wallgeometry, material);             wall.scale.x = wall_width;             wall.scale.y = wall_height;             wall.scale.z = wall_depth;

            wall.position.x = (start.px + end.px) / 2 * resizeRatio             wall.position.y = wall_height / 2;             wall.position.z = -(start.py + end.py) / 2 * resizeRatio;

            var angle = Math.atan2(v.y, v.x);             wall.rotateY(angle);             wall.castShadow = true;             scene.add(wall);         }     }

    var areas = [];     function showAreas() {         for (var i = 0; i < this.floorData.areas.length; i++) {             var area = this.floorData.areas[i];             var areaPts = [];             for (var idx = 0; idx < area.points.length; idx++) {                 var p = area.points[idx];                 var v = new THREE.Vector2(p.px * resizeRatio, -p.py * resizeRatio);                 areaPts.push(v);             }             var areaShape = new THREE.Shape(areaPts);             var geometry = new THREE.ShapeGeometry(areaShape);             var color = area.type == 2 ? '#729CB9' : area.color;             var h = area.type == 2 ? 0 : 0.05 * resizeRatio + Math.random() * 0.05 * resizeRatio;             var opacity = area.type == 2 ? 1 : 0.6;             var material = new THREE.MeshBasicMaterial({                 color: color,                 side: THREE.DoubleSide,                 transparent: true,                 opacity: opacity             });             var mesh = new THREE.Mesh(geometry, material);             mesh.rotateX(Math.PI / 2);             mesh.position.set(area.px * resizeRatio, h, -area.py * resizeRatio);             mesh.receiveShadow = true;             scene.add(mesh);

            mesh.userData.name = area.name;             mesh.userData.objType = "area";             mesh.userData.area = area;             areas.push(mesh);

            if (area.rows && area.rows.length > 0) {                 getColRowMaterial(area, geometry, mesh);             }

            if (area.type == 2) {

                //caculate main floor area and make the camera fix the size                 var b = new THREE.Box3().setFromObject(mesh);                 var camera_z = Math.max(b.max.z, b.max.x) / 0.9;                 var camera_y = Math.tan(Math.PI / 5) * Math.abs(camera_z);                 camera.position.set(b.max.x * 0.2, camera_y, camera_z);

                camera.lookAt(new THREE.Vector3(0, 0, 0));

            }

        }     }

    function getColRowMaterial(area, geometry, mesh) {         assignUVs(geometry);         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;     }

    function getColRowCanvas(rows, cols, sizeWidth, sizeHeight) {         var width = sizeWidth, height = sizeHeight;         var fixSize = 16;         if (rows.length * fixSize > sizeWidth) {             var s = rows.length * fixSize / sizeWidth;             width = rows.length * fixSize;             height = sizeHeight * s;         }         else if (cols.length * fixSize > sizeHeight) {             var s = cols.length * fixSize / sizeHeight;             width = sizeWidth * s;             height = cols.length * fixSize;         }

        var canvas = document.createElement('canvas');         canvas.width = width;         canvas.height = height;

        var rowNumber = rows.length + 1;         var colNumber = cols.length + 1;         var rowStep = height / rows.length;         var colStep = width / cols.length;         //background         var ctx = canvas.getContext('2d');         ctx.fillStyle = '#fefefe';         ctx.fillRect(0, 0, width, height);

        var fontSize = parseInt(Math.min(rowStep, colStep) * 0.5)         ctx.fillStyle = '#2891FF';         ctx.font = fontSize + "px Arial";

        //rows         ctx.beginPath();         for (var i = 0; i < rowNumber; i++) {             ctx.fillText(rows[i], 0, (i * rowStep) + rowStep * 0.8);             ctx.moveTo(0, i * rowStep);             ctx.lineTo(width, i * rowStep);         }         //columns         for (var j = 0; j < colNumber; j++) {             ctx.fillText(cols[j], j * colStep + colStep * 0.1, rowStep * 0.4);             ctx.moveTo(j * colStep, 0);             ctx.lineTo(j * colStep, height);         }         ctx.stroke();         return canvas;     }

    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;     }

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3