shaderToy學習篇


覺得shadertoy上的一些網友的作品寫得很好,加上自己對glsl一些內置函數,內置變量不是很熟悉,於是決定開始學習一下上面一些大佬的代碼。

今天的案例是這個:

附上shaderToy的地址:https://www.shadertoy.com/view/ll2GD3

用three.js的ShaderMaterial實現的着色器代碼如下:

<script id="vertex-shader-1" type="x-shader/x-vertex">
    void main(){
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
    }
</script>


<script id="fragment-shader-9" type="x-shader/x-fragment">
    uniform vec2 resolution;

    vec3 pal( in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d ){
        return a + b*cos( 6.28318*(c*t+d) );
    }
    void main( void ) {
        vec2 p = gl_FragCoord.xy / resolution.xy;
        vec3 col = pal( p.x, vec3(0.5,0.5,0.5),vec3(0.5,0.5,0.5),vec3(1.0,1.0,1.0),vec3(0.0,0.33,0.67) );
        if( p.y>(1.0/7.0) ) col = pal( p.x, vec3(0.5,0.5,0.5),vec3(0.5,0.5,0.5),vec3(1.0,1.0,1.0),vec3(0.0,0.10,0.20) );
        if( p.y>(2.0/7.0) ) col = pal( p.x, vec3(0.5,0.5,0.5),vec3(0.5,0.5,0.5),vec3(1.0,1.0,1.0),vec3(0.3,0.20,0.20) );
        if( p.y>(3.0/7.0) ) col = pal( p.x, vec3(0.5,0.5,0.5),vec3(0.5,0.5,0.5),vec3(1.0,1.0,0.5),vec3(0.8,0.90,0.30) );
        if( p.y>(4.0/7.0) ) col = pal( p.x, vec3(0.5,0.5,0.5),vec3(0.5,0.5,0.5),vec3(1.0,0.7,0.4),vec3(0.0,0.15,0.20) );
        if( p.y>(5.0/7.0) ) col = pal( p.x, vec3(0.5,0.5,0.5),vec3(0.5,0.5,0.5),vec3(2.0,1.0,0.0),vec3(0.5,0.20,0.25) );
        if( p.y>(6.0/7.0) ) col = pal( p.x, vec3(0.8,0.5,0.4),vec3(0.2,0.4,0.2),vec3(2.0,1.0,1.0),vec3(0.0,0.25,0.25) );
        // band
        float f = fract(p.y*7.0);
        // borders
        col *= smoothstep( 0.49, 0.47, abs(f-0.5) );
        // shadowing
        col *= 0.5 + 0.5*sqrt(4.0*f*(1.0-f));
    
        gl_FragColor = vec4(col,1.0);
    }
</script>

參數說明:

resolution:傳入的一個vec2,其實就是畫布的大小(width,height).

 

代碼解析:

1⃣️畫布歸一化

vec2 p = gl_FragCoord.xy / resolution.xy;

上述代碼的作用是,使得畫布上的任意一點p的橫軸值范圍和縱軸值范圍都是[0,1],極大地方便了后續色值的計算,因為色值每個通道的值范圍也是[0,1]

 2⃣️y軸分塊,x軸顏色漸變

不難看出,分成了7等份

針對每單獨的一份呢,y軸顏色不做區分,x軸顏色漸變,並且是與x軸值相關,相關代碼如下:

    vec3 pal( in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d ){
        return a + b*cos( 6.28318*(c*t+d) );
    }

該函數有4個形參,分別是t(x軸坐標值,注意x的范圍此時是0到1)和自定義的4種顏色。

后面的三角函數可以看作 y = b * cos(k(c*x + d)),所以y的值范圍是[-b,b],那么顏色3個通道的值肯定不能小於0啊,最好也是不能大於1的,所以在前面補上a,

並且a-b>0;a+b<1;

解該二元一次不等式得:

b<0.5,

然后我們再來看一下代碼中計算顏色時給的實參:

對於每個維度來說,也確實是a > b的。

補充:對於a、b,每個維度之和小於等於1,

3⃣️加邊框

        // band
        float f = fract(p.y*7.0);
        // borders
        col *= smoothstep( 0.49, 0.47, abs(f-0.5) );

我剛開始想的時候,是想着用透明度做的,看到了作者的代碼,又見到這個smoothstep函數,於是去詳細了解了一下:

圖示法如下:

所以,針對上述代碼,first:

float f = fract(p.y*7.0);

p.y * 7.0 的值為0.1、0.2、...1.0、1.1、1.2、...2.0、2.1、2.2、...3.0、3.1、3.2、........6.0、6.1、6.2、...7.0;

fract是取小數函數,所以對於每一段的f值范圍都是0.0、0.1、0.2、0.3、0.4、0.5....0.9、0.0;

smoothstep( 0.49, 0.47, abs(f-0.5) )

abs(f-0.5) 走勢是這樣的(絕對值):0.5、0.4、0.3、0.2、0.1、0.0、0.1、0.2、0.3、0.4、0.5;

0.49 > 0.47,就是a > b;符合上述第二種計算規則,

所以該函數式算得的值走勢是:0->平滑插值(升)->1->平滑插值(降)->0

col *= smoothstep( 0.48, 0.47, abs(f-0.5) );

所以在y軸上,之前的滿屏,現在有了縫隙,效果如下:

因為末端smoothstep函數值為0,算得顏色為rgb(0.,0.,0.)

4⃣️smoothstep函數處理獲得其他效果

float f = fract(p.y*7.0);
abs(f-0.5)

abs最大值為0.5,所以只要smoothstep(a,b,x)中

a>b,並且

a>0.5,就不會出現縫隙;

b<0.5,才會有平滑效果.

    col *= smoothstep( 0.98, 0.07, abs(f-0.5) );

smoothstep:

效果如下:

col *= smoothstep( 0.98, 0.37, abs(f-0.5) );

smoothstep:

效果如下:

 5⃣️sqrt(x * (1-x))實現y軸弧形漸變

首先看一下y = 0.5 + sqrt(x * (1-x))的表示的圖形

所以對於着色器代碼:

col *= 0.5 + 0.5*sqrt(4.0*f*(1.0-f));

轉化為:

col *= 0.5 + sqrt(f*(1.0-f));

因為f變化順序為0.5->0.0->0.5;所以對照上面的圖形,y軸顏色的被乘數變化為1.0->0.5->1.0,注意這些變化為非線性的,所以y軸變化如下:

再乘一次:

col *= 0.5 + sqrt(f*(1.0-f));

繼續:

繼續:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM