Three.js开发指南-材质

Three.JS的材质

在Three.js中材质就像是物体的皮肤,决定了几何的样子。

类型 名称 描述
网络材质 MeshBasicMaterial(网络基础材质) 基础材质,可以用它赋予几何体一种简单的颜色,或者显示几何体的线框
网络材质 MeshDepthMaterial(网络深度材质) 根据网络到相机的距离,这种材质决定如何给网格着色
网络材质 MeshNormalMaterial(网络法向材质) 这是一种简单的材质,根据物体表面的法向向量计算颜色
网络材质 MeshFaceMaterial(网络面材质) 这是一个容器,可以在这个容器里面为物体的各个表面指定不同的颜色
高级材质 MeshLambertMaterial(网络朗伯材质) 这种材质会考虑光照的影响,可以创造颜色暗淡的,不光亮的物体
高级材质 MeshPhongMaterial(网络Phong式材质) 这种材质会考虑光照的影响,可以用来创造光亮的物体
高级材质 ShaderMaterial(着色器材质) 这种材质允许使用自定义的着色器程序,直接控制顶点的放置方式,以及像素的着色方式
线段几何体材质 LineBasicMaterial(直线基础材质) 这种材质可以用于Three.Line 几何体,从而创建着色的直线
线段几何体材质 LineDashedMaterial(虚线材质) 这种材质跟直线基础材质一样,不过用于创建虚线效果

材质的共同属性

  1. 基础属性:用于控制物体的透明度,是否可见,以及如何引用物体。
  2. 融合属性:每个物体都有一系列的融合属性,这些属性决定物体如何与背景融合。
  3. 高级属性:用于控制底层WebGL上下文渲染物体的方法。

基础属性有以下值:

属性 描述
ID 材质的ID,创建时赋值
name 材质的name,创建时赋值
opacity 透明度(0-1)
transparent 是否透明(true,false)
overdraw过度描绘 如果用THREE.CanvasRenderer(画布渲染器)对象,多边形会被渲染的稍微大一点。当你用渲染器画出来的物体有缝隙时,这个值可以设置为true
visible是否可见 定义该材质是否可见,true,false
side侧面 通过这个属性,可以决定在几何体的哪一面应用这个材质。默认值是THREE.FrontSide前面,表示将材质用于前面。BackSide(后面),DoubleSide(两侧)
needsUpdate是否刷新 是不是用于刷新缓存

融合属性有以下值:

属性 描述
blending融合 决定物体上的材质如何与背景融合,一般为NormalBlending,这种模式下只显示材质的上层
blendsrc融合源 表示指定物体如何跟背景相融合,默认值SrcAlphaFactor,即使用透明度通道进行融合
blenddst融合目标 这是如何使用背景,默认值OneMinusSrcAlphaFactor,目标也使用源的透明通道进行融合,只是用的值是1
blendingequation融合公式 指定如何使用blendsrc和blenddst的值,默认方法是AddEquation,即使将两个颜色值相加

高级属性有以下值:

属性 描述
blending融合 决定物体上的材质如何与背景融合,一般为NormalBlending,这种模式下只显示材质的上层
blendsrc融合源 表示指定物体如何跟背景相融合,默认值SrcAlphaFactor,即使用透明度通道进行融合
blenddst融合目标 这是如何使用背景,默认值OneMinusSrcAlphaFactor,目标也使用源的透明通道进行融合,只是用的值是1
blendingequation融合公式 指定如何使用blendsrc和blenddst的值,默认方法是AddEquation,即使将两个颜色值相加

1. MeshBasicMaterial材质

MeshBasicMaterial是一种非常简单的材质,不考虑光照的影响。它有以下属性:

属性 描述
color 设置材质的颜色
wireframe 设置这个属性可以将材质渲染成线框
wireframeLinewidth wireframe这个打开的时候,设置这个值表示线的宽度
wireframeLinecap 表示顶点间线段的端点如何显示,butt平,round圆,square方。webglrenderer对象不支持该属性
wireframeLinejoin 定义线段的连接点如何显示,round,bevel斜角,miter尖角。webglrenderer对象不支持该属性
shading着色 定义如何着色,可选值是THREE.SmoothShading和THREE.FlatShading
vertexColors顶点颜色 为每个顶点设置不同的颜色。CanvasRanderer时不起作用。webglrenderer对象支持该属性
fog雾化 该属性指定当前材质是否受全局雾化效果设置的形象

