当前位置: 首页 > article >正文

three.js+WebGL踩坑经验合集(5.1):THREE.Line2又一坑:镜像后不见了

什么情况,怎么讲的还是线?就没别的了?

不得不说,笔者自己都有点审美疲劳了。写到第5篇,有4篇都在讲线。

然而线的坑确实比较多,也比较容易暴露引擎的问题。就像第一篇,实际上换成mesh也一样可以出问题,只不过通常情况下mesh的包围球不大,并且修改geometry的频率也不高,问题才不容易被发现。

但本篇的问题就真的只出现在THREE.Line2上,THREE.Mesh乃至THREE.Line都没有任何异常。

下面我们就做个测试用例,建个容器,里面Mesh,Line(细线)和Line2(粗线)各放一个。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>three_line2Flip</title>
  <style>
    body {
      margin: 0;
      overflow: hidden;
    }
  </style>
  <script src="three/build/three.js"></script>
  <script src="three/examples/js/controls/OrbitControls.js"></script>
  <script src="three/examples/js/lines/LineSegmentsGeometry.js"></script>
  <script src="three/examples/js/lines/LineGeometry.js"></script>
  <script src="three/examples/js/lines/LineMaterial.js"></script>
  <script src="three/examples/js/lines/LineSegments2.js"></script>
  <script src="three/examples/js/lines/Line2.js"></script>
  <script src="three/examples/js/libs/dat.gui.min.js"></script>
</head>

<body>
  <script>
    var scene = new THREE.Scene();

    var container = new THREE.Object3D();
    scene.add(container);

    var meshGeometry = new THREE.BoxGeometry(50, 50, 50);
    var meshMaterial = new THREE.MeshLambertMaterial({color: 0xFF6600});
    var mesh = new THREE.Mesh(meshGeometry, meshMaterial);   
    mesh.position.set(5, 5, 5); 
    mesh.rotation.set(0, Math.PI * 0.25, 0);
    container.add(mesh);

    var lineGeometry = new THREE.BufferGeometry();
    lineGeometry.setFromPoints([new THREE.Vector3(-50, 0, 0), new THREE.Vector3(50, 0, 0)]);
    var lineMaterial = new THREE.LineBasicMaterial({color: 0xFF6600});
    var line = new THREE.Line(lineGeometry, lineMaterial);
    line.position.set(-50, 10, 0);
    line.rotation.set(0, 0, -Math.PI / 4);
    container.add(line);

    var line2Geometry = new THREE.LineGeometry();
    line2Geometry.setPositions([-50, 0, 0, 50, 0, 0]);
    var line2Material = new THREE.LineMaterial({color: 0xFF6600, resolution: new THREE.Vector2(window.innerWidth, window.innerHeight), linewidth: 5});
    var line2 = new THREE.Line2(line2Geometry, line2Material);
    line2.position.set(60, -15, 0);
    line2.rotation.set(0, 0, Math.PI / 3);
    container.add(line2);

    var light = new THREE.DirectionalLight({color: 0xFFFFFF, intensity: 0.5});
    light.position.set(-500, 500, 500);
    scene.add(light);

    var ambLight = new THREE.AmbientLight({color: 0xFFFFFF, intensity: 0.3});
    scene.add(ambLight);
    
    var width = window.innerWidth; 
    var height = window.innerHeight; 
    var camera = new THREE.PerspectiveCamera(60, width / height, 1, 20000);
    camera.position.set(0, 0, 150);  

    var renderer = new THREE.WebGLRenderer();
    renderer.setSize(width, height);
    renderer.setClearColor(0x000000, 1); 
    document.body.appendChild(renderer.domElement); 

    var flipX = false;
    var flipY = false;

    var gui = new dat.GUI();
    folderCamera = gui.addFolder("镜像"),
    propsContainer = {
      get '水平镜像'() {
        return flipX;
      },
      set '水平镜像'( v ) {
        flipX = v;
        container.scale.x = v ? -1 : 1;
      },
      get '垂直镜像'() {
        return flipY;
      },
      set '垂直镜像'( v ) {
        flipY = v;
        container.scale.y = v ? -1 : 1;
      },
    };

    gui.width = 150;
    folderCamera.add( propsContainer, '水平镜像');    
    folderCamera.add( propsContainer, '垂直镜像');  
    folderCamera.open();
    
    function render() {
      renderer.render(scene, camera);
      requestAnimationFrame(render);
    }
    
    render();
    
    var controls = new THREE.OrbitControls(camera,renderer.domElement);
    controls.addEventListener('change', render);
    

  </script>
