搜索
首页web前端html教程H5打造3d场景 Amazing CSS3D

我们知道3D的表现形式即让我们通过平面可从不同角度看到真实物体的展示效果。

在计算机世界里,3D世界是由点组成,两个点能够组成一条直线,三个不在一条直线上的点就能够组成一个三角形面,无数三角形面就能够组成各种形状的物体,如下图。

892670031-581ee02140c15_articlex.png

Three中模型解析器的原理是将顶点数组将模型的顶点用数组储存起来,再利用three中的face函数取得定点数组中的三个或四个顶点的索引构成空间平面。如此反复,模型就被完整构造出来了。

于是,越复杂的物体就需要越多的网面拼接。而css中是不存在根据坐标建立空间平面的能力的。

(插个题外话,其实css有一个属性与坐标有关,那就是clip-path。这个属性的特性赋予了css3一定的建模能力。实现方法可参考这篇文章 纯clip-path打造的3D模型渲染器)

CSS3实现3D全景

。上篇文章介绍了Web3D的一些表现形式,这里着重谈谈怎么以CSS3实现3D全景。下面会探索Three实现全景的方案,因为WebGL门槛和学习成本还是比较高的,不适于用于快速开发。造物节的CSS3d全景已有文章对其进行了技术探秘,但都未深入谈及具体实现方式。

要清晰理解实现方式,必须对CSS3的transform、perspective有一定的认识。
原理方面的东西我就不深入讲了,大家可以先看看这篇文章,对CSS3D有一个大致的概念。
玩轉 CSS 3D - 原理篇

CSS全景可通过建立柱形或者立方体再通过贴图方式实现。也许会有人问,球体行不行?实际上是不行的,球体模型由无数个极小的平面拼接构成连贯曲面,而CSS缺乏使平面扭曲的属性。球体模型我们可以使用上文提过的Clip-3d建造出,但是,贴图问题就解决不了了。

天空盒子

相信很多打造过或有了解过3d全景的同行们都知道这个概念。实际上Skybox就是一个立方体,通过给六个面贴上不同的,边缘可以无缝贴合的图片,再将视角伸入盒子内部。可以想象成我们自己站入了一个巨型立方体盒子内部,移动视角便能看到不同的场景。

3686196226-57a94a892fcd0_articlex.gif

1、贴图
来看一张天空盒子的贴图,剪头指向的边缘代表需要无缝贴合的边。

892670031-581ee02140c15_articlex.png

从上图可以看出只要相互贴合的两个面上的图像能够无缝拼接,那么再通过对各个面进行一定的旋转变换,天空盒子就能被打造出来了。

那么问题来了,怎么去拍摄制作这样的图片呢?这就需要通过一些专业软件了,比如pano2vr,max等。其实,需要用到这些专业工具打造的全景对画质和拼合度的要求都非常高了,而单纯依靠CSS3中的变化给不了它们很好的体验。

但我们今天讨论的是某些运营活动H5打造的全景,此全景不一定真实存在,或者是和真实场景有一定的比例差距。例如星空、海底。对于这类贴合度可人为改变的全景图的打造,我们可以采用现有的高清图片,再经由PS转换成六面全景图。
贴一篇文章 Create a Skybox From Photos
其实主要思想是
在一张大图上勾画出六个面的选取 >
选择大图中某个面的相邻面将其旋转到需要拼合的盒子的某个面上,使他们完美贴合 >
得到最合理的六面贴图后,观察有无创造出新的边缘,通过蒙版等工具使他们自然融合。

2、构造贴图完成就可以创建立方体了。首先将创建好的六个面切割出来,以front、back、left、right…命名标记位置。

 .sence {      -webkit-perspective: 1000px;    }
    .cube {      width: 500px;      height: 500px;      margin: 100px auto;      transform-style: preserve-3d;    }
    .cube img {      width: 130px;      height: 130px;      position: absolute;    }
    .cube img:nth-child(1) {    }
    .cube img:nth-child(2) {      transform:  rotateY(180deg);    }
    .cube img:nth-child(3) {      transform:  rotateY(90deg);    }
    .cube img:nth-child(4) {      transform:  rotateY(-90deg);    }
    .cube img:nth-child(5) {      transform:  rotateX(90deg);    }
    .cube img:nth-child(6) {      transform:  rotateX(-90deg);    }
