前言
我们先从一个简单的场景开始,让你对 threejs 有个简单的认识。
上面场景很简单,有一个灰色的平面,上面是一个红色的立方体和一个蓝色的小球,还有一个三维的轴线,它的实现代码如下:
// 定义用来展示场景的div盒子
<div id="WebGL-output">
</div>
<script type="text/javascript">
function init() {
// 创建一个场景,它将承载我们所有的元素,例如渲染对象、相机和灯光等
var scene = new THREE.Scene();
// 创建一个相机,它定义了我们正在看的地方
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// 创建一个renderer渲染器,用来执行渲染工作
var renderer = new THREE.WebGLRenderer();
renderer.setClearColorHex();
renderer.setClearColor(new THREE.Color(0xEEEEEE));
// 设置渲染的大小
renderer.setSize(window.innerWidth, window.innerHeight);
// 在屏幕上显示辅助轴线,也就是xyz轴
// 红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.
var axes = new THREE.AxisHelper(20);
// 把轴线加入到场景中去
scene.add(axes);
// new一个平面几何体
var planeGeometry = new THREE.PlaneGeometry(60, 20);
// new一个基础材质
var planeMaterial = new THREE.MeshBasicMaterial({color: 0xcccccc});
// 使用几何体和材质生成一个渲染对象
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
// 旋转并定位平面
plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 15;
plane.position.y = 0;
plane.position.z = 0;
// 把平面添加到场景中
scene.add(plane);
// 创建一个立方体对象
var cubeGeometry = new THREE.BoxGeometry(4, 4, 4
// 设置立方体的材质,wireframe表示使用线框
var cubeMaterial = new THREE.MeshBasicMaterial({color: 0xff0000, wireframe: true});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
// 同样设置立方体的定位位置
cube.position.x = -4;
cube.position.y = 3;
cube.position.z = 0;
// 把立方体添加到场景中
scene.add(cube);
// 创建一个球体对象
var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
// 设置球体表面的材质,
var sphereMaterial = new THREE.MeshBasicMaterial({color: 0x7777ff, wireframe: true});
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
// 设置球体的定位位置
sphere.position.x = 20;
sphere.position.y = 4;
sphere.position.z = 2;
// 添加球到场景中
scene.add(sphere);
// 将相机定位并指向场景的中心
camera.position.x = -30;
camera.position.y = 40;
camera.position.z = 30;
camera.lookAt(scene.position);
// 将渲染器的输出添加到 最开始定义的div里
document.getElementById("WebGL-output").appendChild(renderer.domElement);
// 执行渲染操作
renderer.render(scene, camera);
}
init();
</script>
总结一下
- 定义承载输出的html元素
- 创建场景
- 创建渲染对象,并把对象加入到场景中
- 创建渲染器
- 创建相机
- 相机对焦,指明要拍摄场景中的哪个位置
- 使用渲染器执行渲染,并把结果展示到第一步定义的html元素中
下面我们会详细的针对renderer,camera,sence等等进行具体的介绍。
WebGLRenderer
three.js中内置了很多渲染器,在实际项目中用什么渲染器可以根据需求来选择。在这里我们只介绍最常用的WebGLRenderer。
WebGLRenderer可以创建一个WebGL 渲染器,用于将场景渲染到HTML页面上。那什么是WebGL呢?
这里借用维基百科的回答:
WebGL是一种JavaScript API,用于在不使用插件的情况下在任何兼容的网页浏览器中呈现交互式2D和3D图形。WebGL完全集成到浏览器的所有网页标准中,可将影像处理和效果的GPU加速使用方式当做网页Canvas的一部分。WebGL元素可以加入其他HTML元素之中并与网页或网页背景的其他部分混合。
总结一下就是 WebGL是一种可以调用GPU,在浏览器中直接渲染2D和3D图形的技术。
那WebGL和Threejs有什么区别呢?
three.js 是以 WebGL 为基础的库,封装了一些3D渲染需求中重要的工具方法与渲染方法。WebGL门槛相对较高,Three.js 对 WebGL 提供的接口进行了友好的封装。
我们直接上代码:
// create a render and set the size
var webGLRenderer = new THREE.WebGLRenderer();
webGLRenderer.setClearColor(new THREE.Color(0x000, 1.0));
webGLRenderer.setSize(window.innerWidth, window.innerHeight);
webGLRenderer.shadowMapEnabled = true;
webGLRenderer.render(scene, camera);
解释下上述代码的作用:
- new 一个
WebGLRenderer实例。 - 设置颜色及其透明度。
- 设置大小
- 允许在场景中使用阴影贴图。
- 执行渲染操作
在 new WebGLRenderer实例时,支持传入很多参数,感兴趣的可以查看官方 链接。
Camera
摄像机决定了屏幕上哪些东西需要渲染,你可以理解为只有摄像机对着的地方才会被Threejs渲染出来。
- lookAt
将摄像机聚焦在指定点上
camera.lookAt(new THREE.Vector3(x,y,z));
camera.lookAt(scene.position);
// 指向场景中的特定对象
camera.lookAt(mesh.position);
PerspectiveCamera 透视投影摄像机
距离camera越远,物体被渲染的就越小,更接近真实世界。
new THREE.PerpectiveCamera接收的参数如下:
- fov
视场,代表摄像机能够看到的那部分场景。
人类有180°的视场,而有些鸟类有360°的视场。由于电脑不能完全显示我们能看到的景象,所以一般会选择一小块区域,在游戏中一般设为60-90°, 推荐的默认值为50。
- aspect
长宽比,代表渲染结果横向尺寸与纵向尺寸的比值。
由于使用显示器作为输出界面,所以一般选择使用显示器窗口的长宽比。推荐的默认值为
window.innerWidth / window.innerHeight
- near
近面距离,该属性代表从距离摄像机多近的距离开始渲染。
通常这个值会设置的尽量小,从而能够渲染从摄像机位置可以看到的所有物体。推荐默认值为0.1。
- far
远面距离,该属性代表摄像机从它所属位置能够看多远。
如果这个值设置得较小,那么场景中有一部分不会被渲染,如果设置的过大,会影响渲染的性能。推荐的默认值为1000。
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
其他常用属性如:
- zoom
变焦,设置该属性可以放大和缩小场景。
如果这个值设置得小于1,则缩小场景。如果设置的大于1,则放大场景。如果设为负数,场景则上下颠倒。
camera.zoom = 0.8;
// 当camera属性变更后,必须执行该方法应用变更
camera.updateProjectionMatrix();
OrthographicCamera 正交投影摄像机
物体所有物体被渲染出来的尺寸是一致的
new THREE.PerpectiveCamera时,构造函数接收的参数如下:
- left 可视范围的左边界
- right 可视范围的右边界
- top 可视范围的上边界
- bottom 可视范围的下边界
- near 近面距离 同透视投影相机
- far 远面距离 同透视投影相机
const camera = new THREE.OrthographicCamera(window.innerWidth / -16, window.innerWidth / 16, window.innerHeight / 16, window.innerHeight / -16, -200, 500);
常用属性
- zoom 变焦 同透视投影相机
Scene
scene是一个场景图,里面包含所有图形场景的必要信息,如相机,光源,对象以及渲染所需要的其他对象。
场景图顾名思义,不仅仅是一个对象数组,还包含了场景图树形结构中的所有节点。每个添加到场景中的对象,甚至包含sence本身,都是继承自一个名为THREE.Object3D的对象。
一些常用方法:
- add
- remove
- getObjectByName 通过名字获取场景中的对象
- traverse 该方法接受一个function,该func会在每一个scene内的子对象上执行,包括子对象的子对象,直到最深层。
- overrideMaterial 强制覆盖scene中所有物体的材质
一些常用属性:
- children 属性 包含所有加入到场景中的对象
- fog 属性 用来设置雾化效果
Light 光源
光源决定材质如何显示,以及用于产生阴影效果。
WebGL本身并不支持光源,如果不使用Three.js,你需要自己写WebGL着色程序来模拟光源。
不可投射阴影光源
THREE.AmbientLight 基本光源
该光源的颜色将会叠加到场景现有物体的颜色上。
请注意图中的阴影是由于同时还使用了SpotLight导致的,并不是AmbientLight的效果。
const ambientLight = new THREE.AmbientLight('#0c0c0c');
scene.add(ambientLight);
// 如果要在初始化后更改颜色,需要重新new一个THREE.Color('xxx')对象
// 或者调用原有color实例的方法修改值,如color.set(value)
ambientLight.color = new THREE.Color('new color');
如果你尝试在前言示例的代码中加入光源,那你可能要失望了,即使你加入了光源,也不会有任何效果,因为在前言示例中,我们物体的材质使用了 MeshBasicMaterial,而这种材质不会对光源产生反应。
THREE.PointLight 点光源
从空间的一点向所有方向发射光线。
const light = new THREE.PointLight( 0xff0000, 1, 100 );
light.position.set( 50, 50, 50 );
scene.add( light );
构造函数:
-
color - (可选参数)) 十六进制光照颜色。 默认值 0xffffff (白色)。
-
intensity - (可选参数) 光照强度。 默认值 1。
-
distance - 表示从光源到光照强度为0的位置距离。 当设置为14时,光线强度在距离为14的地方慢慢会减少为0,当设置为0时,光不会随着距离的增加而减弱。默认值 0。
-
decay - 沿着光照距离的衰退量。默认值 1。 在 physically correct 模式中,decay = 2
常用属性:
-
position 光源在场景中的位置
-
visible 是否可见
THREE.HemisphereLight 更加自然的光源
这是一种特殊的光源,可以通过模拟反光面和光线微弱的天空来创建更加自然的室外光线。
常用属性:
-
groundColor 从地面发出的光线的颜色
-
color 从天空发出的光线的颜色
-
intensity 光线照射的强度
创建一个自然光源
var hemiLight = new THREE.HemisphereLight(0x0000ff, 0x00ff00, 0.6);
hemiLight.position.set(0, 500, 0);
scene.add(hemiLight);
THREE.AreaLight 平面光源
这种光源可以指定散发光线的平面,而不是一个点。
AreaLight不在标准的Three.js库中,而在它的扩展库中。如果要使用AreaLight,就不能使用THREE.WebGLRenderer对象了,因为AreaLight是一种非常复杂的光源,它会给普通的THREE.WebGLRenderer带来严重的性能损失。
一般使用THREE.WebGLDeferredRenderer对象,它可以更好的处理复杂的光照(或者大量的光源)。
var areaLight = new THREE.AreaLight(0xff0000, 3);
areaLight.position.set(-10, 10, -35);
areaLight.rotation.set(-Math.PI / 2, 0, 0);
areaLight.width = 4;
areaLight.height = 9.9;
scene.add(areaLight);
可以投射阴影光源
THREE.SpotLight 聚光光源
这种光源有聚光效果,类似台灯或者手电筒。从特定的一点发射锥形的光线。
常用属性:
- angle 角度 光源发射出的光束的宽度,单位是弧度,默认值为Math.PI / 3
- castShadow 投影 如果设置为true,光源就会产生阴影,同时要注意场景中要投射阴影的对象,也要设置castShadow为true
- color 光源颜色
- distance 表示从光源到光照强度为0的位置距离。当设置为14时,光线强度在距离为14的地方慢慢会减少为0,当设置为0时,光不会随着距离的增加而减弱。默认值 0。
- exponent 光强度衰减指数 该属性决定了光线强度递减的速度。SpotLight 光源发射的光线强度随着光源距离的增加而减弱,使用低值,从光源发出的光线将到达远处看到物体,高值则光线仅能到达非常接近光源的物体。
- intensity 光源照射的强度 默认值为1,为2代表着两倍强度
- onlyShadow 设为true则代表着仅展示阴影,不会在场景中添加任何光照。
- position 光源在场景中的位置
- shadowCameraNear 投影近点 从距离光源的哪一个位置开始可以生成阴影,默认值为50
- shadowCameraFar 投影远点 到距离光源的哪一个位置内可以生成阴影,默认值5000
- shadowCameraFov 投影视场, 用于生成阴影的视场有多大,默认值5
- shadowCameraVisible 如果该属性设为true,可以看到光源在哪里以及如何生成阴影的,类似于开启了debug模式。默认值为false。
- shadowDarkness 阴影暗度 定义了阴影渲染的暗度,在场景渲染之后无法更改,默认值为0.5。
- shadowMapWidth和shadowMapHeight 决定了有多少像素用来生成阴影。当阴影有锯齿状边缘时,可以增加这个值。场景渲染之后无法更改,默认值为0.5.
- target 指向目标 使用target属性,可以将SpotLight光源指向场景中特定对象或位置。 此属性需要一个THREE.Object3D对象,如THREE.Mesh。
- visible 是否可见,默认为true
- shadowBias 偏移阴影位置 如果使用非常薄的对象时,遇到阴影失真或者看起来很怪,可以尝试将该属性设为很小的值,如0.01。此属性默认值为0。
创建聚光光源:
var pointColor = '#ffffff';
var spotLight = new THREE.SpotLight(pointColor);
spotLight.position.set(-40, 60, -10);
spotLight.castShadow = true;
spotLight.shadowCameraNear = 2;
spotLight.shadowCameraFar = 200;
spotLight.shadowCameraFov = 30;
spotLight.target = plane;
spotLight.distance = 0;
spotLight.angle = 0.4;
scene.add(spotLight);
THREE.DirectionalLight 平行光
从这种光源散发出的光线可以看作是平行的,就像太阳光。不是从单个点发射光线了,而是从平面发射,光线彼此平行。
该光源与SpotLight的主要区别时:平行光不像聚光灯那样离目标越远越暗淡,被平行光照亮的整个区域接收到的光强度时一样的。也就是说场景里的光线照射区域不时锥形的,而是一个立方体,且立方体内的对象接收到的强度一样。
在立方体范围内所有对象都可以接收投影和阴影,与SpotLight相比,除了shadowCameraNear,shadowCameraFar外,还需要设置立方体的范围(SpotLight是设置angle角度),跟正交投影Camera类似,需要设置上下左右可视范围边界。即shadowCameraLeft,shadowCameraRight,shadowCameraTop,shadowCameraBottom
常用的属性与SpotLight相比,除了angle外基本一致。
var pointColor = "#ff5808";
var directionalLight = new THREE.DirectionalLight(pointColor);
directionalLight.position.set(-40, 60, -10);
directionalLight.castShadow = true;
directionalLight.shadowCameraNear = 2;
directionalLight.shadowCameraFar = 200;
directionalLight.shadowCameraLeft = -50;
directionalLight.shadowCameraRight = 50;
directionalLight.shadowCameraTop = 50;
directionalLight.shadowCameraBottom = -50;
directionalLight.distance = 0;
directionalLight.intensity = 0.5;
directionalLight.shadowMapHeight = 1024;
directionalLight.shadowMapWidth = 1024;
scene.add(directionalLight);
关于该光源还有一个shadowCascade属性,当需要使用directionalLight在一个很大的区域设置阴影时,这个属性可以帮助创建更好的阴影效果,如果将这个属性设为true,它会将阴影分裂成shadowCascadeCount指定的值,这将导致靠近摄像机会产生更具细节的阴影,远离摄像机视点的阴影细节更少。要使用这个选项,还需要设置一些shadowCascade相关的属性。
其它
THREE.LensFlare 镜头光晕效果
为场景中的光源添加镜头光晕效果
常用属性:
- texture 纹理 就是一个图片,用来决定光晕的形状。
- size 光晕尺寸(单位为像素)。
- distance 从光源(0)到摄像机(1)的距离。
- color 光晕的颜色。
- blending 混合 由于可以为光晕提供多种材质,该属性决定了多种材质如何进行混合。默认的混合方式是 THREE.AdditiveBlending。
var textureFlare0 = THREE.ImageUtils.loadTexture("../assets/textures/lensflare/lensflare0.png");
var textureFlare3 = THREE.ImageUtils.loadTexture("../assets/textures/lensflare/lensflare3.png");
var flareColor = new THREE.Color(0xffaacc);
var lensFlare = new THREE.LensFlare(textureFlare0, 350, 0.0, THREE.AdditiveBlending, flareColor);
lensFlare.add(textureFlare3, 60, 0.6, THREE.AdditiveBlending);
lensFlare.add(textureFlare3, 70, 0.7, THREE.AdditiveBlending);
lensFlare.add(textureFlare3, 120, 0.9, THREE.AdditiveBlending);
lensFlare.add(textureFlare3, 70, 1.0, THREE.AdditiveBlending;
lensFlare.position.copy(spotLight.position);
scene.add(lensFlare);