阅山

  • WIN
    • CSharp
    • JAVA
    • OAM
    • DirectX
    • Emgucv
  • UNIX
    • FFmpeg
    • QT
    • Python
    • Opencv
    • Openwrt
    • Twisted
    • Design Patterns
    • Mysql
    • Mycat
    • MariaDB
    • Make
    • OAM
    • Supervisor
    • Nginx
    • KVM
    • Docker
    • OpenStack
  • WEB
    • ASP
    • Node.js
    • PHP
    • Directadmin
    • Openssl
    • Regex
  • APP
    • Android
  • AI
    • Algorithm
    • Deep Learning
    • Machine Learning
  • IOT
    • Device
    • MSP430
  • DIY
    • Algorithm
    • Design Patterns
    • MATH
    • X98 AIR 3G
    • Tucao
    • fun
  • LIFE
    • 美食
    • 关于我
  • LINKS
  • ME
Claves
长风破浪会有时,直挂云帆济沧海
  1. 首页
  2. Programming
  3. babylon.js
  4. 正文

Babylonjs设置minZ/maxZ 解决glb面闪烁问题

2025-09-02

一、问题

babylonjs场景,需要动态加载glb,但是发现glb会出现mesh面闪烁问题。

经过研究后发现:babylonjs 先import glb Mesh再添加灯光、相机,那glb不会闪。反之先创建灯光、相机 后 import glb Mesh ,那么glb就会很闪烁。

经过与大佬们沟通发现,是因为每次创建Camera时,相机会自动计算maxZ/minZ,所以会避免闪烁。但如何先加载glb,再创建mesh时,就会闪烁,因为Camera的计算精度不对。

解决方法:

  • 每次加载glb后,都自动计算scene.activeCamera 的maxZ/minZ 。具体参考最下面的Babylonjs源代码;
  • 手动设置,针对大场景minZ建议在5~30之间,maxZ >10万。

二、测试记录

var delayCreateScene = function () {
    
    var scene = new BABYLON.Scene(engine);  
  scene.createDefaultCameraOrLight(true, true, true);
        scene.createDefaultEnvironment();

        scene.activeCamera.alpha = 1.610775814317132;
        scene.activeCamera.beta = 1.52000034522929;
        scene.activeCamera.radius = 10;
        scene.activeCamera.wheelPrecision = 30;

    BABYLON.SceneLoader.ImportMesh("", "https://www.baidu.cn/iios-minio/attachment/64/libraries/iios-insight/%E4%B8%89%E7%BB%B4%E6%A8%A1%E5%9E%8B/", "0826133216.glb", scene, function (meshes) {          
        scene.activeCamera.maxZ=100000;
        scene.activeCamera.minZ=100;
        console.log("minZ:", scene.activeCamera.minZ, "maxZ:", scene.activeCamera.maxZ);

    });

    return scene;
};

三、babylonjs自动计算maxz逻辑

源代码位置:

packages\dev\core\src\scene.ts 5766行

packages\dev\core\src\Helpers\sceneHelpers.ts 124行

    /**
     * 获取场景中所有网格的世界范围(包围盒的最小点和最大点)
     *
     * @param filterPredicate 可选的过滤函数 —— 用来决定哪些网格会被计算在世界范围中
     * @returns {{ min: Vector3; max: Vector3 }} 返回计算得到的最小点和最大点(世界坐标系下)
     */
    public getWorldExtends(filterPredicate?: (mesh: AbstractMesh) => boolean): { min: Vector3; max: Vector3 } {
        // 初始化最小值向量,先设为无穷大(保证任何实际值都比它小)
        const min = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);

        // 初始化最大值向量,先设为负无穷大(保证任何实际值都比它大)
        const max = new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);

        // 如果没有传入过滤函数,就默认全部网格都参与计算
        filterPredicate = filterPredicate || (() => true);

        // 获取通过过滤条件的网格
        const meshes = this.meshes.filter(filterPredicate);

        // 遍历每个网格
        for (const mesh of meshes) {
            // 计算网格的世界矩阵,保证后续包围盒是最新的
            mesh.computeWorldMatrix(true);

            // 如果网格没有子网格,或者是无限远(不会计算包围盒),则跳过
            if (!mesh.subMeshes || mesh.subMeshes.length === 0 || mesh.infiniteDistance) {
                continue;
            }

            // 获取网格的包围信息(含包围盒和包围球)
            const boundingInfo = mesh.getBoundingInfo();

            // 获取包围盒的世界最小点和最大点
            const minBox = boundingInfo.boundingBox.minimumWorld;
            const maxBox = boundingInfo.boundingBox.maximumWorld;

            // 更新全局的最小点和最大点(检查并扩展)
            Vector3.CheckExtends(minBox, min, max);
            Vector3.CheckExtends(maxBox, min, max);
        }

        // 返回计算好的整体世界范围
        return {
            min: min,
            max: max,
        };
    }


