1.动画构思

就是中间有个红太阳,外面有几个行星球体环绕着太阳在各自轨道上做圆周运动。下面是效果图

2.基本要素

使用threejs的基本构件包括:渲染器(renderer),相机(camera),场景(scene),光线(light)。首先将这些基本构件都分别初始化一下。

2.1初始化渲染器

渲染器可以理解为画布,用于绘制照相机观察到的画面,一般使用WebGLRenderer这种类型的渲染器,其余还有CanvasRenderer等别的类型,这里不做介绍了。

  1. function initThree(){
  2. width=window.innerWidth;
  3. height=window.innerHeight;
  4. renderer=new THREE.WebGLRenderer({
  5. antialias : true //开启抗锯齿
  6. });
  7. renderer.setSize(width,height);//设置画布大小
  8. renderer.setClearColor(0x000000);//设置画布背景颜色
  9. document.body.appendChild(renderer.domElement);//将画布追加到html文档中
    }

2.2初始化照相机

照相机分为两种,透视相机(PerspectiveCamera)和正交相机(OrthographicCamera)。

透视相机符合人的视觉直接,近大远小。新建一个透视相机

  1. new THREE.PerspectiveCamera(
  2. ,//视野角度
  3. width/height,//纵横比=>视野形状长形还是方形
  4. ,//近景距离
  5. );//远景距离

正交相机,物体大小不随距离远近变化,常用于建筑方面,新建一个正交相机视景体

  1. new THREE.OrthographicCamera( //正交摄像机=>视景体
  2. window.innerWidth / - 2, //左平面距离视点距离
  3. window.innerWidth / 2, //右平面距离视点距离
  4. window.innerHeight / 2, //上平面距离视点距离
  5. window.innerHeight / - 2, //下平面距离视点距离
  6. 10, //近平面距离视点距离
  7. 1000 );//远平面距离视点距离

这里我使用透视相机,初始化相机代码

  1. function initCamera(){
  2. camera=new THREE.PerspectiveCamera(45,width/height,1,10000);
  3. camera.position.x=100;
  4. camera.position.y=100;
  5. camera.position.z=100;
  6. camera.up.x=0;
  7. camera.up.y=1;
  8. camera.up.z=0;
  9. camera.lookAt(0,0,0);
    }

camera.position设置了相机的位置,这里是在空间坐标系中的(100,100,100)这个位置。camera.up设置了相机头部朝向方向,这里设置了y=1,就是(0,0,0)和(0,1,0)连线作为上方,就是y轴向上,整个空间坐标系以此方向来放置。

camera.lookAt设置了相机视点方向,就是照相机往哪里拍的问题。

2.3初始化场景

只需要scene=new THREE.Scene();场景是存放光线,物体等各类构件的容器,只有在场景中的物体才能被相机拍到。

2.4初始化光源

光源一共有好多种,各类光源都有自身的特性。这里介绍几种光源:

环境光(AmbientLight):各个位置的光线相同,无法确定光源。例:new THREE.AmbientLight( 0xff0000 );//参数:颜色;

方向光(DirectionLight):来自某一方向的光源。例:new THREE.DirectionalLight(0xFF0000,1); //参数:颜色,强度(0~1);

点光源(PointLight):光线来自某一点。例:new THREE.PointLight( 0xff0000,1,0 );//参数:颜色,光源强度(0~1),距离(从最大值衰减到0,需要的距离,默认为0,不衰减);

聚光灯(SpotLight):例:new THREE.SpotLight( 0xff0000,1,0,30,0 );//参数:颜色,光源强度(0~1),距离(从最大值衰减到0,需要的距离,默认为0,不衰减),角度(聚光灯着色的角度,用弧度作为单位,这个角度是和光源的方向形成的角度),exponent(光源模型中,衰减的一个参数,越大衰减约快。)

