我正在忙着开展一个小型项目,在那里我不会使用网络摄像头来跟踪用户的眼睛位置(从人眼位置相对于屏幕中心的向量)并使用这个“摄像机角度”进行操作一个threeJS场景(或者可能只是直接的WebGL),使得它看起来像是一个进入3d环境的“窗口”。太完成这一点我相信所需要的一切(假设你已经有'眼睛摄像机角度')是操纵视图矩阵。例如,给定直的摄像机角度,左侧的图像将是标准视图矩阵的典型视锥体。如果“眼睛摄像机位置”向上移动,右侧的图像将是理想的矩阵并产生视锥体。
这是否像交换使用的标准视图矩阵一样简单? 什么会说矩阵看起来像。
回答时请记住,我对线性代数的了解与一年级学生一样多。 另外,对于更多细节,该项目将使用WebGL和JavaScript基于浏览器。
BTW我刚刚发现,我在这里要做的就是:'头部耦合透视'
答案 0 :(得分:0)
这不是一个完整的答案,但......目前我太懒惰/忙碌,但如果我理解正确,你基本上想要一个像glFrustum
这样的功能。算术在那里,但格式很差。这使您可以生成平截头体矩阵,其中眼睛不在中心(就像透视函数那样)。 Three.js也支持Camera.setViewOffset
。
要使用Camera.setViewOffset
基本上你定义一个更大的视图和画布代表那个更大视图的three.js画布的哪个子部分。在这种情况下,我们不关心更大的视图,因此我们可以将较大的视图和子视图设置为相同的大小,但仍然偏移较小的视图。
因此,有效地匹配用户的眼睛位置,你想要选择较大区域内的中心区域和移动相机。
我现在懒得做的就是弄清楚每个移动多少。虽然相机在世界单位中移动并且setViewOffset
以像素为单位工作,但它们似乎是相同的。 CSS像素应该是每英寸96像素,因此对于每英寸眼睛移动我们需要偏移视图96 *眼睛英寸移动的相反方向。
我们还需要知道用户眼睛离屏幕的距离和屏幕的大小,以便我们可以计算屏幕的视野。 60英寸电视用户站在3英尺远的地方,11英寸笔记本电脑前面的用户会有一个不同的视野,他们的脸在1英尺远的地方
let mouse = new THREE.Vector2(), INTERSECTED;
let radius = 100, theta = 0;
const camera = new THREE.PerspectiveCamera(70, 1, 1, 10000);
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xf0f0f0);
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set( 1, 1, 1 ).normalize();
scene.add( light );
const geometry = new THREE.BoxBufferGeometry(1, 1, 1);
for (let z = 0; z < 50; ++z) {
for (let y = -1; y <= 1; ++y) {
for (let x = -1; x <= 1; ++x) {
if (x === 0 && y === 0) {
continue;
}
const object = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ color: Math.random() * 0xffffff}));
object.position.x = x * 1.5;
object.position.y = y * 1.5;
object.position.z = -z * 2.5;
scene.add(object);
}
}
}
renderer = new THREE.WebGLRenderer({canvas: document.querySelector("canvas")});
resize(true);
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
requestAnimationFrame(render);
function resize(force) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
if (force || canvas.width !== width || canvas.height !== height) {
renderer.setSize(width, height, true);
camera.aspect = width / height;
camera.updateProjectionMatrix();
}
}
function onDocumentMouseMove( event ) {
event.preventDefault();
const canvas = renderer.domElement;
// compute mouse movement in inches
mouse.x = (event.clientX - canvas.clientWidth / 2) / 96;
mouse.y = (event.clientY - canvas.clientHeight / 2) / 96;
}
function render(time) {
const worldUnits = 1;
const pixelUnits = worldUnits * 96;
const canvas = renderer.domElement;
const fullWidth = canvas.clientWidth;
const fullHeight = canvas.clientHeight;
const aspect = fullWidth / fullHeight;
const eyeOffsetX = mouse.x;
const eyeOffsetY = mouse.y;
const eyeDistFromScreenInches = 12;
const halfHeightOfScreenInches = fullHeight / 96 * .5;
const halfFov = Math.atan2(halfHeightOfScreenInches, eyeDistFromScreenInches) * 180 / Math.PI;
const width = fullWidth;
const height = fullHeight;
const centerX = fullWidth / 2;
const centerY = fullHeight / 2;
const left = centerX - width / 2 - eyeOffsetX * pixelUnits;
const top = centerY - height / 2 - eyeOffsetY * pixelUnits;
camera.fov = halfFov * 2;
camera.position.x = eyeOffsetX * worldUnits;
camera.position.y = -eyeOffsetY * worldUnits;
camera.setViewOffset(fullWidth, fullHeight, left, top, width, height);
camera.updateProjectionMatrix();
renderer.render(scene, camera);
requestAnimationFrame(render);
}
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/87/three.min.js"></script>
<canvas></canvas>