举个栗子

var meshMaterial = new THREE.MeshBasicMaterial({color:0x7777ff});
meshMaterial.visible = false;

2. MeshDepthMaterial材质

MeshBasicMaterial材质的物体,外观不是由光照和某个材质属性决定的,而是由物体到相机的距离决定的。可以将该材质与其他材质结合。它有以下属性:

属性 描述
wireframe 设置这个属性可以将材质渲染成线框
wireframeLinewidth wireframe这个打开的时候,设置这个值表示线的宽度

举个栗子

var scene = new THREE.Scene();
scene.overrideMaterial = new THREE.MeshDepthMaterial();

3. MeshNormalMaterial材质

MeshBasicMaterial法向颜色材质,外观不是由光照和某个材质属性决定的,而是由物体到相机的距离决定的。可以将该材质与其他材质结合。它有以下属性:

属性 描述
wireframe 设置这个属性可以将材质渲染成线框
wireframeLinewidth wireframe这个打开的时候,设置这个值表示线的宽度
shading着色 定义如何着色,可选值是THREE.SmoothShading平滑着色和THREE.FlatShading平面着色

法向量是与面垂直的向量,法向量在THEREE中用于决定光的反射反向,用于计算光照,阴影时提供信息,用于为物体表面像素上色。

举个栗子

//表示法向量的箭头,可以使用THREE.ArrowHelper对象
for(var f=0,fl=sphere.geometry.faces.length;f<fl;f++){
    var arrow = new THREE.ArrowHelper(face.normal,face.centroid,2);
    shpere.add(arrow);
}

4. MeshFaceMaterial材质

MeshBasicMaterial,是一种材质容器。可以以几何体的每一个面指定不同的材质。

举个栗子

var metArray = [];
metArray.push(new THREE.MeshBasicMaterial({color:0x009e60}));
metArray.push(new THREE.MeshBasicMaterial({color:0x0051ba}));
metArray.push(new THREE.MeshBasicMaterial({color:0xffd500}));
metArray.push(new THREE.MeshBasicMaterial({color:0xc49e60}));
metArray.push(new THREE.MeshBasicMaterial({color:0xffffff}));
metArray.push(new THREE.MeshBasicMaterial({color:0x000000}));

var faceMaterial = new THREE.MeshFaceMaterial(metArray);

//1
var cubeGeom1 = new THREE.CubeGemotry(3,3,3);
var cube1 = new THREE.Mesh(cubeGeom1, faceMaterial);

//2
var group = new THREE.Mesh();
for(var x = 0;x<3;x++){
   for(var y=0;y<3;y++){
      for(var z=0;z<3;z++){

         var cubeGeom = new THREE.CubeGemotry(2.9,2.9,2.9);
         var cube = new THREE.Mesh(cubeGeom, faceMaterial);

         cube.position = new THREE.Vector3(x*3-3,y*3,z*3-3);
         group.add(cube);
      }
   }
}

5. 联合材质

  1. MeshDepthMaterial使用的时候设置方块的颜色,而材质是使用默认的。 举个栗子
var cubem = new THREE.MeshDepthMaterial();
var colorm = new THREE.MeshBasicMaterial({
  color:0x00ff00,
  transparent:true,//指定融合模式,不透明
  blending:THREE.MultiplyBlending //前景的颜色跟背景的颜色相乘,得出结果
});

var cube = new THREE.SceneUtils.createMultiMaterialObject(cubeGeometry,[colorm, cubem]);//createMultiMaterialObject用于创建一个网格,几何体会被复制,返回一个新的网格组
cube.childreb[1].scale.set(0.99,0.99,0.99)//防止画面闪烁。

6.高级材质

6.1 用于暗淡、不光亮表面的MeshLambertMaterial

MeshLambertMaterial除了以上所有属性,还有两个特有属性;

属性 描述
ambient环境色 这是该材质的环境色,跟AmbientLight一起使用。其值会与AmbientLight光源的颜色相乘。默认白色
emissive反射的 这是该材质的反射的颜色,一种不受其他光源影响的颜色,默认黑色

