1.三大组建
var scene = new THREE.Scene(); // 场景
var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);// 透视相机
2.渲染器renderer
//注意,渲染器renderer的domElement元素,表示渲染器中的画布,所有的渲染都是画在domElement上的,所以这里的appendChild表示将这个domElement挂接在body下面,这样渲染的结果就能够在页面中显示了。
var renderer = new THREE.WebGLRenderer(); // 渲染器
renderer.setSize(window.innerWidth, window.innerHeight); // 设置渲染器的大小为窗口的内宽度,也就是内容区的宽度
document.body.appendChild(renderer.domElement);
3.场景
//场景是所有物体的容器,如果要显示一个苹果,就需要将苹果对象加入场景中。
var scene = new THREE.Scene();
//将一个物体添加到场景中
var geometry = new THREE.CubeGeometry(1,1,1);
var material = new THREE.MeshBasicMaterial({color: 0x00ff00});
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);
//width:立方体x轴的长度 height:立方体y轴的长度 depth:立方体z轴的深度,也就是长度 想一想大家就明白,以上3个参数就能够确定一个立方体。
width:表示立方体的宽度。
height:表示立方体的高度。
depth:表示立方体的长度或者说深度吧。
widthSegments:宽度分段份数
heightSegments:高度分段份数
depthSegments:长度分段份数
-- CubeGeometry(width, height, depth, segmentsWidth, segmentsHeight, segmentsDepth, materials, sides)
4.渲染器
//渲染应该使用渲染器,结合相机和场景来得到结果画面。实现这个功能的函数是
renderer.render(scene, camera);
//scene:前面定义的场景 camera:前面定义的相机
//renderTarget:渲染的目标,默认是渲染到前面定义的render变量中
//forceClear:每次绘制之前都将画布的内容给清除,即使自动清除标志autoClear为false,也会清除。
-- render( scene, camera, renderTarget, forceClear )
5.渲染循环
渲染有两种方式:实时渲染和离线渲染 。
先看看离线渲染,想想《西游降魔篇》中最后的佛主,他肯定不是真的,是电脑渲染出来的,其画面质量是很高的,它是事先渲染好一帧一帧的图片,然后再把图片拼接成电影的。这就是离线渲染。如果不事先处理好一帧一帧的图片,那么电影播放得会很卡。CPU和GPU根本没有能力在播放的时候渲染出这种高质量的图片。
实时渲染:就是需要不停的对画面进行渲染,即使画面中什么也没有改变,也需要重新渲染。下面就是一个渲染循环:
requestAnimationFrame,这个函数就是让浏览器去执行一次参数中的函数,这样通过上面render中调用requestAnimationFrame()函数,requestAnimationFrame()函数又让rander()再执行一次,就形成了我们通常所说的游戏循环了。
function render() {
cube.rotation.x += 0.1;
cube.rotation.y += 0.1;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
6.场景,相机,渲染器之间的关系
Three.js中的场景是一个物体的容器,开发者可以将需要的角色放入场景中,例如苹果,葡萄。同时,角色自身也管理着其在场景中的位置。
相机的作用就是面对场景,在场景中取一个合适的景,把它拍下来。
渲染器的作用就是将相机拍摄下来的图片,放到浏览器中去显示。他们三者的关系如下图所示:
实例
7.在三维空间中的某一个点可以用一个坐标点来表示。一个坐标点由x,y,z三个分量构成。
THREE.Vector3 = function ( x, y, z ) {
this.x = x || 0;
this.y = y || 0;
this.z = z || 0;
};
//现在来看看怎么定义个点,假设有一个点x=4,y=8,z=9。你可以这样定义它:
var point1 = new THREE.Vecotr3(4,8,9);
//另外你也可以使用set方法,代码如下:
var point1 = new THREE.Vector3();
point1.set(4,8,9);
8.几何体geometry
//几何体里面有一个vertices变量,可以用来存放点。
var geometry = new THREE.Geometry();
9.定义一种线条的材质,使用THREE.LineBasicMaterial类型来定义,它接受一个集合作为参数,其原型如下:
LineBasicMaterial( parameters )
Parameters是一个定义材质外观的对象,它包含多个属性来定义材质,这些属性是:
Color:线条的颜色,用16进制来表示,默认的颜色是白色。
Linewidth:线条的宽度,默认时候1个单位宽度。
Linecap:线条两端的外观,默认是圆角端点,当线条较粗的时候才看得出效果,如果线条很细,那么你几乎看不出效果了。
Linejoin:两个线条的连接点处的外观,默认是“round”,表示圆角。
VertexColors:定义线条材质是否使用顶点颜色,这是一个boolean值。意思是,线条各部分的颜色会根据顶点的颜色来进行插值。(如果关于插值不是很明白,可以QQ问我,QQ在前言中你一定能够找到,嘿嘿,虽然没有明确写出)。
Fog:定义材质的颜色是否受全局雾效的影响。
好了,介绍完这些参数,你可以试一试了,在课后,我们会展示不同同学的杰出作品。下面,接着上面的讲,我们这里使用了顶点颜色vertexColors: THREE.VertexColors,就是线条的颜色会根据顶点来计算。
var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );
//定义颜色
var color1 = new THREE.Color( 0x444444 ),color2 = new THREE.Color( 0xFF0000 );
//定义点的位置
var p1 = new THREE.Vector3( -100, 0, 100 );
var p2 = new THREE.Vector3( 100, 0, -100 );
geometry.vertices.push(p1);
geometry.vertices.push(p2);
//为4中定义的2个顶点,设置不同的颜色
//geometry中colors表示顶点的颜色,必须材质中vertexColors等于THREE.VertexColors 时,颜色才有效,如果vertexColors等于THREE.NoColors时,颜色就没有效果了。那么就会去取材质中color的值,这个很重要,大家一定记住。
geometry.colors.push( color1, color2 );
//定义线条,使用THREE.Line类,代码如下所示:
//第一个参数是几何体geometry,里面包含了2个顶点和顶点的颜色。第二个参数是线条的材质,或者是线条的属性,表示线条以哪种方式取色。第三个参数是一组点的连接方式,我们会在后面详细讲解。
var line = new THREE.Line( geometry, material, THREE.LinePieces );
//然后,将这条线加入到场景中,代码如下:
scene.add(line);
10.网格的坐标
//在x轴上定义两个点p1(-500,0,0),p2(500,0,0)。
geometry.vertices.push( new THREE.Vector3( - 500, 0, 0 ) );
geometry.vertices.push( new THREE.Vector3( 500, 0, 0 ) );
这两个点决定了x轴上的一条线段,将这条线段复制20次,分别平行移动到z轴的不同位置,就能够形成一组平行的线段。
同理,将p1p2这条线先围绕y轴旋转90度,然后再复制20份,平行于z轴移动到不同的位置,也能形成一组平行线。
//经过上面的步骤,就能够得到坐标网格了。代码如下:
for ( var i = 0; i <-= 20; i ++ ) {
var line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0x000000, opacity: 0.2 } ) );
line.position.z = ( i * 50 ) - 500;
scene.add( line );
var line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0x000000, opacity: 0.2 } ) );
line.position.x = ( i * 50 ) - 500;
line.rotation.y = 90 * Math.PI / 180; // 旋转90度
scene.add( line );
}
11.性能检测
你现在可以自己写一段代码,来验证一下,你的程序的帧数了。
Stats到底做了什么事情呢?我们来分析一下。
1、setMode函数
参数为0的时候,表示显示的是FPS界面,参数为1的时候,表示显示的是MS界面。
2、stats的domElement
stats的domElement表示绘制的目的地(DOM),波形图就绘制在这上面。
3、begin
begin,在你要测试的代码前面调用begin函数,在你代码执行完后调用end()函数,这样就能够统计出这段代码执行的平均帧数了。
var stats = new Stats();
stats.setMode(1); // 0: fps, 1: ms
// 将stats的界面对应左上角
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.body.appendChild( stats.domElement );
setInterval( function () {
stats.begin();
// 你的每一帧的代码
stats.end();
}, 1000 / 60 );
//调用stats.update()函数来统计时间和帧数。代码如下
stats.update();
12.使用动画引擎Tween.js来创建动画
1.引入js
<-script src="../js/tween.min.js" data-ke-src="../js/tween.min.js"><-/script>
2.构件一个Tween对象,对Tween进行初始化,本例的代码是:
function initTween()
{
new TWEEN.Tween( mesh.position)
.to( { x: -400 }, 3000 ).repeat( Infinity ).start();
}
//to函数,接受两个参数,第一个参数是一个集合,里面存放的键值对,键x表示mesh.position的x属性,值-400表示,动画结束的时候需要移动到的位置。第二个参数,是完成动画需要的时间,这里是3000ms。
//repeat( Infinity )表示重复无穷次,也可以接受一个整形数值,例如5次。
//Start表示开始动画,默认情况下是匀速的将mesh.position.x移动到-400的位置。
//TWEEN.update()完成了让动画动起来到目标。如果不调用这个函数场景就不能动起来了。
function animation()
{
renderer.render(scene, camera);
requestAnimationFrame(animation);
stats.update();
TWEEN.update();
}
3.
13.正投影相机,正投影的构造函数如下所示:
1、 left参数
left:左平面距离相机中心点的垂直距离。从图中可以看出,左平面是屏幕里面的那个平面。
2、 right参数
right:右平面距离相机中心点的垂直距离。从图中可以看出,右平面是屏幕稍微外面一点的那个平面。
3、 top参数
top:顶平面距离相机中心点的垂直距离。上图中的顶平面,是长方体头朝天的平面。
4、 bottom参数
bottom:底平面距离相机中心点的垂直距离。底平面是头朝地的平面。
5、near参数
near:近平面距离相机中心点的垂直距离。近平面是左边竖着的那个平面。
6、far参数
far:远平面距离相机中心点的垂直距离。远平面是右边竖着的那个平面。
OrthographicCamera( left, right, top, bottom, near, far )
有了这些参数和相机中心点,我们这里将相机的中心点又定义为相机的位置。通过这些参数,我们就能够在三维空间中唯一的确定上图的一个长方体。这个长方体也叫做视景体。
投影变换的目的就是定义一个视景体,使得视景体外多余的部分裁剪掉,最终图像只是视景体内的有关部分。
//好了,看一个简单的例子:
var camera = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 1, 1000 );
scene.add( camera );
这个例子将浏览器窗口的宽度和高度作为了视景体的高度和宽度,相机正好在窗口的中心点上。这也是我们一般的设置方法,基本上为了方便,我们不会设置其他的值。
14.透视投影相机
1、视角fov:这个最难理解,我的理解是,眼睛睁开的角度,即,视角的大小,如果设置为0,相当你闭上眼睛了,所以什么也看不到,如果为180,那么可以认为你的视界很广阔,但是在180度的时候,往往物体很小,因为他在你的整个可视区域中的比例变小了。
2、近平面near:这个呢,表示你近处的裁面的距离。补充一下,也可以认为是眼睛距离近处的距离,假设为10米远,请不要设置为负值,Three.js就傻了,不知道怎么算了,
3、远平面far:这个呢,表示你远处的裁面,
4、纵横比aspect:实际窗口的纵横比,即宽度除以高度。这个值越大,说明你宽度越大,那么你可能看的是宽银幕电影了,如果这个值小于1,那么可能你看到的是如下的图中的LED屏幕了。
//透视投影相机的构造函数如下所示:
PerspectiveCamera( fov, aspect, near, far )
//好了,看看下面一个简单的例子:
var camera = new THREE.PerspectiveCamera( 45, width / height, 1, 1000 );
scene.add( camera );
- 光源基类
//在Threejs中,光源用Light表示,它是所有光源的基类。它的构造函数是:
THREE.Light ( hex )
//它有一个参数hex,接受一个16进制的颜色值。例如要定义一种红色的光源,我们可以这样来定义
Var redLight = new THREE.Light(0xFF0000);
1.环境光用THREE.AmbientLight来表示,它的构造函数如下所示:
THREE.AmbientLight( hex )
//它仍然接受一个16进制的颜色值,作为光源的颜色。环境光将照射场景中的所有物体,让物体显示出某种颜色。环境光的使用例子如下所示:
var light = new THREE.AmbientLight( 0xff0000 );
scene.add( light );
//只需要将光源加入场景,场景就能够通过光源渲染出好的效果来了。
2.点光源:由这种光源放出的光线来自同一点,且方向辐射自四面八方。例如蜡烛放出的光,萤火虫放出的光。
//点光源用PointLight来表示,它的构造函数如下所示:
PointLight( color, intensity, distance )
这个类的参数稍微复杂一些,我们花点时间来解释一下:
Color:光的颜色
Intensity:光的强度,默认是1.0,就是说是100%强度的灯光,
distance:光的距离,从光源所在的位置,经过distance这段距离之后,光的强度将从Intensity衰减为0。 默认情况下,这个值为0.0,表示光源强度不衰减。
3.聚光灯的构造函数是:
THREE.SpotLight( hex, intensity, distance, angle, exponent )
函数的参数如下所示:
Hex:聚光灯发出的颜色,如0xFFFFFF
Intensity:光源的强度,默认是1.0,如果为0.5,则强度是一半,意思是颜色会淡一些。和上面点光源一样。
Distance:光线的强度,从最大值衰减到0,需要的距离。 默认为0,表示光不衰减,如果非0,则表示从光源的位置到Distance的距离,光都在线性衰减。到离光源距离Distance时,光源强度为0.
Angle:聚光灯着色的角度,用弧度作为单位,这个角度是和光源的方向形成的角度。
exponent:光源模型中,衰减的一个参数,越大衰减约快。
16.绘制网格
1、var helper = new THREE.GridHelper( 1000, 50 )这一行,生成了一个网格模型,边长为1000,大网格中有小网格,小网格的边长是50.
2、网格线的颜色一头是0x0000ff,另一头是0x808080。线段中间的颜色取这两个颜色的插值颜色。
function initGrid(){
// 网格的边长是1000,每个小网格的边长是50
var helper = new THREE.GridHelper( 1000, 50 );
helper.setColors( 0x0000ff, 0x808080 );
scene.add( helper );
}