WebGL是针对Canvas的3D上下文。与其他web技术不同,WebGL并不是W3C制定的标准。
发现了一个很酷网站,以下内容来自该网站。
2015.10.20
WebGL是在浏览器中实现三维效果的一套规范。
Three.js能写出在浏览器上流畅运行的3D程序。
在Three.js中,要渲染物体到网页中,我们需要3个组建:场景(scene)、相机(camera)和渲染器(renderer)。有了这三样东西,我们才能够使用相机将场景渲染到网页上去。
场景是所有物体的容器,如果要显示一个苹果,就需要将苹果对象加入场景中。
另一个组建是相机,相机决定了场景中那个角度的景色会显示出来。相机就像人的眼睛一样,人站在不同位置,抬头或者低头都能够看到不同的景色。
场景只有一种,但是相机却又很多种。
最后一步就是设置渲染器,渲染器决定了渲染的结果应该画在页面的什么元素上面,并且以怎样的方式来绘制。
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
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);
camera.position.z = 5;
function render() {
requestAnimationFrame(render);
cube.rotation.x += 0.1;
cube.rotation.y += 0.1;
renderer.render(scene, camera);
}
render();
在CSDN上发现有张雯莉 翻译的three.js入门指南,之前在知乎上了解过她,感觉很棒,所以打算先把她翻译的这本书看了。2015.10.21
照相机是一个抽象,它定义了三维空间到二维屏幕的投影方式。
而针对投影方式的不同,照相机又分为正交投影照相机和透视投影照相机。使用透视投影照相机获得的结 果是类似人眼在真实世界中看到的有“近大远小”的效果;而使用正交投影照相机获得的结果就像我们在数学几何学课上老师教我们画的效果。
THREE.OrthographicCamera(left, right, top, bottom, near, far)
这六个参数分别代表正交投影照相机拍摄到的空间的六个面的位置,这两个面围成一个长 方体,我们称其为视景体(Frustum)。
为了保持照相机的横竖比例,需要保证 (right - left) 与 (top - bottom) 的比例与 Canvas 宽度与高度的比例一致。


