three.js使用Shadertoy的着色器

three.js使用Shadertoy的着色器

shadertor上有許多非常有趣的着色器,我們可以借鑑並用在自己的three的項目上。

1 在shadertoy上新建一個着色器,默認有一個主函數,如下圖所示。
在這裏插入圖片描述

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // Normalized pixel coordinates (from 0 to 1)
    vec2 uv = fragCoord/iResolution.xy;

    // Time varying pixel color
    vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));

    // Output to screen
    fragColor = vec4(col,1.0);
}

我們先修改這個基本的demo,然後再去借鑑別人的着色器。

觀察一下shadertoy的着色器代碼,發現和three中常用的有些區別:

  1. 沒有頂點着色器
  2. 沒有main函數,而是mainImage。
  3. 沒有gl_FragColor,而是fragColor 。
  4. iTime 和 iResolution 、fragCoord的問題

處理方式:
1. 沒有頂點着色器,這問題不大,先不關心。
2. 把mainImage改造成main函數,out和in的兩個參數就不要了。
3. 把fragColor 換成 gl_FragColor。
4. iTime是一個變量(shadertoy自帶的變量),在three中可以通過uniform的方式傳入。
5. iResolution是canvas的分辨率,因爲shadertoy是‘滿屏’顯示的,相當於three中用了正交相機然後用一個平面鋪滿整個canvas的。在three中,可以設置成新建的平面的分辨率。同時fragCoord我們對應的也改成uv的座標。這樣我們就需要引入頂點着色器了,然後傳入uv給片元着色器。

關於着色器內置變量:

頂點着色器可用的內置變量如下表:

名稱 類型 描述
gl_Color vec4 輸入屬性-表示頂點的主顏色
gl_SecondaryColor vec4 輸入屬性-表示頂點的輔助顏色
gl_Normal vec3 輸入屬性-表示頂點的法線值
gl_Vertex vec4 輸入屬性-表示物體空間的頂點位置
gl_MultiTexCoordn vec4 輸入屬性-表示頂點的第n個紋理的座標
gl_FogCoord float 輸入屬性-表示頂點的霧座標
gl_Position vec4 輸出屬性-變換後的頂點的位置,用於後面的固定的裁剪等操作。所有的頂點着色器都必須寫這個值。
gl_ClipVertex vec4 輸出座標,用於用戶裁剪平面的裁剪
gl_PointSize float 點的大小
gl_FrontColor vec4 正面的主顏色的varying輸出
gl_BackColor vec4 背面主顏色的varying輸出
gl_FrontSecondaryColor vec4 正面的輔助顏色的varying輸出
gl_BackSecondaryColor vec4 背面的輔助顏色的varying輸出
gl_TexCoord[] vec4 紋理座標的數組varying輸出
gl_FogFragCoord float 霧座標的varying輸出

片段着色器的內置變量如下表:

名稱 類型 描述
gl_Color vec4 包含主顏色的插值只讀輸入
gl_SecondaryColor vec4 包含輔助顏色的插值只讀輸入
gl_TexCoord[] vec4 包含紋理座標數組的插值只讀輸入
gl_FogFragCoord float 包含霧座標的插值只讀輸入
gl_FragCoord vec4 只讀輸入,窗口的x,y,z和1/w
gl_FrontFacing bool 只讀輸入,如果是窗口正面圖元的一部分,則這個值爲true
gl_PointCoord vec2 點精靈的二維空間座標範圍在(0.0, 0.0)到(1.0, 1.0)之間,僅用於點圖元和點精靈開啓的情況下。
gl_FragData[] vec4 使用glDrawBuffers輸出的數據數組。不能與gl_FragColor結合使用。
gl_FragColor vec4 輸出的顏色用於隨後的像素操作
gl_FragDepth float 輸出的深度用於隨後的像素操作,如果這個值沒有被寫,則使用固定功能管線的深度值代替
  var vertexShader = `
       varying vec2 vUv;
       void main(){
           vUv = uv;
           gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
       }
   `;
   var fragmentShader = `
        varying vec2 vUv;
        uniform float iTime;
        uniform vec3 iResolution;
        void main(){
               vec3 col = 0.5 + 0.5*cos(iTime+vUv.xyx+vec3(0,2,4));
               gl_FragColor = vec4(col,1.0);
           }
   `;
   var uniforms = {
       iTime:{value:0},
       iResolution:  { value: new THREE.Vector3( 40,40,1) },
   };
   var shadertoy = new THREE.ShaderMaterial({
       vertexShader,
       fragmentShader,
       uniforms,
       side:2,
   });
   var plane = new THREE.Mesh(planeGeo, shadertoy);
   setInterval(()=>{
                uniforms.iTime.value += 0.1;
            },20);

運行結果如下:
在這裏插入圖片描述
修改原理我們已經理解了,下面找個複雜點的例子試一下。

先來個這個吧:https://www.shadertoy.com/view/wtfXW2 ,看起來像細胞分裂。
在這裏插入圖片描述
shadertoy源碼

const int n = 800;
const float rate = 7.;
const float lineThickness = 2.2;
const float colours = 0.05; // proportion of cells to colour in
const bool zoom = true;

const float phi = 1.6180339887498948;
const float tau = 6.2831853071795865;

void mainImage( out vec4 fragColour, in vec2 fragCoord )
{
    vec2 uv = (fragCoord-iResolution.xy*.5)/iResolution.y;
    float penOut = lineThickness/iResolution.y;
    float penIn = (lineThickness-2.8)/iResolution.y;

    float t = iTime*rate;
    
    fragColour = vec4(0,0,0,1);

    float scale = sqrt(float(n));
    if ( zoom ) scale = min( scale, pow((iTime+7.)*rate*.5,.6) ); // keep the edgemost points in shot as we zoom
    
    float closest = 1e38;
    float closest2 = 1e38;
    for ( int i=0; i < n; i++ )
    {
        float f = float(i);
        f += fract(t);
        float r = sqrt(f/128.);
        r *= 13./scale;
        float a = fract((f-t)*phi)*tau;
        vec2 pos = r*vec2(sin(a),cos(a));
        
        vec3 col = sin(vec3(3,1,6)*(float(i)-floor(t)))*.5+.5;
        if ( fract(col.y*64.) > colours ) col = vec3(1);

        float l = length(pos-uv);

        // add a ring to help me track size (so it doesn't look like we're zooming out)
        //col *= smoothstep(penIn,penOut,abs(l/scale-.001)*scale);
		
        if ( i == 0 ) l += smoothstep(1.,0.,fract(t))*1.2/scale; // grow the new point
		if ( l < closest )
        {
            if ( closest < closest2 ) closest2 = closest;
            closest = l;
			fragColour.rgb = col; // *(1.-l*sqrt(float(n)));
        }
        else if ( l < closest2 )
        {
            closest2 = l;
        }
        fragColour.rgb = mix(fragColour.rgb,vec3(0),smoothstep(penOut,penIn,length(pos-uv)));
    }
    
    // cell borders
    fragColour.rgb *= smoothstep(penIn,penOut,(closest2-closest));//*scale);
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章