three.js 著色器材質之變數(一)

郭先生的部落格發表於2020-08-05

上一篇說頂點著色器和片元著色器的皮毛,這篇郭先生說一說著色器變數,通過變數可以設定材質。先看看今天要做的如下圖。線上案例請點選部落格原文

在這個案例之前,我們先複習一下著色器變數

  • Uniforms是所有頂點都具有相同的值的變數。 比如燈光,霧,和陰影貼圖就是被儲存在uniforms中的資料。 uniforms可以通過頂點著色器和片元著色器來訪問。
  • Varyings 是從頂點著色器傳遞到片元著色器的變數。因此需要在兩個著色器中同時定義,對於每一個片元,每一個varying的值將是相鄰頂點值的平滑插值。
  • Attributes 與每個頂點關聯的變數。例如,頂點位置,法線和頂點顏色都是儲存在attributes中的資料。attributes 只可以在頂點著色器中訪問。

嗯,現在我們知道了這些變數的用法,接下來我們使用它。

1. 製作紅綠燈幾何體

要製作這樣一個紅綠燈,我們考慮使用Geometry的merge方法

var shape = new THREE.Shape();
shape.moveTo(-10, 20);
shape.absarc(0, 20, 10,  Math.PI, Math.PI * 2, true);
shape.lineTo(10, -20);
shape.absarc(0, -20, 10, 0, Math.PI, true );
shape.lineTo(-10, 20);

var extrudeSettings = {
    steps: 2, //用於沿著擠出樣條的深度細分的點的數量,預設值為1
    depth: 5, //擠出的形狀的深度,預設值為100
    bevelEnabled: true, //對擠出的形狀應用是否斜角,預設值為true
    bevelThickness: 1, //設定原始形狀上斜角的厚度。預設值為6
    bevelSize: 1, //斜角與原始形狀輪廓之間的延伸距離
    bevelSegments: 10, //斜角的分段層數,預設值為3
    curveSegments: 12, //曲線上點的數量,預設值是12
};
var frame = new THREE.ExtrudeGeometry(shape, extrudeSettings);
// var material = new THREE.MeshPhongMaterial({color: 0x222222, emissive: 0x222222});

var cylinGeom = new THREE.CylinderGeometry(6, 6, 6, 30, 20);
frame.merge(cylinGeom.clone(), new THREE.Matrix4().compose(new THREE.Vector3(0, 15, 3.1), new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1,0,0), Math.PI/2), new THREE.Vector3(1,1,1)));
frame.merge(cylinGeom.clone(), new THREE.Matrix4().compose(new THREE.Vector3(0, 0, 3.1), new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1,0,0), Math.PI/2), new THREE.Vector3(1,1,1)));
frame.merge(cylinGeom.clone(), new THREE.Matrix4().compose(new THREE.Vector3(0, -15, 3.1), new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1,0,0), Math.PI/2), new THREE.Vector3(1,1,1)));

通過ExtrudeGeometry擠壓出我們想要的幾何體,然後新增三個圓柱體,形成我們想要的幾何體。

2. 設定uniform變數

現在使用uniform變數

uniforms = {
    time: {
        type: 'f', value: 0.0
    }
}

這裡我們在其中設定一個叫做time的變數,它的型別是一個float型別,預設值設定成0.0。然後我們在requestAnimationFrame的每一幀動畫中呼叫uniforms.time.value += 0.01;讓著色器動起來。

3. 頂點著色器

頂點著色器我們不做太多操作

varying vec3 vPosition;
uniform float time;
void main() {
    vPosition = position;
    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}

這裡我們定義一個三維向量vPosition,用來將頂點著色器裡面的position屬性傳遞到片元著色器中(three.js會預設傳入一些屬性,像uv,position,normal等)

4. 片元著色器

varying vec3 vPosition;
uniform float time;
void main() {
    float time = mod(time, 3.0);//time值對3取模,得到[0,3)範圍內的值。
        //由於我們製作紅綠燈時用了小技巧,讓其z分量比較大,所以可以根據z的值判斷是否為紅綠燈面。然後在根據y值,判斷為哪個燈。
    if(vPosition.z == 6.1 && vPosition.y > 8.0) {
        if(time < 1.0) {//時間為[0,1)紅燈
            gl_FragColor=vec4(1.0, 0.0, 0.0, 1.0);
        } else {
            gl_FragColor=vec4(0.2, 0.0, 0.0, 1.0);
        }
    } else if(vPosition.z == 6.1 && vPosition.y > -8.0) {//時間為[1,2)黃燈
        if(time >= 1.0 && time < 2.0) {
            gl_FragColor=vec4(1.0, 0.7, 0.0, 1.0);
        } else {
            gl_FragColor=vec4(0.2, 0.1, 0.0, 1.0);
        }
    } else if(vPosition.z == 6.1) {//時間為[2,3)綠燈
        if(time >= 2.0) {
            gl_FragColor=vec4(0.0, 1.0, 0.0, 1.0);
        } else {
            gl_FragColor=vec4(0.0, 0.2, 0.0, 1.0);
        }
    } else {//其餘部分為灰色
        gl_FragColor=vec4(0.2, 0.2, 0.2, 1.0);
    }
}

這裡我們使用頂點著色器傳過來的向量vPosition和uniform中的time值做一些判斷,實現對每個點顏色進行控制(根據顏色插值從而實現顏色面的控制),裡面使用了一些方法,例如mod,請參見上一篇文章。

雖然這個小案例很簡單,但是我相信大家肯定有了很好的想法,前面幾篇都是比較基礎的,後面還有很多好看的案例,喜歡就點個贊吧!

 

轉載請註明地址:郭先生的部落格

相關文章