Three.js如何计算3DObject的2D包围框?

推荐:用 NSDT编辑器 快速搭建可编程3D场景

在Three.js应用开发中,有时你可能需要为3D场景中的网格绘制2D的包围框,应该怎么做?

朴素的想法是把网格的3D包围框投影到屏幕空间,例如,下图中的绿色框 3D包围框, 当将其投影为 2D 时,得到的红色2D包围框,显然比想要的蓝色框大很多:

正确的做法是先将3D网格投影到屏幕空间,再计算2D包围框。

1、计算单个网格的2D包围框

只需将所有顶点转换为屏幕空间并从中创建一个 2d 边界框:

复制代码
function computescreenspaceboundingbox(mesh, camera) {
  var vertices = mesh.geometry.vertices;
  var vertex = new three.vector3();
  var min = new three.vector3(1, 1, 1);
  var max = new three.vector3(-1, -1, -1);

  for (var i = 0; i < vertices.length; i++) {
    var vertexworldcoord = vertex.copy(vertices[i]).applymatrix4(mesh.matrixworld);
    var vertexscreenspace = vertexworldcoord.project(camera);
    min.min(vertexscreenspace);
    max.max(vertexscreenspace);
  }

  return new three.box2(min, max);
}

生成的 box2 位于标准化屏幕坐标 -1, 1 中, 可以通过乘以渲染器高度和宽度的一半来获得像素:

复制代码
function normalizedtopixels(coord, renderwidthpixels, renderheightpixels) {
  var halfscreen = new three.vector2(renderwidthpixels/2, renderheightpixels/2)
  return coord.clone().multiply(halfscreen);
}

2、计算3DObject的2D包围框

更完善的实现需要考虑组、子节点等各种3DObject,其中可能包含多个网格,这需要一个递归实现,代码如下:

复制代码
function computescreenspaceboundingbox(obj, camera) {
    var min;
    var max;

    // is this an array of objects?
    if(array.isarray(obj)) {
        for(var i = 0; i < obj.length; ++i) {
            let box2 = computescreenspaceboundingbox(obj[i], camera);
            if(min === undefined) {
                min = box2.min.clone();
                max = box2.max.clone();
            } else {
                min.min(box2.min);
                max.max(box2.max);
            }
        }
    }

    // does this object have geometry?
    if(obj.geometry !== undefined) {
        var vertices = obj.geometry.vertices;
        if(vertices === undefined
            && obj.geometry.attributes !== undefined
            && 'position' in obj.geometry.attributes) {
            // buffered geometry
            var vertex = new three.vector3();       
            var pos = obj.geometry.attributes.position;
            for(var i = 0; i < pos.count * pos.itemsize; i += pos.itemsize)
            {
                vertex.set(pos.array[i], pos.array[i + 1], pos.array[1 + 2]);
                var vertexworldcoord = vertex.applymatrix4(obj.matrixworld);
                var vertexscreenspace = vertexworldcoord.project(camera);
                if(min === undefined) {
                    min = vertexscreenspace.clone();
                    max = vertexscreenspace.clone();
                }
                min.min(vertexscreenspace);
                max.max(vertexscreenspace);
            }
        } else {
            // regular geometry
            var vertex = new three.vector3();       
            for(var i = 0; i < vertices.length; ++i) {
                var vertexworldcoord = vertex.copy(vertices[i]).applymatrix4(obj.matrixworld);
                var vertexscreenspace = vertexworldcoord.project(camera);
                if(min === undefined) {
                    min = vertexscreenspace.clone();
                    max = vertexscreenspace.clone();
                }
                min.min(vertexscreenspace);
                max.max(vertexscreenspace);
            }
        }
    }
    
    // does this object have children?
    if(obj.children !== undefined) {
        for(var i = 0; i < obj.children.length; ++i) {
            let box2 = computescreenspaceboundingbox(obj.children[i], camera);
            if(min === undefined) {
                min = box2.min.clone();
                max = box2.max.clone();
            } else {
                min.min(box2.min);
                max.max(box2.max);
            }
        }
    }
    
    return new three.box2(min, max);
}

原文链接:计算3D对象的2D包围框 --- BimAnt

相关推荐
子兮曰3 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰3 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万3 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝3 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋3 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁3 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
李少兄3 天前
JavaScript 隐式全局变量解析
javascript
汉堡大王95273 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大3 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师3 天前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端