three.js 04-08 之 ShaderMaterial 材质

    今天我们将要介绍的高级材质叫 THREE.ShaderMaterial (我把它翻译成自定义着色器材质)。它是 three.js 库中功能最为丰富、也是最为复杂的一种高级材质。通过它,可以定义自己的着色器,直接在 WebGL 环境中运行。着色器可以将 three.js 中的 JavaScript 对象转换为屏幕上的像素。通过这些自定义的着色器,你可以明确指定你的对象如何渲染及遮盖,或者修改 three.js 库中的默认值。

    但我们本篇将不会涉及如何定制着色器的细节,这已经属于另一个专门的技术体系叫“GLSL 着色器语言”,有兴趣的朋友可以专门买一本这方面的书去仔细研究。接下来我们先看看 ShaderMaterial 可以设置的几个常用属性,大部分跟其他基础材质类似,有 wireframe、wireframeLinewidth、flatShading、fog、vertexColors 等。

    特别提一下vertexColors 属性,我们可以通过它为每一个顶点定义不同的颜色。它在 CanvasRenderer 下不起作用,只在 WebGLRenderer 下起作用。关于此属性可以参考后面提到的关于 LineBasicMaterial 的例子。

    出上面提到的这些属性外,ShaderMaterial 还有几个特别的属性,使用它们你可以传入数据,定制你的着色器。但它们看起来不好理解,需要结合“GLSL 着色器语言”的相关知识,如下表所示:

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

    其中,最重要的部分就是:如果想要使用 ShaderMaterial 材质,就必须传入两个不同的着色器:

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

    到目前为止,我们讨论过的所有材质,three.js 库都会提供相应的 vertexShader 和 fragmentShader ,不必我们自己去定义。

    接下来,我们将要给出的这个示例,其中会创建一种动态材质,里面会用到较简单的 vertexShader,用来修改一个方块各个顶点的 x、y、z 坐标。还会用到另一个 fragmentShader(网站 http://glslsandbox.com/ 提供了很多着色器),用来创建连续变化的材质。完整代码如下:




    示例 04.08 - ShaderMaterial
	
	
	
	
	
    




    其中 id="vertex-shader" 的那段是 vertexShader 着色器脚本,只能用类 C 的 GLSL 语言来写。这里不做深入,只对重要的部分稍作说明。为了能够在 JavaScript 中与着色器进行通信,我们会使用所谓的统一值 uniform,譬如我们在例子中使用语句“uniform float time;” 传入外部数据,根据这个数据,我们会改变传入顶点的 x、y、z 坐标的值(通过 position 变量传入),代码片段如下所示:

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)));
现在向量 posChanged 中包含的就是顶点的新坐标,通过传入的 time 变量计算得到。最后,我们将这个新坐标传回给 three.js 库,代码如下:

gl_Position = projectionMatrix * modelViewMatrix * vec4(position*(abs(sin(time)/2.0)+0.5),1.0);
gl_Position 是一个特殊的变量,用来返回最终的位置。

    接着要做的就是要构造一个 ShaderMaterial 对象,并把这个 vertexShader 传给 ShaderMaterial 对象。为此,我们创建了一个简单的辅助函数 createMaterial(vertexShader, fragmentShader),其中两个参数所指的是 HTML 页面中脚本的元素 ID。在这个函数里可以看到我们创建了一个 uniforms 变量,它是用来从我们的渲染器中向着色器传递信息的。此处我们是通过在渲染函数中调用 changeVertex() 函数来到达这个目的,在这个 changeVertex() 函数里,渲染每循环一次就把 time 变量的值增加 0.01,这样就可以把信息传递给我们的 vertexShader 着色器,以便用来计算方块每个顶点的新位置。

    另外,通过运行本示例,你可以看到方块的每一个面都在不断变化,正是每个面上的 fragmentShader 片段着色器造就了这种变化。关于 fragmentShader 这部分你可以参考相关的 GLSL 着色器语言知识,这已经完全属于另一门专业的技术范畴了,请读者自行挖掘。

未完待续···


你可能感兴趣的:(javascript,threejs,webgl,three.js)