中級Shader教程04 2D海洋


本篇主要技術點有:
1. sin波函數的理解和應用
2. 極座標+sin 對於波動圓環的實現
3. 2D模擬3D中”分層”的概念

1.原理:

sin函數是周期函數,對於無限重複的現象是一種很好的模擬
類似傅里葉變換一樣,複雜的函數可以用簡單的sin cos 組合進行比逼近
波浪這種效果是一種典型。

2.分析:

1.雲的實現

  1. 形狀:多個不同大小,偏移的圓,通過mix的方式合成雲朵
  2. 動態:簡單的走UV
    當然你也可以使用類似太陽光暈的那種方式實現

2.太陽的實現

  1. 形狀:光的邊緣是通過極座標中的角度帶入多個不同頻率的sin函數中是實現
  2. 動態:不同頻率的sin函數不同的相位偏移速度來實現

3. 海洋的實現

  1. 原理:觀察海洋,海浪的變化是連續的,隨機的,而且是相似的。
    這幾個特點是不是非常像前面文章提到過的Noise的感覺,局部是連續的,但整體的感覺又是隨機的。
    很類似對吧,後面我們實現山脈(FBM)和3D海洋(FFT)的時候,再回過頭來看待這兩種方式,會發現他們的合成形式是一樣的,這裏我們使用多個頻率,振幅不同的Sin函數海實現對波浪的模擬。
    我們通過”多層”的方式來模擬3D效果,同樣需要考慮透視對於圖像大小,範圍,清晰度的影響
  2. 形態:分層實現,單層中疊加不同頻率,振幅,偏移的方式sin函數
  3. 動態:不同的層不同的頻率,同一層中,不同頻率之間擁有不同的相位偏移速度

3.代碼實現:

爲了方便畫圓,調整UV爲(-0.5,-0.5,0.5,0.5)

1.雲朵

通過不同的圓來合成雲朵

float Circle(fixed2 uv,fixed2 center,float size,float blur){
    uv = uv - center;
    uv /= size;
    float len = length(uv);
    return smoothstep(1.,1.-blur,len);
}
//簡單直觀的合成方式
float DrawCloud(fixed2 uv,fixed2 center,float size){
    uv = uv - center;
    uv /= size;
    float col = Circle(uv,fixed2(0.,0.),0.2,0.05);
    col =col *  smoothstep(-0.1,-0.1+0.01,uv.y);//將圓中不想要的的部分給剪切掉
    col += Circle(uv,fixed2(0.15,-0.05),0.1,0.05);
    col += Circle(uv,fixed2(0.,-0.1),0.11,0.05);
    col += Circle(uv,fixed2(-0.15,-0.1),0.1,0.05);
    col += Circle(uv,fixed2(-0.3,-0.08),0.1,0.05);
    col += Circle(uv,fixed2(-0.2,0.),0.15,0.05);
    return col;
}
float DrawClouds(fixed2 uv){
    uv.x += 0.03*_Time.y;
    uv.x = frac(uv.x+0.5) - 0.5;
    float col = DrawCloud( uv,fixed2(-0.4,0.3),0.2);
    col += DrawCloud( uv,fixed2(-0.2,0.42),0.2);
    col += DrawCloud( uv,fixed2(0.0,0.4),0.2);
    col += DrawCloud( uv,fixed2(0.15,0.3),0.2);
    col += DrawCloud( uv,fixed2(0.45,0.45),0.2);
    return col;
}

2.太陽

float AngleCircle(fixed2 uv,fixed2 center,float size,float blur){
    uv = uv - center;
    uv /= size;
    float deg = atan2(uv.y,uv.x) + _Time.y * -0.1;//轉化爲極座標
    float len = length(uv);//轉化爲極座標
    //通過極座標角度的方式來繪製波浪型的圓環
    float offs =( sin(deg*9)*3.+sin(deg*11+sin(_Time.y*6)*.5))*0.05;
    return smoothstep(1.+offs,1.-blur+offs,len);
}

fixed2 sunPos = fixed2(0.3,0.35);
fixed sun = Circle(uv,sunPos,0.06,0.05);//繪製太陽中心的圓
fixed sunCircle = AngleCircle(uv,sunPos,0.08,0.05);//繪製太陽光暈
col = lerp( col ,fixed3(0.9,0.6,0.15),sunCircle);//融合光暈和背景色
col = lerp( col ,fixed3(0.98,0.9,0.1),sun);//融合太陽和光暈

3.海洋

爲了計算方便這裏的UV範圍爲(0,0,1.0,1.0)

fixed Wave(float layer,fixed2 uv,fixed val){
    float amplitude =  layer*layer*0.00004;//這些數值都是爲了美術效果  怎麼漂亮怎麼來
    float frequency = val*200*uv.x/layer;
    float phase = 9.*layer+ _Time.z/val;
    return amplitude*sin(frequency+phase); 
}
fixed3 col = fixed3(0.0,0.0,0.0);
float num = 0.;
for (float i=1.; i < LAYER; i++) {
    //類似FBM的疊加方式,沒加一層整幅下降一半,平率提升兩倍左右,目的是
    //既可以用第一個函數控制大概的形狀,又可以增加後面的函數添加高頻的變化,方便控制細節
    //同樣這些參數 是爲了讓函數的形狀變得好看
    float wave = 2.*Wave(i,uv,1.)+Wave(i,uv,1.8)+.5*Wave(i,uv,3.);
    float layerVal = 0.7-0.03*i + wave;//控制波浪的高度
    if(uv.y >layerVal){
        break;
    }
    num = i;//計算所在層的ID
}
col = num*fixed3(0,.03,1);//計算每一層的基本顏色
col += (LAYER - num) * fixed3(.04,.04,.04);//顏色疊亮

4.背景色處理

//在最高的一層海浪之上
if(num ==0){
    //添加海平面泛光
    float ry = Remap(0.7,1.0,1.0,0.0,uv.y);//0.7是最高海浪值的水平面
    col = lerp(fixed3(0.1,0.6,0.9),fixed3(0.1,0.7,0.9),ry);//簡單的顏色漸變
    col += pow(ry,10.)*fixed3(0.9,0.2,0.1)*0.2;//讓接近海平面的地方泛白 pow是爲了控制影響範圍
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章