3D模型工具栏-VR效果

简介: 3D模型工具栏-VR效果

工具栏-VR效果代码展示:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
    <title>工具栏-VR效果</title>
    <script src="./js/three.js"></script>
    <script src="./js/OBJLoader.js"></script>
    <script src="./js/OrbitControls.js"></script>
    <script src="./js/StereoEffect.js"></script>
    <style>
      /* 初始化页面 */
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
      /* 设置渲染容器的大小 */
      #webgl {
        width: 100%;
        height: 100vh;
        overflow: hidden;
      }
      /* 设置最大元素宽高 */
      .content{
        width: 100%;
        height: 100vh;
        position: relative;
      }
      /* 将工具栏定位到页面右下角 */
      .tool{
        position: absolute;
        bottom: 0;
        right: 0;
        margin-right: 20px;
        display: flex;
        user-select: none;
      }
      /* 设置每一个块的大小 */
      .tool-item{
        width: 35px;
        height: 35px;
        padding: 5px;
        position: relative;
      }
      /* 设置小图标黑色背景 */
      .tool-img{
        display: flex;
        justify-content: center;
        align-items: center;
        width: 25px;
        height: 25px;
        border-radius: 100%;
        background-color: rgba(0,0,0,0.8);
      }
      /* 设置小图标大小 */
      .tool-img img{
        width: 13px;
        height: 13px;
      }
      /* 设置小图标的提示文本 */
      .tool-text{
        display: block;
        line-height: 20px;
        padding: 6px 0px;
        border-radius: 5px;
        background-color: #000000;
        font-size: 12px;
        text-align: center;
        color: #FFFFFF;
        position: absolute;
        bottom: 40px;
        left: 50%;
        display: none;
      }
      /* 设置提示文本下边的小三角 */
      .tool-item i{
        display: none;
        width: 10px;
        height: 10px;
        background-color: #000000;
        position: absolute;
        bottom: 36px;
        left: 50%;
        margin-left: -5px;
        transform: rotate(45deg);
      }
      /* 鼠标放到每一块上显示提示文本 */
      .tool-item:hover .tool-text{
        display: block;
      }
      /* 鼠标放到每一块上显示小三角 */
      .tool-item:hover i{
        display: block;
      }
      /* 设置查看弹窗大小和位置 */
      .popout{
        width: 160px;
        position: absolute;
        bottom: 40px;
        background-color: #000000;
        padding: 6px 0;
        border-radius: 10px;
        font-size: 12px;
        color: #FFFFFF;
        right: -30px;
        z-index: 100;
      }
      /* 设置查看弹窗下边的小三角 */
      .popout-arrow{
        display: block;
        width: 10px;
        height: 10px;
        background-color: #000000;
        position: absolute;
        bottom: 36px;
        left: 50%;
        margin-left: -5px;
        transform: rotate(45deg);
      }
      /* 设置弹窗中每行的大小 */
      .popout p{
        width: 90%;
        height: 30px;
        margin-left: 5%;
        display: flex;
        justify-content: space-between;
        line-height: 30px;
      }
      /* 设置弹窗中每行箭头的大小 */
      .popout>p img{
        width: 12px;
        height: 12px;
        margin: 9px 0px;
      }
      /* 设置弹窗中的分界线 */
      .popout em{
        display: block;
        width: 100%;
        height: 1px;
        background-color: #525252;
      }
      /* 设置弹窗中背景图的那一行 */
      .popout-list{
        width: 90%;
        height: 30px;
        margin-left: 5%;
        padding: 5px 0;
        display: flex;
      }
      /* 设置背景图中左右按钮的大小 */
      .popout-list>img{
        width: 14px;
        height: 14px;
        margin: 3px;
      }
      /* 设置背景图列表 */
      .popout-pic{
        display: flex;
        justify-content: space-between;
        width: calc(100% - 40px);
      }
      /* 设置背景图大小 */
      .popout-pic img{
        width: 20px;
        height: 20px;
      }
      /* 设置帮助弹窗 */
      .help{
        width: 100%;
        height: 100%;
        position: absolute;
        top: 0;
        z-index: 999;
        background-color: rgba(0,0,0,0.9);
        display: none;
      }
      /* 设置帮助弹窗标题 */
      .help h3{
        width: 100%;
        line-height: 50px;
        text-align: center;
        color: #FFFFFF;
        font-weight: 500;
      }
      /* 设置帮助弹窗关闭按钮 */
      .help-pic{
        width: 20px;
        height: 20px;
        margin: 10px;
        position: absolute;
        top: 0;
        right: 0;
      }
      /* 设置帮助弹窗提示列表 */
      .help-hint{
        width: 100%;
        display: flex;
        justify-content: space-around;
      }
      /* 设置每项提示的大小 */
      .help-item{
        width: 200px;
        display: flex;
        justify-content: center;
        flex-wrap: wrap;
      }
      /* 设置每项提示的图标 */
      .help-item img{
        width: 20px;
        height: 20px;
        margin-bottom: 5px;
      }
      /* 设置每项提示的文字 */
      .help-hint p{
        width: 100%;
        list-style: 30px;
        color: #FFFFFF;
        text-align: center;
        font-size: 14px;
      }
      /* 设置重置相机按钮 */
      .help-button{
        width: 150px;
        height: 30px;
        border-radius: 5px;
        background-color: #FFFFFF;
        color: #2489F3;
        line-height: 30px;
        text-align: center;
        position: absolute;
        bottom: 10px;
        left: 50%;
        margin-left: -100px;
      }
    </style>
  </head>
  <body>
    <div class="content">
      <!-- 渲染容器 -->
      <div id="webgl"></div>
      <!-- 工具栏 -->
      <div class="tool">
        <div class="tool-item">
          <span class="tool-text" id="rotate-text" style="width: 70px;margin-left: -35px;">开启旋转</span>
          <i></i>
          <div class="tool-img" onclick="rotates()">
            <img src="./img/旋转.png" alt="">
          </div>
        </div>
        <div class="tool-item">
          <span class="tool-text" id="AR-text" style="width: 70px;margin-left: -35px;">开启AR</span>
          <i></i>
          <div class="tool-img" onclick="ARs()">
            <img src="./img/AR.png" alt="">
          </div>
        </div>
        <div class="tool-item">
          <span class="tool-text" style="width: 50px;margin-left: -25px;">帮助</span>
          <i></i>
          <div class="tool-img" onclick="helpShow()">
            <img src="./img/帮助.png" alt="">
          </div>
        </div>
        <div class="tool-item">
          <span class="tool-text" style="width: 70px;margin-left: -35px;">查看方式</span>
          <i></i>
          <div onclick="look()" class="tool-img">
            <img src="./img/查看.png" alt="">
          </div>
          <!-- 查看弹窗 -->
          <div class="popout" style="display: none;">
            <p onclick="unfold(event)"><span>展示模式</span><img src="img/展开.png" alt=""></p>
            <p onclick="zoom()" style="padding-left: 10px;display: none;"><span>允许缩放</span><img class="zoomImg" src="./img/对.png" alt=""></p>
            <em></em>
            <p onclick="unfold(event)"><span>背景切换</span><img src="./img/展开.png" alt=""></p>
            <div class="popout-list" style="display: none;">
              <img onclick="picLeft()" src="./img/左箭头.png" alt="">
              <div class="popout-pic"></div>
              <img onclick="picRight()" src="./img/右箭头.png" alt="">
            </div>
          </div>
          <b class="popout-arrow" style="display: none;"></b>
        </div>
        <div class="tool-item">
          <span class="tool-text" id="VR-text" style="width: 70px;margin-left: -35px;">开启VR</span>
          <i></i>
          <div class="tool-img" onclick="openVR()">
            <img src="./img/VR.png" alt="">
          </div>
        </div>
        <div class="tool-item">
          <span class="tool-text" style="width: 70px;margin-left: -35px;">开启全屏</span>
          <i></i>
          <div class="tool-img">
            <img src="./img/全屏.png" alt="">
          </div>
        </div>
      </div>
      <!-- 帮助弹窗 -->
      <div class="help">
        <h3>操作导航</h3>
        <img class="help-pic" src="./img/错.png" alt="" onclick="helpHide()">
        <div class="help-hint">
          <div class="help-item">
            <img src="./img/拖动.png" alt="">
            <p style="margin-bottom: 10px;">旋转</p>
            <p>单击 + 拖动</p>
            <p>或一根手指拖动</p>
          </div>
          <div class="help-item">
            <img src="./img/变焦.png" alt="">
            <p style="margin-bottom: 10px;">变焦</p>
            <p>滚轮或两根手指</p>
            <p>向内或向外分开</p>
          </div>
          <div class="help-item">
            <img src="./img/移动.png" alt="">
            <p style="margin-bottom: 10px;">移动</p>
            <p>右击 + 拖动</p>
            <p>或三根手指拖动</p>
          </div>
        </div>
        <div class="help-button" onclick="helpReset()">重置相机视图</div>
      </div>
    </div>
    <script>
      // 获取渲染容器
      let webgl = document.getElementById("webgl");
      // 获取渲染容器的宽高
      let webglWidth = webgl.offsetWidth;
      let webglHeight = webgl.offsetHeight;
      // 创建场景
      let scene = new THREE.Scene();
      // 设置环境光(十六进制颜色)
      let ambient = new THREE.AmbientLight(0x444444);
      // 将环境光添加到场景中
      scene.add(ambient);
      // 设置点光源(十六进制颜色)
      let point = new THREE.PointLight(0xffffff);
      // 设置点光源的位置(x轴, y轴, z轴)
      point.position.set(400, 200, 300); 
      // 将点光源添加到场景中
      scene.add(point);
      // 创建透视相机(角度, 宽高比, 最近距离, 最远距离)
      let camera = new THREE.PerspectiveCamera(60,webglWidth/webglHeight,0.1,2000);
      // 设置相机的位置(x轴, y轴, z轴)
      camera.position.set(100, 100, 100); 
      // 将相机指向场景中心
      camera.lookAt(scene.position);
      // 创建渲染器
      let renderer = new THREE.WebGLRenderer();
      // 设置渲染器的初始颜色(十六进制颜色, 透明度)
      renderer.setClearColor(0xEEEEEE,1);
      // 设置渲染器大小(标签宽度, 标签高度)
      renderer.setSize(webglWidth,webglHeight);
      // 将渲染器添加到渲染容器中(渲染器元素)
      webgl.appendChild(renderer.domElement);
      // 创建旋转状态
      let rotateStatus = false;
      // 创建 VR 渲染
      let VR = new THREE.StereoEffect(renderer);
      // 创建 VR 开启状态,默认 false 表示不开启
      let VRStatus = false;
      // 创建渲染函数
      function render(){
        // 判断旋转状态为 ture 则执行
        if(rotateStatus){
          // 设置场景旋转速度
          scene.rotation.y += 0.002;
        }
        // 判断 VR 开启状态为 true 则开启
        if(VRStatus){
          // 设置 VR 渲染器的宽高
          VR.setSize(webglWidth,webglHeight);
          // 通过 VR 渲染场景和相机
          VR.render(scene, camera);
        }else{
          // 渲染场景和相机(场景, 相机)
          renderer.render(scene,camera);
        }
        // 重复执行渲染函数
        requestAnimationFrame(render);
      }
      // 调用渲染函数
      render();
      // 设置窗口变化自适应调整事件
      window.onresize = function(){
        // 重新获取渲染容器的宽高
        webglWidth = webgl.offsetWidth;
        webglHeight = webgl.offsetHeight;
        // 重置渲染器canvas画布大小
        renderer.setSize(webglWidth,webglHeight);
        // 重置相机显示范围的宽高比
        camera.aspect = webglWidth/webglHeight;
        // 更新相机的投影矩阵
        camera.updateProjectionMatrix();
        // 重新调用渲染函数
        render();
      };
      // 创建 OBJ 模型加载器
      let loader = new THREE.OBJLoader();
      // 加载 OBJ 文件
      loader.load('./img/鹤.OBJ', function(obj) {
        // 加载纹理贴图
        let texture = new THREE.TextureLoader().load('./img/he.png',function(){
          render(); // 加载成功后重新调用渲染函数
        });
        // 给 OBJ 模型设置纹理贴图
        obj.children[0].material = new THREE.MeshBasicMaterial({ map: texture });
        // 将 OBJ 模型添加到场景中
        scene.add(obj);
        // 设置 OBJ 模型居中
        obj.children[0].geometry.center();
        // 设置 OBJ 模型缩放大小
        obj.children[0].scale.set(100, 100, 100);
      })
      // 鼠标操作三维场景
      let controls = new THREE.OrbitControls(camera,renderer.domElement);
      // 查看弹窗显示隐藏事件
      function look(){
        // 获取查看弹窗
        let popout = document.getElementsByClassName("popout")[0];
        // 获取查看弹窗下边的小三角
        let arrow = document.getElementsByClassName("popout-arrow")[0];
        // 判断弹窗是否隐藏
        if(popout.style.display == "none"){
          // 如果隐藏,显示弹窗和小三角
          popout.style.display = "block";
          arrow.style.display = "block";
        }else{
          // 如果显示,则隐藏弹窗和小三角
          popout.style.display = "none";
          arrow.style.display = "none";
        }
      }
      // 查看弹窗功能展开事件
      function unfold(e){
        // 获取事件目标对象
        e = e || window.event;
        let targets = e.target || e.srcElement;
        // 创建变量用于存储下一个同级元素
        let nexts;
        // 创建变量用于存储当前 p 标签内的 img 标签
        let pImg;
        // 当前目标元素是否为 p 标签
        if(targets.nodeName == "P"){
          // 如果为p标签,存储当前目标元素的下一个同级元素
          nexts = targets.nextElementSibling;
          // 存储当前 p 标签内的 img 标签
          pImg = targets.getElementsByTagName("img")[0];
        }else{
          // 如果不是p标签,存储当前目标元素的父元素的下一个同级元素
          nexts = targets.parentElement.nextElementSibling;
          // 存储当前目标元素的父元素内的 img 标签
          pImg = targets.parentElement.getElementsByTagName("img")[0];
        }
        // 判断下一个同级元素是否隐藏
        if(nexts.style.display == "none"){
          // 如果隐藏则显示
          nexts.style.display = "flex";
          // 设置小图标箭头朝下
          pImg.style.transform = "rotate(90deg)";
        }else{
          // 如果显示则隐藏
          nexts.style.display = "none";
          // 设置小图标箭头朝右
          pImg.style.transform = "rotate(0deg)";
        }
      }
      // 模拟背景图片数据
      // 设置状态 1 表示正常图片,2 表示 3D 背景图
      let arr = [
        {img:"./img/pic1.jpg",status:1},
        {img:"./img/pic2.webp",status:1},
        {img:"./img/pic3.webp",status:1},
        {img:"./img/pic4.webp",status:1},
        {img:"./img/pic5.jpg",status:2},
      ];
      // 输出背景图数据
      // 创建起始值,表示从第几张图片开始输出
      let begin = 0;
      // 创建结束值,表示到第几张图片输出结束
      // 并且判断图片数量是否大于等于4,如果大于设置为 4,否则设置为图片的数量
      let record = arr.length >= 4 ? 4 : arr.length;
      // 创建背景图渲染函数
      function picRender(){
        // 创建变量,存储拼接的字符串
        let picStr = "";
        // 循环拼接数据,从 begin 开始,到 record 结束
        for(let i = begin; i < record; i++){
          picStr += `<img src="${arr[i].img}" alt="${arr[i].status}" >`;
        }
        // 向页面输出数据
        document.getElementsByClassName("popout-pic")[0].innerHTML = picStr;
      }
      // 调用背景图渲染函数
      picRender();
      // 背景图切换-左按钮事件
      function picLeft(){
        // 判断起始值是否大于 0
        if(begin > 0){
          // 如果大于 0 让 起始值 和 结束值 都减一
          record--;
          begin--;
          // 重新调用背景图渲染函数
          picRender();
        }
      }
      // 背景图切换-右按钮事件
      function picRight(){
        // 判断图片数量减起始值是否大于 4
        if(arr.length - begin > 4){
          // 如果大于 4 让 起始值 和 结束值 都加一
          record++;
          begin++;
          // 重新调用背景图渲染函数
          picRender();
        }
      }
      // 创建模型旋转点击事件
      function rotates(){
        // 获取旋转提问文本
        let rotateText = document.getElementById("rotate-text");
        // 取反旋转状态
        rotateStatus = !rotateStatus;
        // 判断旋转状态是否为 true
        if(rotateStatus){
          // 如果为 true,设置提示文本为停止旋转
          rotateText.innerHTML = "停止旋转";
        }else{
          // 如果为 false,设置提示文本为开启旋转
          rotateText.innerHTML = "开启旋转";
        }
      }
      // 创建 AR 状态
      let ARStatus = false;
      // 创建 AR 开关事件
      function ARs(){
        // 获取 AR 提示文本
        let ARText = document.getElementById("AR-text");
        // 取反 AR 状态
        ARStatus = !ARStatus;
        // 判断 AR 状态是否为 true
        if(ARStatus){
          // 清除背景图
          scene.background = "";
          // 删除球几何体
          scene.remove(scene.getObjectByName("ball"));
          // 如果为 true 设置渲染器背景色为黑色
          renderer.setClearColor(0x000000,1);
          // 设置提示文本为关闭AR
          ARText.innerHTML = "关闭AR";
        }else{
          // 加载背景图
          let texture = new THREE.TextureLoader().load(arr[0].img);
          // 设置背景图
          scene.background = texture;
          // 否则设置渲染器背景色为灰色
          renderer.setClearColor(0xEEEEEE,1);
          // 设置提示文本为开启AR
          ARText.innerHTML = "开启AR";
        }
      }
      // 显示帮助弹窗
      function helpShow(){
        // 获取帮助弹窗并显示
        let help = document.getElementsByClassName("help")[0];
        help.style.display = "block";
      }
      // 隐藏帮助弹窗
      function helpHide(){
        // 获取帮助弹窗并隐藏
        let help = document.getElementsByClassName("help")[0];
        help.style.display = "none";
      }
      // 重置相机视角
      function helpReset() {
        // 获取帮助弹窗并隐藏
        let help = document.getElementsByClassName("help")[0];
        help.style.display = "none";
        // 重置相机的位置(x轴, y轴, z轴)
        camera.position.set(100, 100, 100); 
        // 重置控制器目标位置   
        controls.target.set(0, 0, 0);
        // 更新控制器
        controls.update();
        // 重新调用渲染函数
        render();
      }
      // 创建禁止缩放状态
      let zoomStatus = false;
      // 设置是否禁止缩放事件
      function zoom(){
        // 先获取禁止缩放中的小图标
        let zoomImg = document.getElementsByClassName("zoomImg")[0];
        // 取反禁止缩放状态
        zoomStatus = !zoomStatus;
        // 取反后判断禁止缩放状态是否为 true
        if(zoomStatus){
          // 如果为 true,设置控制器禁止缩放
          controls.enableZoom = false;
          // 并修改图片为 X 的小图标
          zoomImg.src = "./img/错.png";
        }else{
          // 否则设置控制器开启缩放
          controls.enableZoom = true;
          // 并修改图片为 √ 的小图标
          zoomImg.src = "./img/对.png";
        }
      }
      // 加载默认背景图
      let texture = new THREE.TextureLoader().load(arr[0].img);
      // 设置背景图
      scene.background = texture;
      // 获取背景图容器
      let backPic = document.getElementsByClassName("popout-pic")[0];
      // 给背景图容器绑定事件委托
      backPic.addEventListener("click",function(e){
        // 获取事件源
        e = e || window.event;
        let targets = e.target || e.srcElement;
        // 判断事件源是否 img 标签
        if(targets.nodeName == "IMG"){
          // 判断是否 3D 图片,1 表示普通图片,2 表示 3D 图片
          if(targets.alt == 1){
            // 重新加载背景图
            texture = new THREE.TextureLoader().load(targets.src);
            // 设置背景图
            scene.background = texture;
            // 删除球几何体
            scene.remove(scene.getObjectByName("ball"));
            // 设置禁止缩放状态为 false
            zoomStatus = false;
            // 设置控制器开启缩放
            controls.enableZoom = true;
          }else{
            // 清除背景图
            scene.background = "";
            // 删除球几何体
            scene.remove(scene.getObjectByName("ball"));
            // 创建球体作为 3D 背景使用(半径,分段数,分段数)
            let geometry = new THREE.SphereGeometry(500, 60, 60);
            // 重新加载背景图
            texture = new THREE.TextureLoader().load(targets.src);
            // 将背景图设置为球体的材质
            let material = new THREE.MeshBasicMaterial({
              map: texture
            });
            // 创建球几何体
            let sphere = new THREE.Mesh(geometry, material);
            // 设置球几何体的名称
            sphere.name = "ball";
            // 设置球几何体的大小
            sphere.geometry.scale(-1, 1, 1);
            // 将球几何体添加到场景中
            scene.add(sphere);
            // 调用重置相机方法
            helpReset();
            // 设置禁止缩放状态为 true
            zoomStatus = true;
            // 设置控制器禁止缩放
            controls.enableZoom = false;
          }
        }
      })
      // 创建 VR 开关事件
      function openVR(){
        // 获取 VR 提示文本
        let VRText = document.getElementById("VR-text");
        // 取反 VR 开启状态
        VRStatus = !VRStatus;
        // 判断 VR 开启状态是否为 true
        if(VRStatus){
          // 设置 VR 提示文本为 关闭VR
          VRText.innerHTML = "关闭VR";
        }else{
          // 设置 VR 提示文本为 开启VR
          VRText.innerHTML = "开启VR";
          // 重新设置渲染器的宽高
          renderer.setViewport(0, 0, webglWidth, webglHeight);
        }
      }
    </script>
  </body>
