天天看點

Shader 優化 | OpenGL 繪制網格效果Shader 講解總結對比

作者:星隕

來源:

音視訊開發進階 前幾天釋出了這樣一篇文章: KodeLife | Shader 實時編輯預覽的強大工具使用實踐

除了介紹 KodeLife 的使用之外,還附帶了一個 Shader 繪制網格效果的代碼。

把這篇文章發到技術群裡,随機就有大佬指出不足之處,提示說代碼還可以進一步優化,并且提供了源碼學習。

可見加入一個高品質的技術群是多麼重要,哪怕平時不說話,圍觀大佬們聊天都能學到很多。

現在加入還來得及,尚有餘位,詳情點選如下連結:

移動端技術交流喊你入群啦~~~

Shader 講解

在我的 Shader 代碼中是這樣繪制網格的:

vec2 fragcoord = vec2(gl_FragCoord.xy / u_resolution);
    vec3 bgColor = vec3(1.0,1.0,1.0);
    vec3 pixelColor = bgColor;
    vec3 gridColor = vec3(0.5,0.5,0.5);

    const float width = 0.1;
    const float minWidth = 0.003;
    for(float i = 0.0; i < 1.0; i+=width){
    if (mod(fragcoord.x,width) < minWidth || mod(fragcoord.y,width) < minWidth){
            pixelColor = gridColor;
        }
    }
    gl_FragColor = vec4(pixelColor,1.0);           

首先,講解幾個概念:

gl_FragCoord

代表目前像素相對于螢幕的坐标,螢幕左下角為原點。

u_resolution

是目前圖像的分辨率。

gl_FragCoord

除以

u_resolution

得到的結果

fragcoord

就是歸一化的螢幕坐标。

由于已經歸一化了那麼

fragcoord

的值就在

[0,1]

的閉區間内。

同時用

gridColor

作為網格的顔色,

bgColor

作為背景色,也是預設的顔色,

pixelColor

作為最後輸出的顔色。

那麼,代碼的重點就在于

for

循環裡面了。

由于

fragcoord

歸一化有了确定的值域範圍,是以可以在

for

循環中将它十等分。

另外,因為片段着色器每個像素都會執行一遍,每次

fragcoord

值都是變化的,但不管怎麼變化,它的範圍都會落在

for

循環的十等份裡面。

比如其中某一份的範圍是

[0.2,0.3)

的左閉右開區間,目前像素就落在這個範圍内。

那麼

mod

取模函數就會判斷目前值距離左區間門檻值是否在

minWidth

範圍内,其中

minWidth

相當于是指定網格線的寬度。

如果在範圍内,那麼顯示的顔色就是網格色,否則就是預設的背景色。

以上的講解對于坐标的

x

y

值是一樣的道理。原理通過判斷該像素點的坐标是否位于臨界範圍内來選擇性着色。

顯示這種繪制方式是有它的弊端,因為每一個像素執行片段着色器的時候,都要進行一次

for

循環判斷它處于哪個區域内。

這樣就有了太多不必要的計算流程,尤其是

for

循環的每次周遊。

接下來就是微信群中大佬給出的 Shader 代碼:

vec2 st = vec2(gl_FragCoord.xy / u_resolution);
st.x *= u_resolution.x / u_resolution.y;

vec3 color = vec3(.0);

st *= 10.;

vec2 i_st = floor(st);
vec2 f_st = fract(st);

color += step(.98,f_st.x) + step(.98,f_st.y);

gl_FragColor = vec4(color,1.0);           

可以一眼看出這裡面沒有

for

循環的操作了。

還是先講解幾個級别操作:

floor

函數就是向下取整的操作

fract

函數是

x - floor(x)

的操作,也就是取小數部分的意思。

通過對

st

進行

floor

fract

操作可以分出它的整數和小數部分。

step

函數類似于

if

判斷,當第二個參數大于等于第一個參數,則傳回 1 ,否則傳回 0 。

整個 Shader 代碼第一行還是相同的,都是歸一化操作。

然後在第二行

st.x *= u_resolution.x / u_resolution.y           

實際上是做了一個比例切換的操作。将

st

x

y

值按照圖像分辨率的寬高比做了調整,其中以 y 為基準 1 。

這樣一來,

st

y

值還是在

[0,1]

範圍内,而

x

值可能大于也可能小于這個範圍了,這都取決于圖像分辨率了。

接下來将

st

乘了 10 ,這下

st

的值域範圍就在

[0,10]

了 ,這樣的操作是為了接下來的

floor

函數,因為它是取整,如果都在

[0,1]

範圍内,取的整數永遠都是 0 了。

前面轉換操作是為了接下來的重點函數

step

color += step(.98,f_st.x) + step(.98,f_st.y);           

前面的

floor

其實也是将

x

y

軸做了等分,比如

y

的值域是

[0,1]

,乘以 10 之後,就是十等分,

x

的值域如果是

[0,1.7]

,乘以 10 之後,就是十七等分。

fract

操作的結果範圍必然是

[0,1)

的左閉右開區間。

step 函數的意圖就是如果該像素點的坐标接近于等分線,那麼 color 的顔色值傳回的就是 1 ,顯示白色,否則傳回 0 ,顯示黑色。

比如,st 的

x

值是 7.99 了,接近于 8 ,那麼就要顯示白色網格線了,對于 y 值同理。

這樣一來就可以對每個像素點進行判斷,根據它的坐标決定要顯示什麼顔色。

總結對比

在第二種繪制中,由于做了比例轉換操作,是以繪制出來的網格大小都是一緻的,且都是正方形。

而第一種沒有比例切換操作,當寬高不同的情況下,同樣進行十等分的話,畫出來的網格是個長方形了。

但是,兩種繪制的思路都是相同的,姑且稱它為

接近法

吧,當繪制的像素接近等分線時,就顯示不一樣的顔色。

于是,等分線的操作思路就各有不同了。前者是利用

for

循環來制造劃分,後者則是利用目前像素的

x

y

值的特點來繪制的。

當然更推崇後者的繪制方式了,也是學到了新技巧~~~

OpenGL 系列文章
「視訊雲技術」你最值得關注的音視訊技術公衆号,每周推送來自阿裡雲一線的實踐技術文章,在這裡與音視訊領域一流工程師交流切磋。
Shader 優化 | OpenGL 繪制網格效果Shader 講解總結對比

繼續閱讀