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

three.js+WebGL踩坑经验合集(8.1):用于解决z-fighting叠面问题的polygonOffset远没我们想象中那么简单

初八开工后,笔者又停了下来,今天总算又抽出来了一丢丢的时间继续。今天打算给大家聊聊困扰很多3D开发者的z-fighting叠面闪烁问题。

该问题从严格意义上说,是属于业务问题,因为现实中是不会有完全重叠的两个平面物体存在(总得有厚度差吧,哪怕很小)。所以笔者的想法跟很多博主一样,应该在业务上尽可能避免出现叠面场景的出现,不然问题永远解决不完。

笔者先来跟大家探讨前端开发用得特别频繁,网上文章也最多见的polygonOffset方法。不得不说,这个方法能快速解决大家的燃眉之急。但如果你的项目是规模较大的,需要长时间维护的那种类型,就会发现,这样的方法适应的场景很有限,并且用多了之后多个polygonOffset之间会相互打架。

废话少说,下面上demo。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>three_cameraNear</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/libs/dat.gui.min.js"></script>
</head>

<body>
  <script>
    var scene = new THREE.Scene();
    
    var geometry = new THREE.PlaneGeometry(100, 100);

    var srcColor = 0xFF6600;
    var material = new THREE.MeshBasicMaterial({color: srcColor});
    var mesh = new THREE.Mesh(geometry, material);
	  material.polygonOffset = true;
	  material.polygonOffsetFactor = 0;
	  material.polygonOffsetUnits = 0;
    scene.add(mesh);
	
	  var geometry2 = new THREE.PlaneGeometry(50, 50);

    var srcColor2 = 0xFFFFFF;
    var material2 = new THREE.MeshBasicMaterial({color: srcColor2});
    var mesh2 = new THREE.Mesh(geometry2, material2);
	
    scene.add(mesh2);
    
    var width = window.innerWidth; 
    var height = window.innerHeight; 
    var camera = new THREE.PerspectiveCamera(60, width / height, 0.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 gui = new dat.GUI(),
    folderCamera = gui.addFolder("相机"),
    propsCamera = {
      get '裁剪'() {
        return camera.near;
      },
      set '裁剪'( v ) {
        camera.near = v;
        camera.updateProjectionMatrix();
      },
    };	

    folderCamera.add( propsCamera, '裁剪', 0.01, 0.1 );    
    folderCamera.open();
	
    var folderPolygonOffset = gui.addFolder("polygonOffset");
	  var propsPolygonOffset = {
      get 'polygonOffsetFactor'() {
        return material.polygonOffsetFactor;
      },
      set 'polygonOffsetFactor'( v ) {
        material.polygonOffsetFactor = v;
      },
      get 'polygonOffsetUnits'() {
        return material.polygonOffsetUnits;
      },
      set 'polygonOffsetUnits'( v ) {
        material.polygonOffsetUnits = v;
      },
    }
    folderPolygonOffset.add(propsPolygonOffset, 'polygonOffsetFactor', 0, 5);
    folderPolygonOffset.add(propsPolygonOffset, 'polygonOffsetUnits', 0, 5);
    folderPolygonOffset.open();
	
    function render() {
      renderer.render(scene, camera);
      requestAnimationFrame(render);
    }
    
    render();
    
    var controls = new THREE.OrbitControls(camera,renderer.domElement);
    controls.addEventListener('change', render);

    var raycaster = new THREE.Raycaster(); 

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

这段代码创建了两个大小不一样,但是位置上是重叠的两个平面,运行起来的效果如下:

可以看到,在旋转相机的时候,两个面的深度会不停交替,来回闪烁,这就是经典的z-fighting叠面问题。

然后网上给的解决方案是给其中一个面设置上polygonOffset,并且那个被抄到烂大家的文章,给polygonOffset对应的两个参数:polygonOffsetFactor和polygonOffsetUnits都设置为1,那这里我们也这样试试。

调整到1之后,叠面基本消失,场景拉远之后,我们发现还是有些角度产生了叠面。然后我们再把两个参数都调整到2,发现这下好了很多。

然而事实并没有我们想象中那么简单,注意到笔者还加了个裁剪的滑动条。

我们项目最初用的camera.near值是0.001,而不是我们现在给的0.1。我们很意外地发现,这个值对叠面的效果竟然也有影响,并且还不小。

这样下来,影响叠面的因素又多了一个。所以在笔者同事遇到这些问题时,他们都会尽可能地把polygonOffset的两个参数往大里调,从而规避这个问题。

但很不幸,我们项目玩得很花,好些功能会把三四个面叠在一起,这时候,不管参数值调大调小,我们都很容易顾此失彼,无法正确处理多个面之间的冲突。

笔者有段时间费了很大力气去查找跟polygonOffset相关的资料,但天下文章一大抄,找回来的都是webgl官方提供的那条公式m*polygonOffsetFactor+r*polygonOffsetUnits,并且两个不可控的变量m和r也描述得不清楚。

讲得相对深入一点的,是这篇:

深度冲突--threejs(webgl)_polygonoffsetfactor-CSDN博客

以下是公式中几个变量的讲解。

m: 表示最大深度斜率(Maximum Depth Slope)的值,是一个根据当前渲染的多边形相对于视线的角度自动计算出来的值。它基本上表示该多边形表面有多倾斜;如果表面与视线平行,则m值小,如果表面近乎垂直于视线,则m值大。

factor: 这是一个你可以控制的变量,对应于Three.js中的material.polygonOffsetFactor。这个数值会乘以上述的m值,也就是说,它表示深度偏移量将随着多边形的视觉倾斜程度而增加。

r: 这是指解析度,表示深度缓冲区每个单位的更改所能表示的最小深度差异。不同的系统和深度缓冲区的配置可能具有不同的解析度。

units: 同样是一个你可以控制的数值,对应于Three.js中的material.polygonOffsetUnits。这个数值会乘以r,提供了一个恒定的深度偏移,这个偏移与多边形的倾斜无关。
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/weixin_45705239/article/details/138075785

不得不说,这里的说明让笔者对polygonOffset的理解深刻了一些。尽管在找到这篇文章之前,笔者就已经知道camera的near也是一个因素,但所幸的是,笔者完全可以通过先固定camera.near来以上文为方向单独研究这两个变量了。

下一篇,笔者会跟大家一起探讨这个坑爹玩意儿的工作原理,敬请期待!


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

相关文章:

  • Java并发编程——线程创建的四种方式
  • 数据仓库与数据湖的协同工作:智慧数据管理的双引擎
  • 【Linux系统编程】进程概念
  • Redis 主从复制的核心原理
  • pnpm和npm安装TailwindCss
  • 【人工智能】深度学习中的梯度检查:原理详解与Python实现
  • Leetcode Hot100 第30题 416.分割等和子集
  • InnoDB如何解决幻读?深入解析MySQL的并发控制机制
  • dify新版,chatflow对deepseek的适配情况
  • 72.git指南(简单)
  • HTTP
  • cmake Qt Mingw windows构建
  • 物联网 网络安全 概述
  • 杜绝遛狗不牵绳,AI技术助力智慧城市宠物管理
  • 介绍两本学习智谱大模型的入门图书
  • 大数据实训室解决方案(2025年最新版)
  • 小米14 机型工程固件预览 刷写以及更改参数步骤 nv.img的写入
  • 【Bluedroid】 BLE连接源码分析(一)
  • LeetCode每日精进:203.移除链表元素
  • 开发中需要使用到volatile的情况