1.动画构思

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

2.基本要素

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

2.1初始化渲染器

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

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

2.2初始化照相机

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

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

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

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

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

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

function initCamera(){
camera=new THREE.PerspectiveCamera(45,width/height,1,10000);
camera.position.x=100;
camera.position.y=100;
camera.position.z=100;
camera.up.x=0;
camera.up.y=1;
camera.up.z=0;
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(光源模型中,衰减的一个参数,越大衰减约快。)

这里我们使用方向光:

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

2.5画个球体

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

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

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

初始化球体代码

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

关于材质这里介绍三种:

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

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

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

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

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

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

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

生成16进制颜色

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

循环新建球体

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

2.7绘制圆环

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

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

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

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

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

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

function init(){
initThree();
initCamera();
initScene();
initLight();
initSun();
initball();
// initCycle();
initCycle2();
renderer.render( scene, camera );
}

3.让画面动起来

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

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

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

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

function anim(){
ballAnim();
renderer.render( scene, camera );
requestAnimationFrame(anim);
} //执行即可
init()
anim()

4.性能监控(FPS)

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

function setStats(){
stats=new Stats();
stats.domElement.style.position='absolute';
stats.domElement.style.left='0';
stats.domElement.style.top='0';
document.body.appendChild(stats.domElement);
}

修改anim方法

function anim(){
ballAnim();
renderer.render( scene, camera );
stats.update()
requestAnimationFrame(anim);
}

5.完整代码

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>demo-sun</title>
<style>
canvas{width:100%;height:100%;}
</style>
<script src="./three.min.js"></script>
<script src="./stats.js"></script>
</head>
<body>
<script>
var renderer,camera,scene,stats,light;
var width,height; function initThree(){
width=window.innerWidth;
height=window.innerHeight;
renderer=new THREE.WebGLRenderer({
antialias : true //开启抗锯齿
});
renderer.setSize(width,height);//设置画布大小
renderer.setClearColor(0x000000);//设置画布背景颜色
document.body.appendChild(renderer.domElement);//将画布追加到html文档中
} function setStats(){
stats=new Stats();
stats.domElement.style.position='absolute';
stats.domElement.style.left='0';
stats.domElement.style.top='0';
document.body.appendChild(stats.domElement);
} function initCamera(){
camera=new THREE.PerspectiveCamera(45,width/height,1,10000);
camera.position.x=100;
camera.position.y=100;
camera.position.z=100;
camera.up.x=0;
camera.up.y=1;
camera.up.z=0;
camera.lookAt(0,0,0);
} function initScene(){
scene=new THREE.Scene();
} function initLight(){
light=new THREE.DirectionalLight(0xffffff,1);
light.position.set(10,10,3);
scene.add(light);
} var sunMesh;//太阳
function initSun(){
var geometry=new THREE.SphereGeometry(10,28,22);//球体半径为10,经度切为28份,纬度切为22份(根据需要自行设置)
var material=new THREE.MeshLambertMaterial({color:0xff0000})//使用Lambert材质,设为红色
sunMesh=new THREE.Mesh(geometry,material);
sunMesh.position.set(0,0,0);//设置球体位置
scene.add(sunMesh);//添加到场景中
} var balls=[];
function initball(){
for(var i=2;i<6;i++){
var geometry=new THREE.SphereGeometry(2+i/2,22,16);
var material=new THREE.MeshLambertMaterial({color:getColor()})
ball=new THREE.Mesh(geometry,material);
ball.position.set(10*i,0,0);
scene.add(ball);
balls.push(ball)
}
} function getColor(){
//定义字符串变量colorValue存放可以构成十六进制颜色值的值
var colorValue="0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f";
//以","为分隔符,将colorValue字符串分割为字符数组["0","1",...,"f"]
var colorArray = colorValue.split(",");
var color="#";//定义一个存放十六进制颜色值的字符串变量,先将#存放进去
//使用for循环语句生成剩余的六位十六进制值
for(var i=0;i<6;i++){
//colorArray[Math.floor(Math.random()*16)]随机取出
// 由16个元素组成的colorArray的某一个值,然后将其加在color中,
//字符串相加后,得出的仍是字符串
color+=colorArray[Math.floor(Math.random()*16)];
}
return color;
} var circles=[]
function initCycle(){
//用画二维图形的方式画圆
for(var i=2;i<6;i++){
var radius=10*i;//设置同心圆,只有半径不一样
var geometry=new THREE.CircleGeometry(radius,10);//半径,分段数
var material=new THREE.MeshBasicMaterial({color:0xffa500,wireframe:true })
var cycleMesh=new THREE.Mesh(geometry,material);
cycleMesh.position.set(0,0,0);
cycleMesh.rotateX(Math.PI/2);//默认是绘制在xy平面的,所以这里要旋转下放到xz平面
scene.add(cycleMesh);
circles.push(radius)
}
} function initCycle2(){
//用画线方式画圆
for(var j=2;j<6;j++){
var radius=10*j;
var lineGeometry=new THREE.Geometry();
for(var i=0;i<2*Math.PI;i+=Math.PI/30){
lineGeometry.vertices.push(new THREE.Vector3(radius*Math.cos(i),0,radius*Math.sin(i),0))
}
var material=new THREE.LineBasicMaterial({color:0xffa500 })
var cycleMesh=new THREE.Line(lineGeometry,material);
cycleMesh.position.set(0,0,0);
scene.add(cycleMesh);
circles.push(radius)
}
} var deg=Math.PI/2; function ballAnim(){
deg+=1/6*Math.PI/180;//每次转动1/6度
balls.forEach((ball,index)=>{
var ballDeg=3*deg/(index+1);//根据索引值设置每个球体转动不同的角度
ball.position.x=Math.sin(ballDeg)*circles[index];
ball.position.z=Math.cos(ballDeg)*circles[index];
})
} function init(){
initThree();
setStats();
initCamera();
initScene();
initLight();
initSun();
initball();
// initCycle();
initCycle2();
} function anim(){
ballAnim();
renderer.render( scene, camera );
stats.update()
requestAnimationFrame(anim);
} init()
anim() </script>
</body>
</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. MySQL分库备份

    1.需求概述 每天00:00备份MySQL数据库数据: 每一个库生成一个文件,使用gzip压缩,文件名:backup_库名_yyyymmdd.sql.gz,注意yyyymmdd需要是前一天: 备份文件 ...

  2. Flutter裁剪图片

    最近在学习中需要用到裁剪图片,记录一下解决方法 思路: 使用canvas的drawImageRect()方法,对Image进行裁剪,这里的Image需要 'dart:ui' 库中的Image. 1. ...

  3. Sqlite in flutter, how database assets work

    First off, you will need to construct a sqlite database from your csv. This can be done in the follo ...

  4. OO——UML解析

    目录 第四单元博客作业 一.前两次作业架构设计 1. 第一次作业 2. 第二次作业 二.架构设计以及对OO方法理解的演进 1. 表达式求导 2. 多线程电梯 3. 地铁线路查询 4. UML图的解析 ...

  5. tkinter 按钮响应函数传值

    tkinter 中的Button组件的响应函数如何传入参数,可能非常困扰新手,这里记录一下. 步骤: 1. 写好响应函数(形参设置好) 2. 在Button command 设置形式:command ...

  6. 微信小程序自定义toast的实现

    今天写微信小程序突然发现一个尴尬的问题,请求报错需要提示,就去小程序API里找,可悲的小程序的toast并不能满足我的需求,原生提供的方法调用如下 wx.showToast({ title: '成功' ...

  7. Jenkins配置Publish Over SSH讲解说明

    原创 Jenkins配置Publish Over SSH讲解说明 2018-08-22 11:59:30 M.Blog 阅读数 3764更多 分类专栏: Jenkins   版权声明:本文为博主原创文 ...

  8. linux下载并安装redis

    1 到http://redis.io/download 官网下下载最新稳定的redis 2 下载的redis安装包移动到要安装的位置 3 解压安装包 tar zxvf redis-3.0.5.tar. ...

  9. 自动网页截图并指定元素位置裁剪图片并保存到excel表格

    # coding=utf-8 import os import time from selenium import webdriver from selenium.webdriver.chrome.o ...

  10. angularcli 第三篇(数据循环*ngFor、条件判断*ngIf)

    1.数据循环 *ngFor (1)普通循环 <ul> <li *ngFor = "let item of list" > {{ item }} </l ...