首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将向量投影到NDC时的抖动xy坐标

将向量投影到NDC时的抖动xy坐标
EN

Stack Overflow用户
提问于 2022-11-19 13:49:20
回答 1查看 37关注 0票数 0

我在玩ThreeJS,把世界空间中的向量投射到正规化设备坐标(NDC)上,它没有任何问题。

然而,我注意到,当快速平移时,投影产生的x/y坐标中存在大量的抖动。对于物体#2来说,这一点不太明显,因为它是围绕原点运行的,但对于物体#1 (放置在原点上),它根本不应该改变位置,即使当相机围绕原点旋转时,它也不应该改变位置:

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

这里的例子:

代码语言:javascript
复制
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);

});
代码语言:javascript
复制
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;
}
代码语言:javascript
复制
<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>

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-11-20 00:11:22

作为最小解决方案,在camera.updateMatrixWorld();之前调用project(camera),以便使用当前矩阵进行计算。

但是,实际上,如果overlayElementscene需要在每个帧中完全同步,那么首先调用controls.update()renderer.render(scene, camera),然后更新覆盖,如下所示。

代码语言:javascript
复制
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);
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74500583

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档