这里我们使用方向光:

  1. function initLight(){
  2. light=new THREE.DirectionalLight(0xffffff,1);
  3. light.position.set(10,10,3);//设置光源方向
  4. scene.add(light);//将光源添加到场景中
  5. }

2.5画个球体

首先需要绘制一个几何形状,threejs里面有很多的二维和三维形状,比如长方体(cubeGeometry),平面(PlaneGeometry),球体(SphereGeometry),圆形(CircleGeometry),圆柱体(CylinderGeometry)等,这里不展开了。

这里需要绘制个球形(SphereGeometry):

  1. THREE.SphereGeometry(
  2. radius, //半径
  3. segmentsWidth, //经度上的切片数 (在起始经度和经度跨度的范围内,像切西瓜从上往下切,切的片数)
  4. segmentsHeight, //纬度上的切片数 (在起始纬度和纬度跨度的范围内,切西瓜从左往右切,切出来的层数)
  5. phiStart, //经度开始的弧度
  6. phiLength, //经度跨过的弧度
  7. thetaStart, //纬度开始的弧度
  8. thetaLength //纬度经过的弧度
  9. )

初始化球体代码

  1. var sunMesh;//太阳
  2. function initSun(){
  3. var geometry=new THREE.SphereGeometry(10,28,22);//球体半径为10,经度切为28份,纬度切为22份(根据需要自行设置)
  4. var material=new THREE.MeshLambertMaterial({color:0xff0000})//使用Lambert材质,设为红色
  5. sunMesh=new THREE.Mesh(geometry,material);//使用网格创建物体
  6. sunMesh.position.set(0,0,0);//设置球体位置
  7. scene.add(sunMesh);//添加到场景中
  8. }

关于材质这里介绍三种:

基本材质(BasicMaterial):渲染后物体的颜色始终为该材质的颜色,而不会由于光照产生明暗、阴影效果。如果没有指定材质的颜色,则颜色是随机的。

Lambert材质(LambertMaterial):是符合 Lambert 光照模型的材质。Lambert 光照模型的主要特点是只考虑漫反射而不考虑镜面反射的效果,因而对于金属、镜子等需要镜面反射效果的物体就不适应,对于其他大部分物体的漫反射效果都是适用的。

Phong材质(PhongMaterial):是符合 Phong 光照模型的材质。和 Lambert 不同的是,Phong 模型考虑了镜面反射的效果,因此对于金属、镜面的表现尤为适合。

构造函数都要传入配置项options,常用属性包括:

  1. visible :是否可见,默认为 true
  2. side :渲染面片正面或是反面,默认为正面 THREE.FrontSide ,可设置为反面THREE.BackSide ,或双面 THREE.DoubleSide
  3. wireframe :是否渲染线而非面,默认为 false
  4. color :十六进制 RGB 颜色,如红色表示为 0xff0000
  5. map :使用纹理贴图

2.6,绘制多个不同颜色的球体

使用循环体循环上面的代码,注意根据需要修改一些参数,比如半径和位置信息,然后将生成的球体存放到数组中方便后面动画中使用。

生成16进制颜色

  1. function getColor(){
  2. //定义字符串变量colorValue存放可以构成十六进制颜色值的值
  3. var colorValue="0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f";
  4. //以","为分隔符,将colorValue字符串分割为字符数组["0","1",...,"f"]
  5. var colorArray = colorValue.split(",");
  6. var color="#";//定义一个存放十六进制颜色值的字符串变量,先将#存放进去
  7. //使用for循环语句生成剩余的六位十六进制值
  8. for(var i=0;i<6;i++){
  9. //colorArray[Math.floor(Math.random()*16)]随机取出
  10. // 由16个元素组成的colorArray的某一个值,然后将其加在color中,
  11. //字符串相加后,得出的仍是字符串
  12. color+=colorArray[Math.floor(Math.random()*16)];
  13. }
  14. return color;
  15. }