<div class="sence">
    <div class="cube">
      <img src="/static/imghwm/default1.png"  data-src="img/skybox/front.jpg"  class="lazy"   alt="" />
      <img src="/static/imghwm/default1.png"  data-src="img/skybox/back.jpg"  class="lazy"   alt="" />
      <img src="/static/imghwm/default1.png"  data-src="img/skybox/left.jpg"  class="lazy"   alt="" />
      <img src="/static/imghwm/default1.png"  data-src="img/skybox/right.jpg"  class="lazy"   alt="" />
      <img src="/static/imghwm/default1.png"  data-src="img/skybox/top.jpg"  class="lazy"   alt="" />
      <img src="/static/imghwm/default1.png"  data-src="img/skybox/bottom.jpg"  class="lazy"   alt="" />
    </div>
  </div>

准备好6个面,载入贴图。通过旋转,使得每个面旋转到相印的位置。如左边的面由原本面朝我们的图片绕Y轴逆时针旋转90°得到。(注意Y轴逆时针旋转是正数)

此时会得到下图这样的效果:

892670031-581ee02140c15_articlex.png

但是由于每个面的旋转中心都在其正中位置,因此还不能形成正方体。于是我们需要让每个面产生一定的位移。

贴一张坐标系图以助于大家理解。

892670031-581ee02140c15_articlex.png

现在首先让front位移到应该到的位置,由于全景图的镜头在立方体内部,因此,可以想象一下,我们需要将图片往后移动。移动距离很明显为立方体边长的一半。在这里是65px。得到下图结果。

.cube img:nth-child(1) {    
  transform: translateZ(-65px); 
     }

892670031-581ee02140c15_articlex.png

照这样看,是不是back位移为translateZ(65px),left为translateX(-65px),top translateY(-65px)呢?但结果并不是我们想要的。

892670031-581ee02140c15_articlex.png

重新看回上文空间坐标系的那张贴图,我们会发现,平面旋转后,其对应的三个轴的位置也改变了。如图片绕Y旋转后,Z轴指向为屏幕的水平方向。绕X旋转后,Z轴指向垂直方向。因此我们很容易发现,其实要将贴面移动到正确的位置,都只需要让他们translateZ(-width/2px)就可以了。

892670031-581ee02140c15_articlex.png

为了让大家容易理解,我这里设置了一个较大的perspective。要想得到全景的效果,我们将镜头拉近让它进入到box里面就可以了。

892670031-581ee02140c15_articlex.png

接下来绑定手势,就可以让它动起来啦。

部分代码:

viewer.on(&#39;touchstart&#39;, function(e) {
    x1 = e.targetTouches[0].pageX; - $(this).offset().left;
    y1 = e.targetTouches[0].pageY; - $(this).offset().top;
});

viewer.on(&#39;touchmove&#39;,function(){
    var dist_x = x2 - x1,
        dist_y = y2 - y1,
        deg_x = Math.atan2(dist_y, perspective) / Math.PI * 180,
        deg_y = -Math.atan2(dist_x, perspective) / Math.PI * 180,
        i,
        c_x_deg += deg_x;
        c_y_deg += deg_y;
        
    cube.css(&#39;transform&#39;, &#39;rotateX(&#39; + deg_x + &#39;deg) rotateY(&#39; + deg_y + &#39;deg)&#39;);
})

Math.atan2(y,x) 方法:得到从 x 轴到点 (x,y) 之间的角度。对于空间左边系比较难理解,大家可以想象成一张以空间Z轴为Y轴的平面绕X轴正方向旋转的角度即为cube绕空间Y轴旋转的角度。

柱形

柱形全景也不算复杂。关于圆柱形的打造方法,大家可以参考下这篇文章CSS3 3D transforms系列教程-3D旋转木马
有了这个基础,我们可以写一段函数快速构造柱形全景。

先来看下页面结构

