THREE.JS入門教程(3)著色器-下

來源:互聯網
上載者:User

譯序
Three.js是一個偉大的開源WebGL庫,WebGL允許JavaScript操作GPU,在瀏覽器端實現真正意義的3D。但是目前這項技術還處在發展階段,資料極為匱乏,愛好者學習基本要通過Demo源碼和Three.js本身的源碼來學習。

.簡介
這是WebGL著色器教程的後半部分,如果你沒看過前一篇,閱讀這一篇教程可能會使你感到困惑,建議你翻閱前面的教程。

上一篇結束的時候,我們在螢幕中央畫了一個好看的粉紅色的球體。現在我要開始建立一些更加有意思的東西了。

在這一篇教程中,我們會先花點時間來加入一個動畫迴圈,然後是頂點attributes變數和一個uniform變數。我們還要加一些varying變數,這樣頂點著色器就可以向片元著色器傳遞資訊了。最終的結果是哪個粉紅色的球體會從頂部開始向兩側“點燃”,然後作有規律的運動。這有一點迷幻,但是會協助你對著色器中的三種變數有更好的瞭解:他們互相聯絡,實現了整個集合體。當然我們會在Three.js的架構中做這些。
1.類比光照
讓我們更新顏色吧,這樣球體看起來就不會是個扁平晦暗的圓了。如果我們想看看Three.js是怎樣處理光照的,我敢肯定你會發現這比我們需要的要複雜得多,所以我們先類比光照吧。你應該瀏覽一下Three.js中那些奇妙的著色器,還有一些來自最近的一個 Chris Milk 和 Google, Rome 的WebGL項目。
回到著色器,我們要更新頂點著色器來向片元著色器傳遞頂點的法向量。利用一個varying變數: 複製代碼 代碼如下:// 建立一個varying變數vNormal,頂點著色器和片元著色器都包含了該變數
varying vec3 vNormal;
void main() {
// 將vNormal設定為normal,後者是Three.js建立並傳遞給著色器的attribute變數
vNormal = normal;
gl_Position = projectionMatrix *
modelViewMatrix *
vec4(position, 1.0);
}

在片元著色器中,我們將會建立一個相同變數名的變數,然後將法線向量和另一個表示來自右上方光線的向量點乘,並將結果作用於顏色。最後結果的效果有點像平行光。 複製代碼 代碼如下:// 和頂點著色器中一樣的變數vNormal
varying vec3 vNormal;
void main() {
// 定義光線向量
vec3 light = vec3(0.5,0.2,1.0);
// 確保其歸一化
light = normalize(light);
// 計算光線向量和法線向量的點積,如果點積小於0(即光線無法照到),就設為0
float dProd = max(0.0, dot(vNormal, light));
// 填充片元顏色
gl_FragColor = vec4(dProd, // R
dProd, // G
dProd, // B
1.0); // A
}

使用點積的原因是:兩個向量的點積表明他們有多麼“相似”。如果兩個向量都是歸一化的,而且他們的方向一模一樣,點積的值就是1;如果兩個向量的方向恰巧完全相反,點積的值就是-1。我們所做的就是把點積的值拿來作用到光纖上,所以如果這個點在球體的右上方,點積的值就是1,也就是完全照亮了;而在另一邊的點,獲得的點積值接近0,甚至到了-1。我們將獲得的任何負值都設定為0。當你將資料傳入之後,你就會看到最基本的光照效果了。

下面是什嗎?我們會將頂點的座標摻和進來。
2.Attribut變數
接下來我要通過Attribute變數為每一個頂點傳遞一個隨機數,這個隨機數被用來將頂點沿著法線向量推出去一段距離。新的結果有點像一個怪異的不規則物體,每次重新整理頁面物體都會隨機變化。現在,他還不會動(後面我會讓他動起來),但是幾次重新整理就可以很好地觀察到,他的形狀是隨機的。
讓我們開始為頂點著色器加入attribute變數吧: 複製代碼 代碼如下:attribute float displacement;
varying vec3 vNormal;
void main() {
vNormal = normal;
// 將隨機數displacement轉化為三維向量,這樣就可以和法線相乘了
vec3 newPosition = position +
normal * vec3(displacement);
gl_Position = projectionMatrix *
modelViewMatrix *
vec4(newPosition, 1.0);
}

