canvas实现3D魔方
摘要:使用canvas实现可交互的3D魔方
一、简单分析
魔方物理性质:
1.中心块(6个):中心块与中心轴连接在一起,但可以顺着轴的方向自由的转动。
2.棱块(12个):棱块的表面是两个正方形,结构类似一个长方体从立方体的一个边凸出来。
3.角块(8个):角块的表面是三个正方形,结构类似一个小立方体从立方体的一个边凸出来,这样的结构可以让角块嵌在三个棱块之间。
4.可见方块(26个):魔方对于我们可见的方块有26块。
5.魔方六个面,每个面九个方块,转动时每个面绕着自己面的中心块旋转。
6.在观察魔方时,我们将整个魔方绕着三维空间中的某个点旋转
7.在人的视野范围内,魔方可见的部分只有三个面,19个方块。
二、实现方式
1)完成方块的旋转
通过简略的类图,先了解一下实现的思路。在一个三维空间中,最基础的就是点(Vertex),点组成了面(Face),面组成了空间立方体(Cube)。魔方(MagicCube)由26个Cube组成,同时将26个Cube单组不重复的分为6组(MagicFace)。当旋转观察魔方时,26个Cube绕着魔方的中心旋转。当转动魔方的某一面时,MagicFace上所有的Cube围绕以原点为起点,面的中心为终点的矢量旋转。只需要掌握点在空间中的旋转,即可完成整个魔方的旋转。三维空间中点的旋转主要用到了数学上的旋转公式。关于如何将一个三维空间的立方体投影到平面并实现旋转请看我的上一篇随笔。
2)完成方块的上色
第一步只是完成了一个透明魔方旋转。我们需要给魔方上色。初始化时为每个cube的face上色,我们不可见的面设置为白色。就像我们在现实中绘画一样,在canvas上绘制图像时后渲染的图像会把之前的覆盖掉,因此魔方的渲染顺序我们需要从后往前渲染。
我们通过面的中心距离原点的Y值(本例以Y轴垂直与屏幕作为参考轴),来确定渲染顺序。单个方块的渲染如上图,6个面(*代表看不见的面),渲染顺序为1*,2*,3*,4,5,6,后渲染的面将前面渲染的面盖住。
整个魔方的渲染,举例标了数字的三个方块,也是通过方块中点距离原点的Y值来确定渲染顺序。渲染顺序先是方块1,然后方块2把1的上半部分盖住,然后方块3把方块二的上半部分盖住。
3)完成鼠标的点击事件
交互是本例中比较难的一个点,canvas 没有提供为其内部元素添加事件监听的方法,因此如果要使 canvas 内的元素能够响应事件,需要自己动手实现。通过获得鼠标在 canvas 上的坐标,计算当前坐标在哪些元素内部,然后对元素进行相应的操作。配合自定义事件,我们就可以实现为 canvas 内的元素添加事件监听的效果。
在上图中,可旋转的面投影到二维平面的区域共有6个,图中只标识了三个。如何确定投影的区域呢?想了很多方法,最终采用了建立参考点的方法。
1.获取到3个可视面MagicFace(F1F2F3)---6个面中中心Y值最大的3个
2.获取到3个可视面无穷远的参考点(V1V2V3)--6个参考点中Y值最大的3个
4.我们循环6个面,例如循环到图中的F2面,我们过滤离F2中心最近的参考点(过滤掉与F2中心空间距离最小的点),剩下V1V3
5.获取到F2面中离V1最最近的三个方块(即上方红色区域的三个方块) -- 离参考点的空间距离
6.获取三个方块中里F2这个面中点最远的4个点(即上方红色区域的4个顶点)
通过3层循环,即可获取到所有可视面的投影。当我们鼠标点击并滑动一段距离时(图中黑点),我们就可以判断两个点所在的投影的位置是哪一个面的,获取到面之后就可以旋转那个面。
PS:我们通过建立参考点,6个无穷远的点(分别在X,Y,Z轴的正负方向上,魔方整体旋转时参考点也会旋转)。使用他与各个元素中点的距离用于判断一些特殊位置的点和面的方法。滑动部分的投影的8个点就是用参考点辅助求出的。
判断点是否在面中使用的是pnpoly算法 https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html
三、最终效果
PS:面积投影越小事件效果就不灵敏了,有待优化
canvas实现3D魔方的更多相关文章
- css3之3D魔方动画(小白版)
在这里分享一下3D魔方动画,html5+CSS3即可完成~无图无真相,先上效果图 第一步非常简单,就是先将魔方的结构画出来.大家都玩过魔方,知道魔方是一个有六个面的正方体.这里我们先写一个大的di ...
- Seen.js – 使用 SVG 或者 Canvas 渲染 3D 场景
Seen.js 渲染3D场景为 SVG 或者 HTML5 画布.Seen.js 包含对于 SVG 和 HTML5 Canvas 元素的图形功能的最简单的抽象.所有这个库的其它组件都是不用关心将要渲染的 ...
- DirectX11--实现一个3D魔方(3)
前言 (2019/1/9 09:23)上一章我们主要讲述了魔方的旋转,这个旋转真是有毒啊,搞完这个部分搭键鼠操作不到半天应该就可以搭完了吧... (2019/1/9 21:25)啊,真香 有人发这张图 ...
- DirectX11--实现一个3D魔方(2)
前言 上一章我们主要讲述了魔方的构造和初始化.纹理的准备工作.目前我还没有打算讲Direct3D 11关于底层绘图的实现,因此接下来这一章的重点是魔方的旋转.因为我们要的是能玩的魔方游戏,而不是一个观 ...
- DirectX11--实现一个3D魔方(1)
前言 可以说,魔方跟我的人生也有一定的联系. 在高中的学校接触到了魔方社,那时候的我虽然也能够还原魔方,可看到大神们总是可以非常快地还原,为此我也走上了学习高级公式CFOP的坑.当初学习的网站是在魔方 ...
- 如何用CSS3画出懂你的3D魔方?
作者:首席填坑官∙苏南公众号:honeyBadger8,群:912594095,本文原创,著作权归作者所有,转载请注明原链接及出处. 前言 最近在写<每周动画点点系列>文章,上一期分享了& ...
- 用Canvas玩3D:点-线-面
声明:本文为原创文章,如需转载,请注明来源WAxes,谢谢! 玩Canvas玩了有两三个礼拜了,平面的东西玩来玩去也就那样,所以就开始折腾3D了. 因为Canvas画布终究还是平面的,所以要有3D就得 ...
- 基于 HTML5 Canvas 的 3D 模型贴图问题
之前注意到的一个例子,但是一直没有沉下心来看这个例子到底有什么优点,总觉得就是一个 list 列表,也不知道右边的 3d 场景放两个节点是要干嘛,今天突然想起来就仔细地看了一下这个例子的代码,实际操作 ...
- 基于 HTML5 Canvas 的 3D 模型列表贴图
少量图片对于我们赋值是没有什么难度,但是如果图片的量大的话,我们肯定希望能很直接地显示在界面上供我们使用,再就是排放的位置等等,这些都需要比较直观的操作,在实际应用中会让我们省很多力以及时间.下面这个 ...
随机推荐
- cocos2d-x 读写 xml 文件
cocos2d-x 读写 xml 文件 A product of cheungmine使用cocos2d-x开发2d游戏确实方便,但是对于一般的小游戏,经常需要的工作是UI布局设计和调整,代码改来改去 ...
- C 打印格式小记
转自:http://blog.csdn.net/fivedoumi/article/details/7077504 d,lx,ld,,lu,这几个都是输出32位的 hd,hx,hu,这几个都是输出16 ...
- linux的link命令
sudo ln -s 源文件 目标文件 sudo ln -s /usr/local/mysql/bin/mysqladmin /sbin/mysqladmin 建立软连接 ln -d existfil ...
- java的io库用到的装饰模式是如何体现的?
概论 java的io包下大概有85个类,真复杂.其实不然这些类又可以分为以下四个部分. 输入流 输出流 字节流 InputStream ...
- 为什么我们要使用ssh框架技术,及感想
前言: 在公司从C++转向Java Web方向大概有3个月(11月初-1月底)了.三个月前对Java和Web还几乎是零基础.然后从安装Eclipse,MySQL,tomcat开始,到学习HTML/CS ...
- 杭电ACM 1000题
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner cin=n ...
- rails中validates_confirmation_of验证方法无效的解决办法
rails的model中提供了很多种自带的验证方法,validates_confirmation_of可以验证变量xxx和xxx_confirmation是否相等:这可以用于验证2遍输入的密码是否一致 ...
- 项目群MSP课程最大的特点
1.课程中间让大家去了解和理解项目群管理的知识体系.方法论,更关注大家的个性化需求: 2.课程中间还会有很多练习和讨论,特别是会请到一些业界在实践MSP的客户,进行他们的实践案例分享.所以从知识到实际 ...
- webpack基础
首先我们需要手动创建webpack.config.js文件 然后在文件中配置选项 //webpack的配置选项 //__dirname:当前文件所在的目录路径 const config ={ //入口 ...
- oracle索引建立和删除
1.多列建立索引 SQL> create index dex_index2 on dex(sex,name); Index created. SQL> select object_name ...