本文详细介绍如何使用Three.js在网页中展示3D模型。从初始化WebGL渲染器、配置透视相机,到加载STL模型文件及设置多光源,提供了完整的代码实现。此外,还包含通过TrackballControls实现模型旋转、缩放交互控制的方法,以及获取模型边界框尺寸的技巧,适合前端开发者快速上手3D可视化项目。
Three.js 3D模型展示与交互控制完整示例
鉴于项目中有展示3D模型的需求,我对Three.js进行了一些探索与实践,现将相关经验整理分享,期望能为有类似需求的开发者提供帮助。
首先展示最终的运行效果:
整体而言,Three.js的学习曲线相对平缓,只要耐心研读文档并动手实践,能够较快掌握其核心用法。
初始化渲染器与画布设置
第一步,需要在网页中创建一个用于渲染3D模型的画布,这对应于Three.js的初始化过程。
var WIDTH,HEIGHT;
var renderer;
function initThree() {
WIDTH = document.documentElement.clientWidth/2; <!--{foreach from=$recommended_goods item=rgoods}--> <!-- {/foreach} -->
HEIGHT = document.documentElement.clientHeight/2;
/* 渲染器 */
renderer = new THREE.WebGLRenderer();
renderer.setSize(WIDTH , HEIGHT);
renderer.setClearColor(new THREE.Color(0x66666));
renderer.gammaInput = true;
renderer.gammaOutput = true;
document.body.appendChild(renderer.domElement);
}
从上述代码可以看出,我们在body元素中动态添加了一块画布区域,其宽高设置为浏览器可视窗口宽度与高度的一半,背景颜色设定为0x66666。需要特别注意的是,实例化渲染器的代码为renderer = new THREE.WebGLRenderer();,后续所有的渲染设置均基于该renderer对象进行配置。
摄像机视角配置
接下来,我们需要调整摄像机的参数,以控制观察视角。
/* 摄像头 */
var camera;
function initCamera() {
var VIEW_ANGLE = 45,
ASPECT = WIDTH / HEIGHT,
NEAR = 0.1,
FAR = 10000;
camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
camera.position.set(20, 0, 0);
//设置视野的中心坐标
camera.lookAt(scene.position);
}
相关代码主要用于设定视觉角度,具体的数值参数可根据实际项目需求在后期进行灵活调整。
场景与灯光加载
随后是场景的加载步骤:
/* 场景 */
var scene;
function initScene() {
scene = new THREE.Scene();
}
接着配置灯光效果,以增强模型的立体感。
/* 灯光 */
var light,light2,light3;
function initLight() {
//平行光
light = new THREE.DirectionalLight(0xFFFFFF);
light.position.set(0, 99, 0).normalize();
scene.add(light);
//环境光
light2 = new THREE.AmbientLight(0x999999);
scene.add(light2);
//点光源
light3 = new THREE.PointLight(0x00FF00);
light3.position.set(300, 0, 0);
scene.add(light3);
}
STL模型加载与显示
最后是模型对象的显示处理:
/* 显示对象 */
var cube;
function initObject(){
// ASCII file
var loader = new THREE.STLLoader();
loader.addEventListener( 'load', function ( event ) {
var loading = document.getElementById("Loading");
loading.parentNode.removeChild(loading);
var geometry = event.content;
//砖红色
var material = new THREE.MeshPhongMaterial( { ambient: 0xff5533, color: 0xff5533, specular: 0x111111, shininess: 200 } );
//纯黑色
// var material = new THREE.MeshBasicMaterial( { envMap: THREE.ImageUtils.loadTexture( 'http://localhost:8080/textures/metal.jpg', new THREE.SphericalReflectionMapping() ), overdraw: true } ) ;
//粉色 带阴影
// var material = new THREE.MeshLambertMaterial( { color:0xff5533, side: THREE.DoubleSide } );
//灰色
// var material = new THREE.MeshLambertMaterial({color: 000000}); //材质设定 (颜色)
var mesh = new THREE.Mesh( geometry, material );
var center = THREE.GeometryUtils.center(geometry);
var boundbox=geometry.boundingBox;
var vector3 = boundbox.size(null);
var vector3 = boundbox.size(null);
console.log(vector3);
var scale = vector3.length();
camera.position.set(scale, 0, 0);
camera.lookAt(scene.position);
scene.add(camera);
//利用一个轴对象以可视化的3轴以简单的方式。X轴是红色的。Y轴是绿色的。Z轴是蓝色的。这有助于理解在空间的所有三个轴的方向。
var axisHelper = new THREE.AxisHelper(800);
scene.add(axisHelper);
//周围边框
bboxHelper = new THREE.BoxHelper();
bboxHelper.visible = true;
var meshMaterial = material;
mainModel = new THREE.Mesh(geometry, meshMaterial);
bboxHelper.update(mainModel);
bboxHelper.geometry.computeBoundingBox();
scene.add(bboxHelper);
//地板网格
// var gridHelper = new THREE.GridHelper(500, 40); // 500 is grid size, 20 is grid step
// gridHelper.position = new THREE.Vector3(0, 0, 0);
// gridHelper.rotation = new THREE.Euler(0, 0, 0);
// scene.add(gridHelper);
// var gridHelper2 = gridHelper.clone();
// gridHelper2.rotation = new THREE.Euler(Math.PI / 2, 0, 0);
// scene.add(gridHelper2);
// var gridHelper3 = gridHelper.clone();
// gridHelper3.rotation = new THREE.Euler(Math.PI / 2, 0, Math.PI / 2);
// scene.add(gridHelper3);
//
// var grid = new THREE.GridHelper(300, 40, 25, [0, 0, 1], 0x000055, 0.2, true, "#FFFFFF", "left");
// scene.add(grid);
var x = (boundbox.max.x - boundbox.min.x).toFixed(2);
var y = (boundbox.max.y - boundbox.min.y).toFixed(2);
var z = (boundbox.max.z - boundbox.min.z).toFixed(2);
console.log(x);
console.log(y);
console.log(z);
console.log(boundbox);
mesh.position.set(0,0,0);
// mesh.position.x = scene.position.x;
// mesh.position.y = scene.position.y ;
// mesh.position.z = scene.position.z;
scene.add(mesh);
renderer.clear();
renderer.render(scene, camera);
} );
loader.load( '3dfile/莫比乌斯环.STL' );
}
根据所加载的文件类型,需引入对应的JavaScript库。本例中加载的是STL格式模型,因此引入了STLLoader.js文件。
<script src="js/STLLoader.js"></script>
若需在场景中显示网格标尺,只需取消相关代码段的注释即可启用。
交互控制与初始调用
接下来介绍控制逻辑(尽管显示代码中未直接包含键盘按键放大缩小的实现,但仍提供此部分代码供参考):
//控制
var effect;
var controls;
function initControl(){
effect = new THREE.AsciiEffect( renderer );
effect.setSize( WIDTH, HEIGHT );
controls = new THREE.TrackballControls( camera,renderer.domElement);
}
最后一步是执行初始调用,启动整个3D场景。
function animate() {
requestAnimationFrame( animate );
controls.update();
effect.render( scene, camera );
}
function threeStart() {
initThree();
initScene();
initCamera();
initLight();
initObject();
initControl();
animate();
}
以下是完整的实现代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>WebGL</title>
<script type="text/javascript" charset="utf-8" src="js/three.js"></script>
<script src="js/STLLoader.js"></script>
<script src="js/TrackballControls.js"></script>
<script src="js/AsciiEffect.js"></script>
<style>body{overflow:hidden;background:#eee}</style>
</head>
<script>
var WIDTH,HEIGHT;
var renderer;
function initThree() {
WIDTH = document.documentElement.clientWidth/2; <!--{foreach from=$recommended_goods item=rgoods}--> <!-- {/foreach} -->
HEIGHT = document.documentElement.clientHeight/2;
/* 渲染器 */
renderer = new THREE.WebGLRenderer();
renderer.setSize(WIDTH , HEIGHT);
renderer.setClearColor(new THREE.Color(0x66666));
renderer.gammaInput = true;
renderer.gammaOutput = true;
document.body.appendChild(renderer.domElement);
}
/* 摄像头 */
var camera;
function initCamera() {
var VIEW_ANGLE = 45,
ASPECT = WIDTH / HEIGHT,
NEAR = 0.1,
FAR = 10000;
camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
camera.position.set(20, 0, 0);
//设置视野的中心坐标
camera.lookAt(scene.position);
}
/* 场景 */
var scene;
function initScene() {
scene = new THREE.Scene();
}
/* 灯光 */
var light,light2,light3;
function initLight() {
//平行光
light = new THREE.DirectionalLight(0xFFFFFF);
light.position.set(0, 99, 0).normalize();
scene.add(light);
//环境光
light2 = new THREE.AmbientLight(0x999999);
scene.add(light2);
//点光源
light3 = new THREE.PointLight(0x00FF00);
light3.position.set(300, 0, 0);
scene.add(light3);
}
/* 显示对象 */
var cube;
function initObject(){
// ASCII file
var loader = new THREE.STLLoader();
loader.addEventListener( 'load', function ( event ) {
var loading = document.getElementById("Loading");
loading.parentNode.removeChild(loading);
var geometry = event.content;
//砖红色
var material = new THREE.MeshPhongMaterial( { ambient: 0xff5533, color: 0xff5533, specular: 0x111111, shininess: 200 } );
//纯黑色
// var material = new THREE.MeshBasicMaterial( { envMap: THREE.ImageUtils.loadTexture( 'http://localhost:8080/textures/metal.jpg', new THREE.SphericalReflectionMapping() ), overdraw: true } ) ;
//粉色 带阴影
// var material = new THREE.MeshLambertMaterial( { color:0xff5533, side: THREE.DoubleSide } );
//灰色
// var material = new THREE.MeshLambertMaterial({color: 000000}); //材质设定 (颜色)
var mesh = new THREE.Mesh( geometry, material );
var center = THREE.GeometryUtils.center(geometry);
var boundbox=geometry.boundingBox;
var vector3 = boundbox.size(null);
var vector3 = boundbox.size(null);
console.log(vector3);
var scale = vector3.length();
camera.position.set(scale, 0, 0);
camera.lookAt(scene.position);
scene.add(camera);
//利用一个轴对象以可视化的3轴以简单的方式。X轴是红色的。Y轴是绿色的。Z轴是蓝色的。这有助于理解在空间的所有三个轴的方向。
var axisHelper = new THREE.AxisHelper(800);
scene.add(axisHelper);
//周围边框
bboxHelper = new THREE.BoxHelper();
bboxHelper.visible = true;
var meshMaterial = material;
mainModel = new THREE.Mesh(geometry, meshMaterial);
bboxHelper.update(mainModel);
bboxHelper.geometry.computeBoundingBox();
scene.add(bboxHelper);
//地板网格
// var gridHelper = new THREE.GridHelper(500, 40); // 500 is grid size, 20 is grid step
// gridHelper.position = new THREE.Vector3(0, 0, 0);
// gridHelper.rotation = new THREE.Euler(0, 0, 0);
// scene.add(gridHelper);
// var gridHelper2 = gridHelper.clone();
// gridHelper2.rotation = new THREE.Euler(Math.PI / 2, 0, 0);
// scene.add(gridHelper2);
// var gridHelper3 = gridHelper.clone();
// gridHelper3.rotation = new THREE.Euler(Math.PI / 2, 0, Math.PI / 2);
// scene.add(gridHelper3);
//
// var grid = new THREE.GridHelper(300, 40, 25, [0, 0, 1], 0x000055, 0.2, true, "#FFFFFF", "left");
// scene.add(grid);
var x = (boundbox.max.x - boundbox.min.x).toFixed(2);
var y = (boundbox.max.y - boundbox.min.y).toFixed(2);
var z = (boundbox.max.z - boundbox.min.z).toFixed(2);
console.log(x);
console.log(y);
console.log(z);
console.log(boundbox);
mesh.position.set(0,0,0);
// mesh.position.x = scene.position.x;
// mesh.position.y = scene.position.y ;
// mesh.position.z = scene.position.z;
scene.add(mesh);
renderer.clear();
renderer.render(scene, camera);
} );
loader.load( '3dfile/莫比乌斯环.STL' );
}
//控制
var effect;
var controls;
function initControl(){
effect = new THREE.AsciiEffect( renderer );
effect.setSize( WIDTH, HEIGHT );
controls = new THREE.TrackballControls( camera,renderer.domElement);
}
function animate() {
requestAnimationFrame( animate );
controls.update();
effect.render( scene, camera );
}
function threeStart() {
initThree();
initScene();
initCamera();
initLight();
initObject();
initControl();
animate();
}
</script>
<body onload="threeStart()">
<div id="Loading" style="color:#fff">Loading...</div>
</body>
</html>
此外,附上项目的文件目录结构图:

如需获取所有相关文件,欢迎在评论区留言。

补充说明:由于在显示模型的方法中加入了bboxHelper = new THREE.BoxHelper(),我们可以直接获取模型在X、Y、Z三个轴向上的尺寸数据,这等同于获取模型的长、宽、高信息。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。