GLSL textureCube 在同一原点的任意几何体上的透视投影不保存直线

Perspective projection of GLSL textureCube on arbitrary geometry from same origin does not conserve straight lines

问题:在非立方体屏幕上投影带有立方体着色器的旋转立方体相机的图片并从理想状态查看屏幕时,某些直线(本例中的蓝色 z 轴)未投影为直线使用透视相机定位。为什么?我哪里错了?

我对cube camera的理解是,它由6个fov=90,aspect=1的perspective camera组成,每一个都对齐正负主轴,覆盖整个space(近和远)。它可以自由定位和旋转(我想甚至可以缩放和倾斜),然后生成的图像将说明这一点。这些图像是 CubeTexture,它或多或少只有 6 个正方形纹理。参数中的"cube size"是正方形贴图的分辨率。

我对立方体着色器和 textureCube 的理解是,提供的向量仅被视为一个方向,颜色是通过在该方向(从中心)与立方体表面相交的光线获得的.

在定位透视相机时,我的理解是相机的位置是眼睛位置,而不是投影平面位置或其他东西。

考虑到这一点,我预计: 如果投影表面几何体覆盖观察者(透视)相机的整个视图并且观察者相机位于与立方体着色器使用的相同原点, 那么即使旋转立方体相机, 对于每个可能的投影表面几何形状,来自观察者相机的图片都是相同的(除了一些较小的 sampling/aliasing 伪像)。只有当观察者相机或立方体着色器原点被移动时,我才会预料到由几何体引起的差异。

相反,我得到 this,其中旋转的 CubeCamera 投影到圆柱体的图像: 注意蓝色的 z 轴是 "broken"(大概在圆柱体的边缘)。

这与投影到立方体的相同图像:

这是将未旋转的 CubeCamera 投影到圆柱体的图像:

这与投影到立方体的相同图像:

每张图片的裁剪都不同(手动完成),但观察者相机设置和旋转是相同的。看起来未旋转的 CubeCamera 为立方体和圆柱体提供相同的图像,而旋转的 CubeCamera 提供截然不同的图像。

下面附有足够的代码来演示问题。我还上传了一个 运行 版本 here.

<html>
<head>
    <title>Projectors problem test</title>
    <script src="three.js-master/build/three.js"></script>
</head>

<body>
<script>
"use strict";

//Globals
var renderer, simulatedScene, simulationCubeCamera, idealSimulatorScene, camera;

(function main() {
    //Renderer setup
    document.body.style = "overflow: hidden;";
    var container = document.createElement("div");
    container.style = "position: absolute; top: 0; left: 0;"
    document.body.appendChild(container);
    renderer = new THREE.WebGLRenderer({antialias: true});
    renderer.setSize(window.innerWidth, window.innerHeight);
    container.appendChild(renderer.domElement);

    //Setup of simulated scene: 
    simulatedScene = new THREE.Scene();

    simulatedScene.add(new THREE.AxisHelper(500));

    //Cubecamera setup
    simulationCubeCamera = new THREE.CubeCamera(5, 1000, 2048);
    simulationCubeCamera.position.set(0,50,0);
    //Comment out CubeCamera rotation to see the difference:
    simulationCubeCamera.rotation.z = -0.25*Math.PI;
    simulatedScene.add(simulationCubeCamera);
    simulationCubeCamera.update(renderer, simulatedScene);  

    //Define (arbitrary mesh-based) projector screen geometry:
    //Box geometry seems to work. Cylinder geometry works(?) for non-rotated     CubeCamera:
    var screenGeometry = /*new THREE.BoxBufferGeometry(5,5,5, 1,1,1);*/new     THREE.CylinderBufferGeometry(5,5,5, 12288, 1);

    //Make "ideal" projection on the screen geometry using cube shader:
    idealSimulatorScene = new THREE.Scene();
    let cubeShader = THREE.ShaderLib.cube;
    cubeShader.uniforms.tCube.value =     simulationCubeCamera.renderTarget.texture;
    let idealScreenMat = new THREE.ShaderMaterial({
        uniforms: cubeShader.uniforms,
        vertexShader: cubeShader.vertexShader,
        fragmentShader: cubeShader.fragmentShader,
        //depthWrite: false,
        side: THREE.BackSide});

    var idealProjectorScreen = new THREE.Mesh(
        screenGeometry,
        idealScreenMat  
    );
    idealSimulatorScene.add(idealProjectorScreen);

    //Observer camera setup:
    camera = new THREE.PerspectiveCamera(90, window.innerWidth /     window.innerHeight, 0.01, 15);
    camera.lookAt(new THREE.Vector3(5,-50,35));

    renderer.render(idealSimulatorScene, camera);
})();

</script>
</body>
</html>

我解决了。事实证明,立方体着色器专门用于显示天空立方体。具有讽刺意味的是,解决方案是一种简化。我只需要使用世界位置而不是 transformDirection:

现在我的顶点着色器看起来像这样(在替换了一些宏、函数调用和中间变量之后):

varying vec3 vWorldPosition;

void main() {
    //With transformDirection:
    //vWorldPosition = normalize( ( modelMatrix * vec4( position, 0.0 ) ).xyz );
    //With just the world position (interpreted by the textureCube as a direction)
    vWorldPosition = ( modelMatrix * vec4( position, 1.0 ) ).xyz;

    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}

请注意,规范化也被删除了。 textureCube 处理未归一化的向量,事实上插值向量通常不会被归一化。

我可以重用旧的片段着色器。