循环新建球体

  1. var balls=[];
  2. function initball(){
  3. for(var i=2;i<6;i++){
  4. var geometry=new THREE.SphereGeometry(2+i/2,22,16);
  5. var material=new THREE.MeshLambertMaterial({color:getColor()})
  6. ball=new THREE.Mesh(geometry,material);
  7. ball.position.set(10*i,0,0);
  8. scene.add(ball);
  9. balls.push(ball)
  10. }
  11. }

2.7绘制圆环

一种简单的方式是使用几何形状圆形来绘制,缺点是会有多余的切片线。

  1. var circles=[]
  2. function initCycle(){
  3. //用画二维图形的方式画圆
  4. for(var i=2;i<6;i++){
  5. var radius=10*i;//设置同心圆,只有半径不一样
  6. var geometry=new THREE.CircleGeometry(radius,10);//半径,分段数
  7. var material=new THREE.MeshBasicMaterial({color:0xffa500,wireframe:true })
  8. var cycleMesh=new THREE.Mesh(geometry,material);
  9. cycleMesh.position.set(0,0,0);
  10. cycleMesh.rotateX(Math.PI/2);//默认是绘制在xy平面的,所以这里要旋转下放到xz平面
  11. scene.add(cycleMesh);
  12. circles.push(radius)
  13. }
  14. }

另一种方式是用画线的方式来画圆THREE.Line

  1. function initCycle2(){
  2. //用画线方式画圆
  3. for(var j=2;j<6;j++){
  4. var radius=10*j;
  5. var lineGeometry=new THREE.Geometry();
  6. for(var i=0;i<2*Math.PI;i+=Math.PI/30){
  7. lineGeometry.vertices.push(new THREE.Vector3(radius*Math.cos(i),0,radius*Math.sin(i),0))
  8. }
  9. var material=new THREE.LineBasicMaterial({color:0xffa500 })
  10. var cycleMesh=new THREE.Line(lineGeometry,material);
  11. cycleMesh.position.set(0,0,0);
  12. scene.add(cycleMesh);
  13. circles.push(radius)
  14. }
  15. }

首先定义一个几何形状,在几何形状的vertices中push进每一个点(Vector3),点坐标是根据角度计算得出的。弧度制中一整个圆就是2PI的弧度,PI/180代表1角度,分成60段,每段就是PI/30的角度。再由sin和cos计算出坐标值。

现在运行下可以看到效果啦

  1. function init(){
  2. initThree();
  3. initCamera();
  4. initScene();
  5. initLight();
  6. initSun();
  7. initball();
  8. // initCycle();
  9. initCycle2();
  10. renderer.render( scene, camera );
  11. }

3.让画面动起来

我们希望不同的球体以不同的速度做圆周运动。这里的速度应该是角速度,也就是不同的球体要在每一帧中转过不同的角度。然后根据每个球体所在圆环的半径来重新设置球体的坐标,球体就会运动起来了。

首先设置统一的起始角度,设置在x轴上,那么角度就是PI/2。然后设置每次转动的角度。

  1. var deg=Math.PI/2;
  2. function ballAnim(){
  3. deg+=1/6*Math.PI/180;//每次转动1/6度
  4. balls.forEach((ball,index)=>{
  5. var ballDeg=3*deg/(index+1);//根据索引值设置每个球体转动不同的角度
  6. ball.position.x=Math.sin(ballDeg)*circles[index];
  7. ball.position.z=Math.cos(ballDeg)*circles[index];
  8. })
  9. }

最后是使用requestAnimationFrame来不停的刷新画面,从而产生动画。

  1. function anim(){
  2. ballAnim();
  3. renderer.render( scene, camera );
  4. requestAnimationFrame(anim);
  5. }
  6.  
  7. //执行即可
  8. init()
  9. anim()

4.性能监控(FPS)

