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

three.js+WebGL踩坑经验合集(9.2):polygonOffsetFactor工作原理大揭秘

本篇延续上篇内容:

three.js+WebGL踩坑经验合集(9.1):polygonOffsetUnits工作原理大揭秘-CSDN博客

跟polygonOffsetUnits相比,polygonOffsetFactor的系数m要复杂得多,因为它跟平面的视角相关,而不像r那样,在一个固定的设备环境下是一个常量。

说起视角,笔者第一反应是法线跟屏幕所在平面的夹角,然后笔者就试着拿一个只有一个坐标轴旋转的平面来进行测试,但很不幸,锁定了很多条件,都得不到一个期望的“线性”结果。动一下滚轮,调整下camera的position,前面试出来的系数值就套不上去。

为此笔者还换了好多方案,比如用射线检测向量,透视扭曲,平面法线,再到最后改用正交相机,都以失败告终,导致笔者一度陷入绝望状态。不过正交相机下条件比较简单,笔者还是发现了个规律:深度偏移量跟camera的zoom成正比。

zoom也会缩放深度缓冲,这样子想,我们拿project的值来算视角是不是就可以的呢?有了这一灵感之后,笔者又屁颠屁颠地跑去看写得那篇不错的文章:

深度冲突--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

这里写的是最大深度斜率,这么想,我们拿平面跟屏幕的夹角似乎就可以了,并且基于project(camera)的结果。因为project出来的z就是深度。

这里提一个高中立体几何的概念:二面角。笔者直接拿网上找来的一张图贴到这里。

其中θ值代表α和β两平面的夹角(平面角),AP和AB均垂直于交线l。当然了,实际计算中,拿两平面的法线求点乘即可。

笔者用一个更简单的案例(跟上篇差不多)来试验,求深度斜率会更方便。

<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>three_polygonOffset</title><style>body {margin: 0;overflow: hidden;}</style><script src="three.js-master/build/three.js"></script><script src="three.js-master/examples/js/libs/dat.gui.min.js"></script>
</head><body><script>var scene = new THREE.Scene();var geometry = new THREE.PlaneGeometry(100, 100);var loader = new THREE.TextureLoader();var map = loader.load("three.js-master/examples/textures/UV_Grid_Sm.jpg")var srcColor = 0xFFFFFF;var material = new THREE.MeshBasicMaterial({ color: srcColor, map: map});var mesh = new THREE.Mesh(geometry, material);material.polygonOffset = true;material.polygonOffsetFactor = 0;material.polygonOffsetUnits = 0;mesh.rotation.y = Math.PI / 6;scene.add(mesh);var geometry2 = new THREE.PlaneGeometry(100, 100);var srcColor2 = 0xEEDDCC;var material2 = new THREE.MeshBasicMaterial({ color: srcColor2, map: null });var mesh2 = new THREE.Mesh(geometry2, material2);mesh2.rotation.y = Math.PI * 0;scene.add(mesh2);var width = window.innerWidth;var height = window.innerHeight;var scale = 4;// var camera = new THREE.OrthographicCamera(-width * 0.5 / scale, width * 0.5 / scale, height * 0.5 / scale, -height * 0.5 / scale, 0.1, 20000);var camera = new THREE.PerspectiveCamera(90, width / height, 0.1, 20000);camera.position.set(0, 0, 100);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', -500, 500);folderPolygonOffset.add(propsPolygonOffset, 'polygonOffsetUnits', -20000, 20000);folderPolygonOffset.open();function render() {renderer.render(scene, camera);requestAnimationFrame(render);}render();</script>
</body></html>

本案例跟上篇差不多,不同的是设置polygonOffset的平面改成带旋转角度的那个,并且把水平面的网格去掉,观察起来要清晰一些。运行效果如下图所示。

在平面绕y轴旋转了30度的情况下,polygonOffsetFactor调整为50,可以让两平面的交线对齐到网格线的第4条。

改到106则对齐到第3条网格线。

在研究polygonOffsetUnits的系数r时,我们已经验证到了偏移量的线性效果,所以本篇不会在固定的一个视角下做一张表来探讨polygonOffsetUnits的线性关系,毕竟本篇的重点是视角变量。

跟上篇类似,我们把第4条网格线的中点和第5条网格线的中点的project(camera)结果输出到控制台。

这样,我们就可以得出polygonOffsetFactor等于50时深度的偏移值为

0.9980099900499501-0.9979047263657369=0.00010526368421315269

也就是说,每一点factor为深度值带来的偏移量为

0.00010526368421315269/50=0.0000021052736842630535

然后重点来了,最大深度斜率的计算。由于我们要用project的结果来算,所以直接拿法线算不准确,因此还是用中学里的方法,找垂直于交线的直线进行求解。

对于本案例来说,我们直接拿上图中两个点的连线跟屏幕的夹角即可,因为那两个点的连线垂直于交线。深度斜率k具体的计算公式为

vec = (p2-p1)

k=vec.z/sqrt(vec.x^2+vec.y^2)

我们用到的点,y恒等于0,所以式子可以直接简化为

k=vec.z/vec.x。

好了,上控制台代码

也就是说,y旋转30度的平面在当前相机下的深度斜率k为。

现在我们理一下测试出来的结果。

一点factor的偏移量result=0.0000021052736842630535

深度斜率k=0.0014459929855301934