你看到什麼都沒變,因為attribute變數displacement還沒有被設定你,所以著色器就使用了0作為預設值。這時displacement還沒起作用,但我們馬上就要在著色器材質中加上attribute變數了,然後Three.js就會自動地把它們綁在一起運行了。

同時也要注意這樣一個事實,我將更新後的位置指定給了一個新的三維向量變數,因為原來的位置變數position,就像所有的attribute變數一樣,都是唯讀。
3.更新著色器材質
現在我們來更新著色器材質,傳入一些東西給attribute對象displacement。記住,attribute對象是和頂點一一對應的,所以我們對球體的每一個頂點都有一個值,就像這樣: 複製代碼 代碼如下:var attributes = {
displacement: {
type: 'f', // 浮點數
value: [] // 空數組
}
};
var vShader = $('#vertexshader');
var fShader = $('#fragmentshader');
// 建立一個包含attribute屬性的著色器材質
var shaderMaterial =
new THREE.MeshShaderMaterial({
attributes: attributes,
vertexShader: vShader.text(),
fragmentShader: fShader.text()
});
// 向displacement中填充隨機數
var verts = sphere.geometry.vertices;
var values = attributes.displacement.value;
for(var v = 0; v < verts.length; v++) {
values.push(Math.random() * 30);
}

這樣,就可以看到一個變形的球體了。最Cool的是:所有這些變形都是在GPU中完成的。
4.動起來
要使這東西動起來,應該怎麼做?好吧,應該做這兩件事情。
一個uniform變數amplitude,在每一幀控制displacement實際造成了多少位移。我們可以使用正弦或餘弦函數來在每一幀中產生它,因為這兩個函數的取值範圍從-1到1。
一個幀迴圈。

我們需要將這個uniform變數加入到著色器材質中,同時也需要加入到頂點著色器中。先來看頂點著色器: 複製代碼 代碼如下:uniform float amplitude;
attribute float displacement;
varying vec3 vNormal;
void main() {
vNormal = normal;
// 將displacement乘以amplitude,當我們在每一幀中平滑改變amplitude時,畫面就動起來了
vec3 newPosition =
position +
normal *
vec3(displacement *
amplitude);
gl_Position = projectionMatrix *
modelViewMatrix *
vec4(newPosition, 1.0);
}

然後更新著色器材質: 複製代碼 代碼如下:var uniforms = {
amplitude: {
type: 'f', // a float
value: 0
}
};
var vShader = $('#vertexshader');
var fShader = $('#fragmentshader');
// 建立最終的著色器材質
var shaderMaterial =
new THREE.MeshShaderMaterial({
uniforms: uniforms,
attributes: attributes,
vertexShader: vShader.text(),
fragmentShader: fShader.text()
});

我們的著色器也已經就緒了。但我們好像又倒退了一步,螢幕中又只剩下光滑的球了。別擔心,這是因為amplitude值設定為0,因為我們將amplitude乘上了displacement,所以現在看不到任何變化。我們還沒設定迴圈呢,所以amplitude只可能是0.

在我們的JavaScript中,需要將渲染過程打包成一個函數,然後用requestAnimationFrame去調用該函數。在這個函數裡,我們更新uniform(譯者註:即amplitude)的值。 複製代碼 代碼如下:var frame = 0;
function update() {
// amplitude來自於frame的正弦值
uniforms.amplitude.value =
Math.sin(frame);
// 更新全域變數frame
frame += 0.1;
renderer.render(scene, camera);
// 指定下一次螢幕重新整理時,調用update
requestAnimFrame(update);
}
requestAnimFrame(update);

5.小結
就是它了!你看到球體正在奇怪地脈動著。關於著色器,還有太多的內容沒有講到呢,但是我希望這篇教程能夠對你有一些協助。現在,當你看到一些其他的著色器時,我希望你能夠理解它們,而且你應該有信心去建立自己的著色器了!

和往常一樣,我將這一課的源碼打包了

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.