博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Canvas之蛋疼的正方体绘制体验
阅读量:5755 次
发布时间:2019-06-18

本文共 2967 字,大约阅读时间需要 9 分钟。

事情的起因

  之前写了篇,并且写了个简单的demo -> 。正当我在为写 谈谈文字图片粒子化II 准备demo时,突然想到能不能用正方体代替demo中的球体粒子。我不禁被自己的想法吓了一跳,球体的实现仅仅是简单的画圆,因为球体在任意角度任意距离的视图都是圆(如果有视图的话);而正方体有6个面8个点12条线,在canvas上的渲染多了n个数量级。先不说性能的问题,单单要实现六个面的旋转和绘制就不是一件特别容易的事情。

  说干就干,经过曲折的过程,终于得到了一个半成品 ->

  

事情的经过

  事情的经过绝不像得到的结果那样简单。虽然半成品demo在视觉上还有些许违和感,但已经能基本上达到我对粒子化特效的要求了。

  那么接下来说说我这次的蛋疼经历吧。

  之前我们已经实现了一个点在三维系的坐标转换(如不懂,可参考),并且得到了这样的一个demo -> 。 那么我想,既然能得到点在三维系的空间转换坐标,根据点-线-面的原理,理论上应该很容易实现正方体在三维系的体现,不就是初始化相对位置一定的8个点么?而且之前也简单地实现了一个面的demo -> ,当时认为并不难。

  于是我根据一定的相对位置,在三维系中初始化了8个点,每帧渲染的同时实现8个点的位置转移,并且根据8个点的位置每帧重绘12条线,得到demo ->

  似乎很顺利,接着给6个面上色,效果图如下:

  这时我意识到应该是面的绘制顺序出错了,在每帧的绘制前应该先给面排个序,比如图示的正方体的体心是三维系的原点,那么正方体的后面肯定是不可见的,所以应该先绘制。而在制作三维球体旋转时,是根据球体中心在三维系的坐标z值排序的,这一点也很好理解,越远的越容易被挡就越先画嘛;同时我在WAxes的这篇中看到他绘制正方体的方法是根据6个面中心点的z值进行排序,乍一想似乎理所当然,于是我去实现了,体心在原点体验良好,demo ->,但是体心一改变位置,就坑爹了...

  

  图示的正方体体心在原点的右侧(沿x轴正方向),但是画出来的正方体却有违和感,为何?接着我还原了绘制的过程:

                 

  绘制过程先绘制了正方体的左面,再绘制了上面,而根据生活经验这两个面的绘制顺序应该是先上面,再左面!不断的寻找错误,我发现这两个面中点的z值是一样的,甚至除了前后两个面,其他的四个面的z值都是一样的,也就是说这个例子中后面最先绘,前面最后绘,其他四个面的绘制顺序是任意的。我继续朝着这个方向前进,根据我的生活经验,如果像上图一样体心在原点右边(其实应该是视点,当时认为是原点),那么如果面的z值相同,应该根据面与原点的x方向的距离进行排序,毕竟距离小的先看到,如果x方向距离又相同,那么根据y方向的距离进行排序,代码如下:

  

