我有一个盒子(collada文件)加载在three.js画布中.我可以按预期与它互动.但是,框大小会因用户可以更改大小而异.
当我将它加载到500px乘500px画布时,如果框大,用户必须在看到它之前放大,如果它很小,它很小,用户需要放大.尺寸根据变量而变化通过.
如何在加载画布时将对象(collada文件)放入画布中,然后让用户缩放?下面是加载点击以在three.js画布中显示3D对象的代码:
$scope.generate3D = function () { // 3D OBJECT - Variables var texture0 = baseBlobURL + 'Texture_0.png'; var boxDAE = baseBlobURL + 'Box.dae'; var scene; var camera; var renderer; var box; var controls; var newtexture; // Update texture newtexture = THREE.ImageUtils.loadTexture(texture0); // Initial call to render scene, from this point, Orbit Controls render the scene per the event listener THREE.DefaultLoadingManager.onProgress = function (item, loaded, total) { // console.log( item, loaded, total ); // debug if (loaded === total) render(); }; //Instantiate a Collada loader var loader = new THREE.ColladaLoader(); loader.options.convertUpAxis = true; loader.load(boxDAE, function (collada) { box = collada.scene; box.traverse(function (child) { if (child instanceof THREE.SkinnedMesh) { var animation = new THREE.Animation(child, child.geometry.animation); animation.play(); } }); box.scale.x = box.scale.y = box.scale.z = .2; box.updateMatrix(); init(); }); function init() { scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera(100, window.innerWidth / window.innerHeight, 0.1, 1000); renderer = new THREE.WebGLRenderer(); renderer.setClearColor(0xdddddd); //renderer.setSize(window.innerWidth, window.innerHeight); renderer.setSize(500, 500); // Load the box file scene.add(box); // Lighting var light = new THREE.AmbientLight(); scene.add(light); // Camera camera.position.x = 40; camera.position.y = 40; camera.position.z = 40; camera.lookAt(scene.position); // Rotation Controls controls = new THREE.OrbitControls(camera, renderer.domElement); controls.addEventListener('change', render); controls.rotateSpeed = 5.0; controls.zoomSpeed = 5; controls.noZoom = false; controls.noPan = false; // Add 3D rendering to HTML5 DOM element var myEl = angular.element(document.querySelector('#webGL-container')); myEl.append(renderer.domElement); } // Render scene function render() { renderer.render(scene, camera); console.log('loaded'); } } // Initial 3D Preview Load $scope.generate3D();
更新:我已经评估了此处介绍的解决方案:如何将相机安装到对象但不确定如何定义我的collada文件的距离,因为它可能会有所不同,具体取决于用户输入的尺寸.collada文件是由用户向第三方供应商发送变量生成的,该第三方供应商返回随后加载到three.js中的collada文件.
更新2:感谢@ Blindman67,我更接近了解这是如何相互作用的.当我手动启动camera.position x,y,z值时,对象在屏幕上.我面临的挑战是如何确定每个盒子动态变化时正确的x,y,z值是多少,而且我确实有超过2.8亿的变化.我知道@ Blindman67已经在逻辑上给了我答案,但我只需要最后一步来发现如何为每次变化的对象找到正确的位置,这样我就可以设置正确的x,y,z.
将3D对象适合视图
有几种方法可以将3d对象适合摄像机视图.
向后或向前移动相机.
增加或减少焦距(这也影响FOV(视野))
更改世界空间比例或对象的局部空间比例以使对象变大或变小.
您可以打折最后一个选项,因为在大多数情况下它是不切实际的.(虽然我注意到你在代码中这样做,因为在这个答案中有足够的信息来解决如何缩放对象以适应.但我不建议你这样做)
因此,您要么将摄像机移入或移出,要么保持静止并进行缩放.它与真实相机相同,可以缩放或靠近.
这两种方法都有利有弊.
翻译
移动相机(小车)对于大多数情况来说是最好的,因为它保持透视相同(线条收敛到消失点的速度有多快),因此不会扭曲视野中的物体.在3D中,该方法存在3个问题.
视锥体(显示场景的体积)具有背板(将显示对象的最大距离)和字体平面(可以显示最接近的对象).移动相机以适合非常大或非常小的物体可能导致物体位于前后平面之外并被部分或完全夹住.
移动平面以适合对象也可能产生不希望的结果.移动前平面和后平面以保持对象可以使场景中更近或更远的对象被剪掉.
扩展后平面和前平面之间的总距离也会导致Z缓冲区混叠伪像.但是这些问题仅适用于非常大或非常小的物体和场景.
放大
缩放涉及改变相机的焦距.在库中,您可以通过调整FOV(视野)来完成,这是以度为单位的视图左侧和右侧之间的角度.减小FOV有效地增加了焦距和放大(3D图形没有像相机那样的焦距).增加FOV缩小.这种方法存在问题.
随着FOV减小,视角(视差)减小,使得场景看起来越来越少3D.随着FOV的增加,透视会增加扭曲的物体并使距离内的物体非常小.
由于相机不移动,前后平面保持原位,但放大或缩小背面或前平面附近的物体仍可能导致z缓冲区混叠伪像.
使用什么方法是ip,你可以使用其中一种或两者兼用.
怎么做
对于手头的问题.我们需要知道对象在场景中的大小,并使用此信息将摄像机设置更改为所需的效果(即使对象适合显示).
图1.摄像机,对象和视图.
所以需要一些值.有关视觉说明,请参见图1.
FOV.我会把它转换成Radians
物体与相机的距离
对象的边界球半径.
前飞机
背板
屏幕像素大小
您需要计算对象的边界球体.或者使用近似于边界球的另一个值.我会把它留给你.
代码
var oL,cL; // for the math to make it readable var FOV = 45 * (Math.PI / 180); // convert to radians var objectLocation = oL = {x : 0, y : 0, z : 400}; var objectRadius = 50; var cameraLocation = cL = {x : 0, y : 0, z : 0}; var farPlane = 1000; var nearPlane = 200; var displayWidth = 1600; var displayHeight = 1000;
要计算出边界球体在视图上的显示大小是简单的trig.
// Get the distance from camera to object var distToObject = Math.sqrt(Math.pow(oL.x - cL.x, 2) + Math.pow(oL.y - cL.y, 2) + Math.pow(oL.z - cL.z, 2));Figure 1
当我们使用直角三角形(见图1)时,我们将结果乘以2得到总角度大小
// trig inverse tan of opposite over adjacent. var objectAngularSize = Math.atan( (objectRadius) / distToObject ) * 2;
获取对象占据的FOV的分数.
var objectView = objectAngularSize / FOV;
最后你得到了对象的像素大小.
var objectPixelSize = objectView * displayWidth;
这就是你要做的所有事情.这将有助于您了解是否使用上述代码和数学来尝试重新排列计算,以便通过移动相机或FOV使对象占据所需的像素大小.复制代码并没有教会你太多,通过应用它来使用上述信息将在你的脑海中设置它,并将在未来更容易地制作3D所需的许多其他过程.
这就是说复制代码是快速解决方案,而且是框架和库的全部内容.无需知道如何学习更好的东西.
缩放以适应.
这是最简单的并获取对象的角度大小并调整FOV以适应.(注意我使用弧度.Tour.js使用你需要转换的FOV度数)
var requieredObjectPixelSize = 900; var distToObject = Math.sqrt(Math.pow(oL.x - cL.x, 2) + Math.pow(oL.y - cL.y, 2) + Math.pow(oL.z - cL.z, 2)); var objectAngularSize = Math.atan( (objectRadius) / distToObject ) * 2; // get the amount the FOV must be expanded by var scaling = displayWidth / requieredObjectPixelSize; // change the FOV to set the objects size FOV = objectAngularSize * scaling;
将FOV转换为度数并使用它来创建相机.
翻译适合
移动相机以适合对象.这有点复杂,但是更好的方法.
// Approx size in pixels you want the object to occupy var requieredObjectPixelSize = 900; // camera distance to object var distToObject = Math.sqrt(Math.pow(oL.x - cL.x, 2) + Math.pow(oL.y - cL.y, 2) + Math.pow(oL.z - cL.z, 2)); // get the object's angular size. var objectAngularSize = Math.atan( (objectRadius) / distToObject ) * 2; // get the fraction of the FOV the object must occupy to be 900 pixels var scaling = requieredObjectPixelSize / displayWidth; // get the angular size the object has to be var objectAngularSize = FOV * scaling; // use half the angular size to get the distance the camera must be from the object distToObject = objectRadius / Math.tan(objectAngularSize / 2);
现在移动相机.它必须沿着物体和相机之间的矢量移动.
// Get the vector from the object to the camera var toCam = { x : cL.x - oL.x, y : cL.y - oL.y, z : cL.z - oL.z, }
规范化矢量.这意味着使向量的长度等于1,并通过将每个分量(x,y,z)除以向量的长度来完成.
// First length var len = Math.sqrt(Math.pow(toCam.x, 2) + Math.pow(toCam.y, 2) + Math.pow(toCam.z, 2)); // Then divide to normalise (you may want to test for divide by zero) toCam.x /= len; toCam.y /= len; toCam.z /= len;
现在,您可以缩放矢量,使其等于摄像机与对象之间的距离.
toCam.x *= distToObject; toCam.y *= distToObject; toCam.z *= distToObject;
然后只需将矢量添加到对象的位置并将其放入摄像机位置即可
cL.x = oL.x + toCam.x; cL.y = oL.y + toCam.y; cL.z = oL.z + toCam.z;
cl
现在持有相机的位置.
最后一件事.您需要检查对象是否在视图内.
if (distToObject - objectRadius < nearPlane) { nearPlane = (distToObject - objectRadius) * 0.8; // move the near plane towards the camera // by 20% of the distance between the front of the object and the camera } if (distToObject + objectRadius > farPlane) { farPlane = distToObject + objectRadius * 1.2; // move the far plane away from the camera // by 1.2 time the object radius }
还有一个问题.如果物体很小,物体可能非常靠近物体的前部位于相机后面.如果发生这种情况,您将需要使用缩放方法并将相机移回.这仅适用于非常精选的情况,大多数情况下可以忽略.
我没有提供有关如何将其与Three.js集成的信息,但这是一个适用于所有3D软件包的通用答案.您将不得不查阅three.js文档,了解如何更改各种相机设置.它很简单,适用于透视相机.
好的答案我需要稍微忘记它,因为我没有看到错字和错误.我将在当天晚些时候返回并修复它.
希望能帮助到你