我在玩ThreeJS,把世界空间中的向量投射到正规化设备坐标(NDC)上,它没有任何问题。
然而,我注意到,当快速平移时,投影产生的x/y坐标中存在大量的抖动。对于物体#2来说,这一点不太明显,因为它是围绕原点运行的,但对于物体#1 (放置在原点上),它根本不应该改变位置,即使当相机围绕原点旋转时,它也不应该改变位置:

我首先怀疑这是因为DOM更新不够快,赶不上rAF回调,但是当检查对象#1的x/y坐标时,它似乎仍然很不稳定。一个例子是,当只看x坐标时,你可以看到当旋转相机时,它的值大约是323.50,即使摄像机被指向矢量的中心是死的:

这里的例子:
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({
color: 0x00ff00
});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
const controls = new THREE.OrbitControls(camera, renderer.domElement);
camera.position.set(2, 2, 2);
controls.enableDamping = true;
controls.minPolarAngle = Math.PI * 0.2;
controls.maxPolarAngle = Math.PI * 0.45;
controls.update();
// NOTE: Append DOM overlays
const pointsToProject = [new THREE.Vector3(0, 0, 0), new THREE.Vector3(2,2,-2)];
pointsToProject.forEach((_, i) => {
const overlayElement = document.createElement('div');
overlayElement.className = 'overlay';
overlayElement.id = `overlay-${i}`;
overlayElement.textContent = i + 1;
document.body.appendChild(overlayElement);
});
function animate() {
requestAnimationFrame(animate);
const halfStageWidth = window.innerWidth * 0.5;
const halfStageHeight = window.innerHeight * 0.5;
pointsToProject.forEach((vector, i) => {
const {
x,
y
} = vector.clone().project(camera);
const screenX = x * halfStageWidth + halfStageWidth;
const screenY = y * halfStageHeight * -1 + halfStageHeight;
const overlayElement = document.querySelector(`#overlay-${i}`);
if (!overlayElement) return;
overlayElement.style.setProperty('--x', `${screenX.toFixed(2)}px`);
overlayElement.style.setProperty('--y', `${screenY.toFixed(2)}px`);
});
controls.update();
renderer.render(scene, camera);
};
animate();
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});body {
position: relative;
margin: 0;
padding: 0;
font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}
.overlay {
position: absolute;
z-index: 2;
top: 0;
left: 0;
display: grid;
place-items: center;
background-color: #555;
width: 48px;
height: 48px;
border: 4px solid #fff;
border-radius: 50px;
transform: translate(calc(var(--x, 0) - 50%), calc(var(--y, 0) - 50%));
color: #fff;
}<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.146.0/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.146.0/examples/js/controls/OrbitControls.js"></script>
发布于 2022-11-20 00:11:22
作为最小解决方案,在camera.updateMatrixWorld();之前调用project(camera),以便使用当前矩阵进行计算。
但是,实际上,如果overlayElement和scene需要在每个帧中完全同步,那么首先调用controls.update()和renderer.render(scene, camera),然后更新覆盖,如下所示。
controls.update();
renderer.render(scene, camera); // camera.updateMatrixWorld() is called in renderer.render()
// camera.updateMatrixWorld();
pointsToProject.forEach((vector, i) => {
const {
x,
y
} = vector.clone().project(camera);
:
});
// controls.update();
// renderer.render(scene, camera);https://stackoverflow.com/questions/74500583
复制相似问题