引入stats.js,新建一个实例,在动画函数anim中调用其update方法。

  1. function setStats(){
  2. stats=new Stats();
  3. stats.domElement.style.position='absolute';
  4. stats.domElement.style.left='0';
  5. stats.domElement.style.top='0';
  6. document.body.appendChild(stats.domElement);
  7. }

修改anim方法

  1. function anim(){
  2. ballAnim();
  3. renderer.render( scene, camera );
  4. stats.update()
  5. requestAnimationFrame(anim);
  6. }

5.完整代码

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>demo-sun</title>
  6. <style>
  7. canvas{width:100%;height:100%;}
  8. </style>
  9. <script src="./three.min.js"></script>
  10. <script src="./stats.js"></script>
  11. </head>
  12. <body>
  13. <script>
  14. var renderer,camera,scene,stats,light;
  15. var width,height;
  16.  
  17. function initThree(){
  18. width=window.innerWidth;
  19. height=window.innerHeight;
  20. renderer=new THREE.WebGLRenderer({
  21. antialias : true //开启抗锯齿
  22. });
  23. renderer.setSize(width,height);//设置画布大小
  24. renderer.setClearColor(0x000000);//设置画布背景颜色
  25. document.body.appendChild(renderer.domElement);//将画布追加到html文档中
  26. }
  27.  
  28. function setStats(){
  29. stats=new Stats();
  30. stats.domElement.style.position='absolute';
  31. stats.domElement.style.left='0';
  32. stats.domElement.style.top='0';
  33. document.body.appendChild(stats.domElement);
  34. }
  35.  
  36. function initCamera(){
  37. camera=new THREE.PerspectiveCamera(45,width/height,1,10000);
  38. camera.position.x=100;
  39. camera.position.y=100;
  40. camera.position.z=100;
  41. camera.up.x=0;
  42. camera.up.y=1;
  43. camera.up.z=0;
  44. camera.lookAt(0,0,0);
  45. }
  46.  
  47. function initScene(){
  48. scene=new THREE.Scene();
  49. }
  50.  
  51. function initLight(){
  52. light=new THREE.DirectionalLight(0xffffff,1);
  53. light.position.set(10,10,3);
  54. scene.add(light);
  55. }
  56.  
  57. var sunMesh;//太阳
  58. function initSun(){
  59. var geometry=new THREE.SphereGeometry(10,28,22);//球体半径为10,经度切为28份,纬度切为22份(根据需要自行设置)
  60. var material=new THREE.MeshLambertMaterial({color:0xff0000})//使用Lambert材质,设为红色
  61. sunMesh=new THREE.Mesh(geometry,material);
  62. sunMesh.position.set(0,0,0);//设置球体位置
  63. scene.add(sunMesh);//添加到场景中
  64. }
  65.  
  66. var balls=[];
  67. function initball(){
  68. for(var i=2;i<6;i++){
  69. var geometry=new THREE.SphereGeometry(2+i/2,22,16);
  70. var material=new THREE.MeshLambertMaterial({color:getColor()})
  71. ball=new THREE.Mesh(geometry,material);
  72. ball.position.set(10*i,0,0);
  73. scene.add(ball);
  74. balls.push(ball)
  75. }
  76. }
  77.  
  78. function getColor(){
  79. //定义字符串变量colorValue存放可以构成十六进制颜色值的值
  80. var colorValue="0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f";
  81. //以","为分隔符,将colorValue字符串分割为字符数组["0","1",...,"f"]
  82. var colorArray = colorValue.split(",");
  83. var color="#";//定义一个存放十六进制颜色值的字符串变量,先将#存放进去
  84. //使用for循环语句生成剩余的六位十六进制值
  85. for(var i=0;i<6;i++){
  86. //colorArray[Math.floor(Math.random()*16)]随机取出
  87. // 由16个元素组成的colorArray的某一个值,然后将其加在color中,
  88. //字符串相加后,得出的仍是字符串
  89. color+=colorArray[Math.floor(Math.random()*16)];
  90. }
  91. return color;
  92. }
  93.  
  94. var circles=[]
  95. function initCycle(){
  96. //用画二维图形的方式画圆
  97. for(var i=2;i<6;i++){
  98. var radius=10*i;//设置同心圆,只有半径不一样
  99. var geometry=new THREE.CircleGeometry(radius,10);//半径,分段数
  100. var material=new THREE.MeshBasicMaterial({color:0xffa500,wireframe:true })
  101. var cycleMesh=new THREE.Mesh(geometry,material);
  102. cycleMesh.position.set(0,0,0);
  103. cycleMesh.rotateX(Math.PI/2);//默认是绘制在xy平面的,所以这里要旋转下放到xz平面
  104. scene.add(cycleMesh);
  105. circles.push(radius)
  106. }
  107. }
  108.  
  109. function initCycle2(){
  110. //用画线方式画圆
  111. for(var j=2;j<6;j++){
  112. var radius=10*j;
  113. var lineGeometry=new THREE.Geometry();
  114. for(var i=0;i<2*Math.PI;i+=Math.PI/30){
  115. lineGeometry.vertices.push(new THREE.Vector3(radius*Math.cos(i),0,radius*Math.sin(i),0))
  116. }
  117. var material=new THREE.LineBasicMaterial({color:0xffa500 })
  118. var cycleMesh=new THREE.Line(lineGeometry,material);
  119. cycleMesh.position.set(0,0,0);
  120. scene.add(cycleMesh);
  121. circles.push(radius)
  122. }
  123. }
  124.  
  125. var deg=Math.PI/2;
  126.  
  127. function ballAnim(){
  128. deg+=1/6*Math.PI/180;//每次转动1/6度
  129. balls.forEach((ball,index)=>{
  130. var ballDeg=3*deg/(index+1);//根据索引值设置每个球体转动不同的角度
  131. ball.position.x=Math.sin(ballDeg)*circles[index];
  132. ball.position.z=Math.cos(ballDeg)*circles[index];
  133. })
  134. }
  135.  
  136. function init(){
  137. initThree();
  138. setStats();
  139. initCamera();
  140. initScene();
  141. initLight();
  142. initSun();
  143. initball();
  144. // initCycle();
  145. initCycle2();
  146. }
  147.  
  148. function anim(){
  149. ballAnim();
  150. renderer.render( scene, camera );
  151. stats.update()
  152. requestAnimationFrame(anim);
  153. }
  154.  
  155. init()
  156. anim()
  157.  
  158. </script>
  159. </body>
  160. </html>

