前言
公司需求,让我研究一个3D渲染,把模型放到网址上去,就像国内很多网站一样像淘宝,天猫,以及大疆都有3D预览,这样网上一挂,网站就瞬间高大上一些(前提,是有3D模型的文件哦,我们公司是有3D模型的,所以我的任务就是给这个3D模型在网页上渲染出来),能360度旋转,以及放大缩小。
实现效果
three.js介绍
相信搞3D模型的,应该都会去了解three.js,总之就是一句话three.js是一个非常强大和灵活的JavaScript 3D图形库,他很适合去构建,其最大的优点也是可以与其他网页元素和JavaScript交互,从而实现高度可定制的交互式Web体验,里面有非常丰富的api文档,但学习难度也是挺高的。
实现步骤
第一步:html部分
因为我是做了一个加载动画的,代码如下
<div class="modelMax">
<!-- 3D模型存放区域 -->
<div id="model"></div>
<div id="loading">
<div class="loaddS">
<img src="/selling/loadding.gif" alt="" width="30" height="30">
<p>加载中...</p>
</div>
</div>
</div>
这里没什么好讲的,就是一个3D模型渲染区域,在加载的时候,给了一个gif也就是一个加载中的效果,这样看起来舒服一些,
第二步:css部分
.modelMax{
width: 400px;
height: 500px;
background: #ffffff;
position: relative;
margin: 0px auto;
}
#model {
width: 400px;
height: 500px;
background: #ffffff;
}
#model canvas{
display: block;
width: 400px;
height: 500px;
touch-action: none;
border: 1px solid #dedede;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
#loading {
position: absolute;
top: 0%;
left: 0%;
z-index: 999;
font-size: 24px;
font-weight: bold;
color: #333;
text-align: center;
background: rgba(0,0,0,0.9);
width: 100%;
height: 100%;
}
#loading .loaddS{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.loaddS P{
color: #ffffff;
font-size: 18px;
}
第三步,js部分
我遇到的第一个难题,three.js库的引入
threejs有很多库,你想实现一些效果,都要引入对应的js,有时候版本对不上,还会报各种错。所以非常考验人的耐心,百度上面例子还很少,参考的也不多,three.js官网,也没找到对应的案例,参考的不是很多。
第一个:three.js文件,
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script>
第二个:控制器的js(用于在3D场景中添加鼠标或触摸控制的功能,就是实现360度和放大缩小的)
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/controls/OrbitControls.min.js"></script>
第三个:渲染3D模型的js(决定着你的3D模型格式,我这里渲染的是FBX格式,也可以是,OBJ,GLB,GLTF格式的)渲染什么格式就引入对应的js
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/loaders/FBXLoader.min.js"></script>
第四个:依赖于,FBXLoader.js的js(在使用FBXLoader.js加载FBX模型时,需要依赖fflate.js来进行数据解压缩。如果没有引入fflate.js,FBXLoader.js将无法正常工作,并抛出"External library fflate.min.js required"的错误)
<script src="https://cdn.jsdelivr.net/npm/fflate@0.7.4/umd/index.min.js"></script>
第二个难题,相机位置
camera.position.set(0, 150, 500);(相机在 3D 空间中的坐标位置,分别表示相机的 X、Y、Z 轴坐标),如果你没设置好,渲染出来的模型,可能会看不见,或者位置不好,
// 初始化场景、相机、渲染器等基本配置
let container = document.getElementById("model");//获取容器
let scene = new THREE.Scene();//创建场景
let camera = new THREE.PerspectiveCamera(45, container.clientWidth / container.clientHeight, 10, 10000);//创建透视相机
camera.position.set(0, 150, 500);//设置相机位置(这个一定要设置好)
camera.zoom = 0.5; // 设置初始缩放级别
let renderer = new THREE.WebGLRenderer({ antialias: true });// 创建渲染器
// canvas大小
renderer.setSize(container.clientWidth, container.clientHeight);
//canvas背景色
renderer.setClearColor(0xffffff, 1);
container.appendChild(renderer.domElement);
初始化控制器
控制器的作用:用户交互,也就是360度旋转和放大缩小都在这里设置,
这里需要注意的是设置最大缩放级别和设置最小缩放级别也就是说,你的3D模型最大允许放大到多少,最小缩小到多少,都由这个控制controls.minDistance,controls.maxDistance, 还有一个是旋转速度controls.rotateSpeed = 1,值在0到1之间,看你需求,通俗的讲,这个控制的是鼠标的灵敏度,越小,旋转的角度就越小。
以下代码是控制器的设置
let controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.rotateSpeed = 1;// 旋转速度
controls.enablePan = false;
controls.minDistance =200;//设置最大缩放级别
controls.maxDistance = 1500;//设置最小缩放级别
controls.target.set(0, 50, 0); // 新增:将目标点设置在模型中心位置
controls.update(); // 新增:更新控制器以确保更改生效
初始化灯光
灯光的作用:场景更加真实、细节更加清晰
以下代码是灯光的设置
let light = new THREE.HemisphereLight(0x555555, 0xf0f0ff);
light.position.set(0, 200, 0);// 设置光源位置
scene.add(light);// 添加到场景中
light = new THREE.DirectionalLight(0xffffff);
light.position.set(0, 200, 100);
light.castShadow = true;// 启用阴影
light.intensity = 0.5;//光强
light.shadow.camera.top = 180;// 设置阴影相机参数
light.shadow.camera.bottom = -100;
light.shadow.camera.left = -120;
light.shadow.camera.right = 120;
scene.add(light);
加载3D模型
这里作用就非常明确了,加载你的3D模型。
这里需要注意的是,前文说到的3D格式有很多种,渲染不同的格式,需要用到不同的api和js, 下面注释中解释的很清楚了
let fbxLoader = new THREE.FBXLoader();// 创建FBXLoader加载器(不同格式的3D模型,这里不一样,GLTF的则是:THREE.GLTFLoader())
//let gltfLoader = new THREE.GLTFLoader();
//gltfLoader.load('b.gltf', function (gltf) { (加载Gltf格式的写法)
let animationMixer;
fbxLoader.load('/selling/54D1.fbx', function (object) {
// object.position.y = 10;(设置模型的位置)
object.traverse((child) => {
if (child.isMesh && child.geometry && child.geometry.computeFaceNormals) {
// 使用 MeshStandardMaterial 来渲染模型
child.material = new THREE.MeshStandardMaterial({
color: child.material.color,
metalness: 0.7,
roughness: 0.2
});
// 标记需要重新计算顶点法向量
child.geometry.computeFaceNormals();
child.geometry.computeVertexNormals();
}
})
// 调整模型位置使其居中
centerObject(object);
scene.add(object);
// 移除加载动画
let loadingElement = document.getElementById("loading");
loadingElement.parentElement.removeChild(loadingElement);
},function(xhr) {
console.log((xhr.loaded / xhr.total * 100) + '% loaded');
},function(error) {
console.error('Error loading model:', error);
});
给模型自动渲染到容器的中间
作用就是模型渲染的位置
// 将物体中心点移动到原点
function centerObject(object) {
let bbox = new THREE.Box3().setFromObject(object);
let center = bbox.getCenter(new THREE.Vector3());
object.position.sub(center);
}
最后开始场景渲染
// 渲染场景
function render() {
// 手动设置相机缩放属性
camera.updateProjectionMatrix();
requestAnimationFrame(render);
controls.update();
renderer.render(scene, camera);
}
render();
到这里,模型就能渲染出来了,这样一讲是不是很简单,但其中还是遇到过挺多问题的,但最后也都解决了,下一篇将会出一个vue2版本的3D模型加载。
附上全部代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>3D模型渲染</title>
<style>
.modelMax{
width: 400px;
height: 500px;
background: #ffffff;
position: relative;
margin: 0px auto;
}
#model {
width: 400px;
height: 500px;
background: #ffffff;
}
#model canvas{
display: block;
width: 400px;
height: 500px;
touch-action: none;
border: 1px solid #dedede;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
#loading {
position: absolute;
top: 0%;
left: 0%;
z-index: 999;
font-size: 24px;
font-weight: bold;
color: #333;
text-align: center;
background: rgba(0,0,0,0.9);
width: 100%;
height: 100%;
}
#loading .loaddS{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.loaddS P{
color: #ffffff;
font-size: 18px;
}
/* @keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
} */
</style>
</head>
<body>
<div class="modelMax">
<!-- 模型存放区域 -->
<div id="model"></div>
<div id="loading">
<div class="loaddS">
<img src="/selling/loadding.gif" alt="" width="30" height="30">
<p>加载中...</p>
</div>
</div>
</div>
<!-- 加载three.js -->
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script>
<!-- 加载OrbitControls.min.js -->
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/controls/OrbitControls.min.js"></script>
<!-- 加载FBXLoader.min.js -->
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/loaders/FBXLoader.min.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/loaders/GLTFLoader.min.js"></script> -->
<script src="https://cdn.jsdelivr.net/npm/fflate@0.7.4/umd/index.min.js"></script>
<script>
// 初始化场景、相机、渲染器等基本配置
let container = document.getElementById("model");//获取容器
let scene = new THREE.Scene();//创建场景
let camera = new THREE.PerspectiveCamera(45, container.clientWidth / container.clientHeight, 10, 10000);//创建透视相机
camera.position.set(0, 150, 500);//设置相机位置(相机在 3D 空间中的坐标位置)
camera.zoom = 0.5; // 设置初始缩放级别
let renderer = new THREE.WebGLRenderer({ antialias: true });// 创建渲染器
// canvas大小
renderer.setSize(container.clientWidth, container.clientHeight);
//canvas背景色
renderer.setClearColor(0xffffff, 1);
container.appendChild(renderer.domElement);
// 初始化控制器
let controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.rotateSpeed = 1;// 旋转速度
controls.enablePan = false;
controls.minDistance =200;//设置最大缩放级别
controls.maxDistance = 1500;//设置最小缩放级别
controls.target.set(0, 50, 0); // 新增:将目标点设置在模型中心位置
controls.update(); // 新增:更新控制器以确保更改生效
// 初始化灯光
let light = new THREE.HemisphereLight(0x555555, 0xf0f0ff);
light.position.set(0, 200, 0);// 设置光源位置
scene.add(light);// 添加到场景中
light = new THREE.DirectionalLight(0xffffff);
light.position.set(0, 200, 100);
light.castShadow = true;// 启用阴影
light.intensity = 0.5;//光强
light.shadow.camera.top = 180;// 设置阴影相机参数
light.shadow.camera.bottom = -100;
light.shadow.camera.left = -120;
light.shadow.camera.right = 120;
scene.add(light);
// 加载3D模型
let fbxLoader = new THREE.FBXLoader();// 创建FBXLoader加载器(不同格式的3D模型,这里不一样,GLTF的则是:THREE.GLTFLoader())
//let gltfLoader = new THREE.GLTFLoader();
//gltfLoader.load('b.gltf', function (gltf) {
let animationMixer;
fbxLoader.load('/selling/54D1.fbx', function (object) {
// object.position.y = 10;
object.traverse((child) => {
if (child.isMesh && child.geometry && child.geometry.computeFaceNormals) {
// 使用 MeshStandardMaterial 来渲染模型
child.material = new THREE.MeshStandardMaterial({
color: child.material.color,
metalness: 0.7,
roughness: 0.2
});
// 标记需要重新计算顶点法向量
child.geometry.computeFaceNormals();
child.geometry.computeVertexNormals();
}
});
// 调整模型位置使其居中
centerObject(object);
scene.add(object);
// 移除加载动画
let loadingElement = document.getElementById("loading");
loadingElement.parentElement.removeChild(loadingElement);
},
function(xhr) {
console.log((xhr.loaded / xhr.total * 100) + '% loaded');
},
function(error) {
console.error('Error loading model:', error);
}
);
// 将物体中心点移动到原点
function centerObject(object) {
let bbox = new THREE.Box3().setFromObject(object);
let center = bbox.getCenter(new THREE.Vector3());
object.position.sub(center);
}
// 渲染场景
function render() {
// 手动设置相机缩放属性
camera.updateProjectionMatrix();
requestAnimationFrame(render);
controls.update();
renderer.render(scene, camera);
}
render();
</script>
</body>
</html>
结语
最后也是说下,本文为什么不把threejs的库下载到本地来,然后再引用,这也是迫于无奈,threejs里面有es6的语法,然后再加上积成了很多其他的库,你引入进来,会报很多的错误,后面就只能引用cdn链接。
还有需要注意的是,threejs版本一定要对上,不能穿插版本,因为有时候一些新版本中,有些api已经舍弃了,建议都用最新的。
以上就是本篇文章的全部内容,希望对大家的学习有所帮助,以上就是关于html页面3D模型渲染之——three.js的详细介绍,如果有什么问题,也希望各位大佬们能提出宝贵的意见。