near 与 far 都是指到照相机位置在深度平面的位置,而照相机不应该拍摄到其后方的物体,因此这两个值应该均为正值。为了保证场景中的物体不会因为太近或太远而被照相机 忽略,一般 near 的值设置得较小, far 的值设置得较大,具体值视场景中物体的位置等 决定。
成像效果“近大远小“。
THREE.PerspectiveCamera(fov, aspect, near, far)
透视图中,灰色的部分是视景体,是可能被渲染的物体所在的区域。
fov 是视景体竖直方向上的张角(是角度制而非弧度制),如侧视图所示。
aspect 等于 width / height ,是照相机水平方向和竖直方向长度的比值,通常设为
Canvas 的横纵比例。
near 和 far 分别是照相机到视景体最近、最远的距离,均为正值,且 far 应大于 near 。
CubeGeometry
)
虽然名为立方体,但是实际上是长方体。
THREE.CubeGeometry(width, height, depth, widthSegments, heightSegments, depthSegments)
这里, width 是 x 方向上的长度; height 是 y 方向上的长度; depth 是 z 方向上的长度;后三个参数分别是在三个方向上的分段数,如 widthSegments 为3的话,代表 x 方向上水平分为三份。一般情况下不需要分段的话,可以不设置后三个参数,后三个参数的缺省值为 。其他几何形状中的分段也是类似的,下面不做说明。
PlaneGeometry
)
这里的平面其实是一个长方形,而不是数学意义上无限大小的平面,其构造函数为:
THREE.PlaneGeometry(width, height, widthSegments, heightSegments)
SphereGeometry
)
THREE.SphereGeometry(radius, segmentsWidth, segmentsHeight, phiStart, phiLength, the taStart, thetaLength)
CircleGeometry
)
THREE.CircleGeometry(radius, segments, thetaStart, thetaLength)
CylinderGeometry
)
THREE.CylinderGeometry(radiusTop, radiusBottom, height, radiusSegments, heightSegments, openEnded)
CylinderGeometry
)
THREE.CylinderGeometry(radiusTop, radiusBottom, height, radiusSegments, heightSegments, openEnded)
THREE.TetrahedronGeometry(radius, detail) THREE.OctahedronGeometry(radius, detail) THREE.IcosahedronGeometry(radius, detail)
THREE.TorusGeometry(radius, tube, radialSegments, tubularSegments, arc)
THREE.TorusKnotGeometry(radius, tube, radialSegments, tubularSegments, p, q, heightS cale)
文字形状(TextGeometry
)可以用来创建三维的文字形状。需要引入helvetiker_regular.typeface.js。
THREE.TextGeometry(text, parameters)
其中, text 是文字字符串, parameters 是以下参数组成的对象:
size :字号大小,一般为大写字母的高度
height :文字的厚度
curveSegments :弧线分段数,使得文字的曲线更加光滑
font :字体,默认是 ‘helvetiker’ ,需对应引用的字体文件
weight :值为 ‘normal’ 或 ‘bold’ ,表示是否加粗
style :值为 ‘normal’ 或 ‘italics’ ,表示是否斜体
bevelEnabled :布尔值,是否使用倒角,意为在边缘处斜切
bevelThickness :倒角厚度
bevelSize :倒角宽度
由于自定义形状需要手动指定每个顶点位置,以及顶点连接情况,如果该形状非常复杂, 程序员的计算量就会比较大。在这种情况下,建议在 3ds Max 之类的建模软件中创建模 型,然后使用 Three.js 导入到场景中,这样会更高效方便。
材质(Material)是独立于物体顶点信息之外的与渲染效果相关的属性。通过设置材质可以改变物体的颜色、纹理贴图、光照模式等。
THREE.MeshBasicMaterial(opt)
eg:
new THREE.MeshBasicMaterial({
color: 0xffff00,
opacity: 0.75
});
其中,opt可以缺省,或者为包含各种属性的值。
visible :是否可见,默认为 true
side :渲染面片正面或是反面,默认为正面 THREE.FrontSide ,可设置为反面THREE.BackSide ,或双面 THREE.DoubleSide
wireframe :是否渲染线而非面,默认为 false
color :十六进制 RGB 颜色,如红色表示为 0xff0000
map :使用纹理贴图
对于基本材质,即使改变场景中的光源,使用该材质的物体也始终为颜色处处相同的效果。当然,这不是很具有真实感,因此,接下来我们将介绍更为真实的光照模型:Lambert 光照模型以及 Phong 光照模型。
Lambert 材质(MeshLambertMaterial)是符合 Lambert 光照模型的材质。Lambert 光照模型 的主要特点是只考虑漫反射而不考虑镜面反射的效果,因而对于金属、镜子等需要镜面反 射效果的物体就不适应,对于其他大部分物体的漫反射效果都是适用的。
new THREE.MeshLambertMaterial(opt)
color是用来表现材质对散射光的反射能力,也是最常用来设置材质颜色的属性。除此之外,还可以用 ambient 和 emissive 控制材质的颜色。ambient 表示对环境光的反射能力,只有当设置了AmbientLight 后,该值才是有效的, 材质对环境光的反射能力与环境光强相乘后得到材质实际表现的颜色。emissive 是材质的自发光颜色,可以用来表现光源的颜色。
Phong 材质(MeshPhongMaterial)是符合 Phong 光照模型的材质。和 Lambert 不同的是, Phong 模型考虑了镜面反射的效果,因此对于金属、镜面的表现尤为适合。
new THREE.MeshPhongMaterial(opt);
opt中的属性可以设置,color(如果不设置镜面反射系数,而只设定漫反射,其效果同Lambert相同),emissive , ambient ,specular(用于指定镜面发射系数,高光?),shininess(这个值越大时,高光的光斑越小,默认值为30)
法向材质可以将材质的颜色设置为其法向量的方向,有时候对于调试很有帮助。
new THREE.MeshNormalMaterial()
在此之前,我们使用的材质都是单一颜色的,有时候,我们却希望使用图像作为材质。这时候,就需要倒入图像作为纹理贴图,并添加到相应的材质中,下面,就是几种具体的做法。
//将图片倒入纹理中
var texture = THREE.ImageUtils.loadTexture('../image.png');
//由于我们还没有使用动画,画面只被渲染了一次,而在导入纹理前,已经完成了这次渲染,所以看到的是一片黑。所以,就需要在完成导入纹理的步骤后,重新绘制画面。
var texture = THREE.ImageUtils.loadTexture('../image.png', {},
function() {
renderer.render(scene, camera);
});
//将材质的map属性设置为texture
var material = new THREE.MeshLambertMaterial({
map:texture
});
var materials = [];
for (var i = 0; i < 6; ++i) {
materials.push(new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('../img/' + i +'.png',
{}, function() {
renderer.render(scene, camera);
}),
overdraw: true
}));
}
var cube = new THREE.Mesh(new THREE.CubeGeometry(5, 5, 5),
new THREE.MeshFaceMaterial(materials)
);
scene.add(cube);
//其他的按照之前的方法依法泡制
//首先,我们需要指定重复方式为两个方向(wrapS和wrapT)都重复
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
//然后设置两个方向上都重复4次
texture.repeat.set(4, 4);
网格是由顶点,边,面等组成的物体。
Mesh(geometry, material)
位置,缩放,旋转是物体三个常用的属性,由于THREE.Mesh基础自THREE.Object3D,position,scale,rotation这三个值都是THREE.Vector3实例,因此修改其值的方法是相同的。下面以位置为例。
\\THREE.Vector3有x,y,z三个属性,如果只设置其中一个属性,则可以用以下方法:
mesh.position.z = 1;
\\如果需要同时设置多个属性,可以使用以下两种方法
mesh.position.set(1.5, -0.5, 0);
\\或者是
mesh.position = new THREE.Vector3(1.5, -0.5, 0);
对于Three.js而言,动画的实现也是通过在每秒中多次重绘画面实现的,为了衡量画面切换速度,引入了每秒帧数FPS(Frames Per Second)的概念,是指每秒画面重绘的次数。FPS越大,则动画效果越平滑,当FPS小于20时,一般就能明显感受到画面的卡滞现象。
那么 FPS 是不是越大越好呢?其实也未必。当 FPS 足够大(比如达到 60),再增加帧数人眼也不会感受到明显的变化,反而相应地就要消耗更多资源(比如电影的胶片就需要更长了,或是电脑刷新画面需要消耗计算资源等等)。因此,选择一个适中的 FPS 即可。
//有两个函数用于动画setInterval和requestAnimationFrame,两者最明显的差别是前者可以手动的设定FPS,后者则会自动设定FPS。总而言之,requestAnimationFrame 适用于对于时间较为敏感的环境(但是动画逻辑更加 复杂),而 setInterval 则可在保证程序的运算不至于导致延迟的情况下提供更加简洁的 逻辑(无需自行处理时间)
//setInterval
var id = setInterval(draw, 20);
function draw() {
mesh.rotation.y = (mesh.rotation.y + 0.01) % (Math.PI * 2);
renderer.render(scene, camera);
}
function stop() {
if (id !== null) {
clearInterval(id);
id = null;
}
}
//requestAnimationFrame
var id = requestAnimationFrame(draw);
function draw() {
mesh.rotation.y = (mesh.rotation.y + 0.01) % (Math.PI * 2);
renderer.render(scene, camera);
id = requestAnimationFrame(draw);
}
function stop() {
if (id !== null) {
cancelAnimationFrame(id);
id = null;
}
}
stat.js 是 Three.js 的作者 Mr. Doob 的另一个有用的 JavaScript 库。很多情况下,我们希望知 道实时的 FPS 信息,从而更好地监测动画效果。
使用 Three.js 创建常见几何体是十分方便的,但是对于人或者动物这样非常复杂的模型使用几何体组合就非常麻烦了。因此,Three.js 允许用户导入由 3ds Max 等工具制作的三维模型,并添加到场景中。
*.obj 是最常用的模型格式,导入 *.obj 文件需要OBJLoader.js ;导入带*.mtl 材质的 *.obj 文件需要 MTLLoader.js 以及 OBJMTLLoader.js 。另有 PLYLoader.js 、 STLLoader.js 等分别对应不同格式的加载器,可以根据模型格式自行选择。
本章将讨论四种光源(环境光,点光源,平行光,聚光灯)
环境光是指场景整体的光照效果,是由于场景内若干光源的多次反射形成的亮度一致的效 果,通常用来为整个场景指定一个基础亮度。因此,环境光没有明确的光源位置,在各处形成的亮度也是一致的。
THREE.AmbientLight(hex)
//在设置环境光时,只需要指定光的颜色,hex是十六进制的RGB颜色信息
环境光通常使用白色或者灰色,作为整体光照的基础。
点光源是不计光源大小,可以看作一个点发出的光源。点光源照到不同物体表面的亮度是 线性递减的,因此,离点光源距离越远的物体会显得越暗。
THREE.PointLight(hex, intensity, distance)
//hex是光源十六进值,intensity是亮度,缺省值为1,distance是光源最远照射到的距离,缺省值是0。
var light = new THREE.PointLight(0xffffff, 2, 100); light.position.set(0, 1.5, 2);
scene.add(light);
太阳光常常被看作平行光,这是因为相对地球上物体的尺度而言,太阳离我 们的距离足够远。对于任意平行的平面,平行光照射的亮度都是相同的,而与平面所在位 置无关。
THREE.DirectionalLight(hex, intensity)
//对于平行光而言,设置光源位置尤为重要。只要平面是平行的,那么得到的光照 也一定是相同的。
聚光灯是一种特殊的点光源,它能够朝着一个方向投射光线。聚光灯投射出的 是类似圆锥形的光线,这与我们现实中看到的聚光灯是一致的。
THREE.SpotLight(hex, intensity, distance, angle, exponent)
//angle是聚光灯的张角,缺省值是Math.PI/3,最大值是Math,PI/2;exponent是光强在偏离target的衰减指数。
light.position.set(x1, y1, z1); light.target.position.set(x2, y2, z2);
//如果想让聚光灯跟着某一物体移动(就像真的聚光灯),可以target指定为该物体。
var cube = new THREE.Mesh(
new THREE.CubeGeometry(1,1,1),
new THREE.MeshLambertMaterial({color: 0x00ff00})
);
var light = new THREE.SpotLight(0xffff00, 1, 100, Math.PI / 6, 25);
light.target = cube;
在Three.js中,能形成阴影的光源只有THREE.DirectionalLight,THREE.SpotLight;而相对地,能表现阴影效果的材质只有 THREE.LambertMaterial 与THREE.PhongMaterial 。
//首先,我们需要在初始化时,告诉渲染器渲染阴影
renderer.shadowMapEnable = true;
//然后,对于光源以及所要产生阴影的物体调用
xxx.castShadow = true;
//对于接收阴影的物体调用
xxx.receiveShadow = true;
用通俗的话来说,渲染就是将模型数据在屏幕上显示出来的过程。
着色器是屏幕上呈现画面之前的最后一步,用它可以对先前渲染的结果做修改,包括对颜色、位置等等信息的修改,甚至可以对先前渲染的结果做后处理,实现高级的渲染效果。
WebGL 强制需要程序员定义着色器,即使你只是希望采用默认的渲染方法。这似乎有些不 近人情,尤其对于对图形学理解不多的开发者而言。
幸运的是,Three.js 允许你不定义着色器(就像前面所有章节的例子)采用默认的方法渲 染,而仅在你有需要时,才使用自定义的着色器。
着色器代码可以写在单独的文件中(顶点着色器的文件名后缀为 .vs ,片元着色器的文件 名后缀为 .fs ),也可以在 HTML 文件中定义 script 标签实现。通常对于较长的着色器 代码,建议使用单独的文件;对于较短的着色器代码,在 HTML 文件中定义也是一个不错的选择。
定点着色器中的“顶点”指的正是 Mesh 中的顶点,对于每个顶点调用一次。因此,如果场 景中有一个正方体,那么对八个顶点将各自调用一次顶点着色器,可以修改顶点的位置或 者颜色等信息,然后传入片元着色器。
片元是栅格化之后,在形成像素之前的数据。片元着色器是每个片元会调用一次的程序, 因此,片元着色器特别适合用来做图像后处理。