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

思政网站建设管理自查报告asp.net 怎样生成网站

思政网站建设管理自查报告,asp.net 怎样生成网站,推广方案框架,东莞网站建设网站建立上一篇,笔者留下了一个问题,three.js内置的THREE.Vector3.project方法算出来的结果对于超出屏幕可见范围的点来说错得相当离谱。 three.jsWebGL踩坑经验合集(4.1):THREE.Line2的射线检测问题(注意本篇说的是Line2,同样也不是阈值…

上一篇,笔者留下了一个问题,three.js内置的THREE.Vector3.project方法算出来的结果对于超出屏幕可见范围的点来说错得相当离谱。

three.js+WebGL踩坑经验合集(4.1):THREE.Line2的射线检测问题(注意本篇说的是Line2,同样也不是阈值方面的问题)-CSDN博客

从代码上看,project方法和常规shader的实现原理并无太大差异,但是为什么错得这么离谱的算法被使用了这么多年都没人去管的呢?

原因很简单,不管怎么错,它都已经出界,不可见了,所以结果的正确与否对于显示来说并不重要。

我们回顾一下上一篇错得很离谱的那个数值,它的xyz都不在0~1的范围内。

但要是想拿它来进行计算的话就完犊子了。就拿上一篇的线来说,假设有且只有一个端点出界,一个点在(0.5,0.1),另一个点在(-1.2, -1.3),按照预期,连线向量应该为(1.7, 1.4),但如果反了,变成(1.2, 1.3),那连线向量就会被误算成(-0.7, -1.2),导致后续的结果全部不正确。THREE.Line2射线检测的bug就是这样子来的了。

所以现在我们就来探讨下project方法算不正确的原因。

Vector3的project方法实现代码如下:

project(camera) {return this.applyMatrix4(camera.matrixWorldInverse).applyMatrix4(camera.projectionMatrix);
}

然后我们再来研读下applyMatrix4的代码

applyMatrix4(m) {const x = this.x, y = this.y, z = this.z;const e = m.elements;const w = 1 / (e[3] * x + e[7] * y + e[11] * z + e[15]);this.x = (e[0] * x + e[4] * y + e[8] * z + e[12]) * w;this.y = (e[1] * x + e[5] * y + e[9] * z + e[13]) * w;this.z = (e[2] * x + e[6] * y + e[10] * z + e[14]) * w;return this;}

大体上,它是拿矩阵m跟3D点进行矩阵乘法运算,但是它还多了一个步骤,就是在xyz以外还算了一个w值,并且把w值分别乘到xyz上。

这一步是个什么原理呢?

透视成像近大远小的特性,在数学上大致为反比例的关系。不严谨的说法,同一个物体,我们肉眼所见的大小(s)跟物到我们的距离(z)成反比,即s=1/z。

z在分母,已经无法用矩阵的线性运算进行表达。为了让开发者在处理3D变换的过程中尽可能只跟矩阵打交道,GPU渲染底层在xyz的基础上新增一个w变量,用于处理透视变换,然后称(x, y, z, w)这样的坐标为齐次坐标,最终呈现到屏幕上的,是(x/w, y/w, z/w)这样的值。

可见,THREE.js的Vector3.applyMatrix4中的w跟齐次坐标中的w互为倒数。

笔者不打算给大家探讨投影矩阵的推导过程,网上文章一抓一大把。笔者本人也没啥特别的想法,这里随便给大家搜个两篇:

透视投影矩阵推导

透视投影过程中z值为什么要映射到[0,1]?


笔者只打算给大家列个大纲,说明一下GPU渲染的一些数据结构和工作流程

1 大多数时候,3D点基础变换的计算都基于矩阵乘法。

2 3D点按道理是应该跟3*3矩阵相乘,但是平移是典型的1*3矩阵相加,为了统一,这两种变换合并为1*4和4*4矩阵相乘。并且扩充的行填充单位矩阵的数值[0,0,0,1],1*4矩阵的最后一列填1

至于为什么合并后会从3*3变成4*4,笔者以前有写过2D矩阵的文章,那里展示了从2*2变成3*3的过程,3*3到4*4可类推。

【原创】《矩阵的史诗级玩法》连载五:45度地图砖块所蕴含的矩阵基础知识(下)_45度地图深度排序-CSDN博客

3 既然合并过程中,点的坐标新增了个1,那么GPU渲染底层就把它拿过来做透视变换了,并且赋予变量w。

4 3D点为齐次坐标(x, y, z, w),物体自身的变换Model,相机变换都各自有一个矩阵View,投影到屏幕/画布的NDC坐标系Projection会先后作用于该坐标上得到最终结果(x/w, y/w, z/w),3个矩阵合并称为MVP矩阵。

5 齐次坐标的w值初始为1,前两个矩阵不会修改w,之后P矩阵会修改w从而产生透视效果。

这个大纲还是有点啰嗦了,下面再列一下透视投影矩阵的关键点。

1 w跟z通常成正比,可能是w=z或者w=-z。

2 最终呈现的结果变量,w在分母中。因此,如果w的绝对值很小,甚至等于0,那么这些位置的坐标绝对值将会非常地大,甚至是无穷大。

这么一顿操作之后我们发现,问题点似乎就出现在w上。视锥体的边缘会不会正是w=0的边界?

下面我们不妨就以此为切入点,去看看project方法在视锥体边缘的表现。

既然GPU渲染使用的是齐次坐标,那么three.js也会配套定义了对应的类,Vector4。我们用它来研究将会更加方便。不过Vector4只有applyMatrix4方法,没有project,我们照着Vector3的抄一个。

这里我们单独用一个简单点的相机来测试(顺带加上Vector4的project方法):

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>three_cameraProject</title><style>body {margin: 0;overflow: hidden;}</style><script src="three/build/three.js"></script><script src="three/examples/js/controls/OrbitControls.js"></script>
</head><body><script>function v4Project(v4, camera){return v4.clone().applyMatrix4(camera.matrixWorldInverse).applyMatrix4(camera.projectionMatrix);}var testCamera = new THREE.PerspectiveCamera(60, 1, 1, 20000);testCamera.position.set(0, 0, 100);testCamera.updateMatrixWorld();testCamera.updateProjectionMatrix();for(var i = 95; i <= 105; i ++){var proj = v4Project(new THREE.Vector4(2, 3, i), testCamera)console.log("z=" + i + "时,坐标为(" + proj.x + "," + proj.y + "." + proj.z + "," + proj.w + ")");}    </script>
</body>
</html>

这段代码创建了一个z坐标为100的透视相机,然后测试一个点坐标在100附近(95~105)变化时的投影结果。控制台输出如下:

我们看到,变化的只有z和w,并且均为单调递减,变化过程还比较平缓。但因为齐次坐标中,xyz最终都会除以w,所以最终的呈现结果就完全不一样了。分母越接近0,结果的绝对值越大,越趋向于无穷大,并且当分母从正数过渡到负数时,坐标值都会从正无穷突变到负无穷从而形成断层(数学上这叫无穷间断点)。

我们把区间取小一点,步长设短一点,并且改用Vector3的project方法进行测试

for(var i = 99; i <= 101; i +=0.125){var proj = new THREE.Vector3(2, 3, i).project(testCamera);console.log("z=" + i + "时,坐标为(" + proj.x + "," + proj.y + "." + proj.z + ")");
} 

可以看到,从99到100,xyz的绝对值都飙升得很快。等于100的时候直接等于无穷大,因为此时的w等于0了。超过100就立马来个断层,变成负无穷大,然后绝对值又急剧下降回来。

从这里我们也可以看出,问题的本质

100是相机的z坐标,z=100时,物体刚好贴着相机,完全没距离,z>100时,物体在相机背面,完全不可见,所以这时候不管算出来的是啥值都不能说它错。但与此同时,它也是个不可用的数值。用我们技术大佬的话讲,这个结果只能用在逻辑的终点,而不能是中间的某个环节。

当然了,正交相机无需考虑这个问题,没透视变换的话,w始终等于1,不存在断层的情况。

来小结一下:

1 GPU渲染底层使用包含w的齐次坐标让开发者仅通过线性变换就能实现近大远小的非线性透视效果,非线性部分藏到了底层,w是分母。

2 project方法出问题的位置是在透视相机背面,跟视锥体范围无关,因为一个物体从相机正面到背面的移动过程中,分母w从一个很小的正数缓慢过渡到一个很小的负数,产生了断层,断层后的结果不可用。

3 一个坐标点在透视相机背面,是不会有一个正确的NDC坐标值,因此这个时候只能用来判断该点是否可见或出界,但不能再继续用它进行后续的计算。

4 THREE.Line2的shader通过trimSegment规避了越界的坐标点,但是射线检测没有做类似的处理,从而导致出界的线条存在射线检测错误的bug。

5 把LineMaterial上的trimSegment方法实现到LineSegments2的raycast方法中,问题就得到解决了。

THREE.Line2不是three.js主包的内容,而是在examples文件夹里面,尽管它也是官方提供的类,但是这样的目录规划不得不让笔者感觉到THREE.Line2更像个土八路,没入正编的样子。所以,这玩意儿还存在别的问题,下一篇我会给大家讲讲THREE.Line2的镜像问题,这又是一个大坑,请大家做好心理准备,嘿嘿!

http://www.yayakq.cn/news/249746/

相关文章:

  • 郑州做网站优化网站网站制作费用
  • 工业和信息化网站备案系统广州工商学院官网
  • 建设旅游电子商务网站的目的网站开发的步骤过程
  • wordpress卖产品深圳seo公司
  • 广东建设安全质量协会网站网站建设服务器租赁
  • 哪个网站可以免费做招牌北京软件开发公司排名榜
  • 福州网站建设哪家专业房山网站建设
  • 网页制作与网站建设实战大全 视频品牌建设有哪些方面
  • 亚马逊欧洲站上海先进网站建设公司
  • 个人网站备案需要什么商河做网站公司
  • 响应式营销型网站建设企业设计网站公司排名
  • 如何让百度搜索到自己的网站专业开发网站公司
  • 如何建网站详细步骤网站的主页按钮怎么做
  • 怎样更新网站快照社保服务个人网页
  • 小型网站搭建做下载类网站前景
  • 网站的视频做gif宿迁网络科技有限公司
  • 公众号免费素材网站网站平台建设服务承诺书
  • 中小型网站建设机构德州网站制作哪家好
  • 上海网站建设公司哪家好?招聘网站建设流程图
  • 手机网站页面布局html怎么做
  • 网站可以做信息抓取吗做视频直播网站需要多少资金
  • 万网衡水网站备案wordpress 伪静态 中文
  • 网站规划设计是什么样的什么网站可以自己做名片
  • php面向对象网站开发上海市工程咨询行业协会
  • 中国工程建设网站移动平台开发技术
  • 高端网站定制建站wordpress侧边悬浮联系方式插件
  • 泊头哪给做网站的好注册一个公司需要多少钱?
  • wordpress房屋网站模板seo优化的优点
  • 河南建设监理协会新网站网页分析
  • 如何建设动漫网站百度 seo排名查询