<style>
  body {
    height: 100%;
    overflow: hidden;
  }
    .scene {
      width: 100%;
      height: 1170px;
      transform: translateX(-50%) translateY(-50%);
      top: 50%;
      left: 50%;
      position: absolute;
    }
    .cube {
      transform-style: preserve-3d;
      height: 100%;
      width: 100%;
      margin: 0px auto;
    }
    .cube_bg {
      transform-style: preserve-3d;
      height: 100%;
      width: 128px;
      margin: 0px auto;
    }
    .cube_bg div {
      height: 100%;
      
      /* 这里为圆柱形的每个面都设定了同样的背景图 那么在建造柱形时不再需要手动切图 */
      background-image: url("img/zao/zao.png");
      
      background-repeat: no-repeat;
      position: absolute;
      top: 0;
    }
</style>

<body>
  <div class="scene">
    <div class="cube">
      <div class="cube_bg">
        <!--
        这里是柱形全景背景贴图
        -->
      </div>
      <div class="cube_item">
        <!--
        这里是柱形全景中的小元件
        -->
      </div>
    </div>
  </div>
</body>
function creCylinder(lenZ,pieceWid,angle,slice){

    /* 
    pieceWid 表示单个柱形块状宽度
    angle表示柱形内角
    slice表示有多少个面拼接 
    slice越多,拼合的面越接近曲面
    */
    
  var l = pieceWid*slice; // 画布全长
  var ag = angle/slice // 旋转角度

  var html = &#39;&#39;;

  /*
    设置每个面的旋转角度和位移 因为要分割成多个面,所以应该为每个面的背景图设置不同的`background-position`
    */

  for(var i=0,len=slice;i<len;i++){
    html+=&#39;<div style="transform: rotateY(-&#39;+ag*i+&#39;deg) &#39;+
          &#39;translateZ(&#39;+lenZ+&#39;px);&#39;+
          &#39;width:&#39;+(pieceWid)+&#39;px;&#39;+
          &#39;background-position: -&#39;+(i*pieceWid)+&#39;px 0;&#39;+
          &#39;background-size: &#39;+(l)+&#39;px 100%;"></div>&#39;;
  }

    return html;
}

