Sef*_*efe 3 javascript camera projection coordinate-transformation three.js
我有一个场景,我想将透视对象(即远离时看起来较小的对象)与正交对象(即,无论距离如何都显示相同大小的对象)结合起来。透视对象是渲染“世界”的一部分,而正交对象是装饰品,如标签或图标。与 HUD 不同的是,我希望正交对象在世界“内部”渲染,这意味着它们可以被世界对象覆盖(想象一下在标签之前经过的平面)。
我的解决办法是使用一个渲染器,但两个场景,一个具有PerspectiveCamera和一个带OrthogographicCamera。我在不清除 z 缓冲区的情况下按顺序渲染它们(渲染器的autoClear属性设置为false)。我面临的问题是,我需要同步每个场景中对象的放置,以便为一个场景中的对象分配一个 z 位置,该位置位于另一个场景中位于它之前的对象之后,但位于其之前的对象之前在它后面。
为此,我将我的透视场景指定为“领先”场景,即。所有对象的所有坐标(透视和正交)都是基于这个场景分配的。透视对象直接使用这些坐标并在该场景中使用透视相机进行渲染。正交对象的坐标被转换为正交场景中的坐标,然后使用正交相机在该场景中渲染。我通过将透视场景中的坐标投影到透视相机的视图窗格,然后使用正交相机返回到正交场景来进行转换:
position.project(perspectiveCamera).unproject(orthogographicCamera);
Run Code Online (Sandbox Code Playgroud)
唉,这并不像预期的那样工作。正交对象总是在透视对象之前渲染,即使它们应该在它们之间。考虑这个例子,其中蓝色圆圈应该显示在红色方块后面,但在绿色方块之前(它不是):
var pScene = new THREE.Scene();
var oScene = new THREE.Scene();
var pCam = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000);
pCam.position.set(0, 40, 50);
pCam.lookAt(new THREE.Vector3(0, 0, -50));
var oCam = new THREE.OrthographicCamera(window.innerWidth / -2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / -2, 1, 500);
oCam.Position = pCam.position.clone();
pScene.add(pCam);
pScene.add(new THREE.AmbientLight(0xFFFFFF));
oScene.add(oCam);
oScene.add(new THREE.AmbientLight(0xFFFFFF));
var frontPlane = new THREE.Mesh(new THREE.PlaneGeometry(20, 20), new THREE.MeshBasicMaterial( { color: 0x990000 }));
frontPlane.position.z = -50;
pScene.add(frontPlane);
var backPlane = new THREE.Mesh(new THREE.PlaneGeometry(20, 20), new THREE.MeshBasicMaterial( { color: 0x009900 }));
backPlane.position.z = -100;
pScene.add(backPlane);
var circle = new THREE.Mesh(new THREE.CircleGeometry(60, 20), new THREE.MeshBasicMaterial( { color: 0x000099 }));
circle.position.z = -75;
//Transform position from perspective camera to orthogonal camera -> doesn't work, the circle is displayed in front
circle.position.project(pCam).unproject(oCam);
oScene.add(circle);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
renderer.autoClear = false;
renderer.render(oScene, oCam);
renderer.render(pScene, pCam);
Run Code Online (Sandbox Code Playgroud)
您可以在此处试用代码。
在透视世界中,圆的(世界)z 轴位置为 -75,位于方块(-50 和 -100)之间。但它实际上显示在两个方格的前面。如果您手动将圆圈 z 位置(在正交场景中)设置为 -500,它会显示在方块之间,因此如果定位正确,原则上我应该可以尝试。
我知道我无法使用正交和透视相机渲染相同的场景。我的目的是在每次渲染之前重新定位所有正交对象,使它们看起来处于正确的位置。
我该怎么做才能从透视坐标计算正交坐标,以便使用正确的深度值渲染对象?
更新:
如果有人遇到类似问题,我已经添加了我当前的问题解决方案的答案。但是,由于该解决方案无法提供与正交相机相同的质量。因此,如果 somoeone 能解释为什么正交相机不能按预期工作和/或提供问题的解决方案,我仍然会很高兴。
你非常接近你所期望的结果。您忘记更新相机矩阵,必须计算该操作project并project可以正常工作:
pCam.updateMatrixWorld ( false );
oCam.updateMatrixWorld ( false );
circle.position.project(pCam).unproject(oCam);
Run Code Online (Sandbox Code Playgroud)
在渲染中,场景的每个网格通常由模型矩阵、视图矩阵和投影矩阵进行变换。
投影矩阵:
投影矩阵描述了从场景的 3D 点到视口的 2D 点的映射。投影矩阵从视图空间变换到裁剪空间,裁剪空间中的坐标变换为(-1, -1, -1)到(1, 1, 1)范围内的归一化设备坐标(NDC)通过除以剪辑坐标的 w 分量。
视图矩阵:
视图矩阵描述了观察场景的方向和位置。视图矩阵从世界空间转换到视图(眼睛)空间。在视口的坐标系中,X 轴指向左侧,Y 轴指向上方,Z 轴指向视图外(注意在右手坐标系中,Z 轴是 X 轴的叉积)轴和 Y 轴)。
模型矩阵:
模型矩阵定义了场景中网格的位置、方向和相对大小。模型矩阵将顶点位置从网格转换到世界空间。
如果一个片段是在另一个片段“后面”或“之前”绘制的,则取决于片段的深度值。对于正交投影,视图空间的 Z 坐标线性映射到深度值,而在透视投影中它不是线性的。
一般来说,深度值的计算方法如下:
float ndc_depth = clip_space_pos.z / clip_space_pos.w;
float depth = (((farZ-nearZ) * ndc_depth) + nearZ + farZ) / 2.0;
Run Code Online (Sandbox Code Playgroud)
投影矩阵描述了从场景的 3D 点到视口的 2D 点的映射。它从眼睛空间转换到剪辑空间,剪辑空间中的坐标通过除以剪辑坐标的 w 分量转换为归一化设备坐标(NDC)。
在正交投影中,眼睛空间中的坐标线性映射到归一化的设备坐标。
在正交投影中,眼睛空间中的坐标线性映射到归一化的设备坐标。
正交投影矩阵:
r = right, l = left, b = bottom, t = top, n = near, f = far
2/(r-l) 0 0 0
0 2/(t-b) 0 0
0 0 -2/(f-n) 0
-(r+l)/(r-l) -(t+b)/(t-b) -(f+n)/(f-n) 1
Run Code Online (Sandbox Code Playgroud)
在正交投影中,Z 分量由线性函数计算:
z_ndc = z_eye * -2/(f-n) - (f+n)/(f-n)
Run Code Online (Sandbox Code Playgroud)
在透视投影中,投影矩阵描述了从针孔相机看到的世界中的 3D 点到视口的 2D 点的映射。
相机视锥体(截棱锥)中的眼睛空间坐标被映射到立方体(标准化设备坐标)。
透视投影矩阵:
r = right, l = left, b = bottom, t = top, n = near, f = far
2*n/(r-l) 0 0 0
0 2*n/(t-b) 0 0
(r+l)/(r-l) (t+b)/(t-b) -(f+n)/(f-n) -1
0 0 -2*f*n/(f-n) 0
Run Code Online (Sandbox Code Playgroud)
在透视投影中,Z 分量由有理函数计算:
z_ndc = ( -z_eye * (f+n)/(f-n) - 2*f*n/(f-n) ) / -z_eye
Run Code Online (Sandbox Code Playgroud)
请参阅堆栈溢出问题的详细说明如何在现代 OpenGL 中使用片段着色器中的 gl_FragCoord.z 线性渲染深度?
在您的情况下,这意味着您必须以这种方式在正交投影中选择圆的 Z 坐标,深度值介于透视投影中对象的深度之间。
由于深度值仅depth = z ndc * 0.5 + 0.5在两种情况下都存在,因此也可以通过归一化的设备坐标而不是深度值来进行计算。
归一化的设备坐标可以很容易地通过 的project函数计算出来THREE.PerspectiveCamera。在project从世界国际空间converrts查看空间和从视图的空间来归一化设备坐标。
为了在正交投影中找到介于两者之间的 Z 坐标,必须将中间归一化设备 Z 坐标转换为视图空间 Z 坐标。这可以通过来完成unproject的功能THREE.PerspectiveCamera。在unproject从标准化设备坐标转换查看空间和视觉空间到世界sapce。
请参阅进一步的OpenGL-鼠标坐标到空间坐标。
看例子:
pCam.updateMatrixWorld ( false );
oCam.updateMatrixWorld ( false );
circle.position.project(pCam).unproject(oCam);
Run Code Online (Sandbox Code Playgroud)
float ndc_depth = clip_space_pos.z / clip_space_pos.w;
float depth = (((farZ-nearZ) * ndc_depth) + nearZ + farZ) / 2.0;
Run Code Online (Sandbox Code Playgroud)
r = right, l = left, b = bottom, t = top, n = near, f = far
2/(r-l) 0 0 0
0 2/(t-b) 0 0
0 0 -2/(f-n) 0
-(r+l)/(r-l) -(t+b)/(t-b) -(f+n)/(f-n) 1
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4008 次 |
| 最近记录: |