three.js自学之旅(3)—— 相机旋转功能、各种光源探究

时间:2021-09-14 04:17:45

在上一篇文章里,可以搭建起一个基本的场景,包括一个会旋转的立方体。还埋了一个坑,一个0xff00ff的立方体为什么显示成黑色,这个问题会在后面的光源部分得到解决。

先来讲讲相机的旋转功能。为什么要做这个功能?因为在three.js的世界里,坐标是三维的,所以里面的每个物体都可以立体起来,既然是三维物体,就有他的反面和正面,因此为了更好的观察物体的背影和正面,先来做相机旋转这个比较通用的功能。

通常情况下,为了能完整地看到一个物体有两种方法。

第一种:让它自己动

第二种:你自己乱动

第一种方法可以如上一篇的demo所示,让物体绕着某个点旋转(rotation),转个360度,就能看到他的全貌了,然而,这种方法其实很不方便,一般人都喜欢把主动权放在自己手里吧。

第二种方法其实也很简单,就是你自己绕着物体转,转换到three.js里就是你的视角(camera)绕着物体(mesh)旋转,从数学的角度讲,就是camera以物体为中心做圆周运动。,来看下具体怎么实现吧

//旋转相机以观察物体的不同角度
var cameraRound = 10;
$("body").bind("mousedown",cameraMove);
var arf = 0;
var oldArf = 0;
function cameraMove(ev){
    var mouseDownY = ev.pageY;
    var mouseDownX = ev.pageX;
    var zeroPoint = new THREE.Vector3(0, 0, 0);
    $("body").bind("mousemove",function(ev){
        var mouseMoveY = ev.pageY;
        var mouseMoveX = ev.pageX;
        arf = (mouseMoveX - mouseDownX)/100+oldArf;
        camera.position.z = cameraRound *(Math.cos(arf));
        camera.position.x = cameraRound *(Math.sin(arf));
        camera.lookAt(zeroPoint);
    });
    $("body").bind("mouseup",function(ev){
        $("body").unbind("mousemove");
        oldArf = arf;
    })
}
实现该相机的思路对于稍微有点JS基础的人来说应该不难。

给body绑定一个mousedown事件,在鼠标点下去的时候,获取下当前鼠标在屏幕中的位置,X,Y,在鼠标移动的时候的时候记录鼠标移动的距离,让相机做圆周运动,注意!这里有个坑,一开始我以为改变相机的位置就好了,后来发现相机并没有像我那么聪明,他不晓得自己要朝着物体看,他只知道直勾勾的往前看,所以要加上一句 camera.lookAt(zeroPoint); 让相机看向原点(因为物体在原点,如果物体在别的地方,请改变相机的朝向),这里用的lookAt()函数应该还是很形象生动的,它的参数是一个点,不是一个面,也不是一个物体,有兴趣的可以自己玩一下。

我没有对X轴进行处理,有兴趣的可以完善一下观察的角度。


讲完了通用的相机旋转功能,再来玩一下three.js里面非常重要的光源。根据官方文档,three.js里一共提供了五种光源,我尽量每种都玩一下,附上代码和效果,并把昨天的坑给填了。首先在玩光源之前,要注意一个点,就是关于材质的问题,从科学的角度上讲,眼睛所看到的颜色,是物体本身的颜色的颜色和光源共同作用经过各种射,射到眼睛里的,因此,如果有些物体根本不反射光呢?那你看到的颜色就是漆黑。因此为了让你在研究光源的时候得到比较好的反馈,请把物体的纹理设成兰伯特材质的,并且把物体本身的颜色设为0xffffff,这样什么颜色的光来了他都能应付。

反应到代码上就是:

var material = new THREE.MeshLambertMaterial({color:0xffffff}); //使用Lambert材质

1.环境光(AmbientLight)

第一种,环境光。

看文档:这种光的颜色被应用到全局范围内的所有对象。

意思就是,这种光会照射到场景内的任何一个物体的任何一面。就很普通,很好懂。放到实战中观察一下。

//环境光
var light = new THREE.AmbientLight(0xff00000);
scene.add(light);
three.js自学之旅(3)—— 相机旋转功能、各种光源探究

可以看到本身是白色(0xFFFFFF)的物体,在红色的环境光中,显示成了红色。很好懂,是吧。

再加一种环境光试试,看看重叠的效果是什么。

var light = new THREE.AmbientLight(0xff00000);
scene.add(light);
var light2 = new THREE.AmbientLight(0x0000ff);
scene.add(light2);

three.js自学之旅(3)—— 相机旋转功能、各种光源探究

可以看到,环境光会自己重叠,最终的效果相当于加了一个(0XFF00FF)的光,因此物体最终呈现为紫色。

第二种:平行光(DirectionalLight)

这是我最喜欢玩的光,因为在后面的学习中,他还会跟阴影搭上关系。

先看文档:影响使用 兰伯特网孔材料(MeshLambertMaterial) 或 Phong网孔材料(MeshPhongMaterial) 的对象。创建一个光照,从一个特定的方向,而不是从一个特定的位置。这个光看起来就像光源位于无限远处,因此它产生的光线都是平行的。 最好的类比是一个像太阳一样的光源:太阳是如此遥远,所有的阳光照射到物体上都几乎来自同一个角度。

好的,不用解释了,文档说的已经很明确了,像太阳光一样(暂时不会生成阴影,后面会玩阴影)。放到实战中观察一下先

//平行光
var light =  new THREE.DirectionalLight(0xff0000,1);
light.position.set(0,10,5);
//light.target=cube;
scene.add(light);
cube.position.set(-1,-1,2);//注意这里物体的位置
three.js自学之旅(3)—— 相机旋转功能、各种光源探究