举个栗子

var cubem = new THREE.MeshLambertMaterial({color:0x7777ff});

6.2 用于光亮表面的MeshPhongMaterial

MeshPhongMaterial除了以上所有属性,还有两个特有属性;

属性 描述
ambient环境色 这是该材质的环境色,跟AmbientLight一起使用。其值会与AmbientLight光源的颜色相乘。默认白色
emissive反射的 这是该材质的反射的颜色,一种不受其他光源影响的颜色,默认黑色
specular镜面的 该属性指定该材质的光亮程度及其高光部分的颜色。如果该值与color属性值一样,会得到一种类似金属的材质。
shininess 该属性指定高光部分的亮度,默认30

举个栗子

var cubem = new THREE.MeshPhongMaterial({color:0x7777ff});

6.3 ShaderMaterial创建自己的着色器

ShaderMaterial是THREE中最复杂的材质。以下是普通属性

属性 描述
wireframe 设置这个属性可以将材质渲染成线框。用于调试
wireframeLinewidth 定义线框中线的宽度
shading 定义如何着色,可选值是THREE.SmoothShading平滑着色和THREE.FlatShading平面着色
vertexColors 可以通过这个属性为每一个顶点定义不同的颜色
fog 该属性指定当前材质是否会受到全局雾化效果设置影响。

除了以上属性,还有以下特别属性;

属性 描述
fragmentShader(像素着色器) 这个着色器定义的是每个传入的像素的颜色
vertexShader(顶点着色器) 修改每一个传入的顶点的位置
uniforms(统一值) 通过这个属性可以向你的着色器发信息。同样的信息会发到每一个顶点和片段
defines 这个属性的值可以转换成vertexShader 和 fragmentShader 里面的#define 的代码。该属性可以用来设置着色器程序里的一些全局变量
attributes 该属性可以修改每个顶点和片段。通常用来传递位置数据和法向量相关的数据。
lights 该属性定义光照数据是否传递给着色器。默认值false

要使用ShaderMaterial材质,需要传入两个不同的着色器:

  1. vertexShader: vertexShader会在几何体的每一个顶点上执行。可以用这个着色器通过改变顶点位置来对几何体进行变换。
  2. fragmentShader: fragmentShader会在几何体的每一个像素上执行。在vertexShader里,我们会返回这个特定像素应该显示的颜色。

举个栗子

<script id='vertex-shader' type='x-shader/x-vertex'>
uniform float time;
void main(){
  vec3 posChanged = position;
  posChanged.x = posChanged.x * (abs(sin(time*1.0)));
  posChanged.y = posChanged.y * (abs(cos(time*1.0)));
  posChanged.z = posChanged.z * (abs(sin(time*1.0)));

  gl_Position = projectionMatrix * modelViewMatrix * vec4(posChanged,1.0)
}
</script>

着色器不是JS编写的,用类似C的GLSL语言来编写的着色器。

gl_Position是一个特殊变量,用来返回最终位置。

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;
}

用法是:var meshMaterial1 = createMaterial(“vertex-shader”,“fragment-shader-1”)。这些参数所指的是HTML页面元素的ID。这个变量是用来从我们的渲染器中向着色器传递信息的。

function render(){
  stats.update();
  cube.rotation.y = step+=0.1;
  cube.rotation.x = step;
  cube.rotation.z = step;

  cube.meterial.meterials.forEach(function(e){
      e.uniforms.time.value +=0.1;
  })
  requestAnimationFrame(render);
  renderer.render(scene,camera);
}

上面中这个渲染循环每执行一次变量time就会增加0.01。这个信息会传递给vertexShader,用来计算方块顶点的新位置。

每一个面都在不断变化,正是每个面上的fragmentShader造就了变化。使用了MeshFaceMaterial。

var cubeGeometry = new THREE.BoxGeometry(20, 20, 20);

var meshMaterial1 = createMaterial("vertex-shader", "fragment-shader-1");
var meshMaterial2 = createMaterial("vertex-shader", "fragment-shader-2");
var meshMaterial3 = createMaterial("vertex-shader", "fragment-shader-3");
var meshMaterial4 = createMaterial("vertex-shader", "fragment-shader-4");
var meshMaterial5 = createMaterial("vertex-shader", "fragment-shader-5");
var meshMaterial6 = createMaterial("vertex-shader", "fragment-shader-6");


