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.pswp.cn/diannao/96891.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/96891.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++高级特性与设计模式答案

目录 C++高级特性与设计模式:从资源管理到架构设计 一、C++高级特性:超越基础语法的利器 1. 什么是RAII(资源获取即初始化)?它有什么作用? 实现原理 核心作用 2. 什么是Pimpl惯用法?它有什么优势? 实现方式 核心优势 3. 什么是CRTP(奇异递归模板模式)?它的应用场景是…

论文阅读:arxiv 2025 Can You Trick the Grader? Adversarial Persuasion of LLM Judges

总目录 大模型安全相关研究&#xff1a;https://blog.csdn.net/WhiffeYF/article/details/142132328 Can You Trick the Grader? Adversarial Persuasion of LLM Judges https://arxiv.org/pdf/2508.07805 https://www.doubao.com/chat/17534937260220418 文章目录论文翻译…

6pen Art

本文转载自&#xff1a;6pen Art - Hello123工具导航 ** 一、&#x1f3a8; 6pen 是什么&#xff1f; 6pen 是一款由国内团队开发的 AI 绘画工具&#xff0c;让你只需用文字描述想法&#xff0c;就能瞬间生成惊艳的视觉画作。不管是写实风景还是抽象概念&#xff0c;它都能理…

Let‘s Encrypt证书在 Android5.x 的设备上报错

报错信息&#xff1a; com.android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.at com.android.volley.toolbox.NetworkUtility.shouldRetryException(N…

C语言数组名与sizeof的深层关联

要理解 “数组名本质代表整个数组的类型和内存块” 与 “sizeof(arr) 输出总字节数” 的关联&#xff0c;核心是抓住 sizeof 运算符的设计逻辑 和 数组类型的本质属性—— 这两者是直接挂钩的&#xff0c;我们一步步拆解&#xff1a;第一步&#xff1a;先明确 sizeof 的核心作用…

最近对javashop做了压力测试:百万级并发下完全不是问题

最近对 javashop 做了压力测试&#xff1a;百万级并发下完全不是问题 在电商行业竞争白热化的今天&#xff0c;系统性能直接决定了用户体验和企业商业成功。本文基于《Javashop 压测报告》&#xff0c;从技术架构、核心指标、业务价值三大维度深度解析其性能优势&#xff0c;并…

Java大厂面试实战:从Spring Boot到微服务架构的全链路技术解析

Java大厂面试实战&#xff1a;从Spring Boot到微服务架构的全链路技术解析 面试场景&#xff1a;某互联网大厂Java后端开发岗 面试官&#xff08;严肃&#xff09;&#xff1a;谢飞机&#xff0c;我们今天来聊点硬核的。先说说你对Java生态的理解。 谢飞机&#xff08;挠头&…

在分布式环境下正确使用MyBatis二级缓存

在分布式环境下使用 MyBatis 二级缓存&#xff0c;核心挑战是解决多节点缓存一致性问题。单机环境中&#xff0c;二级缓存是内存级别的本地缓存&#xff0c;而分布式环境下多节点独立部署&#xff0c;本地缓存无法跨节点共享&#xff0c;易导致 “缓存孤岛” 和数据不一致。本文…

血缘元数据采集开放标准:OpenLineage Integrations Apache Spark Quickstart with Jupyter

OpenLineage 是一个用于元数据和血缘采集的开放标准&#xff0c;专为在作业运行时动态采集数据而设计。它通过统一的命名策略定义了由作业&#xff08;Job&#xff09;、运行实例&#xff08;Run&#xff09;和数据集&#xff08;Dataset&#xff09; 组成的通用模型&#xff0…

人工智能之数学基础:离散随机变量和连续随机变量

本文重点 随机变量是概率论与统计学中的核心概念,用于将随机现象的抽象结果转化为可量化的数值。根据取值特性的不同,随机变量可分为离散型和连续型两大类。 在前面的课程中我们学习了随机变量,随机变量可以理解为一个函数,通过这个函数我们就可以将随机试验中的结果数值…

SQL语句(查询)

