PhiloGL学习(6)——深情奉献:快乐的一家
前言
话说上一篇文章结尾讲到这一篇要做一个地球自转以及月球公转的三维动画,提笔,不对,是提键盘开始写的时候脑海中突然出现了几年前春晚风靡的那首歌:蒙古族小丫头唱的快乐的一家。闲言莫提,进入正题。
一、 原理分析
场景涉及两个对象,一个是地球、一个是月球,当然这基本是废话,不过还可以再添加一个对象,月球的公转轨迹。地球和月球都可以用一个球来模拟(Sphere),稍微困难的是公转轨迹,公转轨迹是一个圆,PhiloGL貌似没有直接提供圆的封装,但是有画线段的API,细细想来,什么是圆?祖冲之早就告诉我们了,所谓圆不过是多边形的无限逼近,那么我们就可以用多条细小的线段来逼近圆。地球自转很简单,而月球的公转就如同公转轨迹一样,只要将月球的位置设置到公转轨道上即可。
有了上述分析之后,我们就可以做出地球和月球的完美曲线来了。整体效果如下图所示:
二、 创建整体场景
创建整体场景即新建PhiloGL对象,配置GLSL、摄像头、灯光、贴图等。
PhiloGL('test1', {
program: [{
id: 'vertex',
from: 'defaults'
}, {
id: 'circle',
from: 'ids',
vs: 'shader-vs',
fs: 'shader-fs'
}],
camera: {
position: {
x: 0, y: 60, z: 60
}
},
textures: {
src: ['earth.jpg', 'moon.gif'],
},
onError: function (e) {
alert(e);
},
onLoad: function (app) {
var gl = app.gl,
canvas = app.canvas,
program = app.program,
camera = app.camera,
scene = app.scene,
view = new PhiloGL.Mat4;
function clear() {
gl.viewport(0, 0, +canvas.width, +canvas.height);
gl.clearColor(0, 0, 0, 1);
gl.clearDepth(1);
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
}
clear();
function animate() {
......
}
function drawScene() {
var lightConfig = scene.config.lights;
lightConfig.enable = true;
lightConfig.ambient = {
r: +ambient.r,
g: +ambient.g,
b: +ambient.b
};
// 线光源
lightConfig.directional.direction = {
x: +light.x,
y: +light.y,
z: +light.z
};
lightConfig.directional.color = {
r: +light.r,
g: +light.g,
b: +light.b
};
......
}
function tick() {
animate();
drawScene();
scene.render();
PhiloGL.Fx.requestAnimationFrame(tick);
}
tick();
}
});
其中program下面包含两个glsl,第一个vertex是PhiloGL提供的默认GLSL,用于地球和月球。第二个circle用于月球公转轨道,定义如下:
<script id="shader-fs" type="x-shader/x-fragment">
#ifdef GL_ES
precision highp float;
#endif
varying vec4 vColor;
void main(void) {
gl_FragColor = vColor;
}
</script>
<script id="shader-vs" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
attribute vec4 aVertexColor;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
varying vec4 vColor;
void main(void) {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
vColor = aVertexColor;
}
</script>
摄像头、灯光、贴图前面几篇文章已经介绍过,这里与之前基本相同,不再赘述。
三、 创建自转的地球
3.1 创建地球对象
创建Sphere对象并设置地球的贴图。代码如下:
var earth = new PhiloGL.O3D.Sphere({
nlat: 30,
nlong: 30,
radius: 5,
textures: ['earth.jpg'],
colors: [1, 1, 1, 1],
program: 'vertex'
});
3.2 自转
地球自转,位置无需变化,只需要随着时间让旋转角度递增即可。在外部设置旋转角度和旋转变量,此处旋转简单起见以Y轴为旋转轴,在animate方法中对旋转角度进行累加,在drawScene方法中设置earth对象进行旋转。
//外部设置旋转变量
var yRot = 1, ySpeed = 0.1;
// animate中旋转累加
yRot += ySpeed;
// drawScene中设置earth的旋转
earth.rotation.set(0, yRot, 0);
earth.position.set(0, 0, 0);
earth.update();
这样就可实现地球的自转。
四、 创建公转的月球
4.1 创建月球
同样创建Sphere对象。
var moon = new PhiloGL.O3D.Sphere({
nlat: 30,
nlong: 30,
radius: 1,
textures: ['moon.gif'],
colors: [1, 1, 1, 1],
program: 'vertex'
});
4.2 公转
公转,只需要改变月球位置即可,让其位置处在公转圆上。
// 外部设置公转变量
var theta = 0, tSpeed = ySpeed / 30,
moon_x, moon_z, r = 30;
// animate中设置公转位置
theta -= tSpeed;
moon_x = r * Math.cos(theta);
moon_z = r * Math.sin(theta);
// drawScene中设置moon公转
moon.position.set(moon_x, 0, moon_z);
moon.update();
其中theta表示公转角度、tSpeed表示公转速度,其速度为地球自转速度的1/30,这里假设月球公转周期为30天,所以其公转速度为地球自转速度的1/30。地球以Y轴为旋转轴,假设月球的公转平面为XOZ平面,即Y值为0。根据三角函数可知,当旋转角度为θ时,X值为r * cos(θ),Z值为r * sin(θ),其中r为公转半径。
五、 创建公转轨道
有了上面几部分的分析之后,公转轨道也很容易了。首先,先来解释一下PhiloGL绘制线段的原理。
PhiloGL使用gl.drawArrays(gl.LINES, 0, count)来绘制多条线段,其中count表示线段的数量,当然这里需要使用之前的GLSL知识,我们要为aVertexPosition和aVertexColor这两个attribute变量赋值。如下:
program.circle.setBuffers({
'aVertexPosition': {
value: new Float32Array(points),
size: 3
},
'aVertexColor': {
value: new Float32Array(colors),
size: 4
}
});
其中points表示线段的点的集合,colors表示线段的端点的颜色,一条线段由两个端点组成,颜色也是由两个端点的颜色渐变而成。所以如果需要绘制连续的线段那么必须要将除首尾端点外全部重复,否则会造成线段断开。而此处绘制的是个封闭的圆,那么必须要在最后一条线段后再添加最后一个点到第一个点的线段,这样才能形成一个封闭的圆,颜色同样如此。代码如下:
function createRoute() {
var points = [];
var colors = [];
for (var t = 0; t < Math.PI * 2; t += 0.01) {
if (t == 0) {
points.push(r * Math.cos(t), 0, r * Math.sin(t));
colors.push(1,1,1,1);
} else {
points.push(r * Math.cos(t), 0, r * Math.sin(t));
points.push(r * Math.cos(t), 0, r * Math.sin(t));
colors.push(1,1,1,1);
colors.push(1,1,1,1);
}
}
points.push(r * Math.cos(0), 0, r * Math.sin(0));
colors.push(1,1,1,1);
return {"points": points, "colors": colors};
}
points存储所有点的集合,colors存储点的颜色的集合。第一个点仅存一次,其余点存两次,当循环结束后再将第一个点存入其中。将其结果赋给上面setBuffers中的两个变量。
设置好后,在drawScene中执行gl.drawArrays(gl.LINES, 0, count)即可。
六、 卫星的封装
写到这里,本来已经完事了,奈何程序员总是有强迫症的,你瞅瞅这月球、这轨道,这明显就是一个大对象嘛,那我必须要对其进行封装,变成卫星。将卫星半径、卫星轨道、公转速度、公转轨道夹角、轨道颜色等封装起来。整体代码如下:
function Satllite(radius, theta, speed, sigmaY, color, globeRadius) {
this.sigmaY = sigmaY;
this.color = color;
this.radius = radius;
this.speed = speed;
this.theta = theta;
this.globeRadius = globeRadius;
this.circleLine = null;
this.model = null;
this.circleModel = null;
this.getModel = function () {
if (this.model == null) {
var mod = new PhiloGL.O3D.Sphere({
nlat: 30,
nlong: 30,
radius: this.radius,
textures: ['img/moon.gif'],
colors: [1, 1, 1, 1],
program: 'vertex'
});
this.model = mod;
}
return this.model;
};
this.getCircleModel = function () {
if (this.circleModel == null) {
if (this.circleLine == null) {
this.circleLine = this.getRoute();
}
var res = this.circleLine;
var circle = new PhiloGL.O3D.Model({
program: 'circle',
render: function (gl, program, camera) {
program.setUniform('uMVMatrix', camera.view);
program.setUniform('uPMatrix', camera.projection);
gl.lineWidth(this.lineWidth || 1);
gl.drawArrays(gl.LINES, 0, res.points.length / 3);
},
attributes: {
aVertexPosition: {
size: 3,
value: new Float32Array(res.points)
},
aVertexColor: {
value: new Float32Array(res.colors),
size: 4
}
}
});
this.circleModel = circle;
}
return this.circleModel;
};
this.getRoute = function () {
var points = [];
var colors = [];
for (var t = 0; t < Math.PI * 2; t += 0.05) {
var pos = this.getPosition(t);
if (t == 0) {
points.push(pos.x, pos.y, pos.z);
colors = colors.concat(this.color);
} else {
points.push(pos.x, pos.y, pos.z);
points.push(pos.x, pos.y, pos.z);
colors = colors.concat(this.color);
colors = colors.concat(this.color);
}
}
pos = this.getPosition(0);
points.push(pos.x, pos.y, pos.z);
colors = colors.concat(this.color);
return {"points": points, "colors": colors};
};
this.getPosition = function (thetaX) {
x = this.globeRadius * Math.cos(thetaX) * Math.cos(this.sigmaY);
y = this.globeRadius * Math.cos(thetaX) * Math.sin(this.sigmaY);
z = this.globeRadius * Math.sin(thetaX);
return {'x': x, 'y': y, 'z': z};
};
this.getRealPosition = function () {
return this.getPosition(this.theta);
};
this.updateTheta = function () {
this.theta -= this.speed;
};
this.updateModel = function () {
if (this.model != null) {
var pos = this.getRealPosition();
this.model.position.set(pos.x, pos.y, pos.z);
this.model.update();
this.circleModel.update();
}
};
};
radius表示卫星的半径, theta表示公转的角度, speed表示公转速度, sigmaY表示公转轨道与Y轴的夹角, color表示公转轨道颜色, globeRadius表示公转轨道半径。
getModel函数用于获取卫星实体对象;drawCircle函数用于绘制公转轨道;getRoute函数获取公转轨道信息,包括点位信息和颜色信息;getPosition函数用于计算当公转角度为theta时的位置坐标,其坐标值计算是在上文分析的基础上加入了Y轴旋转角度的影响;getRealPosition函数获取卫星公转实时位置信息;updateTheta函数用于更新卫星的旋转角度;updateModel直接更新卫星位置。
其调用方法如下:
// 创建
var sat = new Satllite(1, 0, 0.01, Math.PI, [1,1,1,1], 30);
sat.getModel();
scene.add(sat.model);// 加入场景
// animate中修改公转角度
sat.updateTheta();
// drawScene中绘制轨道以及更新位置
sat.updateModel();
多次按上述代码调用就能创建多个卫星对象,让地球的大家庭更加丰满,所以有了此类,只要知道了卫星轨道参数我们就能模拟出来地球外部的全部人造卫星,当然这还只是简单的情况,复杂的情况就要将轨道变成椭圆等等了。
之前做的时候轨道总是跟着地球一起旋转,不知什么原因,猜测是camera造成的,但是始终没有解决,后面我尝试将画圆对象封装成Model,结果完美解决了此问题。封装代码如下:
new PhiloGL.O3D.Model({
program: 'circle',
render: function (gl, program, camera) {
program.setUniform('uMVMatrix', camera.view);
program.setUniform('uPMatrix', camera.projection);
gl.lineWidth(this.lineWidth || 1);
gl.drawArrays(gl.LINES, 0, res.points.length / 3);
},
attributes: {
aVertexPosition: {
size: 3,
value: new Float32Array(res.points)
},
aVertexColor: {
value: new Float32Array(res.colors),
size: 4
}
}
});
其中circle为上面定义的GLSL模块,attributes中是vs的两个attribute变量,这个与之前没有区别,一个控制点一个控制颜色。render函数是最终的渲染函数,两个setUniform设置camera,gl.drawArrays(gl.LINES, 0, res.points.length / 3)控制最终绘制,这与之前都没有区别,所以这里只是封装。有了此对象之后,其添加、更新等就与球等对象相同了。
七、 总结
本文简单介绍了绘制自转的地球以及公转的月球,看似很简单,其实中间有很多的坑,当然是因为自己确实水平有限,然而这正是我做此场景的本意,当然做完之后更加深刻的感受到了这一点。你看地球自转多么简单,月球公转多么简单,而人类才是地球上多么微不足道的一点点,你把自己搞那么复杂干什么,面对着永不停息转动的地球和月球你感受不到自己的渺小吗?再上升到整个宇宙不敢想象!所以,迈开自己的腿,多去看看更大的世界,不求能出得了宇宙,只求能够在有生之年走遍大部分地球,做一个见多识广的程序员,有一个快乐的一家!本系列文章写到这里,已经基本结束,后面如果有新的感悟也会继续写出。
PhiloGL学习(6)——深情奉献:快乐的一家的更多相关文章
- PhiloGL学习(2)——骚年,让我们荡起双桨
前言 上一篇文章中简单介绍了PhiloGL框架如何上手.GLSL语言以及简单的绘制一个方块(见PhiloGL学习(1)--场景创建及二维方块加载).本文很简单,我们一起来让这个方块动起来. 一. ...
- PhiloGL学习(1)——场景创建及方块欲露还羞出水面
前言 上一篇文章中介绍了我认识PhiloGL框架的机缘以及初步的探讨(见JS前端三维地球渲染--中国各城市航空路线展示),在此文中仅仅对此框架进行了简单介绍并初步介绍了一些该框架的知识.首先三维这个东 ...
- PhiloGL学习(4)——三维对象、加载皮肤
前言 上一篇文章中介绍了如何响应鼠标和键盘事件,本文介绍如何加载三维对象并实现给三维对象添加一个漂亮的皮肤. 一. 原理分析 我对三维的理解为:所谓三维对象无非是多个二维对象拼接到一起,贴图就更简单了 ...
- PhiloGL学习(5)——神说要有光,便有了光
前言 上一篇文章中介绍了如何创建三维对象及加载皮肤,本文为大家介绍如何为场景添加光源. 一. 原理分析 光在任何地方都是非常重要的,无论在哪里都说是要发光发热,光和热也是分不开的.光线分为点光源和线光 ...
- PhiloGL学习(3)——程序员的法宝—键盘、鼠标
前言 上一篇文章中介绍了如何让对象动起来,本文介绍如何让场景响应我们的鼠标和键盘以控制场景的缩放及对象的转动和移动等. 一. 原理分析 有了上一篇文章的基础,我们已经知道了如何让场景和对象动起来.本文 ...
- 学习.NET是因为热爱 or 兴趣 or 挣钱?
看到最近园子里掀起了“.NET快不行了”.“.NET工资太低了”.“转行做XX”等一系列之风,不由得想说点什么,我只是基于自己的观点,你认同或者不认同,我就是这样认为,无所谓对与错,写文章就是为了交流 ...
- opengl入门学习
OpenGL入门学习 说起编程作图,大概还有很多人想起TC的#include <graphics.h>吧? 但是各位是否想过,那些画面绚丽的PC游戏是如何编写出来的?就靠TC那可怜的640 ...
- 20145213《Java程序设计》第七周学习总结
20145213<Java程序设计>第七周学习总结 教材学习内容总结 周末快乐的时间总是短暂的,还没好好感受就到了要写博客的周日.有人喟叹时间都去哪儿了,那本周我们就来认识一下Java里的 ...
- 雅思创始人Keith Taylor谈英语学习
雅思创始人Keith Taylor谈英语学习 “要学的是信息,而不是语言” 我们要学习一个国家的语言就得知道这个国家的方方面面.要学习英语就得了解英美国家的社会.经济.人文.历史等各方面的信息. 大家 ...
随机推荐
- Python学习笔记011_模块_标准库_第三方库的安装
容器 -> 数据的封装 函数 -> 语句的封装 类 -> 方法和属性的封装 模块 -> 模块就是程序 , 保存每个.py文件 # 创建了一个hello.py的文件,它的内容如下 ...
- ArrayList底层实现原理
ArrayList概述: ArrayList是List接口的可变数组的实现.实现了所有可选列表操作,并允许包括null在内的所有元素.除了实现列表接口外,此类还提供一些方法来操作内部用来存储列表的数组 ...
- CSS3微信启动页天王星版
今天被微信启动页刷屏了. 一直还以为启动页背景显示的月球的.今天才了解到这么有含义. 我也蹭一下微信的热度,做一个HTML+CSS版本的. 用CSS画地球太困难了,来个简单点的,天王星版. 主要使用到 ...
- 深入理解计算机系统chapter2
---恢复内容开始--- 整数表示: 反码和原码都会有正零和负零 有符号整数和无符号整数之间的转换 反之 扩展一个数字的位级表示 截断操作 无符号加法的益处 补码的加法 规格化的值:E=e-bias ...
- 郑厂长系列故事——排兵布阵 hdu4539(状态压缩DP)
郑厂长系列故事——排兵布阵 Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)To ...
- Codecraft-17 and Codeforces Round #391 (Div. 1 + Div. 2, combined)D. Felicity's Big Secret Revealed
题目连接:http://codeforces.com/contest/757/problem/D D. Felicity's Big Secret Revealed time limit per te ...
- 将childNodes返回的数据转化维数组的方法
//将childNodes返回的数据转化为数组的方法 function convertToArray(nodes){ var array=null; try{ array=Array.prototyp ...
- 寻找bug并消灭系列——记录在Android开发所遇到的bug(二)
bug 1: bug描述: 无法成功地将edittext中的内容传入数据库中 bug动图: 经过: 最近写了个项目,项目要使用到SQL数据库,由于没有相关知识,便是找到了各种资料开始了自学之旅,在de ...
- Message Queuing(MSMQ)
一.前言 MicroSoft Message Queuing(微软消息队列)是在多个不同的应用之间实现相互通信的一种异步传输模式,相互通信的应用可以分布于同一台机器上,也可以分布于相连的网络空间中的任 ...
- 【转】TCP/IP报文格式
1.IP报文格式 IP协议是TCP/IP协议族中最为核心的协议.它提供不可靠.无连接的服务,也即依赖其他层的协议进行差错控制.在局域网环境,IP协议往往被封装在以太网帧(见本章1.3节)中传送.而所有 ...