</html>

工具栏-VR显示效果

 

 

原创作者:吴小糖

创作时间:2022.11.28

相关文章
|
5月前
|
移动开发 监控 前端开发
基于 HTML5 WebGL 和 VR 技术的 3D 机房数据中心可视化
基于 HTML5 WebGL 和 VR 技术的 3D 机房数据中心可视化
|
6月前
|
vr&ar C# 图形学
【Unity 3D】VR飞机拆装后零件说明功能案例实战(附源码和演示视频 超详细)
【Unity 3D】VR飞机拆装后零件说明功能案例实战(附源码和演示视频 超详细)
92 0
|
6月前
|
vr&ar C# 图形学
【Unity 3D】VR飞机动态拆装及引擎开关控制案例(附源码和演示视频 超详细)
【Unity 3D】VR飞机动态拆装及引擎开关控制案例(附源码和演示视频 超详细)
106 0
|
6月前
|
vr&ar 图形学
【Unity 3D】VR飞机起飞喷火游戏案例实战(附源码和演示视频 超详细)
【Unity 3D】VR飞机起飞喷火游戏案例实战(附源码和演示视频 超详细)
236 0
|
机器学习/深度学习 人工智能 编解码
两句话,让AI生成VR场景!还是3D、HDR全景图的那种
两句话,让AI生成VR场景!还是3D、HDR全景图的那种
550 0
两句话,让AI生成VR场景!还是3D、HDR全景图的那种
|
移动开发 前端开发 数据可视化
3D/VR 选座技术探索
实景 VR 目前的行业应用案例逐渐增多,在使用 720°全景相机拍摄,部分厂商基于多实 景照片进行多叉数建模,在链家等房产行业获得广泛应。在票务行业,场馆选座的国内外的同 类产品中也有试点落地,国外的有 TicketMaster、Stubhub 等,国内尝试落地的有摩天轮,针对 大型场馆,目前的实现思路偏向于使用 3D 建模+后渲染输出基于 ECB 的全景照片,下发后用于大前端多端展示。
3D/VR 选座技术探索
|
vr&ar 图形学 开发者
VR这么火,你选UE4还是Unity 3D?
在虚拟现实大热的今天,开发者界也出现了“甜豆花”与“咸豆花”之争。在游戏开发者制作游戏时,面对都是免费、都支持各大平台的UE4和Unity 3D,选择何种游戏引擎,似乎有点难以抉择,左手UE4,右手Unity 3D。
4204 1
|
编解码 安全 vr&ar
看阿里云如何为直播用户营造临场沉浸感?通往8K/3D VR直播之路(一)
2016年可以说是VR元年,从Facebook的Oculus,HTC的Vive到Sony的PS VR,层出不穷的VR设备和VR内容让用户逐步开始体验到了虚拟现实的魅力。VR视频和VR直播俨然已经成为了展览会议、活动赛事的标配,甚至向婚庆、新闻、影视行业延伸。
5110 0