threejs行星运动小demo总结的更多相关文章

  1. 新手 gulp+ seajs 小demo

    首先,不说废话,它的介绍和作者就不在多说了,网上一百度一大堆: 我在这里只是来写写我这2天抽空对seajs的了解并爬过的坑,和实现的一个小demo(纯属为了实现,高手请绕道); 一.环境工具及安装 1 ...

  2. Nancy之基于Nancy.Hosting.Self的小Demo

    继昨天的Nancy之基于Nancy.Hosting.Aspnet的小Demo后, 今天来做个基于Nancy.Hosting.Self的小Demo. 关于Self Hosting Nancy,官方文档的 ...

  3. Nancy之基于Nancy.Owin的小Demo

    前面做了基于Nancy.Hosting.Aspnet和Nancy.Hosting.Self的小Demo 今天我们来做个基于Nancy.Owin的小Demo 开始之前我们来说说什么是Owin和Katan ...

  4. Nancy之基于Self Hosting的补充小Demo

    前面把Hosting Nancy with ASP.NET.Self Hosting Nancy和Hosting Nancy with OWIN 以demo的形式简单描述了一下. 这篇是为Self H ...

  5. [Unity3D]做个小Demo学习Input.touches

    [Unity3D]做个小Demo学习Input.touches 学不如做,下面用一个简单的Demo展示的Input.touches各项字段,有图有真相. 本项目已发布到Github,地址在(https ...

  6. Android -- 自定义View小Demo,动态画圆(一)

    1,转载:(http://blog.csdn.NET/lmj623565791/article/details/24500107),现在如下图的效果: 由上面的效果图可以看到其实是一个在一个圆上换不同 ...

  7. Win10 FaceAPI小demo开发问题汇总

    Win10 FaceAPI小demo开发问题汇总 最近使用微软牛津计划做一个小demo,使用FaceAPI做一个小应用,实现刷脸的功能.开发的过程中用到几个问题,具体如下: Stream 与IRand ...

  8. 模仿京东顶部搜索条效果制作的一个小demo

    最近模仿京东顶部搜索条效果制作的一个小demo,特贴到这里,今后如果有用到可以参考一下,代码如下 #define kScreenWidth [UIScreen mainScreen].bounds.s ...

  9. Android学习小Demo一个显示行线的自定义EditText

    今天在处理一个EditText的时候,想着把EditText做成像一本作业本上的纸一样,每一行都可以由线条隔开,具体效果如下: 1)最开始的思路 一开始的想法是很简单的,找出每一行的高度,然后一行一行 ...

随机推荐

  1. iOS 作为蓝牙外设广播信息

    苹果蓝牙后台的限制,原本广播会有两个段分别是localName和serviceUUID这两块,但现在后台广播时,是不发送在这两段的 手机app可以作为一个蓝牙外设端来模拟外设硬件,但广播包里的数据只能 ...

  2. 模块之 time datetime random json pickle os sys hashlib collections

    目录 1. time模块 1.1表示时间的几种方式: 1.2格式化字符串的时间格式 1.3不同格式时间的转换 2.datetim模块 3.random模块 4. json模块 4.1dumps.loa ...

  3. OpenCV和ffmpeg编码资料分享

    本博也是在进行视频转码的学习道路上,也只是菜鸟一枚,收集了大量的资料,想在这和同路人分享一下 在博园里我发表一个JavaCV的随笔,里面介绍了JavaCV这个框架,它整合了OpenCV和ffmpeg等 ...

  4. request-html 简单爬虫

    import asyncio from requests_html import HTMLSession url = 'http://www.xiaohuar.com/hua/' session = ...

  5. JAVA命名规范性总结

    一:命名规范 1.项目名全部小写2.包名全部小写 在新建一个包项目时可能会涉及到多层设计,每层的包名要遵循包名全部小写的规范,如图在一个功能的逐层上级的包名全部是小写的字符组成3.类名的命名要遵循首字 ...

  6. 根据byte数组,生成文件

    /** * 根据byte数组,生成文件 * filePath 文件路径 * fileName 文件名称(需要带后缀,如*.jpg.*.java.*.xml) */ public static void ...

  7. MVC模式:action、dao、model、service、util

    这就是一个典型的MVC: action:主要是Struts2,用来做跳转,比如jsp页面提交的表单就是进入到action里面,然后action再调用service里面的逻辑,最后返回到jsp响应请求. ...

  8. SpringCloud学习心得之Eureka注册中心的基本使用

      SpringCloud学习心得——Eureka注册中心 示范代码链接 定义 SpringCloud Eureka是 SpringCloud Netflix微服务套件的一部分,基于 REST 的服务 ...

  9. Caused by: org.xml.sax.SAXParseException; lineNumber: 64; columnNumber: 27; The entity name must immediately follow the '&' in the entity reference.

    java.lang.IllegalStateException: Failed to load ApplicationContext at org.springframework.test.conte ...

  10. 《AlwaysRun!》第七次作业:团队项目设计完善&编码

    项目 内容 这个作业属于哪个课程 https://home.cnblogs.com/u/nwnu-daizh/   这个作业的要求在哪里 https://www.cnblogs.com/nwnu-da ...