按照笔者引用的博文,m是根据k算出来的,我们姑且认为m跟k是一个线性关系,也就是m=c*k,c为一个我们待求的常量。

于是有result=m=c*k,c=result/k=0.0000021052736842630535/0.0014459929855301934=0.0014559363048992425

这是30度下的结果,我们把角度改成45度看看,可以改代码刷新,也可以控制台直接改

mesh.rotation.y = Math.PI / 4;

45度下,polygonOffsetFactor等于42可以让交线对齐到第4条网格线

z偏移量为0.00015218302849029364,按前面的做法,result(单个factor带来的偏移值)=0.00015218302849029364/42=0.0000036234054402450866

然后深度斜率这样算:

于是有k=0.002504533318326064

c=result/k=0.0000036234054402450866/0.002504533318326064=0.0014467387651551926

不错,算出来的值跟30度时的相当接近。

虽然文字量不大(代码不算),但是篇幅也长了,所以60度的不再给出测试过程,直接上结果:

c=0.0014571165930502342

嗯,跟30度和45度也相等接近,但是这个数不像r那种2^-23那么的有计算机或者几何特征,所以笔者心里不踏实,就试着把camera.position.z设置为200再测一波。

比如这时候,60度得到的结果是0.0014391370784359598,也还接近,误差在可接受的范围内。

笔者怀疑自己的算深度斜率的方法不准确,然后在尝试着换不同的线来算夹角,比如试图接近千分之根号2,系数就显得不那么“魔术”。但不管怎么取,算出来的结果都在0.00144附近。

然后笔者还是不纠结了,就姑且拿这个魔术数作为结论,如果后面笔者发现这个魔术数的计算机或者几何特征,抑或找到了更准确的算法,笔者会第一时间通知大家。

本文算了一波,就得到一个魔术数0.00144,也不知道该如何小结了,那就把公式也汇总一下吧。

1 跟polygonOffsetFactor相关的系数m跟平面在当前相机下的最大深度斜率k有关,其值约等于0.00144*k

2 最大深度斜率k的值等于平面在相机下的旋转值跟屏幕所在平面的夹角的正切值,具体的计算方法是,找到跟平面交线垂直的直线,在其上取两点,分别算出project(camera)后,相减得到向量vec,用公式k=vec.z/sqrt(vec.x^2+vec.y^2)把k值算出来

3 跟屏幕绝对平行的平面,z值处处相等,所以vec.z恒等于0,k也就等于0,所以m=0.00144*k也等于0

4 结合上文,我们可以得到polygonOffset最终的计算公式,以24位深度缓冲为例,深度偏移量的最终值等于

\frac{0.00144vec.x}{\sqrt{vec.x^{2}+vec.y^{2}}}polygonOffsetFactor+2^{-23}polygonOffsetUnits

其中vec是平面跟屏幕交线垂直的一根直线的向量。

有了这样的量化公式后,我们调整polygonOffset两个参数时,就有了很好的理论指导,不用再老是盲猜了。

理论有点抽象,笔者在考虑要不要再来一篇博文,通过例子说明该如何根据这个公式计算业务场景中的入参数值。如果再来,就再发一篇9.3,否则就开启新的主题,进入10或者10.1。

看这么多,想必大家多少有点晕了,而笔者也是时候歇一下,嗯大家先好好消化消化,辛苦大家了。

http://www.dtcms.com/a/352669.html

相关文章:

  • Langchian-chatchat私有化部署和踩坑问题以及解决方案[v0.3.1]
  • More Effective C++ 条款10:在构造函数中防止资源泄漏
  • 二维费用背包 分组背包
  • 小范围疫情防控元胞自动机模拟matlab
  • 深入剖析容器文件系统:原理、实现与资源占用分析
  • 游戏空间划分技术
  • 家庭财务规划与投资系统的设计与实现(代码+数据库+LW)
  • 声网RTC稳定连麦、超分清晰,出海直播技术不再难选
  • AT_abc403_f [ABC403F] Shortest One Formula
  • 【44页PPT】极简架构MES系统解决方案介绍(附下载方式)
  • 【Python】雷达簇类ply点云仿真生成,以及聚类算法的簇类目标检测
  • flutter专栏--dart基础知识
  • WebGIS开发智慧校园(6)JavaScript
  • 破解VMware迁移难题的技术
  • SSH密钥登录全流程详解
  • LeetCode-221. 最大正方形
  • 多模块 Starter 最佳实践(强烈推荐!!!)
  • Quarkus OIDC 安全链路时序图
  • git换行行为差异简述;.editorconfig换行行为简述
  • 打工人日报#20250826
  • 【PS实战】制作hello标志设计:从选区到色彩填充的流程(大学作业)
  • springboot启动的时候,只打印logo,不打印其他的任何日志的原因
  • 【ElasticSearch】数据同步
  • 人形机器人的“奥运会“:宇树科技领跑,动捕技术成训练关键
  • git submodule的基本使用
  • 数据与端点安全 (Protect data and apps)
  • 利用 Python 爬虫按关键字搜索 1688 商品详情 API 返回值说明(代码示例)实战指南
  • 从零开始配置前端环境及必要软件安装
  • 技术总结:AArch64架构下Jenkins Agent(RPM容器编译节点)掉线问题分析与排查
  • 基于用户行为分析的精确营销系统