相較 PixiJS,three.js 應用 Shader 的方式就相當多種
先前的自己,看到程式碼裡有 Shader 時,會覺得
"啊,是Shader,看不懂啦,最多改改參數啦"
看著系列文到這,或多或少能看得懂原理了
本篇介紹一個 three.js 範例的寫法
先前文章裡提到的範例:
three.js examples - WebGL buffergeometry rawshader:
系列文先前的文章,提到 WebGL 的運算流程:
attribute 把值帶到 vertex shader
varying 把值帶到 fragment shader
uniform 帶值給 vertex shader 與 fragment shader
接著,大概能看懂這個範例的寫法了 - Source Code
JavaScript:
var positions = [];
var colors = [];
for ( var i = 0; i < vertexCount; i ++ ) {
	// adding x,y,z
	positions.push( Math.random() - 0.5 );
	positions.push( Math.random() - 0.5 );
	positions.push( Math.random() - 0.5 );
	// adding r,g,b,a
	colors.push( Math.random() * 255 );
	colors.push( Math.random() * 255 );
	colors.push( Math.random() * 255 );
	colors.push( Math.random() * 255 );
}
var positionAttribute = new THREE.Float32BufferAttribute( positions, 3 );
var colorAttribute = new THREE.Uint8BufferAttribute( colors, 4 );
colorAttribute.normalized = true; // this will map the buffer values to 0.0f - +1.0f in the shader
geometry.addAttribute( 'position', positionAttribute );
geometry.addAttribute( 'color', colorAttribute );
首先是用 attribute 把值帶進 Vertex Shader,帶了 position 與 color
Vertex Shader 程式碼:
<script id="vertexShader" type="x-shader/x-vertex">
	precision mediump float;
	precision mediump int;
	uniform mat4 modelViewMatrix; // optional
	uniform mat4 projectionMatrix; // optional
	attribute vec3 position;
	attribute vec4 color;
	varying vec3 vPosition;
	varying vec4 vColor;
	void main()	{
		vPosition = position;
		vColor = color;
		gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
	}
</script>
attribute vec3 position 與 attribute vec4 color 是剛剛透過 attribute 帶進來的變數
接著再定義兩個 varying 的變數: vPosition 與 vColor,
會傳給 fragment shader
Fragment Shader 程式碼:
<script id="fragmentShader" type="x-shader/x-fragment">
	precision mediump float;
	precision mediump int;
	uniform float time;
	varying vec3 vPosition;
	varying vec4 vColor;
	void main()	{
		vec4 color = vec4( vColor );
		color.r += sin( vPosition.x * 10.0 + time ) * 0.5;
		gl_FragColor = color;
	}
</script>
varying vec3 vPosition 與 varying vec4 vColor 由 vertex shader 傳入
void main()	{
	vec4 color = vec4( vColor );
	color.r += sin( vPosition.x * 10.0 + time ) * 0.5;
	gl_FragColor = color;
}
輸出的顏色:
R,會跟著時間做簡諧運動 (且與 vPosition 有關)x 變化,所以顏色變化看起來是直的往旁移動time:
Fragment Shader 還有一個值 - time
uniform float time
uniform 變數會由 JavaScript 帶入
var material = new THREE.RawShaderMaterial( {
	uniforms: {
		time: { value: 1.0 }
	},
	vertexShader: document.getElementById( 'vertexShader' ).textContent,
	fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
	side: THREE.DoubleSide,
	transparent: true
} );
與更新
function render() {
	var time = performance.now();
	var object = scene.children[ 0 ];
    ...
	object.material.uniforms.time.value = time * 0.005;
}
好像...看得懂了?
覺得從 WebGL pipeline 來理解 WebGL JavaScript library 的一些實作,
是學習與應用 Shader 方法之一
純 WebGL 應用的寫法會更複雜些,本系列暫不討論
同樣的,系列文希望大家不會覺得 Shader 很可怕
只是 Shader 需要前置學習的部分真的很多,
但真的很有趣