在开始这一节前,我们先将要探究的材质列在下表中:
名称 | 描述 |
MeshBasicMaterial(网格基础材质) | 基础材质,可以用它赋予几何体一种简单的颜色,或者显示几何体的线框。 |
MeshDepthMaterial(网格深度材质) | 根据网格到相机的距离,这种材质决定如何给网格染色。 |
MeshNormalMaterial(网格法向材质) | 这是一种简单的材质,根据物体表面的法向向量计算颜色。 |
MeshFaceMaterial(网格面材质) | 这是一个容器,可以在这个容器里为物体的各个表面指定不同的颜色。 |
MeshLambertMaterial(网格朗伯材质) | 这种材质会考虑光照的影响,可以用来创建颜色暗淡的、不光亮的物体。 |
MeshPhongMaterial(网格Phong式材质) | 这种材质也会考虑光照的影响,而且可以用来创建光亮的物体。 |
ShaderMaterial(着色器材质) | 这种材质允许使用自定义的着色器程序,直接控制顶点的放置方式,以及像素的着色方式。 |
LineBasicMaterial(直线基础材质) | 这种材质可以用于THREE.Line(直线)几何体,从而创建着色的直线。 |
LineDashedMaterial(虚线材质) | 这种材质跟直线基础材质一样,不过可以用来创建出一种虚线效果。 |
注:1)Lambert,朗伯,一种亮度单位。
2)Phong,一种着色方式。
一、理解共有属性
Three.js提供了一个材质基类,THREE.Material,这个类列出了所有共有属性。我们将这些共有属性分成了三类:
- 基础属性:这些属性是最常用到的。通过这些属性可以控制物体的透明度、是否可见或如何引用物体(通过ID或自定义名称)。
- 融合属性:每个物体都有一系列的融合属性。这些属性决定物体如何与背景融合。
- 高级属性:有一些高级属性可以控制底层WebGL上下文渲染物体的方法。大多数情况下,不会用到这些属性。
1.基础属性
THREE.Material类的基础属性列在下表中。
属性 | 描述 |
ID(标识符) | 用来标识材质,在创建时赋值。 |
name(名称) | 可以通过这个属性赋予材质名称。 |
opacity(透明度) | 定义物体有多透明。需与属性transparent一起使用。该属性的取值范围是0~1。 |
transparent(是否透明) | 如果设为true,THREE.js库就会根据opacity的值来渲染物体。如果是false,这个物体就不透明,只是着色明亮一些。 |
overdraw(过度描绘) | 如果你用THREE.CanvasRenderer(画布渲染器)对象,多边形会被渲染得稍微大一点儿。当你用这个渲染器画出来的物体有缝隙时,可以将这个属性设为true。 |
visible(是否可见) | 定义该材质是否可见,如果设为false,那么在场景中就看不到该物体。 |
side(侧面) | 通过这个属性,可以决定在几何体的哪一面应用这个材质。默认值是THREE.FrontSIde(前面),这可以将材质应用到物体的前(外)面。也可以将它设为THREE.BackSide(后面),这可以将材质应用到物体的后(内)侧。或者也可以将它设为THREE.DoubleSide(双侧),这样就可以将材质应用到物体的内外两侧。 |
needsUpdate(要否刷新) | 对于材质的某些修改,你需要告诉Three.js库材质已经改变了。如果这个属性设为true,THREE.js就会使用新的材质属性刷新它的缓存。 |
2.融合属性
材质有几个跟融合相关的一些属性,如下表所示:
名称 | 描述 |
blending(融合) | 决定物体上的材质如何跟背景融合。融合模式一般是NormalBlending,在这种模式下只显示材质的上层。 |
blendsrc(融合源) | 除了使用标准融合模式之外,还可以通过指定blendsrc、blenddst和blendequation属性来创建自定义的融合模式。该属性指定物体(源)如何跟背景(目标)相融合。默认值是SrcAlphaFactor,即使用alpha(透明度通道进行融合)。 |
blenddst(融合目标) | 该属性定义融合时如何使用背景(目标),默认值是OneMinusSrcAlphaFactor,其含义是:目标也使用源的alpha通道进行融合,只是用的值是1(源的alpha通道值)。 |
blendingequation(融合公式) | 指定如何使用blendsrc和blenddst的值。默认方法是AddEquation,即将两个颜色值相加。使用这三个属性,就可以创建自定义的融合模式。 |
3.高级属性
高级属性跟WebGL内部如何工作相关。
名称 | 描述 |
depthTest(深度测试) | 使用这个参数可以打开或关闭GL_DEPTH_TEST参数。该参数决定像素深度是否用来计算新的像素值。通常情况下不必修改这个属性。 |
depthWrite | 可以用来决定这个材质是否影响WebGL的深度缓存。如果你将一个物体用作二维贴图时(例如一个套子),你应该将这个属性设为false。但一般来说,你不应该修改这个属性。 |
polygonOffset、polygonOffsetFactor和polygonOffsetUnits | 通过这些属性,可以控制WenGL的POLYGON_OFFSET_FILL功能。一般不需要使用它们 |
alphaTest | 可以给这个属性指定一个值(从0到1)。如果某个像素的alpha值小于这个值,那么该像素就不会显示出来。 |
二、从简单的网格材质(基础、深度和面)开始
1.简单表面的MeshBasicMaterial
MeshBasicMaterial是一种非常简单的材质,这种材质不考虑光照的影响。使用这种材质的网格会被渲染成一些简单的平面多边形,而且你也有机会显示几何体的线框。除了之前提到的共有属性,还可以设置以下属性:
名称 | 描述 |
color | 设置材质的颜色。 |
wireframe | 设置这个属性可以将材质渲染成线框。对调试非常有利。 |
wireframeLinewidth(线框线宽) | 如果已经打开了wireframe,这个属性可以定义线框中线的宽度。 |
wireframeLinecap(线框线端点) | 这个属性定义线框模式下顶点间线段的端点如何定义。可选值包括butt(平)、round(圆)和square(方)。默认值是round。在实际使用中,这个属性的修改结果很难看出来。WebGLRenderer对象不支持该属性。 |
wireframeLinejoin(线框线段连接点) | 定义线段的连接点如何显示。可选的值包括round、bevel(斜角)和miter(尖角)。默认值是round。如果你在一个使用低透明度和很大wireframeLinewidth值的例子里靠近观察,你就可以观察到这个属性的效果。WebGLRenderer对象不支持该属性。 |
shading(着色) | 该属性定义如何 着色。可选的值是THREE.SmoothShading和THREE.FlatShading。该属性在这个材质的例子里没有设置 |
vertexColors(顶点颜色) | 可以通过这个属性为每一个顶点定义不同的颜色。该属性在使用CanvasRenderer时不起作用,但可以在使用WebGLRenderer时起作用。 |
fog(雾化) | 该属性指定当前材质是否会受全局雾化效果设置的影响。 |
我们会像下面这样设置MeshBasicMaterial,所有的属性即可以在构造函数里传递,也可以在材质创建好之后设置:
var meshMaterial = new THREE.MeshBasicMaterial({color:0x7777ff});
meshMaterial.visible = false;
2.基于深度着色的MeshDepthMaterial
使用这种材质的物体,其外表不是由光照或某种材质决定的;而是由物体到相机的距离决定的。可以将这种材质与其他材质相结合,从而很容易地创建出逐渐消失的效果。这种材质只有两个控制线框的属性:
名称 | 描述 |
wireframe | 是否显示线框 |
wireframeLinewidth | 线框线的宽度 |
创建MeshDepthMaterial非常简单。这个对象没有多少参数。在下面这个例子中,我们使用了scene.overrideMaterial属性,以保证场景中的所有物体都会使用该材质,如下面这段代码所示:
var scene = new THREE.Scene();
scene.overrideMaterial = new THREE.MeshDepthMaterial();
3.联合材质
在MeshDepthMaterial中,你会发现没有选项可以用来设置方块的颜色,一切都是有材质的默认属性决定的。但是,Three.js库可以通过联合材质创建出新效果(这也是材质融合起作用的地方)。使用下面的代码,就可以把MeshDepthMaterial材质赋予方块:
var cubeMaterial = new THREE.MeshDepthMaterial();
var cubeColor = new THREE.MeshBasicMAterial({color:0x00ff00,
transparent:true,blending:THREE.MultiplyBlending});
var cube = new THREE.SceneUtils.createMultiMaterialObject(
cubeGeometry,[cubeMaterial,cubeColor]);
cube.children[1].scale.set(0.99,0.99,0.99);
然后就可以获得下图所示的绿色方块,这些方块从MeshBasicMaterial中获得渐变效果,从MeshBasicMaterial中获得颜色。
让我们来看看要达到这种效果需要采取的步骤:
首先,要创建两种材质,对于MeshDepthMaterial没有什么特别要做的,但对于MeshBasicMaterial,要把transparent属性设为true,并指定融合模式;
其次,调用createMultiMaterialObject()函数创建一个网格,此时,几何体会被复制,返回一个网格组,里面两个网格完全相同;
最后,代码的最后一行也很重要,如果没有最后一行,那么在渲染的时候画面就会闪烁,因为它们会直接在彼此的上面渲染。通过缩小带有MeshDepthMaterial材质的网络,就可以避免这种现象。
4.计算法向颜色的MeshNormalMaterial
如下图所示,网格上的 每一面在渲染时颜色都稍有不同,而且即便在球体旋转时,这些颜色也基本上保持原来的位置。之所以是这样,是因为每一个面的颜色是从该面向指的法向量计算得出的。法向量所指的方向决定每个面从MeshNormalMaterial材质获取的颜色。
MeshNormalMaterial对象还有几个属性可以设置,如下表所示:
名称 | 描述 |
wireframe | 该属性指定是否显示线框 |
wireframeLinewidth | 指定线框的宽度 |
shading | 该属性用来设置着色方式:THREE.FlatShading表示平面着色,THREE.SmoothShading表示平滑着色。 |
5.为每个面指定材质的MeshFaceMaterial
这种基础材质并不是真正的材质,更像是一个材质容器。通过MeshFaceMaterial可以为几何体的每个面指定不同的材质。这种材质使用起来很简单,如下所示:
var matArray = [];
matArray.push(new THREE.MeshBasicMaterial({color: 0x009e60}));
matArray.push(new THREE.MeshBasicMaterial({color: 0x0051ba}));
matArray.push(new THREE.MeshBasicMaterial({color: 0xffd500}));
matArray.push(new THREE.MeshBasicMaterial({color: 0xff5800}));
matArray.push(new THREE.MeshBasicMaterial({color: 0xc41e3a}));
matArray.push(new THREE.MeshBasicMaterial({color: 0xffffff}));
var faceMaterial = new THREE.MeshFaceMaterial(matArray);
var cubeGeom = new THREE.CubeGeometry(3,3,3);
var cube = new THREE.Mesh(cubeGeom,faceMaterial);
效果图如下图所示:
三、学习高级材质
1.用于暗淡、不光亮表面的MeshLambertMaterial
这种材质可以用来创建看上去暗淡的并不光亮的表面。该材质非常易用,而且会对场景中的光源产生反应。可以在这个材质上配置之前提到的几个属性:color、opacity、shading、blending、depthTest、depthWrite、wireframe、wireframeLinewidth、wireframeLinecap、wireframeLinejoin、vertexColors以及fog。这种材质还有两种独有的材质:
名称 | 描述 |
ambient(环境色) | 这是该材质的环境光。跟AmbientLight光源一起使用。这个颜色会与AmbientLight光源的颜色相乘。默认值是白色。 |
emissive(发射的) | 这是该材质发射的颜色。它其实并不像一个光源,只是一种纯粹的、不受其他光影响的颜色。默认值是黑色。 |
这种材质的创建跟其他材质都一样,如下所示:
var meshMaterial = new THREE.MeshLambertMaterial({color:0x7777ff});
2.用于光亮表面的MeshPhongMaterial
通过MeshPhongMaterial,可以创建一种光亮的材质。可以使用的属性与创建暗淡材质的MeshLambertMaterial基本一样。对于这种材质我们将感兴趣的属性列在下表中:
名称 | 描述 |
ambient | 这是该材质的环境色。 |
emissive | 这是该材质发射的颜色。它其实并不像一个光源,只是一种纯粹的、不受其他光照影响的颜色。默认值是黑色。 |
specular(镜面的) | 该属性指定该材质的光亮程度及其高光部分的颜色。如果将它设置成跟color属性相同的颜色,将会得到一种更加类似金属的材质。如果设置为grey(灰色),材质将变得更像塑料。 |
shininess | 该属性指定高光部分的亮度。默认值是30. |
3.用ShaderMaterial创建自己的着色器
通过ShaderMaterial,可以使用自己定制的着色器,着色器可以将Three.js中的Javascript对象转换为屏幕上的像素。通过自定义的着色器,你可以明确指定你的对象如何渲染和遮盖,或者修改Three.js库中的默认值。
ShaderMaterial有几个属性可以设置,除了包括之前提到的wireframe、wireframeLinewidth、shading、vertexColors以及fog属性,还有几个特别属性,使用它们你可以传入数据,定制你的着色器。
名称 | 描述 |
fragmentShader(像素着色器) | 这个着色器定义的是每个传入的像素的颜色 |
vertexShader(顶点着色器) | 这个着色器允许你修改每一个传入的顶点的位置 |
uniforms(统一值) | 通过这个属性可以向你的着色器发信息。同样的信息会发到每一个顶点和片段 |
definess | 这个属性的值可以转换成vertexShader和fragmentShader里的#define代码。该属性可以用来设置着色器程序里的一些全局变量 |
attributes | 该属性可以修改每个顶点和片段。通常用来传递位置数据和与法向量相关的数据。如果要用这个属性,那么你要为几何体中的所有顶点提供信息 |
lights | 该属性定义光照数据是否传递给着色器。默认值是false |
要使用ShaderMaterial材质,必须要传入两个不同的着色器:
(1)vertexShader:vertexShader会在几何体的每一个顶点上执行。可以用这个着色器通过改变顶点的位置来对几何体进行变换。
(2)fragmentShader:fragmentShader会在几何体的每一个像素上执行。在vertexShader里,我们会返回这个特定像素应该显示的颜色。
编写好着色器后,我们要将vertexShader与fragmentShader传入ShaderMaterial,出于这个目的,我们可以创建如下一个简单的辅助函数:
function createMaterial(vertexShader,fragmentShader){
var vertShader = document.getElementById(vertexShader).innerHTML;
var fragShader = document.getElementById(fragmentShader).innerHTML;
var attributes = {};
var uniforms = {
time:{type:'f',value:0.2},
scale:{type:'f',value:0.2},
alpha:{type:'f',value:0.6},
resolution:{type:"v2",value:new THREE.Vector2()}
};
uniforms.resolution.value.x = window.innerWidth;
uniforms.resolution.value.y = window.innerHeight;
var meshMaterial = new THREE.ShaderMaterial({
uniforms:uniforms,
attributes:attributes,
vertexShader:vertShader,
fragmentShader:fragShader,
transparent:true
});
return meshMaterial;
}
四、线段几何体的材质
1.LineBasicMaterial
通过线段基础材质可以设置线段的颜色、宽度、端点和连接点属性。下表列出的是这种材质中可用的属性:
名称 | 描述 |
color | 指定线的颜色。如果指定vertexColors,这个属性就会被忽略 |
linewidth | 定义先的宽度 |
LineCap | 定义顶点间的线段端点如何显示。可选的值包括butt(平)、round(圆)和square(方)。默认值是round。在实际应用中,修改这个属性的结果很难看出来。WebGLRenderer也不支持该属性 |
LineJoin | 定义的是线段连接点如何显示。可选的值包括round(圆)、bevel(斜切)和miter(尖角)。默认值是round |
vertexColors | 将这个属性设置成THREE.VertexColors值,就可以为每个顶点指定一个颜色 |
fog | 指定当前物体是否受全局雾化效果i的影响 |
我们先来快速浏览一下如何从顶点集合中创建出一些线段,并为它们赋予LineBasicMaterial材质,构成一个网格。代码如下所示:
var points = gosper(4,60); //返回一个Gosper曲线
var lines = new THREE.Geometry();
var colors = [];
var i = 0;
points.forEach(function(e){
lines.vertices.push(new THREE.Vector3(e.x,e.z,e.y));
colors[i] = new THREE.Color(0xffffff);
colors[i].setHSL(e.x/100+0.5,(e.y*20)/300,0.8); //HSL:色调、饱和度、亮度
i++;
});
lines.colors = colors;
var material = new THREE.LineBasicMaterial({
opacity:1.0,
linewidth:1,
vertexColors:THREE.VertexColors });
var line = new THREE.Line(lines,material);
效果图如下所示:
2.LineDashedMaterial
这种材质的属性跟LineBasicMaterial的属性一样,但是通过指定短划线和空格的长度,可以创建出虚线的效果。这种材质的几个额外的、可以用来定义短划线长度和短划线中间空格长度的属性,如下所示:
名称 | 描述 |
scale(缩放比例) | 缩放dashSize和gapSize。如果scale小于1,dashSize和gapSzie就会增大;如果scale大于1,dashSize和gapSzie就会缩小 |
dashSize | 短划线的长度 |
gapSize | 间隔的长度 |
这种属性的用法跟LineBasicMaterial基本一样,唯一的区别是必须调用computeLineDistances(),如下所示:
lines.computeLineDistances();
var material = new THREE.LineDashedMaterial({
vertexColors:true,
color:0xffffff,
dashSize:10,
gapSize:1,
scale:0.1
});