gpt4 book ai didi

math - 屏幕空间中投影球体的半径

转载 作者:行者123 更新时间:2023-12-02 05:11:26 26 4
gpt4 key购买 nike

我试图在投影到屏幕空间后找到球体的可见尺寸(以像素为单位)。球体以原点为中心,相机正对着它。因此,投影球体在二维上应该是一个完美的圆。我知道这一点1现有的问题。然而,那里给出的公式似乎并没有产生我想要的结果。它太小了几个百分点。我认为这是因为它没有正确考虑观点。投影到屏幕空间后,您看不到半个球体,但由于透视缩短,您看到的球体明显更少(您只能看到球体的一个帽,而不是整个半球 2 )。

如何得出精确的 2D 边界圆?

最佳答案

事实上,使用透视投影,您需要计算距相机的眼睛/中心的球体“地平线”的高度(该“地平线”由眼睛与球体相切的光线确定)。

注释:

Notations

d :眼睛与球体中心之间的距离
r :球体的半径
l :眼睛与球体“地平线”上的点之间的距离,l = sqrt(d^2 - r^2)
h :球体“地平线”的高度/半径
theta :“地平线”锥体与眼睛的(半)角
phi :θ 的余角

h / l = cos(phi)

但是:

r / d = cos(phi)

所以,最后:

h = l * r / d = sqrt(d^2 - r^2) * r / d

然后一旦你有h ,只需应用标准公式(您链接的问题中的公式)即可获得投影半径 pr在标准化视口(viewport)中:

pr = cot(fovy / 2) * h / z

z从眼睛到球体“地平线”平面的距离:

z = l * cos(theta) = sqrt(d^2 - r^2) * h / r

所以:

pr = cot(fovy / 2) * r / sqrt(d^2 - r^2)

最后,乘以 pr通过 height / 2获取实际屏幕半径(以像素为单位)。

接下来是一个使用 three.js 完成的小演示。分别使用 n 可以更改相机的球体距离、半径和垂直视野。/f , m/ps/w key 对。在屏幕空间中渲染的黄色线段显示了屏幕空间中球体半径的计算结果。此计算是在函数 computeProjectedRadius() 中完成的.

Projected sphere demo in three.js

projected-sphere.js :

"use strict";

function computeProjectedRadius(fovy, d, r) {
var fov;

fov = fovy / 2 * Math.PI / 180.0;

//return 1.0 / Math.tan(fov) * r / d; // Wrong
return 1.0 / Math.tan(fov) * r / Math.sqrt(d * d - r * r); // Right
}

function Demo() {
this.width = 0;
this.height = 0;

this.scene = null;
this.mesh = null;
this.camera = null;

this.screenLine = null;
this.screenScene = null;
this.screenCamera = null;

this.renderer = null;

this.fovy = 60.0;
this.d = 10.0;
this.r = 1.0;
this.pr = computeProjectedRadius(this.fovy, this.d, this.r);
}

Demo.prototype.init = function() {
var aspect;
var light;
var container;

this.width = window.innerWidth;
this.height = window.innerHeight;

// World scene
aspect = this.width / this.height;
this.camera = new THREE.PerspectiveCamera(this.fovy, aspect, 0.1, 100.0);

this.scene = new THREE.Scene();
this.scene.add(THREE.AmbientLight(0x1F1F1F));

light = new THREE.DirectionalLight(0xFFFFFF);
light.position.set(1.0, 1.0, 1.0).normalize();
this.scene.add(light);

// Screen scene
this.screenCamera = new THREE.OrthographicCamera(-aspect, aspect,
-1.0, 1.0,
0.1, 100.0);
this.screenScene = new THREE.Scene();

this.updateScenes();

this.renderer = new THREE.WebGLRenderer({
antialias: true
});
this.renderer.setSize(this.width, this.height);
this.renderer.domElement.style.position = "relative";
this.renderer.autoClear = false;

container = document.createElement('div');
container.appendChild(this.renderer.domElement);
document.body.appendChild(container);
}

Demo.prototype.render = function() {
this.renderer.clear();
this.renderer.setViewport(0, 0, this.width, this.height);
this.renderer.render(this.scene, this.camera);
this.renderer.render(this.screenScene, this.screenCamera);
}

Demo.prototype.updateScenes = function() {
var geometry;

this.camera.fov = this.fovy;
this.camera.updateProjectionMatrix();

if (this.mesh) {
this.scene.remove(this.mesh);
}

this.mesh = new THREE.Mesh(
new THREE.SphereGeometry(this.r, 16, 16),
new THREE.MeshLambertMaterial({
color: 0xFF0000
})
);
this.mesh.position.z = -this.d;
this.scene.add(this.mesh);

this.pr = computeProjectedRadius(this.fovy, this.d, this.r);

if (this.screenLine) {
this.screenScene.remove(this.screenLine);
}

geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(0.0, 0.0, -1.0));
geometry.vertices.push(new THREE.Vector3(0.0, -this.pr, -1.0));

this.screenLine = new THREE.Line(
geometry,
new THREE.LineBasicMaterial({
color: 0xFFFF00
})
);

this.screenScene = new THREE.Scene();
this.screenScene.add(this.screenLine);
}

Demo.prototype.onKeyDown = function(event) {
console.log(event.keyCode)
switch (event.keyCode) {
case 78: // 'n'
this.d /= 1.1;
this.updateScenes();
break;
case 70: // 'f'
this.d *= 1.1;
this.updateScenes();
break;
case 77: // 'm'
this.r /= 1.1;
this.updateScenes();
break;
case 80: // 'p'
this.r *= 1.1;
this.updateScenes();
break;
case 83: // 's'
this.fovy /= 1.1;
this.updateScenes();
break;
case 87: // 'w'
this.fovy *= 1.1;
this.updateScenes();
break;
}
}

Demo.prototype.onResize = function(event) {
var aspect;

this.width = window.innerWidth;
this.height = window.innerHeight;

this.renderer.setSize(this.width, this.height);

aspect = this.width / this.height;
this.camera.aspect = aspect;
this.camera.updateProjectionMatrix();

this.screenCamera.left = -aspect;
this.screenCamera.right = aspect;
this.screenCamera.updateProjectionMatrix();
}

function onLoad() {
var demo;

demo = new Demo();
demo.init();

function animationLoop() {
demo.render();
window.requestAnimationFrame(animationLoop);
}

function onResizeHandler(event) {
demo.onResize(event);
}

function onKeyDownHandler(event) {
demo.onKeyDown(event);
}

window.addEventListener('resize', onResizeHandler, false);
window.addEventListener('keydown', onKeyDownHandler, false);
window.requestAnimationFrame(animationLoop);
}

index.html :

<!DOCTYPE html>
<html>
<head>
<title>Projected sphere</title>
<style>
body {
background-color: #000000;
}
</style>
<script src="http://cdnjs.cloudflare.com/ajax/libs/three.js/r61/three.min.js"></script>
<script src="projected-sphere.js"></script>
</head>
<body onLoad="onLoad()">
<div id="container"></div>
</body>
</html>

关于math - 屏幕空间中投影球体的半径,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21648630/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com