前言

话说上一篇文章结尾讲到这一篇要做一个地球自转以及月球公转的三维动画,提笔,不对,是提键盘开始写的时候脑海中突然出现了几年前春晚风靡的那首歌:蒙古族小丫头唱的快乐的一家。闲言莫提,进入正题。

一、 原理分析

场景涉及两个对象,一个是地球、一个是月球,当然这基本是废话,不过还可以再添加一个对象,月球的公转轨迹。地球和月球都可以用一个球来模拟(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_ESprecision highp float;#endifvarying 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)控制最终绘制,这与之前都没有区别,所以这里只是封装。有了此对象之后,其添加、更新等就与球等对象相同了。

七、 总结

本文简单介绍了绘制自转的地球以及公转的月球,看似很简单,其实中间有很多的坑,当然是因为自己确实水平有限,然而这正是我做此场景的本意,当然做完之后更加深刻的感受到了这一点。你看地球自转多么简单,月球公转多么简单,而人类才是地球上多么微不足道的一点点,你把自己搞那么复杂干什么,面对着永不停息转动的地球和月球你感受不到自己的渺小吗?再上升到整个宇宙不敢想象!所以,迈开自己的腿,多去看看更大的世界,不求能出得了宇宙,只求能够在有生之年走遍大部分地球,做一个见多识广的程序员,有一个快乐的一家!本系列文章写到这里,已经基本结束,后面如果有新的感悟也会继续写出。

转载于:https://www.cnblogs.com/shoufengwei/p/7755199.html

PhiloGL学习(6)——深情奉献:快乐的一家相关推荐

  1. PhiloGL学习(5)——神说要有光,便有了光

    前言 上一篇文章中介绍了如何创建三维对象及加载皮肤,本文为大家介绍如何为场景添加光源. 一. 原理分析 光在任何地方都是非常重要的,无论在哪里都说是要发光发热,光和热也是分不开的.光线分为点光源和线光 ...

  2. 高数学习—— 一元函数积分学的快乐

    点击上方蓝字可直接关注!方便下次阅读.如果对你有帮助,麻烦点个在看或点个赞,感谢~         文章首发  公众号-- Pou光明 大家好,这段时间我并没有荒废时间,做了自己认为有意义的几件事: ...

  3. 拼插机器人课和围棋课_创想童年机器人编程课程,让孩子享受学习和游戏的快乐...

    人工智能不是互联网的产品,而将是一个时代. 如果说人工智能学科是一棵树,那么我们现在所发展的互联网.计算机编程等技术只算是刚刚为它培好了土.将来,从算法程序到机器人的"大脑"构建, ...

  4. 在快乐中学习,在学习中寻找乐趣,这是我们的学习环境。

    一次偶然的机会,在朋友的介绍下,我了解到了一家专门传授计算机类技术的培训机构,叫传智播客.         我听说这家机构有免费体验基础班的活动,于是报名参加基础班培训.在报道的那一天,我来到了这家著 ...

  5. HTML的快乐之旅_全方位的学习html_注意细节——细节决定成败

    补充:为了大家更好的掌握前端HTML+CSS的基础,作者写了相关项目方便大家理解与掌握,不懂的欢迎向我咨询. 项目介绍 前端练手项目(1) 前端练手项目(2) 此处学习略过HTML的人文历史环节HTM ...

  6. 程序员如何快乐学习?

    经历了小学到大学的学习,经历了一次又一次应试学习,经历了实践操作的 学车学习,我们的一生都是在不断的学习着.........我们不断的在体会着学习这一过程,那究竟什么才是学习?又如何学习?学习是一件痛 ...

  7. 高年级学长给计算机大一学生的学习建议

      自我介绍, 简单讲述我大学的学习的历程,成果和感想.(1分钟) 我一直都感慨本年级许多同学在大一时因为缺乏好指引,在一开始就对编程很害怕,对计算机的学习没有开好头,动手能力长期跟不上,空会理论,不 ...

  8. 绝对编码和增量编码_用户体验设计师应该学习编码吗? 绝对

    绝对编码和增量编码 Even though I was trained as a graphic designer, I've never limited myself to that field e ...

  9. 【小白】【大学】一名嵌入式软件开发小白的单片机学习历程、心路历程、经验分享

    我的嵌入式学习之路 我为什么写这篇文章 先简单介绍一下我自己 与每一个技术小白共勉 这篇文章写给谁 我的学习经验分享 我的成长之路 我的单片机学习历程分享 51系列单片机学习历程 预备知识:(可以查百 ...

最新文章

  1. linux valgrind Memcheck--内存检查工具
  2. IE 无法点击文本框或输入文字
  3. studio添加依赖工程方法
  4. SQL Server 字符串操作
  5. 15家大数据公司被调查,数据行业面临大清洗?
  6. 科普:算法岗是什么?我适不适合算法岗?选什么方向的算法岗?
  7. ADFS 登录页面自定义
  8. 基于bootstrap模态框的日期选择器
  9. oracle报错查询动态视图,oracle基表和动态性能视图
  10. 通讯录管理系统课设使用c编写基于链表增查删改分组文本操作随程序实时同步
  11. TokyoTyrant的管理工具tcrmgr使用小记
  12. inline-block和float
  13. 研大考研:2015考研政治认识论知识点
  14. uni-app ucharts无法显示
  15. (MDY)2021秋季软件工程 alpha冲刺完善
  16. spring-cloud-gateway GlobalFilter 自定义鉴权失败 返回数据结构
  17. stm32+esp8266 通过MQTT发送到thingsboard平台
  18. [渝粤教育] 广东-国家-开放大学 21秋期末考试网络金融10248k2
  19. 野火EBF 6ULL 开发板 烧录ubuntu18 emmc 固件 并安装桌面
  20. 如何免费下载道客巴巴文档

热门文章

  1. 一直以来伴随我的一些学习习惯(part2)
  2. 游戏配音中常见的节奏类型
  3. 2021_SIGIR_Social Recommendation with Implicit Social Influence
  4. 186、项目超过255个摄像机怎么分配IP地址
  5. 【DSP数字信号处理学习笔记】—— 详细推导DFT的快速实现算法:FFT 基于库利-图基算法的实现
  6. 文件服务器 小文件,小型文件服务器
  7. r语言 柱状图加星号_如何在地图上添加柱形图(R语言)
  8. c语言程序跟踪调试,VC6断点调试之监视变量
  9. visio2007一些知识
  10. proe导出为html文件怎么打开,在Pro/E中打开unigraphiCS文件时,如何选取“UnigraphiCS(*.prt)”过滤器?...