var material = new THREE.MeshFaceMaterial(
        [meshMaterial1,
            meshMaterial2,
            meshMaterial3,
            meshMaterial4,
            meshMaterial5,
            meshMaterial6]);
<script id="fragment-shader-6" type="x-shader/x-fragment">


    uniform float time;
    uniform vec2 resolution;


    void main( void )
    {

    vec2 uPos = ( gl_FragCoord.xy / resolution.xy );//normalize wrt y axis
    //suPos -= vec2((resolution.x/resolution.y)/2.0, 0.0);//shift origin to center

    uPos.x -= 1.0;
    uPos.y -= 0.5;

    vec3 color = vec3(0.0);
    float vertColor = 2.0;
    for( float i = 0.0; i < 15.0; ++i )
    {
    float t = time * (0.9);

    uPos.y += sin( uPos.x*i + t+i/2.0 ) * 0.1;
    float fTemp = abs(1.0 / uPos.y / 100.0);
    vertColor += fTemp;
    color += vec3( fTemp*(10.0-i)/10.0, fTemp*i/10.0, pow(fTemp,1.5)*1.5 );
    }

    vec4 color_final = vec4(color, 1.0);
    gl_FragColor = color_final;
  }

</script>

最终传回给THREE.JS库的颜色是通过gl_FragColor = color_final;设置的。

7.线段几何体的材质

线段几何体的材质主要包括两个:

  1. LineBasicMaterial: 通过线段基础材质可以设置线段的颜色、宽度、端点、连接点的属性。
  2. LineDashedMaterial: 跟LineBasicMaterial一样。但是通过指定短划线和空格的长度,可以创造出虚线效果。

7.1 LineBasicMaterial

属性 描述
color 指定线的颜色。如果指定vertexColors,这个属性忽略。
linewidth 线的宽度
LineCap 该属性定义顶点间的线段端点如何显示。butt,round,square.
LineJoin 定义线段连接点如何显示。round,bevel,miter.
vertexColors 可以为每个顶点指定一种颜色
fog 定义该物体是否受到全局雾化效果影响

var points = gosper(4, 60);//获取一组xy的坐标,返回一个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);
    //使用setHSL设置颜色。还可以使用HSL,RGB等设置
    i++;
});

lines.colors = colors;
var material = new THREE.LineBasicMaterial({
    opacity: 1.0,
    linewidth: 1,
    vertexColors: THREE.VertexColors
});

var line = new THREE.Line(lines, material);
line.position.set(25, -30, -60);
scene.add(line);

7.2 LineDashedMaterial

属性 描述
scale缩放比例 缩放dashSize和gapSize,如果scale小于1,dashSize和gapSize就会增大,反之减小。
dashSize 短划线的长度
gapSize 间隔的长度

lines.computeLineDistances();
var material = new THREE.LineDashedMaterial({
    vertexColors: true,
    color: 0xffffff,
    dashSize: 2,
    gapSize: 2,
    scale: 0.1
});

var line = new THREE.Line(lines, material);
line.position.set(25, -30, -60);
scene.add(line);

8. 总结

three.js库提供的很多覆盖几何体的材质。从简单到复杂。总结:

  1. 各种材质有很多共同的属性。
  2. 并不是所有的材质都会带场景的光源做出反应。MeshPhongMaterial和MeshLambertMaterial会对场景的光源做出反应,
  3. 如果要创建一个透明的材质,仅仅设置opacity属性是不够的,还要将transparent设置成true。
  4. 材质的大部分属性都可以在运行时修改。但是side不能,如果你要修改需要将needsUpdate设置为true,这样就能在运行中修改属性了、
  5. 可以为一个几何体赋值多种材质。这样会复制几何体创建多个网格
  6. THREE.Line几何体不可以用普通材质覆盖。只能用线段几何体的材质。
  7. 如果想要一个光亮的物体,可以使用MeshPhongMaterial;如果想要一个暗淡的物体可以使用MeshLambertMaterial;
  8. 可以用dat.GUI来试验各种材质属性。