Three.js 在鼠标点击的地方绘制,但完全平行于相机方向
Three.js Draw where mouse clicks, but entirely parallel to camera orientation
我不太确定我该怎么做。我读了很多关于光线投射的文章,这似乎有助于找到与某物相交的点,但在这种情况下,我只想让它插值 2d 鼠标坐标到鼠标点击的 3d 点,无论缩放、旋转、那里是否有对象等。
我想到但没有采用的一种方法是制作一个与相机平行的不可见平面,该平面始终直立并且始终与 y 轴相交。然后使用光线投射器击中平面,根据需要绘制,然后删除平面。不过这样做似乎很愚蠢。
目前我有一个方法效果很好,但是当线离原点越来越远,或者相机被放大时,它会出现一些问题
在这张照片中,我从两个不同的角度画了两条线。垂直线是当相机与 x 轴和 z 轴水平时的样子,我沿着 y 轴画一条直线,而水平线是当我将相机面朝下涂写时发生的情况。
https://i.imgur.com/f8qw5xV.png
如您所见,似乎是使用到相机的距离来进行此计算,因此距相机的距离越远,计算中的失真就越大。如何摆脱这种扭曲?
来源:https://github.com/AskAlice/mandala-3d-threejs
现场演示:https://askalice.me/mandala/
相关代码如下:
js/content.js@112
function get3dPointZAxis(event)
{
camPos = camera.position;
var mv = new THREE.Vector3((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY/window.innerHeight) * 2 + 1, 1).unproject(camera);
var m2 = new THREE.Vector3(0,0,0);
var pos = camPos.clone();
pos.add(mv.sub(camPos).normalize().multiplyScalar(m2.distanceTo(camPos)));
return pos;
}
我使用了来自两个 Whosebug post 的信息来想出这个,它有我描述的问题。
首先,这个 post 展示了如何绘制并将其转换为 z 轴。它是平坦的。但是我在尝试在三个维度上工作时遇到了很多麻烦。
How to draw a line segment at run time using three.js
然后我使用下面的信息 post 至少让它在 x-z 轴上与相机平行,如下所示: https://i.imgur.com/E9AQNpH.png
Moving objects parallel to projection plane in three.js
带有 THREE.Plane()
和 THREE.Raycaster().ray.intersectPlane()
的选项:
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var plane = new THREE.Plane();
var planeNormal = new THREE.Vector3();
var point = new THREE.Vector3();
function getPoint(event){
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
planeNormal.copy(camera.position).normalize();
plane.setFromNormalAndCoplanarPoint(planeNormal, scene.position);
raycaster.setFromCamera(mouse, camera);
raycaster.ray.intersectPlane(plane, point);
}
运行代码段,点击"draw"复选框将其设置为选中,随机移动鼠标(不按下鼠标),再次点击复选框,用mousedown旋转场景。所有点都在同一平面上。
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 10);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var plane = new THREE.Plane();
var planeNormal = new THREE.Vector3();
var point = new THREE.Vector3();
document.addEventListener("mousedown", onMouseDown, false);
document.addEventListener("mousemove", onMouseMove, false);
function getPoint(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
planeNormal.copy(camera.position).normalize();
plane.setFromNormalAndCoplanarPoint(planeNormal, scene.position);
raycaster.setFromCamera(mouse, camera);
raycaster.ray.intersectPlane(plane, point);
}
function setPoint() {
var sphere = new THREE.Mesh(new THREE.SphereBufferGeometry(.125, 4, 2), new THREE.MeshBasicMaterial({
color: "yellow",
wireframe: true
}));
sphere.position.copy(point);
scene.add(sphere);
}
function onMouseDown(event) {
getPoint(event);
if (draw.checked) setPoint();
}
function onMouseMove(event) {
getPoint(event);
if (draw.checked) setPoint();
}
render();
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<div style="position:absolute;">
<input id="draw" type="checkbox">
<label for="draw" style="color: white;">draw</label>
</div>
我不太确定我该怎么做。我读了很多关于光线投射的文章,这似乎有助于找到与某物相交的点,但在这种情况下,我只想让它插值 2d 鼠标坐标到鼠标点击的 3d 点,无论缩放、旋转、那里是否有对象等。
我想到但没有采用的一种方法是制作一个与相机平行的不可见平面,该平面始终直立并且始终与 y 轴相交。然后使用光线投射器击中平面,根据需要绘制,然后删除平面。不过这样做似乎很愚蠢。
目前我有一个方法效果很好,但是当线离原点越来越远,或者相机被放大时,它会出现一些问题
在这张照片中,我从两个不同的角度画了两条线。垂直线是当相机与 x 轴和 z 轴水平时的样子,我沿着 y 轴画一条直线,而水平线是当我将相机面朝下涂写时发生的情况。 https://i.imgur.com/f8qw5xV.png
如您所见,似乎是使用到相机的距离来进行此计算,因此距相机的距离越远,计算中的失真就越大。如何摆脱这种扭曲?
来源:https://github.com/AskAlice/mandala-3d-threejs 现场演示:https://askalice.me/mandala/
相关代码如下:
js/content.js@112
function get3dPointZAxis(event)
{
camPos = camera.position;
var mv = new THREE.Vector3((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY/window.innerHeight) * 2 + 1, 1).unproject(camera);
var m2 = new THREE.Vector3(0,0,0);
var pos = camPos.clone();
pos.add(mv.sub(camPos).normalize().multiplyScalar(m2.distanceTo(camPos)));
return pos;
}
我使用了来自两个 Whosebug post 的信息来想出这个,它有我描述的问题。
首先,这个 post 展示了如何绘制并将其转换为 z 轴。它是平坦的。但是我在尝试在三个维度上工作时遇到了很多麻烦。
How to draw a line segment at run time using three.js
然后我使用下面的信息 post 至少让它在 x-z 轴上与相机平行,如下所示: https://i.imgur.com/E9AQNpH.png
Moving objects parallel to projection plane in three.js
带有 THREE.Plane()
和 THREE.Raycaster().ray.intersectPlane()
的选项:
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var plane = new THREE.Plane();
var planeNormal = new THREE.Vector3();
var point = new THREE.Vector3();
function getPoint(event){
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
planeNormal.copy(camera.position).normalize();
plane.setFromNormalAndCoplanarPoint(planeNormal, scene.position);
raycaster.setFromCamera(mouse, camera);
raycaster.ray.intersectPlane(plane, point);
}
运行代码段,点击"draw"复选框将其设置为选中,随机移动鼠标(不按下鼠标),再次点击复选框,用mousedown旋转场景。所有点都在同一平面上。
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 10);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var plane = new THREE.Plane();
var planeNormal = new THREE.Vector3();
var point = new THREE.Vector3();
document.addEventListener("mousedown", onMouseDown, false);
document.addEventListener("mousemove", onMouseMove, false);
function getPoint(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
planeNormal.copy(camera.position).normalize();
plane.setFromNormalAndCoplanarPoint(planeNormal, scene.position);
raycaster.setFromCamera(mouse, camera);
raycaster.ray.intersectPlane(plane, point);
}
function setPoint() {
var sphere = new THREE.Mesh(new THREE.SphereBufferGeometry(.125, 4, 2), new THREE.MeshBasicMaterial({
color: "yellow",
wireframe: true
}));
sphere.position.copy(point);
scene.add(sphere);
}
function onMouseDown(event) {
getPoint(event);
if (draw.checked) setPoint();
}
function onMouseMove(event) {
getPoint(event);
if (draw.checked) setPoint();
}
render();
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<div style="position:absolute;">
<input id="draw" type="checkbox">
<label for="draw" style="color: white;">draw</label>
</div>