我正在尝试定位一个div,使得它始终位于对象偏移的最高点,向左偏移一点,因此它不在顶点的顶部。你可以看到here。如果jsfiddle链接不再有效,请参阅下面的使用无场景旋转片段。我可以告诉你,它的工作非常出色。
然而,对于工作中的项目,我需要旋转场景本身。显然,这会混淆转换为2D屏幕坐标。您可以查看此here。您也可以在下方查看不使用场景旋转代码段。如您所见,标签不会在垂直(“y - ”)方向上更新,但会“水平”更新。这是因为相机的位置确实会在x-z平面周围发生变化(phi变化),但它永远不会改变其y位置(θ永远不会改变) - 而是在向上/向下拖动鼠标时旋转场景。正如我所写,我需要旋转场景以达到预期的效果。
如果有人可以指出我正确的方向或在任何html / js / css片段网站(如jsfiddle等等)中做出快速示例,{,s}他将是一个救生员!< / p>
旁注:我尝试(显然没有成功)跳过一些箍,将每个顶点的x坐标和y坐标转换为“正确旋转”位置(即乘以sin(sceneRotation)
和cos(sceneRotation)
,但这只会使情况变得更糟。
side-note 2 :我还花了一个小时来旋转场景中的每个对象,但由于对象实际存储在THREE.Group
中,因此它具有确切的同样的效果。
点击 下面的运行代码段
var scene = new THREE.Scene();
var w = window.innerWidth, h = window.innerHeight;
var camera = new THREE.PerspectiveCamera(60, w / h, 1, 1000);
camera.position.set(0, 0, -60);
var renderer = new THREE.WebGLRenderer({
alpha: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.domElement.style.backgroundColor = "#bbbbbb"
document.body.appendChild(renderer.domElement);
var label = document.getElementById("label");
var controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.addEventListener("change", updateLabel);
var geom = new THREE.Geometry();
geom.vertices.push(new THREE.Vector3(-10, 10, -10));
geom.vertices.push(new THREE.Vector3(10, 10, -10));
geom.vertices.push(new THREE.Vector3(10, -10, -10));
geom.vertices.push(new THREE.Vector3(-10, -10, -10));
geom.vertices.push(new THREE.Vector3(-10, 10, 10));
geom.vertices.push(new THREE.Vector3(10, 10, 10));
geom.vertices.push(new THREE.Vector3(10, -10, 10));
geom.vertices.push(new THREE.Vector3(-10, -10, 10));
geom.faces.push(new THREE.Face3(0, 1, 2));
geom.faces.push(new THREE.Face3(0, 2, 3));
geom.faces.push(new THREE.Face3(7, 6, 5));
geom.faces.push(new THREE.Face3(7, 5, 4));
geom.faces.push(new THREE.Face3(4, 5, 1));
geom.faces.push(new THREE.Face3(4, 1, 0));
geom.faces.push(new THREE.Face3(3, 2, 6));
geom.faces.push(new THREE.Face3(3, 6, 7));
geom.faces.push(new THREE.Face3(4, 0, 3));
geom.faces.push(new THREE.Face3(4, 3, 7));
geom.faces.push(new THREE.Face3(1, 5, 6));
geom.faces.push(new THREE.Face3(1, 6, 2));
var mat = new THREE.MeshBasicMaterial({ color: 0x3377dd, transparent: true, opacity: .65, wireframe: false });
var cube = new THREE.Mesh(geom, mat);
scene.add(cube);
var matWire = new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true });
var cube1 = new THREE.Mesh(geom, matWire);
scene.add(cube1);
render();
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
function getScreenPosition(position) {
var vector = new THREE.Vector3( position.x, position.y, position.z );
vector.project(camera);
vector.x = Math.round( ( vector.x + 1 ) * w / 2 );
vector.y = Math.round( ( - vector.y + 1 ) * h / 2 );
return vector;
}
function updateLabel() {
var minY = null, x = null,
verts = cube.geometry.vertices;
for (var i = 0, iLen = verts.length; i < iLen; i++) {
var pos = getScreenPosition(verts[i]);
if (minY === null || pos.y < minY) {
minY = pos.y;
x = pos.x;
}
}
label.style.left = (x - 3) + "px";
label.style.top = (minY - 28) + "px";
}
body {
overflow: hidden;
margin: 0;
}
#label {
position: absolute;
left: 20px; top: 20px;
}
<div id="label">label</div>
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
点击 下面的运行代码段
/* "globals" */
/* ~~~~~~~~~ */
var PI = Math.PI;
/* camera stuff */
var lastX, lastY, r = 60, phi = 0, c = new THREE.Vector3(0, 0, 0);
/* three.js stuff */
var scene = new THREE.Scene();
var w = window.innerWidth, h = window.innerHeight;
var camera = new THREE.PerspectiveCamera(60, w / h, 1, 1000);
//camera.position.set(0, 0, -60);
updateCamera();
var renderer = new THREE.WebGLRenderer({
alpha: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.domElement.style.backgroundColor = "#bbbbbb"
document.body.appendChild(renderer.domElement);
var label = document.getElementById("label");
//var controls = new THREE.OrbitControls(camera, renderer.domElement);
//controls.addEventListener("change", updateLabel);
document.body.addEventListener("mousedown", handleMouseDown);
document.body.addEventListener("touchstart", handleTouchStart);
var geom = new THREE.Geometry();
geom.vertices.push(new THREE.Vector3(-10, 10, -10));
geom.vertices.push(new THREE.Vector3(10, 10, -10));
geom.vertices.push(new THREE.Vector3(10, -10, -10));
geom.vertices.push(new THREE.Vector3(-10, -10, -10));
geom.vertices.push(new THREE.Vector3(-10, 10, 10));
geom.vertices.push(new THREE.Vector3(10, 10, 10));
geom.vertices.push(new THREE.Vector3(10, -10, 10));
geom.vertices.push(new THREE.Vector3(-10, -10, 10));
geom.faces.push(new THREE.Face3(0, 1, 2));
geom.faces.push(new THREE.Face3(0, 2, 3));
geom.faces.push(new THREE.Face3(7, 6, 5));
geom.faces.push(new THREE.Face3(7, 5, 4));
geom.faces.push(new THREE.Face3(4, 5, 1));
geom.faces.push(new THREE.Face3(4, 1, 0));
geom.faces.push(new THREE.Face3(3, 2, 6));
geom.faces.push(new THREE.Face3(3, 6, 7));
geom.faces.push(new THREE.Face3(4, 0, 3));
geom.faces.push(new THREE.Face3(4, 3, 7));
geom.faces.push(new THREE.Face3(1, 5, 6));
geom.faces.push(new THREE.Face3(1, 6, 2));
var mat = new THREE.MeshBasicMaterial({ color: 0x3377dd, transparent: true, opacity: .65, wireframe: false });
var cube = new THREE.Mesh(geom, mat);
//cube.translateX(10);
scene.add(cube);
var matWire = new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true });
var cube1 = new THREE.Mesh(geom, matWire);
//cube1.translateX(10);
scene.add(cube1);
render();
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
function getScreenPosition(position) {
var vector = new THREE.Vector3( position.x, position.y, position.z );
vector.project(camera);
vector.x = Math.round( ( vector.x + 1 ) * w / 2 );
vector.y = Math.round( ( - vector.y + 1 ) * h / 2 );
return vector;
}
function updateLabel() {
var minY = null, x = null,
verts = cube.geometry.vertices;
for (var i = 0, iLen = verts.length; i < iLen; i++) {
var pos = getScreenPosition(verts[i]);
if (minY === null || pos.y < minY) {
minY = pos.y;
x = pos.x;
}
}
label.style.left = (x - 3) + "px";
label.style.top = (minY - 28) + "px";
}
function handleMouseDown(ev) {
ev.preventDefault();
mouseOrTouchDown(ev.pageX, ev.pageY);
}
function handleTouchStart(ev) {
var touches = ev.touches;
if (touches.length !== 1) {
return;
}
ev.preventDefault();
mouseOrTouchDown(touches.item(0).pageX, touches.item(0).pageY, true);
}
function mouseOrTouchDown(downX, downY, touch) {
if (touch === undefined) { touch = false; }
lastX = downX;
lastY = downY;
if (touch) {
document.ontouchmove = handleTouchMove;
document.addEventListener("touchend", function(ev) {
document.ontouchmove = null;
});
document.addEventListener("touchcancel", function(ev) {
document.removeEventListener("touchmove", handleTouchMove);
});
} else {
document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("mouseup", function(ev) {
document.removeEventListener("mousemove", handleMouseMove);
});
}
}
function handleMouseMove(ev) {
ev.preventDefault();
mouseOrTouchMove(ev.pageX, ev.pageY);
}
function handleTouchMove(ev) {
var touches = ev.touches;
if (touches.length !== 1) {
return;
}
ev.preventDefault();
mouseOrTouchMove(touches.item(0).pageX, touches.item(0).pageY);
}
function mouseOrTouchMove(x, y) {
var dx = lastX - x, dy = y - lastY; /* change in {x, y} */
phi -= dx / 100;
if (phi > 2 * PI) {
phi -= 2 * PI;
} else if (phi < 0) {
phi += 2 * PI;
}
if (phi < PI / 2 || phi > 3 * PI / 2) {
sign = -1;
} else {
sign = 1;
}
if (scene.rotation.z + sign * dy / 100 < -PI) {
scene.rotation.z = -PI;
} else if (scene.rotation.z + sign * dy / 100 > 0) {
scene.rotation.z = 0;
} else {
scene.rotateZ(sign * dy / 100);
}
lastX = x;
lastY = y;
updateCamera();
updateLabel();
}
function updateCamera() {
var z = r * Math.sin(phi); /* new y pos (z-axis) in x-z plane */
var x = r * Math.cos(phi); /* new x pos (x-axis) in x-z plane */
camera.position.set(x, 1, z);
camera.lookAt(c);
}
body {
overflow: hidden;
margin: 0;
}
#label {
position: absolute;
left: 20px; top: 20px;
}
<div id="label">label</div>
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
答案 0 :(得分:2)
在渲染中,场景的每个网格通常由模型矩阵,视图矩阵和投影矩阵进行变换。
模型矩阵:
模型矩阵定义场景中网格的位置,方向和相对大小。模型矩阵将顶点位置从网格转换为世界空间。
查看矩阵:
视图矩阵描述了查看场景的方向和位置。视图矩阵从世界空间转换到视图(眼睛)空间。在视口的坐标系中,X轴指向左侧,Y轴向上,Z轴指向视图外(在右侧系统中注意,Z轴是X-的交叉积)轴和Y轴)。
投影矩阵:
投影矩阵描述了从场景的3D点到视口的2D点的映射。投影矩阵从视图空间转换到剪辑空间,剪辑空间中的坐标转换为范围(-1,-1,-1)到(1,1,1)范围内的规范化设备坐标(NDC)通过用剪辑坐标的w分量划分。
如果你想知道,在视口上看到几何点的位置,那么你必须进行所有这些转换,你必须从标准化设备坐标(NDC)转换为窗口坐标(像素)。
视图矩阵和投影矩阵的转换由project
:
vector.project(camera);
从标准化设备坐标(NDC)到窗口坐标(像素)的转换如下所示:
vector.x = Math.round( ( vector.x + 1 ) * w / 2 );
vector.y = Math.round( ( - vector.y + 1 ) * h / 2 );
您忘记了模型矩阵的转换,可以这样做:
var modelMat = cube.matrixWorld;
vector.applyMatrix4(modelMat);
以某种方式调整函数getScreenPosition
:
function getScreenPosition(position) {
var vector = new THREE.Vector3( position.x, position.y, position.z );
// model to world
var modelMat = cube.matrixWorld;
vector.applyMatrix4(modelMat);
// world to view and view to NDC
vector.project(camera);
// NDC to pixel
vector.x = Math.round( ( vector.x + 1 ) * w / 2 );
vector.y = Math.round( ( - vector.y + 1 ) * h / 2 );
return vector;
}
请参阅代码段:
/* can't see top line; thanks jsfiddle */
/* "globals" */
/* ~~~~~~~~~ */
var PI = Math.PI;
/* camera stuff */
var lastX, lastY, r = 60, phi = 0, c = new THREE.Vector3(0, 0, 0);
/* three.js stuff */
var scene = new THREE.Scene();
var w = window.innerWidth, h = window.innerHeight;
var camera = new THREE.PerspectiveCamera(60, w / h, 1, 1000);
//camera.position.set(0, 0, -60);
updateCamera();
var renderer = new THREE.WebGLRenderer({
alpha: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.domElement.style.backgroundColor = "#bbbbbb"
document.body.appendChild(renderer.domElement);
var label = document.getElementById("label");
//var controls = new THREE.OrbitControls(camera, renderer.domElement);
//controls.addEventListener("change", updateLabel);
document.body.addEventListener("mousedown", handleMouseDown);
document.body.addEventListener("touchstart", handleTouchStart);
var geom = new THREE.Geometry();
geom.vertices.push(new THREE.Vector3(-10, 10, -10));
geom.vertices.push(new THREE.Vector3(10, 10, -10));
geom.vertices.push(new THREE.Vector3(10, -10, -10));
geom.vertices.push(new THREE.Vector3(-10, -10, -10));
geom.vertices.push(new THREE.Vector3(-10, 10, 10));
geom.vertices.push(new THREE.Vector3(10, 10, 10));
geom.vertices.push(new THREE.Vector3(10, -10, 10));
geom.vertices.push(new THREE.Vector3(-10, -10, 10));
geom.faces.push(new THREE.Face3(0, 1, 2));
geom.faces.push(new THREE.Face3(0, 2, 3));
geom.faces.push(new THREE.Face3(7, 6, 5));
geom.faces.push(new THREE.Face3(7, 5, 4));
geom.faces.push(new THREE.Face3(4, 5, 1));
geom.faces.push(new THREE.Face3(4, 1, 0));
geom.faces.push(new THREE.Face3(3, 2, 6));
geom.faces.push(new THREE.Face3(3, 6, 7));
geom.faces.push(new THREE.Face3(4, 0, 3));
geom.faces.push(new THREE.Face3(4, 3, 7));
geom.faces.push(new THREE.Face3(1, 5, 6));
geom.faces.push(new THREE.Face3(1, 6, 2));
var mat = new THREE.MeshBasicMaterial({ color: 0x3377dd, transparent: true, opacity: .65, wireframe: false });
var cube = new THREE.Mesh(geom, mat);
//cube.translateX(10);
scene.add(cube);
var matWire = new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true });
var cube1 = new THREE.Mesh(geom, matWire);
//cube1.translateX(10);
scene.add(cube1);
render();
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
function getScreenPosition(position, object) {
var vector = new THREE.Vector3( position.x, position.y, position.z );
// model to world
if ( object != null ) {
var modelMat = cube.matrixWorld;
vector.applyMatrix4(modelMat);
}
// world to view and view to NDC
vector.project(camera);
// NDC to pixel
vector.x = Math.round( ( vector.x + 1 ) * w / 2 );
vector.y = Math.round( ( - vector.y + 1 ) * h / 2 );
return vector;
}
function updateLabel() {
var minY = null, x = null,
verts = cube.geometry.vertices;
for (var i = 0, iLen = verts.length; i < iLen; i++) {
var pos = getScreenPosition(verts[i], cube);
if (minY === null || pos.y < minY) {
minY = pos.y;
x = pos.x;
}
}
label.style.left = (x - 3) + "px";
label.style.top = (minY - 28) + "px";
}
function handleMouseDown(ev) {
ev.preventDefault();
mouseOrTouchDown(ev.pageX, ev.pageY);
}
function handleTouchStart(ev) {
var touches = ev.touches;
if (touches.length !== 1) {
return;
}
ev.preventDefault();
mouseOrTouchDown(touches.item(0).pageX, touches.item(0).pageY, true);
}
function mouseOrTouchDown(downX, downY, touch) {
if (touch === undefined) { touch = false; }
lastX = downX;
lastY = downY;
if (touch) {
document.ontouchmove = handleTouchMove;
document.addEventListener("touchend", function(ev) {
document.ontouchmove = null;
});
document.addEventListener("touchcancel", function(ev) {
document.removeEventListener("touchmove", handleTouchMove);
});
} else {
document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("mouseup", function(ev) {
document.removeEventListener("mousemove", handleMouseMove);
});
}
}
function handleMouseMove(ev) {
ev.preventDefault();
mouseOrTouchMove(ev.pageX, ev.pageY);
}
function handleTouchMove(ev) {
var touches = ev.touches;
if (touches.length !== 1) {
return;
}
ev.preventDefault();
mouseOrTouchMove(touches.item(0).pageX, touches.item(0).pageY);
}
function mouseOrTouchMove(x, y) {
var dx = lastX - x, dy = y - lastY; /* change in {x, y} */
phi -= dx / 100;
if (phi > 2 * PI) {
phi -= 2 * PI;
} else if (phi < 0) {
phi += 2 * PI;
}
if (phi < PI / 2 || phi > 3 * PI / 2) {
sign = -1;
} else {
sign = 1;
}
if (scene.rotation.z + sign * dy / 100 < -PI) {
scene.rotation.z = -PI;
} else if (scene.rotation.z + sign * dy / 100 > 0) {
scene.rotation.z = 0;
} else {
scene.rotateZ(sign * dy / 100);
}
lastX = x;
lastY = y;
updateCamera();
updateLabel();
}
function updateCamera() {
var z = r * Math.sin(phi); /* new y pos (z-axis) in x-z plane */
var x = r * Math.cos(phi); /* new x pos (x-axis) in x-z plane */
camera.position.set(x, 1, z);
camera.lookAt(c);
}
body {
overflow: hidden;
margin: 0;
}
#label {
position: absolute;
left: 20px; top: 20px;
}
<div id="label">label</div>
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>