javascript - Three.js 使用正交相机将 2D 映射到 3D
问题描述
我有一个使用正交相机的 three.js 场景
this.camera = new THREE.OrthographicCamera(this.width / - 2, this.width / 2, this.height / 2, this.height / - 2, 1, 1000);
this.camera.position.z = this.width / 2;
this.scene.add(this.camera);
然后我有一个立方体(红色),我想将它定位在一些 2D 坐标(绿色)上。我目前正在使用此功能将 2D 坐标转换为 3D 坐标。
from2Dto3D( position2d ) {
var vector = new THREE.Vector3(),
camera = this.camera,
x = (position2d.x / this.width) * 2 - 1,
y = (position2d.y / this.height) * 2 + 1;
vector.set(x, y, 0.5);
vector.unproject(this.camera);
var direction = vector.sub(this.camera.position).normalize(),
distance = - this.camera.position.z / direction.z,
scaled = direction.multiplyScalar(distance),
coords = this.camera.position.clone().add(scaled);
return new THREE.Vector3(coords.x, coords.y, 0);
}
...
let position3d = this.from2Dto3D(position2d);
...
this.cube.position.x = position3d.x; // updating only x position for the moment
如何将立方体准确定位在 2D 位置?
解决方案
您需要显示更多代码。我们怎么知道问题不在于您的鼠标位置计算?
无论相机方向如何,只要没有对画布应用 css 变换,这里的代码都应该可以工作。
canvas.addEventListener('mousemove', (e) => {
const rect = canvas.getBoundingClientRect();
// get a canvas content pixel position
const {width, height} = renderer.domElement;
const canvasX = (e.clientX - rect.left) * width / rect.width;
const canvasY = (e.clientY - rect.top) * height / rect.height;
const clipX = (canvasX / width) * 2 - 1;
const clipY = (canvasY / height) * -2 + 1;
// get the object's clip space Z
const clipPos = new THREE.Vector3();
mesh.getWorldPosition(clipPos);
clipPos.project(camera);
const pos = new THREE.Vector3(clipX, clipY, clipPos.z);
pos.unproject(camera);
mesh.position.copy(pos);
render();
});
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const size = 5;
const near = 5;
const far = 50;
const camera = new THREE.OrthographicCamera(-size, size, size, -size, near, far);
camera.position.set(5, 7, 12);
camera.lookAt(0, 0, 0);
const scene = new THREE.Scene();
scene.background = new THREE.Color('white');
const gridHelper = new THREE.GridHelper(10, 10);
scene.add(gridHelper);
const cubeSize = 1;
const cubeGeo = new THREE.BoxBufferGeometry(cubeSize, cubeSize, cubeSize);
const cubeMat = new THREE.MeshBasicMaterial({color: 'red'});
const mesh = new THREE.Mesh(cubeGeo, cubeMat);
scene.add(mesh);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render() {
if (resizeRendererToDisplaySize(renderer)) {
const aspect = renderer.domElement.clientWidth / renderer.domElement.clientHeight;
camera.left = -size * aspect;
camera.right = size * aspect;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
}
render();
canvas.addEventListener('mousemove', (e) => {
const rect = canvas.getBoundingClientRect();
// get a canvas content pixel position
const {width, height} = renderer.domElement;
const canvasX = (e.clientX - rect.left) * width / rect.width;
const canvasY = (e.clientY - rect.top) * height / rect.height;
const clipX = (canvasX / width) * 2 - 1;
const clipY = (canvasY / height) * -2 + 1;
// get the object's clip space Z
const clipPos = new THREE.Vector3();
mesh.getWorldPosition(clipPos);
clipPos.project(camera);
// note this code moves the object in the plane
// of the camera, not the plane of the grid
const pos = new THREE.Vector3(clipX, clipY, clipPos.z);
pos.unproject(camera);
mesh.position.copy(pos);
render();
});
window.addEventListener('resize', render);
}
main();
html, body {
margin: 0;
height: 100%;
}
#c {
width: 100%;
height: 100%;
display: block;
}
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r112/build/three.min.js"></script>
<canvas id="c"></canvas>
请注意,相同的代码也适用于PerspecitveCamera
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 60;
const near = 0.1;
const far = 50;
const camera = new THREE.PerspectiveCamera(fov, 1, near, far);
camera.position.set(2, 5, 10);
camera.lookAt(0, 0, 0);
const scene = new THREE.Scene();
scene.background = new THREE.Color('white');
const gridHelper = new THREE.GridHelper(10, 10);
scene.add(gridHelper);
const cubeSize = 1;
const cubeGeo = new THREE.BoxBufferGeometry(cubeSize, cubeSize, cubeSize);
const cubeMat = new THREE.MeshBasicMaterial({color: 'red'});
const mesh = new THREE.Mesh(cubeGeo, cubeMat);
scene.add(mesh);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render() {
if (resizeRendererToDisplaySize(renderer)) {
camera.aspect = renderer.domElement.clientWidth / renderer.domElement.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
}
render();
canvas.addEventListener('mousemove', (e) => {
const rect = canvas.getBoundingClientRect();
// get a canvas content pixel position
const {width, height} = renderer.domElement;
const canvasX = (e.clientX - rect.left) * width / rect.width;
const canvasY = (e.clientY - rect.top) * height / rect.height;
const clipX = (canvasX / width) * 2 - 1;
const clipY = (canvasY / height) * -2 + 1;
// get the object's clip space Z
const clipPos = new THREE.Vector3();
mesh.getWorldPosition(clipPos);
clipPos.project(camera);
// note: this code moves the object in the plane
// of the camera not the plane of the grid
const pos = new THREE.Vector3(clipX, clipY, clipPos.z);
pos.unproject(camera);
mesh.position.copy(pos);
render();
});
window.addEventListener('resize', render);
}
main();
html, body {
margin: 0;
height: 100%;
}
#c {
width: 100%;
height: 100%;
display: block;
}
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r112/build/three.min.js"></script>
<canvas id="c"></canvas>
如果你的画布是 css 转换然后看这个
请注意,如果您想在网格(或任意平面)上移动对象,那么一个简单的解决方案是将 RayCaster 用于不可见平面。
此外,如果您的对象在场景中有被平移、旋转或缩放的父对象,那么只需
const parent = object.parent;
scene.attach(object);
// move the object as above
parent.attach(object);
推荐阅读
- python - 使excel数据csv兼容
- android - 布局表达式中可绘制的三元运算符
- html - 如何在 HTML 中显示多行文本
- pgpool - Pgpool-2 和 2 个只读副本的设置,目标是在两个副本之间平均拆分连接
- javascript - 在更改事件中,两位数年份转换为当前年份 Javascript
- typescript - 使用条件值定义打字稿接口
- excel - 如何将pdf中的数据复制并粘贴到excel中?
- c++ - 如何处理 PNG 文件中的伽马校正和颜色?
- python - OpenCV warperspective 切断视频中的左侧和顶部
- google-apps-script - 如何将所有检查值从 html 页面保存到谷歌表