其實在3D引擎/庫的幫助下,我們做webgl開發的難度已經很大大地降低了,熟悉相關API的話,開發一個簡單的3D程序可以說是很輕松的事情。
在我看來,webgl的核心就是着色器(頂點着色器、片元着色器),這兩者決定了如何在屏幕上繪制出我們想要的效果。所以,無論你是剛入門的3D開發者(我也是剛剛探索3D領域)還是該領域的資深老鳥,對着色器的了解都是至關重要的。
我們一般的軟件開發(例如一些管理系統、商城小程序),諸如這些業務邏輯性非常強的程序,都是依靠CPU的高速運算,而做WebGL(3D開發),我們寫的程序代碼是要跑在GPU上的,因為對於繪圖能力來說,GPU是遠遠高於CPU的,所以,我們做的就是GPU編程了。
今天,就以一個demo來體會一下,three.js中如何編寫自定義的着色器並運用到mesh材質上的。
(首先默認大家都已經學會three.js的一些基本操作,例如將一個three.js內置的面板PlaneGeometry放到場景中)
一、添加plane
function addplane(){ var planeGeometry = new THREE.PlaneGeometry(300,150) var meshMaterial = new THREE.MeshPhongMaterial({ color: 0xfff000 * Math.random() }); var plane = new THREE.Mesh(planeGeometry,meshMaterial); scene.add(plane); }
這個函數將會往場景中添加一個Plane,效果如下:
ok,這就是一個plane了。
二、着色器代碼
<!-- 頂點着色器 --> <script id="vertex-shader" type="x-shader/x-vertex">
void main(){ gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); } </script>
<!-- 頂點着色器 --> <script id="fragment-shader-3" type="x-shader/x-fragment"> uniform vec2 resolution;
vec2 rand(vec2 pos){ return fract( 0.00005 * (pow(pos+2.0, pos.yx + 1.0) * 22222.0)); }
vec2 rand2(vec2 pos){ return rand(rand(pos)); } float softnoise(vec2 pos, float scale){ vec2 smplpos = pos * scale; float c0 = rand2((floor(smplpos) + vec2(0.0, 0.0)) / scale).x; float c1 = rand2((floor(smplpos) + vec2(1.0, 0.0)) / scale).x; float c2 = rand2((floor(smplpos) + vec2(0.0, 1.0)) / scale).x; float c3 = rand2((floor(smplpos) + vec2(1.0, 1.0)) / scale).x; vec2 a = fract(smplpos); return mix( mix(c0, c1, smoothstep(0.0, 1.0, a.x)), mix(c2, c3, smoothstep(0.0, 1.0, a.x)), smoothstep(0.0, 1.0, a.y)
); } void main(void){ vec2 pos = gl_FragCoord.xy / resolution.y; pos.x += time * 0.1; float color = 0.0; float s = 1.0; for(int i = 0; i < 8; i++){ color += softnoise(pos+vec2(i)*0.02, s * 4.0) / s / 2.0; s *= 2.0; } gl_FragColor = vec4(color); } </script>
頂點着色器中,進行了一系列的矩陣變換,將圖形的頂點轉換為屏幕上的像素點;
模型矩陣: 將頂點從局部坐標系轉換到世界坐標系中;
視圖矩陣: 將頂點從世界坐標轉化到視圖坐標系下;
投影矩陣: 將頂點從視圖坐標系轉換到規范立方體中(即屏幕中);
局部坐標系(模型/物體本身)----->世界坐標系----->視圖坐標系----->屏幕。
片元着色器中,因為片元着色器是針對頂點着色器輸出的頂點數據 (gl_Position)進行逐點繪制的,所以該着色器就是對每個點賦予一個顏色值,針對上面的片元着色器,他接受外界賦予的變量(uniform類型)。
三、着色器材質
下面我們就用兩種着色器來生成一個材質。
//創建ShaderMaterial紋理的函數 function createMaterial(vertexShader, fragmentShader) { var vertShader = document.getElementById(vertexShader).innerHTML; //獲取頂點着色器的代碼 var fragShader = document.getElementById(fragmentShader).innerHTML; //獲取片元着色器的代碼 //配置着色器里面的attribute變量的值 var attributes = {}; //配置着色器里面的uniform變量的值 var uniforms = { scale: {type: 'f', value: 10}, resolution: {type: "v2", value: new THREE.Vector2(window.innerWidth, window.innerHeight)} }; var meshMaterial = new THREE.ShaderMaterial({ uniforms: uniforms, defaultAttributeValues : attributes, vertexShader: vertShader, fragmentShader: fragShader, transparent: true }); return meshMaterial; }
四、利用shader材質重新繪制plane
function addplane(){ var planeGeometry = new THREE.PlaneGeometry(300,150) var meshMaterial = createMaterial("vertex-shader", "fragment-shader-3"); var plane = new THREE.Mesh(planeGeometry,meshMaterial); scene.add(plane); }
效果:
五、其他着色器效果示例