//玩着玩着下班了,溜了溜了,下礼拜补。会去享受休息时光了。有什么问题可以一起交流的,打我电话,110.

上次玩到平行光,还有几个参数没玩过,首先是构造一束平行光的时候 new DirectionalLight( hex, intensity )

hex -- 光源颜色的RGB数值。
intensity -- 光源强度的数值。(0-1)

这两个参数应该都很好理解,一个是颜色,一个是强度,这个光强跟透明度有点不同又有点相似,不同的是,他不会透明,相同之处是,颜色“看起来”会变淡,这里不放图了,有兴趣可以自己调一下光强。

第二个position设置,根据字面意思理解,就是要把光放在哪里,上图我把光放在坐标轴(0,10,5)的位置,也就是你的视角发出一道平行光(如果你眼睛高出那个立方体的话),俯视着那块立方体,最终效果如上图所示,为了展示不同,我把position参数调整为(0,-10,5),看一下效果,应该就很清楚明白了。

three.js自学之旅(3)—— 相机旋转功能、各种光源探究

可以看到,当平行光移动到物体的小前方的时候,物体的下面就被照亮了,很好理解。

第三个.target设置。官方文档里描述:用于阴影相机定位的目标。

其实跟camera的lookAt差不多,只是camera最终要汇聚到一个点,也就是lookAt的参数是一个vector对象,而平行光可以照射到一个空间内的三维物体,如果没有设置target,平行光会默认照射到以原点为中心的区域,注意是区域,不是点。

three.js自学之旅(3)—— 相机旋转功能、各种光源探究


之前的代码里,我注释了target那句,想看效果的话,把注释去掉就可以了。

按照惯例,看一下平行光叠加是什么效果。

//平行光
var light =  new THREE.DirectionalLight(0xff0000,1);
light.position.set(0,-10,5);
light.target=cube;
scene.add(light);
var light2 = new THREE.DirectionalLight(0x00ff00,1);
light2.target=cube;
light2.position.set(0,10,5);
scene.add(light2);
var light3 = new THREE.DirectionalLight(0x0000ff,1);
light3.target=cube;
light3.position.set(-5,10,5);
scene.add(light3);
three.js自学之旅(3)—— 相机旋转功能、各种光源探究

第三种:点光源(PointLight)

文档描述:在场景中的特定位置创建一个光源。光线照射在各个方向上(好比一个灯泡,注意是灯泡,不是激光灯,也不是聚光灯,我一开始以为是小时候玩的激光灯来着),为了表明他是一个灯泡,我们把光源放的中间一些,把物体放到光源的周围看一下效果。

var cube2 =  new THREE.Mesh(geometry,material);
var cube3 =  new THREE.Mesh(geometry,material);
var cube4 =  new THREE.Mesh(geometry,material);
var cube5 =  new THREE.Mesh(geometry,material);
cube.position.set(0,0,-4);
cube2.position.set(4,0,0);
cube3.position.set(0,4,0);
cube4.position.set(-4,0,0);
cube5.position.set(0,-4,0);
scene.add(cube);
scene.add(cube2);
scene.add(cube3);
scene.add(cube4);
scene.add(cube5);
//点光源
var light = new THREE.PointLight( 0xff0000, 1, 100 );
light.position.set( 0, 0, 0 );
scene.add( light );
three.js自学之旅(3)—— 相机旋转功能、各种光源探究

应该可以看出来,中间有个灯泡(假装)。自己玩的时候可以把这个在原点的灯泡稍微拿出来一点,可以看到,朝着你的面也都被照亮了,按照惯例,玩一下点光源的几个参数。

PointLight( color, intensity, distance, decay )
color — 颜色的RBG数值。
intensity — 光强的数值。
distance -- 光强为0处到光源的距离,0表示无穷大。
decay -- 沿着光照距离的衰退量。

前两个没什么玩的了,来看一下后俩个。也是通过实际操作来看比较直观。

在研究distance之前,先搞清楚,position的概念,cube.position.set到底是设置什么在那个点呢?跟想象中的一样,就是几何物体的中心(还是重心还是什么心来着,反正就是那个意思),因此,当我设置物体的中心为4的时候,物体最靠近光源的一面到光源的距离应该是,4 - 1(物体半径/或二分之一宽) = 3 。所以我们设置distance小一点看一下效果。(由于光强有个衰退的过程,也就是第四个参数,所以我将物体的距离重新设置的不同,并给distance取合适的值,让效果更明显,具体请自己玩一下就知道了)

three.js自学之旅(3)—— 相机旋转功能、各种光源探究

可以看到下面的cube比较远,是全黑的,其他几个由近到远,颜色逐渐变暗。

.decay 沿着光照距离的衰退量。
在“物理正确”模式中,decay = 2将实现现实世界的光衰减。缺省值 - 1。这个参数有兴趣可以自己研究,体验了一下,感觉用处不是很大。(其实是不会用)

关于点光源的光源混合,自己尝试一下。这里不多描述

第四种:半球光(HemisphereLight)

看文档:看不懂

通俗易懂的说法:假设你的场景有天花板和地板,你的物体悬浮在这两者区间,那么你可以设置天花板发的光和地板发
的光,但他们发出的光既有环境光的特性,又有平行光的特性,具体,请看实例长什么样子,自行理解吧。

//半球光源
var light = new THREE.HemisphereLight( 0xff0000, 0x0000ff, 1 );
scene.add( light );


three.js自学之旅(3)—— 相机旋转功能、各种光源探究

看图应该能明白了吧。

关于聚光灯的使用和应用场景会在下一篇里说,因为聚光灯通常会和阴影搭配使用。

今天就玩到这儿,开始上班。。。。