任意尺寸OBJ模型的加载问题
通过以下几个方面确保对于任何尺寸的模型,模型都能够在网页中合理的显示:
模型的加载比例均为1,摄像机的z向坐标值根据模型尺寸动态调整;
计算模型的包围盒,使模型位于世界坐标原点;
选用合适的光源,如基础光源和平行光源。
(1)列出加载OBJ模型需要使用到的外部JavaScript库
<script src="../build/three.js"></script>
<script src="js/loaders/MTLLoader.js"></script>
<script src="js/loaders/OBJLoader.js"></script>
<script src="js/controls/TrackballControls.js"></script>
<script src="js/controls/DragControls.js"></script>
<script src="js/libs/stats.min.js"></script>
(2)定义变量
let camera, scene, renderer,controls,stats;//基本构件
let objects =[];//拖拽控制
let group = new THREE.Group();//加载obj
let i,m,x,y,z;//相机自定位
let dragControls;//模型拖拽
(3)初始化渲染器、场景、摄像机、灯光,其中摄像机采用的是透视摄像机,摄像机的z向坐标值由模型尺寸决定;灯光使用的是平行光源和基础光源;添加Stats进行性能监测,对于小型OBJ模型,帧数可以达到60。
//初始化渲染器
renderer = new THREE.WebGLRenderer({
antialias: true});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild( renderer.domElement );
renderer.setClearColor(0x000000, 1);
//浏览器屏幕纵横比
i=window.innerHeight/window.innerWidth;
//初始化场景
scene = new THREE.Scene();
//初始化相机
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100000000);
//初始化灯光
let ambientLight = new THREE.AmbientLight(0xcccccc,0.6);
scene.add(ambientLight);
let a=1,b=0.6,c=10;
let directionalLight1 = new THREE.DirectionalLight(0xffffff,b);
directionalLight1.position.set(-a,-a,a*c).normalize();
let directionalLight2 = new THREE.DirectionalLight(0xffffff,b);
directionalLight2.position.set(a,-a,-a*c).normalize();
let directionalLight3 = new THREE.DirectionalLight(0xffffff,b);
directionalLight3.position.set(-a,a,-a*c).normalize();
let directionalLight4 = new THREE.DirectionalLight(0xffffff,b);
directionalLight4.position.set(a,a,a*c).normalize();
scene.add(directionalLight1);
scene.add(directionalLight2);
scene.add(directionalLight3);
scene.add(directionalLight4);
//初始化控制器
controls = new THREE.OrbitControls(camera);
controls.rotateSpeed = 3;
controls.zoomSpeed = 3;
controls.panSpeed = 3;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
//初始化性能检测器
stats = new Stats();
document.body.appendChild( stats.dom );
(4)加载OBJ模型,模型的加载比例均为1,通过计算确定摄像机z坐标的数值,确保无论任何模型都能够在页面中合理的显示。
OBJ和MTL是相互配合的两种格式,经常一起使用:在obj文件中,模型由三角面片组成,存储着三角面片的顶点、纹理坐标、顶点法向量、面等数据;在mtl文件中,存储着模型的材质数据。
//加载模型文件obj+mtl
let objloader = new THREE.OBJLoader();
let mtlloader = new THREE.MTLLoader();
//文件路径
Add(objloader,mtlloader, "models/blue");
function Add(objloader,mtlloader, name) {
mtlloader.load(name + '.mtl', function (materials) {
materials.preload();
objloader.setMaterials(materials);
objloader.load(name + '.obj', function (object) {
object.scale.set(1, 1, 1);
object.traverse(function (child) {
child.castShadow = true;
child.receiveShadow = true;
objects.push(child);
}, {
normalizeRGB: true});
object.position.set(0,0,0);
let bbox = new THREE.Box3().setFromObject(object);
x=bbox.max.x-bbox.min.x;
y=bbox.max.y-bbox.min.y;
z=bbox.max.z-bbox.min.z;
object.position.set(-(bbox.max.x+bbox.min.x)/2,
-(bbox.max.y+bbox.min.y)/2,
-(bbox.max.z+bbox.min.z)/2);
if(y/x>=i)
{
let h=y;
let Fov= camera.fov * Math.PI/180;
m=h/(2 * Math.tan(Fov* 0.5) );
camera.position.y = 0;
camera.position.z = 2*m+(z/2);
camera.position.x = 0;
}
else
{
let w=x;
let h=w*i;
let Fov= camera.fov * Math.PI/180;
m=h/(2 * Math.tan(Fov* 0.5) );
camera.position.y = 0;
camera.position.z = 2*m+(z/2);
camera.position.x = 0;
}
group.add(object);
scene.add(group);
});
});
}
(5)模型拖拽
//模型拖拽
dragControls = new THREE.DragControls(objects, camera, renderer.domElement);
dragControls.addEventListener('dragstart', function () {
controls.enabled = false;
});
dragControls.addEventListener('dragend', function () {
controls.enabled = true;
});
(6)屏幕自适应以及渲染循环
//屏幕自适应
window.addEventListener('resize', onWindowResize, false);
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
//帧循环
function animate() {
requestAnimationFrame(animate);
render();
}
//渲染循环
function render() {
camera.updateProjectionMatrix();
controls.update();
renderer.render(scene, camera)
stats.update();
}
下面为完整代码:
<!DOCTYPE html>
<html lang="en">
<head>
<title>models</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
font-family: Monospace;
background-color: #fff;
color: #000;
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<script src="../build/three.js"></script>
<script src="js/loaders/MTLLoader.js"></script>
<script src="js/loaders/OBJLoader.js"></script>
<script src="js/controls/TrackballControls.js"></script>
<script src="js/controls/DragControls.js"></script>
<script src="js/libs/stats.min.js"></script>
<script >
let camera, scene, renderer,controls,stats;//基本构件
let objects =[];//拖拽控制
let group = new THREE.Group();//加载obj
let i,m,x,y,z;//相机自定位
let dragControls;//模型拖拽
init();
animate();
function init() {
//初始化渲染器
renderer = new THREE.WebGLRenderer({
antialias: true});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild( renderer.domElement );
renderer.setClearColor(0x000000, 1);
//浏览器屏幕纵横比
i=window.innerHeight/window.innerWidth;
//初始化场景
scene = new THREE.Scene();
//初始化相机
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100000000);
//初始化灯光
let ambientLight = new THREE.AmbientLight(0xcccccc,0.6);
scene.add(ambientLight);
let a=1,b=0.6,c=10;
let directionalLight1 = new THREE.DirectionalLight(0xffffff,b);
directionalLight1.position.set(-a,-a,a*c).normalize();
let directionalLight2 = new THREE.DirectionalLight(0xffffff,b);
directionalLight2.position.set(a,-a,-a*c).normalize();
let directionalLight3 = new THREE.DirectionalLight(0xffffff,b);
directionalLight3.position.set(-a,a,-a*c).normalize();
let directionalLight4 = new THREE.DirectionalLight(0xffffff,b);
directionalLight4.position.set(a,a,a*c).normalize();
scene.add(directionalLight1);
scene.add(directionalLight2);
scene.add(directionalLight3);
scene.add(directionalLight4);
//初始化控制器
controls = new THREE.OrbitControls(camera);
controls.rotateSpeed = 3;
controls.zoomSpeed = 3;
controls.panSpeed = 3;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
//初始化性能检测器
stats = new Stats();
document.body.appendChild( stats.dom );
//加载模型文件obj+mtl
let objloader = new THREE.OBJLoader();
let mtlloader = new THREE.MTLLoader();
//文件路径
Add(objloader,mtlloader, "models/blue");
function Add(objloader,mtlloader, name) {
mtlloader.load(name + '.mtl', function (materials) {
materials.preload();
objloader.setMaterials(materials);
objloader.load(name + '.obj', function (object) {
object.scale.set(1, 1, 1);
object.traverse(function (child) {
child.castShadow = true;
child.receiveShadow = true;
objects.push(child);
}, {
normalizeRGB: true});
object.position.set(0,0,0);
let bbox = new THREE.Box3().setFromObject(object);
x=bbox.max.x-bbox.min.x;
y=bbox.max.y-bbox.min.y;
z=bbox.max.z-bbox.min.z;
object.position.set(-(bbox.max.x+bbox.min.x)/2,
-(bbox.max.y+bbox.min.y)/2,
-(bbox.max.z+bbox.min.z)/2);
if(y/x>=i)
{
let h=y;
let Fov= camera.fov * Math.PI/180;
m=h/(2 * Math.tan(Fov* 0.5) );
camera.position.y = 0;
camera.position.z = 2*m+(z/2);
camera.position.x = 0;
}
else
{
let w=x;
let h=w*i;
let Fov= camera.fov * Math.PI/180;
m=h/(2 * Math.tan(Fov* 0.5) );
camera.position.y = 0;
camera.position.z = 2*m+(z/2);
camera.position.x = 0;
}
group.add(object);
scene.add(group);
});
});
}
//模型拖拽
dragControls = new THREE.DragControls(objects, camera, renderer.domElement);
dragControls.addEventListener('dragstart', function () {
controls.enabled = false;
});
dragControls.addEventListener('dragend', function () {
controls.enabled = true;
});
}
//init()结束
//屏幕自适应
window.addEventListener('resize', onWindowResize, false);
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
//帧循环
function animate() {
requestAnimationFrame(animate);
render();
}
//渲染循环
function render() {
camera.updateProjectionMatrix();
controls.update();
renderer.render(scene, camera)
stats.update();
}
</script>
</body>
</html>