마우스 / 캔버스 X, Y에서 Three.js 세계 X, Y, Z
내 사용 사례와 일치하는 예제를 검색했지만 찾을 수 없습니다. 카메라를 고려하여 화면 마우스 좌표를 3D 세계 좌표로 변환하려고합니다.
내가 찾은 솔루션은 모두 물체 선택을 달성하기 위해 광선 교차를 수행합니다.
내가하려는 것은 마우스가 현재 "위에있는"좌표에 Three.js 개체의 중심을 배치하는 것입니다.
내 카메라는 x : 0, y : 0, z : 500 (시뮬레이션 중에 움직일 수 있음)에 있고 모든 객체는 z = 0에 있으며 x 및 y 값이 다양하므로 X, Y 기반 세계를 알아야합니다. 마우스 위치를 따를 객체에 대해 az = 0이라고 가정합니다.
이 질문은 비슷한 문제처럼 보이지만 해결책이 없습니다. THREE.js에서 3D 공간과 관련하여 마우스 좌표 얻기
"왼쪽 위 = 0, 0 | 오른쪽 아래 = window.innerWidth, window.innerHeight"범위의 화면에서 마우스 위치가 주어지면 누구나 Three.js 객체를 마우스 좌표로 이동하는 솔루션을 제공 할 수 있습니다. z = 0을 따라?
이 작업을 수행하기 위해 장면에 오브젝트가 필요하지 않습니다.
이미 카메라 위치를 알고 있습니다.
사용 vector.unproject( camera )하면 원하는 방향을 가리키는 광선을 얻을 수 있습니다.
광선 끝의 z 좌표가 0이 될 때까지 카메라 위치에서 광선을 확장하면됩니다.
다음과 같이 할 수 있습니다.
var vec = new THREE.Vector3(); // create once and reuse
var pos = new THREE.Vector3(); // create once and reuse
vec.set(
( event.clientX / window.innerWidth ) * 2 - 1,
- ( event.clientY / window.innerHeight ) * 2 + 1,
0.5 );
vec.unproject( camera );
vec.sub( camera.position ).normalize();
var distance = - camera.position.z / vec.z;
pos.copy( camera.position ).add( vec.multiplyScalar( distance ) );
변수 pos는 3D 공간, "마우스 아래"및 평면에서 점의 위치입니다 z=0.
편집 : "마우스 아래"및 평면에 점이 필요한 경우 z = targetZ거리 계산을 다음으로 바꿉니다.
var distance = ( targetZ - camera.position.z ) / vec.z;
three.js r.98
r.58에서이 코드는 저에게 효과적입니다.
var planeZ = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0);
var mv = new THREE.Vector3(
(event.clientX / window.innerWidth) * 2 - 1,
-(event.clientY / window.innerHeight) * 2 + 1,
0.5 );
var raycaster = projector.pickingRay(mv, camera);
var pos = raycaster.ray.intersectPlane(planeZ);
console.log("x: " + pos.x + ", y: " + pos.y);
아래는 WestLangley의 답변을 기반으로 작성한 ES6 클래스로 THREE.js r77에서 완벽하게 작동합니다.
렌더링 뷰포트가 전체 브라우저 뷰포트를 차지한다고 가정합니다.
class CProjectMousePosToXYPlaneHelper
{
constructor()
{
this.m_vPos = new THREE.Vector3();
this.m_vDir = new THREE.Vector3();
}
Compute( nMouseX, nMouseY, Camera, vOutPos )
{
let vPos = this.m_vPos;
let vDir = this.m_vDir;
vPos.set(
-1.0 + 2.0 * nMouseX / window.innerWidth,
-1.0 + 2.0 * nMouseY / window.innerHeight,
0.5
).unproject( Camera );
// Calculate a unit vector from the camera to the projected position
vDir.copy( vPos ).sub( Camera.position ).normalize();
// Project onto z=0
let flDistance = -Camera.position.z / vDir.z;
vOutPos.copy( Camera.position ).add( vDir.multiplyScalar( flDistance ) );
}
}
다음과 같이 클래스를 사용할 수 있습니다.
// Instantiate the helper and output pos once.
let Helper = new CProjectMousePosToXYPlaneHelper();
let vProjectedMousePos = new THREE.Vector3();
...
// In your event handler/tick function, do the projection.
Helper.Compute( e.clientX, e.clientY, Camera, vProjectedMousePos );
vProjectedMousePos는 이제 z = 0 평면에 투영 된 마우스 위치를 포함합니다.
3d 객체의 마우스 좌표를 얻으려면 projectVector를 사용하십시오.
var width = 640, height = 480;
var widthHalf = width / 2, heightHalf = height / 2;
var projector = new THREE.Projector();
var vector = projector.projectVector( object.matrixWorld.getPosition().clone(), camera );
vector.x = ( vector.x * widthHalf ) + widthHalf;
vector.y = - ( vector.y * heightHalf ) + heightHalf;
특정 마우스 좌표와 관련된 three.js 3D 좌표를 얻으려면 반대의 unprojectVector를 사용하십시오.
var elem = renderer.domElement,
boundingRect = elem.getBoundingClientRect(),
x = (event.clientX - boundingRect.left) * (elem.width / boundingRect.width),
y = (event.clientY - boundingRect.top) * (elem.height / boundingRect.height);
var vector = new THREE.Vector3(
( x / WIDTH ) * 2 - 1,
- ( y / HEIGHT ) * 2 + 1,
0.5
);
projector.unprojectVector( vector, camera );
var ray = new THREE.Ray( camera.position, vector.subSelf( camera.position ).normalize() );
var intersects = ray.intersectObjects( scene.children );
여기에 좋은 예가 있습니다 . 단, 프로젝트 벡터를 사용하기 위해서는 사용자가 클릭 한 객체가 있어야합니다. intersects는 깊이에 관계없이 마우스 위치에있는 모든 개체의 배열이됩니다.
이것은 사용할 때 나를 위해 일했습니다. orthographic camera
let vector = new THREE.Vector3();
vector.set(
(event.clientX / window.innerWidth) * 2 - 1,
- (event.clientY / window.innerHeight) * 2 + 1,
0
);
vector.unproject(camera);
WebGL three.js r.89
ThreeJS는 Projector. (Un) ProjectVector에서 서서히 깎고 있고, projector.pickingRay () 솔루션이 더 이상 작동하지 않고, 방금 내 코드 업데이트를 완료했습니다. 따라서 가장 최근의 작업 버전은 다음과 같아야합니다.
var rayVector = new THREE.Vector3(0, 0, 0.5);
var camera = new THREE.PerspectiveCamera(fov,this.offsetWidth/this.offsetHeight,0.1,farFrustum);
var raycaster = new THREE.Raycaster();
var scene = new THREE.Scene();
//...
function intersectObjects(x, y, planeOnly) {
rayVector.set(((x/this.offsetWidth)*2-1), (1-(y/this.offsetHeight)*2), 1).unproject(camera);
raycaster.set(camera.position, rayVector.sub(camera.position ).normalize());
var intersects = raycaster.intersectObjects(scene.children);
return intersects;
}
여기에서 es6 클래스를 만드는 방법이 있습니다. Three.js r83으로 작업. rayCaster를 사용하는 방법은 여기 mrdoob에서 가져온 것입니다 : Three.js Projector 및 Ray 객체
export default class RaycasterHelper
{
constructor (camera, scene) {
this.camera = camera
this.scene = scene
this.rayCaster = new THREE.Raycaster()
this.tapPos3D = new THREE.Vector3()
this.getIntersectsFromTap = this.getIntersectsFromTap.bind(this)
}
// objects arg below needs to be an array of Three objects in the scene
getIntersectsFromTap (tapX, tapY, objects) {
this.tapPos3D.set((tapX / window.innerWidth) * 2 - 1, -(tapY /
window.innerHeight) * 2 + 1, 0.5) // z = 0.5 important!
this.tapPos3D.unproject(this.camera)
this.rayCaster.set(this.camera.position,
this.tapPos3D.sub(this.camera.position).normalize())
return this.rayCaster.intersectObjects(objects, false)
}
}
장면의 모든 오브젝트에 대해 히트가 있는지 확인하려면 이와 같이 사용합니다. 내 용도를 위해 필요하지 않았기 때문에 위의 재귀 플래그를 false로 만들었습니다.
var helper = new RaycasterHelper(camera, scene)
var intersects = helper.getIntersectsFromTap(tapX, tapY,
this.scene.children)
...
제공된 답변이 일부 시나리오에서 유용 할 수 있지만, 이러한 시나리오 (게임 또는 애니메이션)가 전혀 정확하지 않기 때문에 상상할 수 없습니다 (타겟의 NDC z를 중심으로 추측?). 대상 z- 평면을 알고있는 경우 이러한 방법을 사용하여 화면 좌표를 월드 좌표로 프로젝트 해제 할 수 없습니다. 그러나 대부분의 시나리오에서이 비행기를 알아야합니다.
예를 들어 중심 (모형 공간의 알려진 점)과 반지름으로 구를 그리는 경우-투영되지 않은 마우스 좌표의 델타로 반지름을 가져와야하지만 할 수 없습니다! 모든면에서 @WestLangley의 targetZ 메서드가 작동하지 않고 잘못된 결과를 제공합니다 (필요한 경우 jsfiddle을 제공 할 수 있음). 또 다른 예-마우스 두 번 클릭으로 궤도 제어 대상을 설정해야하지만 장면 오브젝트가있는 "실제"레이 캐스팅은 없습니다 (선택할 항목이없는 경우).
나를위한 해결책은 z 축을 따라 대상 지점에 가상 평면을 만들고 나중에이 평면에 레이 캐스팅을 사용하는 것입니다. 목표 지점은 현재 궤도 제어 대상 또는 기존 모델 공간 등에서 단계별로 그리는 데 필요한 객체의 정점이 될 수 있습니다. 이것은 완벽하게 작동하며 간단합니다 (타이프 스크립트의 예).
screenToWorld(v2D: THREE.Vector2, camera: THREE.PerspectiveCamera = null, target: THREE.Vector3 = null): THREE.Vector3 {
const self = this;
const vNdc = self.toNdc(v2D);
return self.ndcToWorld(vNdc, camera, target);
}
//get normalized device cartesian coordinates (NDC) with center (0, 0) and ranging from (-1, -1) to (1, 1)
toNdc(v: THREE.Vector2): THREE.Vector2 {
const self = this;
const canvasEl = self.renderers.WebGL.domElement;
const bounds = canvasEl.getBoundingClientRect();
let x = v.x - bounds.left;
let y = v.y - bounds.top;
x = (x / bounds.width) * 2 - 1;
y = - (y / bounds.height) * 2 + 1;
return new THREE.Vector2(x, y);
}
ndcToWorld(vNdc: THREE.Vector2, camera: THREE.PerspectiveCamera = null, target: THREE.Vector3 = null): THREE.Vector3 {
const self = this;
if (!camera) {
camera = self.camera;
}
if (!target) {
target = self.getTarget();
}
const position = camera.position.clone();
const origin = self.scene.position.clone();
const v3D = target.clone();
self.raycaster.setFromCamera(vNdc, camera);
const normal = new THREE.Vector3(0, 0, 1);
const distance = normal.dot(origin.sub(v3D));
const plane = new THREE.Plane(normal, distance);
self.raycaster.ray.intersectPlane(plane, v3D);
return v3D;
}
전체 창보다 작은 캔버스가 있었고 클릭의 세계 좌표를 결정해야했습니다.
// get the position of a canvas event in world coords
function getWorldCoords(e) {
// get x,y coords into canvas where click occurred
var rect = canvas.getBoundingClientRect(),
x = e.clientX - rect.left,
y = e.clientY - rect.top;
// convert x,y to clip space; coords from top left, clockwise:
// (-1,1), (1,1), (-1,-1), (1, -1)
var mouse = new THREE.Vector3();
mouse.x = ( (x / canvas.clientWidth ) * 2) - 1;
mouse.y = (-(y / canvas.clientHeight) * 2) + 1;
mouse.z = 0.5; // set to z position of mesh objects
// reverse projection from 3D to screen
mouse.unproject(camera);
// convert from point to a direction
mouse.sub(camera.position).normalize();
// scale the projected ray
var distance = -camera.position.z / mouse.z,
scaled = mouse.multiplyScalar(distance),
coords = camera.position.clone().add(scaled);
return coords;
}
var canvas = renderer.domElement;
canvas.addEventListener('click', getWorldCoords);
여기에 예가 있습니다. 슬라이딩 전후에 도넛의 동일한 영역을 클릭하면 좌표가 일정하게 유지됩니다 (브라우저 콘솔 확인).
// three.js boilerplate
var container = document.querySelector('body'),
w = container.clientWidth,
h = container.clientHeight,
scene = new THREE.Scene(),
camera = new THREE.PerspectiveCamera(75, w/h, 0.001, 100),
controls = new THREE.MapControls(camera, container),
renderConfig = {antialias: true, alpha: true},
renderer = new THREE.WebGLRenderer(renderConfig);
controls.panSpeed = 0.4;
camera.position.set(0, 0, -10);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(w, h);
container.appendChild(renderer.domElement);
window.addEventListener('resize', function() {
w = container.clientWidth;
h = container.clientHeight;
camera.aspect = w/h;
camera.updateProjectionMatrix();
renderer.setSize(w, h);
})
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
controls.update();
}
// draw some geometries
var geometry = new THREE.TorusGeometry( 10, 3, 16, 100, );
var material = new THREE.MeshNormalMaterial( { color: 0xffff00, } );
var torus = new THREE.Mesh( geometry, material, );
scene.add( torus );
// convert click coords to world space
// get the position of a canvas event in world coords
function getWorldCoords(e) {
// get x,y coords into canvas where click occurred
var rect = canvas.getBoundingClientRect(),
x = e.clientX - rect.left,
y = e.clientY - rect.top;
// convert x,y to clip space; coords from top left, clockwise:
// (-1,1), (1,1), (-1,-1), (1, -1)
var mouse = new THREE.Vector3();
mouse.x = ( (x / canvas.clientWidth ) * 2) - 1;
mouse.y = (-(y / canvas.clientHeight) * 2) + 1;
mouse.z = 0.0; // set to z position of mesh objects
// reverse projection from 3D to screen
mouse.unproject(camera);
// convert from point to a direction
mouse.sub(camera.position).normalize();
// scale the projected ray
var distance = -camera.position.z / mouse.z,
scaled = mouse.multiplyScalar(distance),
coords = camera.position.clone().add(scaled);
console.log(mouse, coords.x, coords.y, coords.z);
}
var canvas = renderer.domElement;
canvas.addEventListener('click', getWorldCoords);
render();
html,
body {
width: 100%;
height: 100%;
background: #000;
}
body {
margin: 0;
overflow: hidden;
}
canvas {
width: 100%;
height: 100%;
}
<script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js'></script>
<script src=' https://threejs.org/examples/js/controls/MapControls.js'></script>
참조 URL : https://stackoverflow.com/questions/13055214/mouse-canvas-xy-to-three-js-world-xyz
'Program Club' 카테고리의 다른 글
| Jarsigner는 어디에 있습니까? (0) | 2020.12.29 |
|---|---|
| Pandas로 최대 두 개 이상의 열 찾기 (0) | 2020.12.29 |
| pandas.Series 히스토그램 플롯을 파일에 저장 (0) | 2020.12.29 |
| 미래에 대한 스칼라의 "이해력" (0) | 2020.12.29 |
| 클래스와 메서드에서 @Transactional을 정의하는 것의 차이점은 무엇입니까 (0) | 2020.12.29 |