WPF:3D正四面体旋转改错

一、坐标系

  • WPF 的 3D 坐标系原点通常位于对象中心,X 轴向右,Y 轴向上,Z 轴朝向观察者(右手定则)。
    正四面体旋转以原点为中心,绕Z 轴自动旋转。

  • 视点坐标:
    位置(Position)0,0,50、观察方向(LookDirection)0,0,-1 和向上方向(UpDirection)0,1,0

二、问题

问题:3D正四面体自动旋转,有时候会只看到两个分离的面,有时候什么都看不到,全白色。

三、解析

  • 这个问题是 WPF 3D 的背面剔除(Back-face culling) 机制导致的。
    在 WPF 中,默认只会渲染法线朝向相机的三角形面。
    而三角形面的朝向由顶点索引顺序决定(右手定则,逆时针为正面)。如果三角形面的顶点顺序反了,会导致它们的法线指向了立方体内部(很多是指向原点)。
    法线朝向相机(即指向外边的)能够正确显示,指向内部的不会显示。三角形不渲染,所以看到的是纯白色背景。
    因为 Viewport3D 默认背景为白色(或网格背景为白色),当所有三角形都被剔除、没有任何像素被绘制时,就直接透出白色背景了。

四、计算

  • 顶点:(0,0,0), (10,10,0), (10,0,10) , (0,10,10)
    面1: (0,0,0), (10,10,0), (10,0,10) 索引 0,1,2
    面2: (0,0,0), (10,10,0), (0,10,10) 索引 0,1,2
    面3: (0,0,0), (10,0,10), (0,10,10) 索引 0,1,2
    面4: (10,0,10), (0,10,10), (10,10,0) 索引 0,1,2
    另外三个顶点是 (10,10,0), (10,0,10), (0,10,10)。这个四面体不是以原点为中心,而是以原点为一个顶点。那么背面剔除的问题可能表现在某些角度出现空白?需要检查每个面的法线方向是否朝外。由于所有面都包含原点,且其他顶点都在正象限,法线方向可能需要计算。

  • 通常为了清晰显示立体感,我们只渲染正面,但需要正确顺序。
    对于以原点为顶点的四面体,我们可以选择从外部看每个面的逆时针顺序。
    更简单的方法是将所有三角形索引改为按顺时针从外部看?
    但标准是逆时针为正面。我们可以计算每个面的法线,如果指向内部,则交换最后两个索引。

  • 面1: A,B,C:
    A=(0,0,0), B=(10,10,0), C=(10,0,10)。
    向量AB=(10,10,0), AC=(10,0,10)。法线为: 叉积 AB × AC = (10,10,0) × (10,0,10) = (100, -100, -100),指向 (1,-1,-1) 方向,这大致指向外部吗?
    这个四面体位于第一象限,A在原点,外部应该是指向远离原点的方向?但这个四面体不在原点中心,其外部方向对于每个面是不同的。
    我们通常希望模型围绕原点旋转时,外部法线指向外。但该四面体所有顶点都在x>=0,y>=0,z>=0区域,其重心在 (5,5,5) 附近。对于面A,B,C,外部应该是远离重心方向。
    重心G=( (0+10+10+0)/4, (0+10+0+10)/4, (0+0+10+10)/4 ) = (5,5,5)。
    面ABC的中心是 ( (0+10+10)/3, (0+10+0)/3, (0+0+10)/3 ) ≈ (6.67, 3.33, 3.33)。
    从重心到面中心的方向向量是 (1.67, -1.67, -1.67),即大致方向 (1,-1,-1)。
    而我们的法线 (1,-1,-1) 正好指向外部,所以该顺序 (A,B,C) 是正确的。所以面1的顺序 0,1,2 是正确的。

  • 面2: A,B,D。
    A=(0,0,0), B=(10,10,0), D=(0,10,10)。
    向量AB=(10,10,0), AD=(0,10,10)。
    叉积 AB×AD = (10 * 10-0 * 10, 0 * 0-10 * 10, 10 *1 0-10 * 0) = (100, -100, 100) 即 (1,-1,1)。
    面2中心 ( (0+10+0)/3, (0+10+10)/3, (0+0+10)/3 ) = (3.33, 6.67, 3.33)。
    重心到面中心向量: (5-3.33, 5-6.67, 5-3.33) = (1.67, -1.67, 1.67) 即 (1,-1,1)。
    法线 (1,-1,1) 也是外部,所以顺序 0,1,2 也是正确的。

  • 面3: A,C,D。
    A=(0,0,0), C=(10,0,10), D=(0,10,10)。
    向量AC=(10,0,10), AD=(0,10,10)。
    叉积 AC×AD = (0 * 10-10 * 10, 10 * 0-10 * 10, 10 * 10-0 * 0) = (-100, -100, 100) 即 (-1,-1,1)。
    面3中心 ( (0+10+0)/3, (0+0+10)/3, (0+10+10)/3 ) = (3.33, 3.33, 6.67)。
    重心到面中心向量:(5-3.33, 5-3.33, 5-6.67) = (1.67, 1.67, -1.67) 即 (1,1,-1)。
    我们的法线是 (-1,-1,1),正好相反,是向内。所以顺序 0,1,2 是反的,应该改为 0,2,1 才使得法线向外。

  • 面4: B,C,D。
    B=(10,10,0), C=(10,0,10), D=(0,10,10)。
    向量BC=(0,-10,10), BD=(-10,0,10)。
    叉积 BC×BD = ((-10) * 10 - 10 * 0, 10 * (-10) - 0 * 10, 0 * 0 - (-10) * (-10)) = (-100, -100, -100) 即 (-1,-1,-1)。
    面4中心 ( (10+10+0)/3, (10+0+10)/3, (0+10+10)/3 ) = (6.67, 6.67, 6.67)。
    重心到面中心向量:(5-6.67, 5-6.67, 5-6.67) = (-1.67, -1.67, -1.67) 即 (-1,-1,-1)。
    法线 (-1,-1,-1) 是外部,所以顺序 0,1,2 正确。

  • 因此,只有第三个面(A,C,D)需要改为 0,2,1,而其他三个面保持 0,1,2。