var that = this;this.f.sort(function (a, b) {  if(b.zIndex !== a.zIndex)    return b.zIndex - a.zIndex;  else if(b.xIndex !== a.xIndex) {    // 观察基准点(0,0,0)    if(that.x >= 0)      return b.xIndex - a.xIndex;    else       return a.xIndex - b.xIndex;  } else {    if(that.y >= 0)      return b.yIndex - a.yIndex;    else      return a.yIndex - b.yIndex;  }

  因为排序中this指向了window,还需赋值给一个另外的变量保存。事情似乎在此能画上一个圆满的句号,but...

  调整后继续出现违和感(截图如下),虽然违和感的体验就在那么一瞬,但是我还是觉得是不是这个排序思路出错了?于是进一步验证,通过调试,将面的排序结果和正确的绘制顺序作对比,最终发现排序算法是错误的,最后知道真相的我眼泪掉下来。

       

  于是在知乎上问了下: 有计算机图形学基础的请无视。

  原来这是一个古老的问题,在各位图形学大大的眼里是很基础的问题了。原来这个问题称为隐藏表面消除问题。

      

  然后我跟着这个方法进行了绘制,一开始把视点和原点搞混掉了。也就是判断每个面的法向量(不取指向体心的那条)和面(近似取面中心)到视点的那条向量之间的角度,如果小于90度则是可见。想了一下,似乎还真是那么一回事。然后需要设定视点的坐标,随意设置,只要合乎常理就行,这里我设置了(0,0,-500),在z方向肯定是个负值。

  一个正方体差不多搞定了,多个正方体呢?问题又出现:

  很显然,正方体之间也有绘制的先后顺序,这里粗略地采用根据体心排序的方法,按照Milo Yip的说法,这可以解决大部分情况,但也会漏掉一些最坏情况。最好的做法是zbuffer算法。

  于是乎,一个多正方体demo新鲜出炉了->

  如果要打造  的效果,参考-> 

  这里我设置了场景(Garden)、正方体(Cube)、面(Face)、点(Ball)四个类。

  梳理一下多个正方体具体渲染过程:

  • 先将正方体进行排序,确定正方体的绘制顺序
  • 接着渲染每个正方体,先渲染正方体的各个点,改变各个点最新的坐标
for(var i = 0; i < 8; i++)   this.p[i].render();
  • 点渲染完后,根据最新的点的坐标调整正方体体心坐标,为下一帧的正方体排序准备
this.changeCoordinate();
  • 获取每个面法向量和面中点和视点夹角cos值,如果大于0(夹角小于90)则绘制(这里其实不用排序):
for(var i = 0; i < 6; i++)  this.f[i].angle = this.f[i].getAngle();this.f.sort(function (a, b) {  return a.angle > b.angle;});for(var i = 0; i < 6; i++) {  // 夹角 < 90,绘制  if(this.f[i].angle > 0)    this.f[i].draw();}
  • 反复渲染

  完整代码如下:

1   2   3     4     
5 rotate 3d 6 294 295 296
297 This browser does not support html5.298
299 300
View Code

 

  总之这样的操作正方体之间的遮掩顺序还是会出现错误的,比如下图:

   

  ps,这是在其他地方看到的判断函数,占位,备用:

事情的结果

  事情似乎得到了一个较为满意的结果。如果正方体面没有紧紧相邻,体验效果还是不错的。(紧紧相交会出现闪动)

  事实上,因为canvas暂时只支持2d,所以3d的渲染如果要得到最好的效果还是要使用webGL,但是这个思考的过程还是很重要的。

  That's all.

转载地址:http://fackx.baihongyu.com/

你可能感兴趣的文章
Java Tomcat SSL 服务端/客户端双向认证(二)
查看>>
java基础(1)
查看>>
ORACLE配置,修改tnsnames.ora文件实例
查看>>
用户无法在输入框中键入数字
查看>>
Workstation服务无法启动导致无法访问文件服务器
查看>>
.Net组件程序设计之远程调用(二)
查看>>
ant中文教程
查看>>
Linux常用命令(一)
查看>>
WSUS数据库远端存储条件下切换域及数据库迁移
查看>>
【VMCloud云平台】SCAP(四)租户(一)
查看>>
linux释放内存的方法
查看>>
基于 Android NDK 的学习之旅----- C调用Java
查看>>
Google 或强制 OEM 预装 20 款应用,给你一个不Root的理由
查看>>
我的友情链接
查看>>
双边过滤器(Bilateral filter)
查看>>
Android图形显示系统——下层显示4:图层合成上(合成原理与3D合成)
查看>>
Windows 10 技术预览
查看>>
Tomcat http跳转https
查看>>
一个自动布署.net网站的bat批处理实例
查看>>
tomcat 安装
查看>>