本文中效果主要採用ThreeJS 中的著色器(Shader)以及結合ShaderMaterial實現的。
主要用到的內建方法有:
step:是一個階躍函式,它將一個浮點數與一個閾值進行比較,並返回一個階躍值;
比如step(edge, x), 如果 x 小於等於 edge,則返回 0.0, 如果 x 大於 edge ,則返回 1.0。
fract:用於獲取浮點數的小數部分。它返回輸入值的小數部分,即去除整數部分後的部分。比如fract(1.5),返回0.5;
一、網格平面
const vertex = '\ varying vec3 vPos;\ void main() {\ vPos = position;\ gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ); \ }\ '; const frag = '\ varying vec3 vPos;\ void main() {\ vec3 mask1 = vec3(step(0.5, fract(vPos.x * 2.0)));\ vec3 mask2 = vec3(step(0.5, fract(vPos.y * 2.0)));\ vec3 mask3 = vec3(step(0.5, fract(vPos.z * 2.0)));\ vec3 color = abs(mask1 - mask2);\ gl_FragColor = vec4(color, 1.0);\ }\ ';
原理:如果設定平面的大小為2,那麼座標軸X點的範圍為-1.0 ~ 1.0,已X座標為示例,資料變化形式如下圖
如上圖可以將資料分為4個部分,X軸和Y軸同理:
1) 0~0.25的資料經過fract和step函式處理後資料變為0;
2) 0.25~0.5的資料經過fract和step函式處理後資料變為1;
3) 0.5~0.75的資料經過處理變為0;
4) 0.75~1.0的資料經過處理變為1;
最後將生成的向量X軸-Y軸資料可以繪製成如下圖:
這樣就生成了第一象限的圖形,第二、三、四象限結果同上。
二、網格狀的球體
本列中涉及到GLSL的幾個內建函式:
dot:兩個向量的點積,可以獲得向量的夾角
asin: 反三角函式,獲得弧度值
1. 第一個圖就是要實現的最終效果,一個網格狀的球體,實現原理主要可以分為分別計算經度方向的線圈和緯度方向的線圈。
2. 與平面網格計算顏色值相同,將兩個顏色值相減即可得到一個網格球體
3. 如何實現緯度方向的線圈?
實現邏輯:將球體沿球心縱向切一刀生成一個圓形橫截面M,將圓分成弧度相等的N個圓弧,然後再間隔開賦予不同的顏色就可以形成圖一的效果。
const vertex = '\ varying vec3 vPos;\ void main() {\ vPos = position;\ gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ); \ }\ '; // 獲取UV點對應的單位向量B // 獲取Z軸方向的單位向量A // 計算向量B和向量A的夾角 // 透過degree將夾角的弧度轉換成角度,除以要拆分的條數latBeta,透過配合fract和step即可獲取間隔的0、1值 // 最後生成間隔的黑白顏色值 const frag = '\ uniform float latBeta;\ varying vec3 vPos;\ void main() {\ vec3 latEveryVec = normalize(vPos);\ vec3 latBaseVec = normalize(vec3(vPos.x, 0, vPos.z));\ float latAngle = asin(dot(latBaseVec, latEveryVec));\ vec3 latColor = vec3(step(0.5, fract(degrees(latAngle) / latBeta)));\ gl_FragColor = vec4(latColor, 1.0);\ }\ ';
4. 如何實現經度方向的線圈?
實現邏輯:與第三步生成緯度方向的線圈類似,將球體沿球心橫向切一刀生成一個圓形橫截面M,將圓分成弧度相等的N個圓弧,然後再間隔開賦予不同的顏色就可以形成圖一的效果。
const vertex = '\ varying vec3 vPos;\ void main() {\ vPos = position;\ gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ); \ }\ '; // 獲取UV點對應的單位向量B // 獲取X軸正方向的單位向量A // 計算向量B和向量A的夾角 // 透過degree將夾角的弧度轉換成角度,除以要拆分的條數lonBeta,透過配合fract和step即可獲取間隔的0、1值 // 最後生成間隔的黑白顏色值 const frag = '\ uniform float lonBeta;\ varying vec3 vPos;\ void main() {\ vec3 lonEveryVec = normalize(vec3(vPos.x, 0.0, vPos.z));\ vec3 lonBaseVec = vec3(0.0, 0.0, 1.0);\ float lonAngle = asin(dot(lonBaseVec, lonEveryVec));\ vec3 lonColor = vec3(step(0.5, fract(degrees(lonAngle) / lonBeta)));\ gl_FragColor = vec4(lonColor, 1.0);\ }\ ';
完整程式碼如下:
const vertex = '\ varying vec3 vPos;\ void main() {\ vPos = position;\ gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ); \ }\ '; const frag = '\ uniform float latBeta;\ uniform float lonBeta;\ varying vec3 vPos;\ void main() {\ vec3 latEveryVec = normalize(vPos);\ vec3 latBaseVec = normalize(vec3(vPos.x, 0, vPos.z));\ float latAngle = asin(dot(latBaseVec, latEveryVec));\ vec3 latColor = vec3(step(0.5, fract(degrees(latAngle) / latBeta)));\ \ vec3 lonEveryVec = normalize(vec3(vPos.x, 0.0, vPos.z));\ vec3 lonBaseVec = vec3(0.0, 0.0, 1.0);\ float lonAngle = asin(dot(lonBaseVec, lonEveryVec));\ vec3 lonColor = vec3(step(0.5, fract(degrees(lonAngle) / lonBeta)));\ vec3 color = abs(latColor - lonColor);\ gl_FragColor = vec4(color, 1.0);\ }\ ';