引擎:3.8.5
您好,我是鶴九日!
回顧
Shader的學習是一條漫長的道路。
理論知識的枯燥無味,讓很多人選擇了放棄。然而不得不說:任何新知識、新領域的學習,本身面臨的都是問題!
互聯網和AI給了我們很多的便利性,這里羅列些個人學習使用到資料,希望對您有用。
The Book of shaders 片段著色器的入門指南
Learn OpenGL 中文版 講解Open GL的學習使用
CocosCreatorShader 木限東的Shader效果實現實例,這里感謝作者的公開分享。
慎入!史上最強 Cocos Shader 學習資源推薦(建議收藏) Cocos官方推薦的學習資源。
注:感謝Cocos官方、感謝熱心的大佬的內容整理和分享!
前言
后續的文章將逐步開始匯總一些,自己在學習Shader中編寫的一些效果。
當然,這些效果的實現,并非個人創造,可能是一些模仿,然后是模仿中的延伸。
正式開始文章之前,想告訴您兩件事:
一、因Shader效果的實現,需要CCEffect
屬性配置和 CCProgram
著色器代碼,它們存在相通部分;后續的文章僅會粘貼關鍵部分,避免篇幅過長。
二、個人編寫的Shader效果Demo示例,使用的是Gitee倉庫,名為:CocosShaderDemo
內容的主要結構是:
-
res/common 放置通用的chunk或者其他相關,頂點著色器部分的chunk,就在此處。
-
res/effect 放置一些.effect/.material的shader文件,除后綴不同外,名字相同。
-
resources/prefab 放置效果實現的預制體頁面
-
script/effect 一些effect效果的實現腳本
注:我曾考慮過像木限東大佬一樣,每個效果對應一個項目工程,這樣版本管理控制靈活。
但又考慮到可能查看不便,便采用了此中方式。
頁面的配置使用的是LayerConfig.ts
,主要配置結構:
實現效果頁面如下:
注:代碼倉庫CocosShaderDemo,因時間限制,文檔的補充并不完善,希望您能諒解!
開始
今天的內容,講解的主要是: Shader自定義不同形狀的頭像。
如果不考慮Shader的話,使用Mask遮罩也是可以實現不同形狀頭像的,只是Mask的使用會影響渲染合批,導致性能不高。
既然學習到Shader,我們便用Shader來實現吧。先看示意圖:
注:圓形頭像的實現,參考的是: 木限東 ,其他是延伸,對應的個人倉庫:地址
圓形頭像
Shader實現的圓形頭像,同Mask實現,其實有相似之處。
Mask的實現是構建一個不同形狀的約束框遮罩層,在遮罩層內顯示圖像,遮罩范圍外則裁切,不會進行渲染。
放到Shader當中,Shader處理的是像素,則指定形狀內進行繪制,形狀以外的裁切,不進行繪制而已。
只是Shader比Mask相比,有著更高的靈活度,比如邊緣的模糊度處理、抗鋸齒等。
屬性配置
實現圓形頭像,properties的屬性配置如下:
properties:alphaThreshold: { value: 0.5 }wh_ratio: { value: 1, editor: { tooltip: "寬高比"}}blur: { value: 0.01, editor: { tooltip: "光圈模糊程度"}}radius: { value: 0.5, editor: { tooltip: "光圈半徑"}}center: { value: [0.5, 0.5], editor: { tooltip: "光圈中心"}}
片段著色器部分的主要邏輯如下:
CCProgram sprite-fs %{// 自定義參數的說明,注意UBO內存對齊規則uniform ARGS{float radius; // 光圈半徑float blur; // 光圈模糊程度vec2 center; // 光圈中心點float wh_ratio; // 寬高比};vec4 frag () {// 初始化顏色值為白色且不透明vec4 o = vec4(1, 1, 1, 1);// 通過CCSampleWithAlphaSeparated函數從紋理中采樣顏色,并將其與初始顏色o相乘。o *= CCSampleWithAlphaSeparated(cc_spriteTexture, v_uv0);// 將采樣得到的顏色與頂點顏色v_color相乘,得到最終的顏色值o *= v_color;// 執行透明度測試,根據o的透明度值決定是否丟棄當前片段ALPHA_TEST(o);// 計算圓形的半徑平方,用于后續的圓形邊界判斷float circle = radius * radius;// 計算當前片段的紋理坐標與圓形中心點的偏移量,并考慮寬高比wh_ratio對x方向的調整float rx = (v_uv0.x - center.x) * wh_ratio;float ry = v_uv0.y - center.y;// 計算當前片段到圓形中心點的距離平方float dis = rx * rx + ry * ry;// smoothstep函數的作用是在兩個邊緣值之間平滑過渡,降低鋸齒// circle是圓形的邊界,circle - blur是模糊的起始邊界,dis是當前片段到中心的距離平方o.a = smoothstep(circle, circle - blur, dis) * o.a;return o;}
}
最后添加下Material材質,并設定Effect;然后將材質資源放到對應精靈的customMaterial屬性中,即可。
注: 代碼中的注釋很詳細了,我就不再一一贅述了,后面的實現,與之類似。
橢圓頭像
橢圓頭像同圓形頭像類似,只不過使用shader要生成橢圓而已。
屬性配置:
radiusX: { value: 0.5, editor: { tooltip: "橢圓的X軸半徑"}}
radiusY: { value: 0.3, editor: { tooltip: "橢圓的Y軸半徑"}}
blur: { value: 0.1, editor: { tooltip: "橢圓邊緣的模糊程度"}}
center: { value: [0.5, 0.5], editor: { tooltip: "光圈中心"}}
關鍵性片段代碼:
uniform ARGS {vec2 center; // 橢圓中心點float radiusX; // 橢圓的X軸半徑float radiusY; // 橢圓的Y軸半徑float blur; // 橢圓邊緣的模糊程度
};vec4 frag () {// ...// 計算橢圓的邊界條件float rx = (v_uv0.x - center.x) / radiusX;float ry = (v_uv0.y - center.y) / radiusY;float dis = rx * rx + ry * ry;// smoothstep函數的作用是在兩個邊緣值之間平滑過渡,降低鋸齒o.a = smoothstep(1.0, 1.0 - blur, dis) * o.a;return o;
}
注: ARGS參數的設定,注意UBO內存規則,否則容易報錯。詳情:ERROR_EFX2205
四邊形頭像
屬性配置:
size: { value: 0.5, editor: { tooltip: "四邊形的大小"}}
angle: { value: 0.26, editor: { tooltip: "傾斜角度"}}
blur: { value: 0.05, editor: { tooltip: "邊緣的模糊程度"}}
center: { value: [0.5, 0.5], editor: { tooltip: "中心點"}}
關鍵代碼:
// 計算當前像素相對于中心點的偏移
vec2 offset = v_uv0 - center;// 應用旋轉矩陣,將坐標旋轉回未傾斜的狀態
float cosA = cos(-angle);
float sinA = sin(-angle);
vec2 rotatedOffset = vec2(offset.x * cosA - offset.y * sinA,offset.x * sinA + offset.y * cosA
);// 判斷是否在菱形區域內
float diamond = abs(rotatedOffset.x) + abs(rotatedOffset.y);// 使用smoothstep將菱形的邊緣虛化,降低鋸齒
o.a = smoothstep(size, size - blur, diamond) * o.a;
總結
如上效果,關于屬性參數的配置,均可通過材質的屬性檢查器進行設置。
當然,也可以通過代碼腳本的setProperty
屬性進行動態設置,以圓形頭像的寬高比為例:
@property(Sprite)
circleSprite!: Sprite; // 圓形頭像protected start(): void {// 圓形頭像設置寬高比let avatarSize = this.circleSprite.getComponent(UITransform).contentSize;let ratio = avatarSize.width / avatarSize.height;const circleMaterial = this.circleSprite.getSharedMaterial(0);circleMaterial.setProperty("wh_ratio", ratio);
}
數學公式在這里暫且就不說明了,有兩點考慮:
一、數學公式的繁雜性,如果不懂,會讓人產生更多的畏難。
二、學習的興趣在于好奇,好奇+想象力,會讓人發現更多有意思的地方。
綜上,內容有些淺顯,只是我更希望:作為新人,降低畏難度,增加好奇和興趣,也很有用。
今天的文章,到這里就結束了;可能理解有誤,歡迎您的指出!
如果覺得文章不錯,期待您的點贊和留言,感謝!
我是鶴九日,祝您生活愉快!