</body>
</html>

运行代码,在不点击UI面板上镜像操作的情况下,相机不管怎么动,这3个物体都能正常显示

但若随便勾选一个方向的镜像,粗线(Line2)就消失了。然后如果两个方向同时镜像,那粗线又可以显示回来,呈现出负负得正的效果。

这个问题是笔者一个同事碰到的,他觉得很神奇,为什么同样是线,就只有Line2会没掉,Line就安然无恙。当时笔者还没研读过引擎的这一部分,然后凭着自己半吊子的经验给对方一本正经地解释道:“因为Line是用原生的画线api,没有点绕序(顺逆时针)的问题,而Line2是用两个三角面模拟的,镜像一个方向,绕序变更,正反面颠倒,所以就看不到了。”

笔者还拿着笔在纸上给同事把Line2的原理图给画了出来(这也是前面的博文提到的)。

然而还没等到笔者画完,这位同事就很“机智”地给出了“解决方案”:既然是正面变成了背面,那我直接给线开双面是不是就解决了?

line2Material.side = THREE.DoubleSide;

就这么一行代码,完美了。

虽然这位小伙伴在提交代码之前因为双面的性能损耗犹豫了一下,但苦于没有更好的解决方案,就只能先这样凑合着了。

如果仅考虑本用例,那笔者的这位同事其实是有优化方案的,就是写一个方法判断负缩放的总次数。

function updateFace(){
    line2Material.side = container.scale.x * container.scale.y * container.scale.z > 0 ? THREE.FrontSide : THREE.BackSide;
}

然后在勾选镜像的地方调用一下,也能得到正确的结果。

但此法局限性太大,因为在我们的项目里,Line2自身也可能存在负缩放,这时候,Line2将始终不可见。

如果有更多层的容器嵌套,那么每一层容器的负缩放都要考虑进去,不但读取起来麻烦,而且最重要的是,我们不知道什么时候Line2触发了缩放正负性的变更,什么时候需要调用updateFace。

若先不考虑时机问题,那有个很蹩脚的方法是,把line2的scale也乘进去:

function updateFace(){
    line2Material.side = container.scale.x * container.scale.y * container.scale.z * line2.scale.x * line2.scale.y * line2.scale.z > 0 ? THREE.FrontSide : THREE.BackSide;
}

事已至此,我那位同事还是老老实实地开双面绕过去了先。

但既然Line和Mesh都没有问题,那就说明引擎是有做处理的,最起码Mesh跟Line2是同一类型的物体,镜像时正反面也一样会翻转。

所以这里我们有几个问题:

1 为什么Mesh镜像后没问题(Line走原生画线api,不用再讨论)

2 有没有一个办法,获取到一个Line2对象的全局缩放(scaleWorld)

3 Line2对象被多层容器嵌套时,如何收到缩放更新的通知

这篇好像写长了,分到下一篇去,本篇暂不总结,大家也先喝杯茶休息一下,我们稍后见!


http://www.kler.cn/a/521505.html

相关文章:

  • 利用飞书机器人进行 - ArXiv自动化检索推荐
  • Pandas进行MongoDB数据库CRUD
  • 智慧园区管理平台实现智能整合提升企业运营模式与管理效率
  • [250125] DeepSeek 发布开源大模型 R1,性能比肩 OpenAI o1 | 希捷推出高达 36TB 的硬盘
  • [权限提升] 常见提权的环境介绍
  • 推动知识共享的在线知识库实施与优化指南
  • jEasyUI 创建复杂布局
  • 【知识科普】HTTP相关内容说明
  • JavaWeb 学习笔记 XML 和 Json 篇 | 020
  • 单片机基础模块学习——PCF8591芯片
  • Mac m1,m2,m3芯片使用nvm安装node14报错
  • Excel制作合同到期自动提醒!
  • ESP32服务器和PC客户端的Wi-Fi通信
  • 海浪波高预测(背景调研)
  • Linux——rzsz工具
  • 反向代理模块。。
  • Unity——从共享文件夹拉取资源到本地
  • 1_相向双指针_leetcode_15_2
  • inception_v3
  • 61.异步编程1 C#例子 WPF例子
  • 架构基础常识
  • Go 程序开发的注意事项
  • 安装 Prometheus、Grafana 和 Alertmanager
  • c++的容器和适配器究竟有什么差别?
  • 统计安卓手机一段时间内进程中每个线程的平均CPU使用率
  • mysql 学习6 DQL语句,对数据库中的表进行 查询 操作