Three.js,缩放对象时意外的位置偏移
Three.js, unexpected position shift when scaling object
我正在尝试创建一个缩放框,到目前为止,我设法将光标位置从语言环境转换为世界坐标,并使用正确的 uv 在光标周围创建了一个框对象。
这是我尝试的fiddle:https://jsfiddle.net/2ynfedvk/2/
在不缩放的情况下,框完全以光标为中心,但如果您切换缩放复选框以设置比例 zoomMesh.scale.set(1.5, 1.5, 1)
,则框位置会随着光标离场景中心的移动而移动。
我是不是把 CSS 之类的 "transform origin" 弄乱了 three.js 以围绕对象居中缩放,这是获得这种缩放效果的正确方法吗?
我是 three.js 和 3d 的新手,所以感谢您的帮助。
当您使用 1.5 缩放网格时,这意味着应用缩放顶点值的变换矩阵。
问题来自顶点的更改。顶点位于网格的局部 space 中。而当你将正方形的左上顶点设置为[10, 10, 0]
,然后将.scale.set(1.5, 1.5, 1)
应用于网格时,则顶点坐标变为[15, 15, 0]
。与所有其他 3 个顶点相同。这就是为什么正方形的中心在图片中心到鼠标指针的1.5倍不匹配的原因。
所以,一个选项不是缩放网格,而是改变正方形的大小。
我稍微修改了你的fiddle,所以也许它会更解释:
const
[width, height] = [500, 300],
canvas = document.querySelector('canvas'),
scaleCheckBox = document.querySelector('input')
;
console.log(scaleCheckBox)
canvas.width = width;
canvas.height = height;
const
scene = new THREE.Scene(),
renderer = new THREE.WebGLRenderer({canvas}),
camDistance = 5,
camFov = (2 * Math.atan( height / ( 2 * camDistance ) ) * ( 180 / Math.PI )),
camera = new THREE.PerspectiveCamera(camFov, width/height, 0.1, 1000 )
;
camera.position.z = camDistance;
const
texture = new THREE.TextureLoader().load( "https://picsum.photos/500/300" ),
imageMaterial = new THREE.MeshBasicMaterial( { map: texture , side : 0 } )
;
texture.minFilter = THREE.LinearFilter;
texture.magFilter = THREE.LinearFilter;
texture.format = THREE.RGBFormat;
const
planeGeometry = new THREE.PlaneGeometry( width, height ),
planeMesh = new THREE.Mesh( planeGeometry, imageMaterial )
;
const
zoomGeometry = new THREE.BufferGeometry(),
zoomMaterial = new THREE.MeshBasicMaterial( { map: texture , side : 0 } ),
zoomMesh = new THREE.Mesh( zoomGeometry, zoomMaterial )
;
zoomMaterial.color.set(0xff0000);
zoomGeometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array([
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0
]), 3));
zoomGeometry.setIndex([
0, 1, 2,
2, 1, 3
]);
scene.add( planeMesh );
scene.add( zoomMesh );
var zoom = 1.;
function setZoomBox(e){
const
size = 50 * zoom,
x = e.clientX - (size/2),
y = -(e.clientY - height) - (size/2),
coords = [
x,
y,
x + size,
y + size
]
;
const [x1, y1, x2, y2] = [
coords[0] - (width/2),
coords[1] - (height/2),
coords[2] - (width/2),
coords[3] - (height/2)
];
zoomGeometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array([
x1, y1, 0,
x2, y1, 0,
x1, y2, 0,
x2, y2, 0
]), 3));
const [u1, v1, u2, v2] = [
coords[0]/width,
coords[1]/height,
coords[2]/width,
coords[3]/height
]
zoomGeometry.setAttribute('uv',
new THREE.BufferAttribute(new Float32Array([
u1, v1,
u2, v1,
u1, v2,
u2, v2,
u1, v1,
u1, v2
]), 2));
}
function setScale(e){
//zoomMesh.scale.set(...(scaleCheckBox.checked ? [1.5, 1.5, 1] : [1, 1, 1]));
zoom = scaleCheckBox.checked ? 1.5 : 1 ;
}
function render(){
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
canvas.addEventListener('mousemove', setZoomBox);
scaleCheckBox.addEventListener('change', setScale);
html, body {
margin: 0;
height: 100%;
}
body{
background: #333;
color: #FFF;
font: bold 16px arial;
}
canvas{
}
<script src="https://threejs.org/build/three.min.js"></script>
<canvas></canvas>
<div>Toggle scale <input type="checkbox" /></div>
感谢您的回答,这不是我想要的(不仅调整正方形的大小而且放大图像),但您指出了正确的方向。
就像你说的位置坐标随着比例尺移动,所以我必须重新计算相对于比例尺的新位置。
使用新的 scale
和 offset
变量添加了这些新行:
if(scaleCheckBox.checked){
const offset = scale - 1;
zoomMesh.position.set(
-(x1 * offset) - (size*scale)/2) -(size/2),
-((y1 * offset) + (size*scale)/2) -(size/2)),
0
);
}
这是工作 fiddle :https://jsfiddle.net/dc9f5v0m/
有点乱,需要重新计算很多(尤其是将光标围绕正方形居中),但它完成了工作,并且可以使用任何形状实现缩放效果,而不仅仅是正方形。
再次感谢您的帮助。
我正在尝试创建一个缩放框,到目前为止,我设法将光标位置从语言环境转换为世界坐标,并使用正确的 uv 在光标周围创建了一个框对象。
这是我尝试的fiddle:https://jsfiddle.net/2ynfedvk/2/
在不缩放的情况下,框完全以光标为中心,但如果您切换缩放复选框以设置比例 zoomMesh.scale.set(1.5, 1.5, 1)
,则框位置会随着光标离场景中心的移动而移动。
我是不是把 CSS 之类的 "transform origin" 弄乱了 three.js 以围绕对象居中缩放,这是获得这种缩放效果的正确方法吗?
我是 three.js 和 3d 的新手,所以感谢您的帮助。
当您使用 1.5 缩放网格时,这意味着应用缩放顶点值的变换矩阵。
问题来自顶点的更改。顶点位于网格的局部 space 中。而当你将正方形的左上顶点设置为[10, 10, 0]
,然后将.scale.set(1.5, 1.5, 1)
应用于网格时,则顶点坐标变为[15, 15, 0]
。与所有其他 3 个顶点相同。这就是为什么正方形的中心在图片中心到鼠标指针的1.5倍不匹配的原因。
所以,一个选项不是缩放网格,而是改变正方形的大小。
我稍微修改了你的fiddle,所以也许它会更解释:
const
[width, height] = [500, 300],
canvas = document.querySelector('canvas'),
scaleCheckBox = document.querySelector('input')
;
console.log(scaleCheckBox)
canvas.width = width;
canvas.height = height;
const
scene = new THREE.Scene(),
renderer = new THREE.WebGLRenderer({canvas}),
camDistance = 5,
camFov = (2 * Math.atan( height / ( 2 * camDistance ) ) * ( 180 / Math.PI )),
camera = new THREE.PerspectiveCamera(camFov, width/height, 0.1, 1000 )
;
camera.position.z = camDistance;
const
texture = new THREE.TextureLoader().load( "https://picsum.photos/500/300" ),
imageMaterial = new THREE.MeshBasicMaterial( { map: texture , side : 0 } )
;
texture.minFilter = THREE.LinearFilter;
texture.magFilter = THREE.LinearFilter;
texture.format = THREE.RGBFormat;
const
planeGeometry = new THREE.PlaneGeometry( width, height ),
planeMesh = new THREE.Mesh( planeGeometry, imageMaterial )
;
const
zoomGeometry = new THREE.BufferGeometry(),
zoomMaterial = new THREE.MeshBasicMaterial( { map: texture , side : 0 } ),
zoomMesh = new THREE.Mesh( zoomGeometry, zoomMaterial )
;
zoomMaterial.color.set(0xff0000);
zoomGeometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array([
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0
]), 3));
zoomGeometry.setIndex([
0, 1, 2,
2, 1, 3
]);
scene.add( planeMesh );
scene.add( zoomMesh );
var zoom = 1.;
function setZoomBox(e){
const
size = 50 * zoom,
x = e.clientX - (size/2),
y = -(e.clientY - height) - (size/2),
coords = [
x,
y,
x + size,
y + size
]
;
const [x1, y1, x2, y2] = [
coords[0] - (width/2),
coords[1] - (height/2),
coords[2] - (width/2),
coords[3] - (height/2)
];
zoomGeometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array([
x1, y1, 0,
x2, y1, 0,
x1, y2, 0,
x2, y2, 0
]), 3));
const [u1, v1, u2, v2] = [
coords[0]/width,
coords[1]/height,
coords[2]/width,
coords[3]/height
]
zoomGeometry.setAttribute('uv',
new THREE.BufferAttribute(new Float32Array([
u1, v1,
u2, v1,
u1, v2,
u2, v2,
u1, v1,
u1, v2
]), 2));
}
function setScale(e){
//zoomMesh.scale.set(...(scaleCheckBox.checked ? [1.5, 1.5, 1] : [1, 1, 1]));
zoom = scaleCheckBox.checked ? 1.5 : 1 ;
}
function render(){
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
canvas.addEventListener('mousemove', setZoomBox);
scaleCheckBox.addEventListener('change', setScale);
html, body {
margin: 0;
height: 100%;
}
body{
background: #333;
color: #FFF;
font: bold 16px arial;
}
canvas{
}
<script src="https://threejs.org/build/three.min.js"></script>
<canvas></canvas>
<div>Toggle scale <input type="checkbox" /></div>
感谢您的回答,这不是我想要的(不仅调整正方形的大小而且放大图像),但您指出了正确的方向。
就像你说的位置坐标随着比例尺移动,所以我必须重新计算相对于比例尺的新位置。
使用新的 scale
和 offset
变量添加了这些新行:
if(scaleCheckBox.checked){
const offset = scale - 1;
zoomMesh.position.set(
-(x1 * offset) - (size*scale)/2) -(size/2),
-((y1 * offset) + (size*scale)/2) -(size/2)),
0
);
}
这是工作 fiddle :https://jsfiddle.net/dc9f5v0m/
有点乱,需要重新计算很多(尤其是将光标围绕正方形居中),但它完成了工作,并且可以使用任何形状实现缩放效果,而不仅仅是正方形。
再次感谢您的帮助。