五、问题判断

  • WPF 3D 默认只渲染法线朝向相机的三角形(逆时针顺序为正面)。程序的四个面都使用了 TriangleIndices="0,1,2",但经过计算,第三个面(A,C,D)的法线指向内部,而不是朝外。因此:
    • 当相机从某个方向看时,如果所有面都背对相机,就什么都看不见(全白色);
    • 当相机转到特定角度,只有个别面的法线指向相机,所以只能看到一两个面。

六、改正

第三个面(A,C,D)需要改为 0,2,1,而其他三个面保持 0,1,2。

七、补充

  • 绕 X 轴旋转 160°(接近 180°)相当于把原本在 Y 轴正方向的东西翻到了后面。
    红色原本在视角的正前方(原始状态下只有它可见),但转 160° 后它正好翻到了背面。
    绿色和黄色原本藏在背面,现在随着旋转被翻到了正面。
    WPF 3D 默认只渲染法线朝向相机的三角形来看,红色不渲染,所以看见了藏在背面的绿色和黄色。
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 一、坐标系 WPF 的 3D 坐标系原点通常位于对象中心,X 轴向右,Y 轴向上,Z 轴朝向观察者(右手定则)。正...
    大龙10阅读 191评论 0赞 21
  • 一、坐标系 WPF 的 3D 坐标系原点通常位于对象中心,X 轴向右,Y 轴向上,Z 轴朝向观察者(右手定则)。正...
    大龙10阅读 122评论 1赞 10
  • 一、坐标系 WPF 的 3D 坐标系原点通常位于对象中心,X 轴向右,Y 轴向上,Z 轴朝向观察者(右手定则)。正...
    大龙10阅读 135评论 0赞 11
  • 一、坐标系 WPF 的 3D 坐标系原点通常位于对象中心,X 轴向右,Y 轴向上,Z 轴朝向观察者(右手定则)。正...
    大龙10阅读 113评论 0赞 8
  • 原创声明 本文系作者辛苦码字所得,欢迎分享和转载,但请在明显位置注明作者的如下信息:笔名:来碗鸡蛋面简书主页:ht...
    来碗鸡蛋面阅读 684评论 0赞 2

友情链接更多精彩内容