Scene.prototype.createDefaultCamera = function (
    createArcRotateCamera = false,   // 是否创建 ArcRotateCamera(默认是 FreeCamera)
    replace = false,                 // 是否替换已有相机
    attachCameraControls = false     // 是否自动绑定相机控制(鼠标/触摸控制)
): void {
    // 如果 replace = true,则先销毁现有的活动相机
    if (replace) {
        if (this.activeCamera) {
            this.activeCamera.dispose();  // 释放现有相机资源
            this.activeCamera = null;     // 清空引用
        }
    }

    // 如果当前没有激活相机,则创建一个默认相机
    if (!this.activeCamera) {
        // 获取场景中可见并启用的网格的整体包围盒(最小点和最大点)
        const worldExtends = this.getWorldExtends((mesh) => mesh.isVisible && mesh.isEnabled());

        // 计算整个场景的范围大小(max - min 向量)
        const worldSize = worldExtends.max.subtract(worldExtends.min);

        // 计算场景的中心点(min + size的一半)
        const worldCenter = worldExtends.min.add(worldSize.scale(0.5));

        let camera: TargetCamera;
        // 计算默认相机的半径(取整个场景对角线长度的 1.5 倍)
        let radius = worldSize.length() * 1.5;

        // 如果场景为空(没有网格),则 radius = 1,并设置世界中心为 (0,0,0)
        if (!isFinite(radius)) {
            radius = 1;
            worldCenter.copyFromFloats(0, 0, 0);
        }

        // 判断相机类型:ArcRotateCamera 或 FreeCamera
        if (createArcRotateCamera) {
            // 创建弧形旋转相机(可围绕目标旋转)
            const arcRotateCamera = new ArcRotateCamera(
                "default camera",
                -(Math.PI / 2),       // 初始水平角度
                Math.PI / 2,          // 初始垂直角度
                radius,               // 相机到目标的距离
                worldCenter,          // 相机的观察目标(场景中心)
                this                  // 当前场景
            );
            // 设置相机的最小缩放限制(避免无限靠近)
            arcRotateCamera.lowerRadiusLimit = radius * 0.01;
            // 设置鼠标滚轮缩放灵敏度(场景越大,滚轮越敏感)
            arcRotateCamera.wheelPrecision = 100 / radius;
            camera = arcRotateCamera;
        } else {
            // 创建自由相机(可以在空间中自由移动)
            const freeCamera = new FreeCamera(
                "default camera",
                new Vector3(worldCenter.x, worldCenter.y, -radius), // 初始位置:Z 轴负方向,远离中心
                this
            );
            // 让相机朝向场景中心
            freeCamera.setTarget(worldCenter);
            camera = freeCamera;
        }

        // 设置相机的近裁剪面和远裁剪面
        camera.minZ = radius * 0.01;   // 最近可见距离
        camera.maxZ = radius * 1000;   // 最远可见距离

        // 设置相机移动速度(与场景大小成比例)
        camera.speed = radius * 0.2;

        // 将创建的相机设置为场景的活动相机
        this.activeCamera = camera;

        // 如果需要,自动绑定相机控制(支持鼠标/键盘/触摸操作)
        if (attachCameraControls) {
            camera.attachControl();
        }
    }
};

标签: 暂无
最后更新:2025-09-02

代号山岳

知之为知之 不知为不知

点赞
< 上一篇

COPYRIGHT © 2099 登峰造极境. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

蜀ICP备14031139号-5

川公网安备51012202000587号