单表查询 常量查询 让我们来看一个具体的 SQL 代码和结果示例&#xff0c;假设有一张名为 orders 的数据表&#xff0c;它存储了订单信息&#xff0c;包括订单编号&#xff08;order_id&#xff09;、商品单价&#xff08;unit_price&#xff09;、购买数量&#xff08;quantit…

Java 大视界 -- Java 大数据机器学习模型在金融市场波动预测与资产配置动态调整中的应用

Java 大视界 -- Java 大数据机器学习模型在金融市场波动预测与资产配置动态调整中的应用引言&#xff1a;正文&#xff1a;一、Java 构建的金融数据处理架构1.1 多源数据实时融合与清洗1.2 跨市场数据关联&#xff08;风险传导分析&#xff09;二、Java 驱动的市场波动预测模型…

基于muduo库的图床云共享存储项目(一)

基于muduo库的图床云共享存储项目&#xff08;一&#xff09;项目简介整体架构项目依赖基础组件muduo库Channel类Poller / EpollPoller 类EventLoopAcceptor类FastDfsJSON的使用项目简介 当前所实现的项目是一个基于muduo库的图床云共享存储项目&#xff0c;他的主要的功能就是…

数字化转型三阶段:从信息化、数字化到数智化的战略进化

企业的数字化转型包括信息化、数字化、数智化三个阶段&#xff0c;并非一个阶段结束才能进入到下一个阶段。01信息化→业务数据化信息化是将企业在生产经营过程中产生的业务信息进行记录、储存和管理&#xff0c;通过电子终端呈现&#xff0c;便于信息的传播与沟通。信息化是对…

SpringBoot如何获取系统Controller名称和方法名称

这种代码里面的Controller和里面的方法怎么获取代码&#xff1a;/*** 获取所有Controller名称*/ApiDescription("获取所有Controller名称")PostMapping("/getControllerNames")public Result getControllerNames() {return dataDesensitizationRulesServic…

(二十二)深入了解AVFoundation-编辑:视频变速功能-实战在Demo中实现视频变速

一. 引言视频变速&#xff08;Speed Ramp&#xff09;是视频编辑中最常见的特效之一&#xff1a;慢动作&#xff08;Slow Motion&#xff09;&#xff1a;强调细节&#xff0c;让观众捕捉到肉眼难以察觉的瞬间&#xff1b;快动作&#xff08;Fast Motion&#xff09;&#xff1…

MCP零基础学习(7)|实战指南:构建论文分析智能体

在之前的教程中&#xff0c;我们已经介绍了 MCP&#xff08;Model Context Protocol&#xff09;的基本概念及其核心组件。在本篇教程中&#xff0c;我们将通过一个实际案例&#xff0c;演示如何运用 MCP 构建一个能够分析学术论文的智能体。这个智能体将具备读取 PDF 文件、提…

Unity URP半透明物体自身交叠解决方案

前言 在 Unity 的通用渲染管线&#xff08;URP&#xff09;中&#xff0c;处理半透明物体的自身交叠是一个常见挑战。当半透明物体&#xff08;如玻璃、水或透明材质&#xff09;的某些部分相互重叠时&#xff0c;可能会出现渲染顺序问题&#xff0c;导致视觉瑕疵。 对惹&…

哈希算法入门:深入浅出讲明白HASH哈希算法

一、先搞懂&#xff1a;哈希算法到底是 “啥玩意儿”&#xff1f;咱们先别碰复杂概念&#xff0c;从你每天都会遇到的事说起 —— 你会发现&#xff0c;“哈希思维” 其实早就藏在生活里了。&#xff08;一&#xff09;生活中的 “哈希例子”&#xff1a;给东西 “贴标签、找位…

Vuex 和 Pinia 各自的优点

核心总结&#xff08;一句话概括&#xff09; Vuex&#xff1a;Vue 官方曾经的状态管理标准解决方案&#xff0c;成熟稳定&#xff0c;概念清晰&#xff0c;但语法稍显冗长。Pinia&#xff1a;Vue 官方推荐的新一代状态管理库&#xff0c;API 设计极其简洁&#xff0c;完美支持…