function renderPano(pieceWid,angle,slice){

    var vw = $(window).width();

    var RADIAN = 0.017453293; // 弧度制 将角度转成弧度

    var innerAngle = angle/(2*slice); //内角,用来计算translateZ

    // 这里的原理和上文旋转木马链接一致
    var lenZ = -(pieceWid/2)*Math.tan((90-innerAngle)*RADIAN);

    /*  
        因为默认是由画布的最左端开始旋转 所以处于我们面前的是画布的最左端和最右端及其连接处
        要想画布中央显示再我们面前,这里需要给cube_bg加上一定的绕Y旋转角度
    */
    var rotate = ((angle/slice)*(slice-1))/2,
        perspective = -lenZ-5;

    var cube_bg = $(&#39;.cube_bg&#39;),
        scene = $(&#39;.scene&#39;);

    var cylinder = creCylinder(lenZ,pieceWid,angle,slice);

    cube_bg.html(cylinder).css(&#39;transform&#39;,&#39;rotateY(&#39;+rotate+&#39;deg)&#39;);
    scence.css(&#39;-webkit-perspective&#39;,perspective+&#39;px&#39;);
    
    //最后调用一下
    renderPano(128,360,20);

这里解释一下perspective为什么要设成 -lenZ-5
看一张图,上面的lenZ即translateZ值,为负值。
perspective为镜头到屏幕的距离,因为此时镜头在柱体内部,因此不能看到柱体后面的图像。
当perspective值为-lenZ值时,正好柱体back面能与镜头在同一平面上,为了避免它有一定的机率遮挡镜头,我们可以将镜头拉近一些。便设成了-lenZ-5。这个时候就能保证镜头处于柱体内部,同时也能更广角度地观察到柱体全景。

892670031-581ee02140c15_articlex.png

大家可以复制代码体验一下。这里的背景图我选用的是自己拼合成的造物节背景图。

优劣势对比

相信大家也有体会,天空盒制造起来会相对的简单,并且天空和地面都能被考虑进去。但是由于面面间的贴合角度太大,若物体正好处于相互贴合的两个面,会给人一种被拦腰折断的感觉。而柱形图对这种情况有了比较好的解决,但是天空和地面的贴图就比较困难了,一般情况下只能通过给scene添加背景图片模拟。


声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
公众号网页更新缓存难题:如何避免版本更新后旧缓存影响用户体验?公众号网页更新缓存难题:如何避免版本更新后旧缓存影响用户体验?Mar 04, 2025 pm 12:32 PM

公众号网页更新缓存,这玩意儿,说简单也简单,说复杂也够你喝一壶的。你辛辛苦苦更新了公众号文章,结果用户打开还是老版本,这滋味,谁受得了?这篇文章,咱就来扒一扒这背后的弯弯绕绕,以及如何优雅地解决这个问题。读完之后,你就能轻松应对各种缓存难题,让你的用户始终体验到最新鲜的内容。先说点基础的。网页缓存,说白了就是浏览器或者服务器为了提高访问速度,把一些静态资源(比如图片、CSS、JS)或者页面内容存储起来。下次访问时,直接从缓存里取,不用再重新下载,速度自然快。但这玩意儿,也是个双刃剑。新版本上线,

如何使用HTML5表单验证属性来验证用户输入?如何使用HTML5表单验证属性来验证用户输入?Mar 17, 2025 pm 12:27 PM

本文讨论了使用HTML5表单验证属性,例如必需的,图案,最小,最大和长度限制,以直接在浏览器中验证用户输入。

HTML5中跨浏览器兼容性的最佳实践是什么?HTML5中跨浏览器兼容性的最佳实践是什么?Mar 17, 2025 pm 12:20 PM

文章讨论了确保HTML5跨浏览器兼容性的最佳实践,重点是特征检测,进行性增强和测试方法。

如何高效地在网页中为PNG图片添加描边效果?如何高效地在网页中为PNG图片添加描边效果?Mar 04, 2025 pm 02:39 PM

本文展示了使用CSS为网页中添加有效的PNG边框。 它认为,与JavaScript或库相比,CSS提供了出色的性能,详细介绍了如何调整边界宽度,样式和颜色以获得微妙或突出的效果

&lt; datalist&gt;的目的是什么。 元素?&lt; datalist&gt;的目的是什么。 元素?Mar 21, 2025 pm 12:33 PM

本文讨论了html&lt; datalist&gt;元素,通过提供自动完整建议,改善用户体验并减少错误来增强表格。Character计数:159

&lt; meter&gt;的目的是什么。 元素?&lt; meter&gt;的目的是什么。 元素?Mar 21, 2025 pm 12:35 PM

本文讨论了HTML&lt; meter&gt;元素,用于在一个范围内显示标量或分数值及其在Web开发中的常见应用。它区分了&lt; meter&gt;从&lt; progress&gt;和前

我如何使用html5&lt; time&gt; 元素以语义表示日期和时间?我如何使用html5&lt; time&gt; 元素以语义表示日期和时间?Mar 12, 2025 pm 04:05 PM

本文解释了HTML5&lt; time&gt;语义日期/时间表示的元素。 它强调了DateTime属性对机器可读性(ISO 8601格式)的重要性,并在人类可读文本旁边,增强Accessibilit

&gt; gt;的目的是什么 元素?&gt; gt;的目的是什么 元素?Mar 21, 2025 pm 12:34 PM

本文讨论了HTML&lt; Progress&gt;元素,其目的,样式和与&lt; meter&gt;元素。主要重点是使用&lt; progress&gt;为了完成任务和LT;仪表&gt;对于stati

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
2 周前By尊渡假赌尊渡假赌尊渡假赌
仓库:如何复兴队友
1 个月前By尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒险:如何获得巨型种子
4 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )专业的PHP集成开发工具

安全考试浏览器

安全考试浏览器

Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

SublimeText3 英文版

SublimeText3 英文版

推荐:为Win版本,支持代码提示!

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)