欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

html 页面 3D 模型渲染 --three.js

最编程 2024-03-25 22:05:30
...

前言

公司需求,让我研究一个3D渲染,把模型放到网址上去,就像国内很多网站一样像淘宝,天猫,以及大疆都有3D预览,这样网上一挂,网站就瞬间高大上一些(前提,是有3D模型的文件哦,我们公司是有3D模型的,所以我的任务就是给这个3D模型在网页上渲染出来),能360度旋转,以及放大缩小。

实现效果

product.gif

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.jsFBXLoader.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的详细介绍,如果有什么问题,也希望各位大佬们能提出宝贵的意见。

推荐阅读