基于HTML5的WebGL经典3D虚拟机房漫游动画
3D 中第一人称的使用参考了射击游戏中第一人称的使用,第一人称射击游戏 (FPS) 是一种围绕枪支和其他武器以第一人称视角为中心的视频游戏类型; 以主角的眼睛体验动作。 自该类型诞生以来,先进的 3D 和伪 3D 图形一直在挑战硬件的发展,而多人游戏已经成为不可或缺的一部分。
该类型的突破游戏之一《毁灭战士》的屏幕截图,展示了第一人称射击游戏的典型视角
现在博物馆或者公司经常使用3D动画来制作宣传片等,3D动画解说的最大优点就是无论从内容还是形式上都给人一种真实的感觉。 它比平面作品更直观,比2D动画更真实,因此可以给观众带来身临其境的感觉,大大增强广告的说服力。 3D技术的发展甚至挑战了观众的辨别能力,使得观众的判断脱离了虚拟与现实。
而且3D特效的应用为创意提供了更广阔的思维空间,成为创意执行的可靠保障,丰富了创意的形式和风格。 根据广告主题的表现诉求,营造出梦幻、神奇的氛围,刺激和打动受众,从而达到与受众沟通的目的。
3D动画宣传片将3D动画、特效镜头、企业宣传片、照片、未来展望等内容通过后期合成、配音、解说相结合,形成直观、生动、大众化的高档企业广告视频,让社会不同层面的人们对公司产生正面、正面、良好的印象,从而建立对公司的良好印象和信任,并对公司的产品或服务产生依赖。
3D的快速发展也得益于人类对“真实”的追求,所以学会用好3D是未来成功必不可少的一部分。
本文例子的思路是进入一个机房进行访问。 开门的动作再生动不过了。 再加上适当的转动,基本上完全模拟了人在机房参观的效果。 还有一个好处就是,如果你想演示给领导看而不操作的话,领导会对这个炫酷的效果非常满意!
界面上的“重置”和“开始”按钮直接添加到body中,并为这两个按钮添加点击事件:
整个场景由HT封装的3D组件构建而成。 构建如此大的场景需要一定量的代码。 为了简化,我单独取出场景并使用ht。 HT封装的类,用于将场景序列化为json。 代码中只引入了生成的json文件。 为了让大家更清楚,我这里举个例子,假设3D场景已经搭建好了:
dm = new ht.DataModel(); g3d = new ht.graph3d.Graph3dView(dm); //.......构建好场景 dm.serialize();//可以填入number参数,作为空格缩进值
现在我们已经搭建好了环境,并且转换成了json文件,代码就不太好控制了。 在这种情况下,我们将反序列化数据模型。 该函数的作用是将json格式转换为对象,并将其反序列化的对象传入数据模型中,具体请参考HT for Web序列化手册():
var g3d = window.g3d = new ht.graph3d.Graph3dView(), dataModel = g3d.dm(), view = g3d.getView(), path = null; g3d.setMovableFunc(function(data) { return false; }); g3d.setVisibleFunc(function(data) { if (data.getName() === "path") { return false; } return true; }); g3d.setEye([523, 5600, 8165]); g3d.setFar(60000); dataModel.deserialize(json);
我们当前需要操作场景中的“门”和我们将要走的路线“路径”,遍历数据模型,得到这两个数据:
for (var i = 0; i < dataModel.size(); i++) { var data = dataModel.getDatas().get(i); if (data.getName() === "门") {//json中设置的名称 window.door = data; } if (data.getName() === "path") { path = data; } if (window.door && path) {//获取到door 和 path 的data之后就跳出循环 break; } }
在这个例子中,只有四个简单的动作,“重置”返回原点、“开始动作”、“前进”和“停止”。 单击“开始”按钮。 在“开始动作”中,我们只做了一个动作,“开门”动作。 动作结束后,调用“”函数继续前进:
function startAnim() { if (window.isAnimationRunning) { return; } reset(); window.isAnimationRunning = true;//动画是否正在进行 ht.Default.startAnim({ frames: 30, // 动画帧数,默认采用`ht.Default.animFrames`。 interval: 20, // 动画帧间隔,默认采用`ht.Default.animInterval`。 finishFunc: function() {// 动画结束后调用的函数。 forward(); }, action: function(t){ // action函数必须提供,实现动画过程中的属性变化。 door.setRotationY(-120 * Math.PI / 180 * t); } }); }
这里的“复位”功能就是“复位”回原点的功能。 我们使用这个函数将所有的变化恢复到原来的位置,包括“门”的位置:
function reset() { if (window.isAnimationRunning) { return; } g3d.setCenter([0,0,0]); g3d.setEye([523, 5600, 8165]); window.forwardIndex = 0; door.setRotationY(0); }
要“动”,肯定需要走“路”,也就是我们刚才得到的“路”。 代码中,“path”中的所有元素都是通过 获取的。 = path.()._as,但是这个直接调用私有变量的方法不好,因为path. 函数获取ht.List类型的对象,可以通过以下方式直接获取数组中的节点(后面代码中出现的[]可以直接用[]代替):
var pointsArr = []; for(var i = 0; i < path.getPoints().size(); i++){ var point = path.getPoints().get(i); pointsArr.push(point); }
初始化。 = 0; 通过控制“路径”中前后两点来设置3D场景中的Eye sum,从而营造出我们是第一人称的效果:
var point1 = points[forwardIndex], point2 = points[forwardIndex + 1]; var distanceX = (point2.x - point1.x), distanceY = (point2.y - point1.y), distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY)-200;//两点之间的距离通过三角形勾股定理计算 怕碰墙所以-200 g3d.setEye([point1.x, 1600, point1.y]);//眼睛的位置 g3d.setCenter([point2.x, 1600, point2.y]);//“我”的位置
HT中的3D组件有一个walk(step, anim, )方法,它同时改变eye和anim的位置,即eye和move同时在两点建立的向量方向上移动相同的偏移量。 step 是偏移向量长度值。 当参数为空时,默认使用#()的当前值。 如果在第一人称模式下调用步行操作,该函数将考虑 #() 边界限制。
g3d.walk(distance, { frames: 50, interval: 30, easing: function(t) {return t; }, finishFunc: function() { forwardIndex += 1; if (points.length - 2 > forwardIndex) {//points.length = 5 g3d.setCenter([point2.x, 1600, point2.y]);//把结束点变成起始点 g3d.rotate(Math.PI / 2, 0, { frames: 30, interval: 30, easing: function(t) {return t;}, finishFunc:function() { forward();} }); } else { var lastPoint = points[points.length - 1];//json 中path的points 的最后一个点 g3d.setCenter([lastPoint.x, 1400, lastPoint.y]); g3d.rotate(-Math.PI / 2, 0, { frames: 30, interval: 30, finishFunc: function() { window.isAnimationRunning = false; } }); } } });
无论“路径”中有多少个点,这个判断语句仍然有效,只是最后一个点是跳出动画结束后调用的函数,并且 的值是 . 设置为 false 以停止该功能。 如果不是最后一个点,用户“旋转”后,回调函数。 至此,所有的代码都解释完了,用很短的代码量就做出了这么大的项目!