<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          基于 Blinn-Phong 的高性能 Shader,支持陰影和環(huán)境反射

          共 9602字,需瀏覽 20分鐘

           ·

          2023-08-30 14:24

          引言:社區(qū)高產(chǎn)大戶孫二喵同學(xué)今天給大家?guī)砹巳椎膫鹘y(tǒng)光照模型 Shader,并集成了 Cocos Creator 的光照、陰影和環(huán)境反射能力,讓你在渲染效果和性能之間自由權(quán)衡。


          正文開始

          Cocos Creator 引擎的 3D 渲染功能,從一開始就支持了標(biāo)準(zhǔn)的現(xiàn)代化渲染流程,如 PBR材質(zhì)、HDR 渲染等等。但對于一些算力緊張或者對發(fā)熱耗電控制嚴(yán)格的平臺,我們需要一些更為低功耗的渲染方式來降低渲染開銷。基于這個需求,我基于傳統(tǒng)光照模型重寫了一套高性能的 Cocos Shader,并且能夠利用 Cocos Creator 內(nèi)置的光照、陰影和環(huán)境反射能力。希望能給有需要的開發(fā)者帶來幫助。

          • 體驗(yàn)地址:
          http://learncocos.com/light
          • 源碼地址:

          https://store.cocos.com/app/detail/5256

          在計(jì)算機(jī)圖形學(xué)中,光照模型(Lighting Model)用于模擬物體表面在光線照射下的反射效果。本文使用 Cocos Creator 來演示常用的光照模型的效果和示例代碼(使用GLSL語言)。

          85b978b9869dd27f8098fddb395326df.webp


          Unlit(無光照)

          無光照模型并不考慮光照影響,只將物體的顏色或紋理直接渲染到屏幕。這種模型適用于不需要光照影響的場景,例如廣告牌或者地面指引等特效。

          f5c96e6b3bf9d6fa3f9e1cf1b233bf40.webp

          實(shí)現(xiàn)代碼如下:

              
              
                void main()
          {
             vec4 o = mainColor; //材質(zhì)顏色
             return CCFragOutput(o);
          }


          Lambert(蘭伯特)

          蘭伯特光照模型是一種描述漫反射的光照模型,其假設(shè)物體表面對光的反射不依賴于觀察者的位置。這種模型常用于模擬非金屬、非鏡面的物體表面。

          044a91221a5bc53faf52b8aa1fde3b10.webp


          實(shí)現(xiàn)代碼如下:

              
              
                void Lambert(inout vec4 diffuseColor,in vec3 normal)
          {
             vec3 N = normalize(normal);
             vec3 L = normalize(cc_mainLitDir.xyz * -1.0);
             float NL = max(dot(N, L), 0.0);
             vec3 diffuse = NL * (diffuseColor.rgb * cc_mainLitColor.xyz);
             vec3 ambient = cc_ambientGround.rgb * diffuseColor.rgb * cc_ambientSky.w;
             diffuseColor.rgb = ambient + diffuse;
          }


          Half Lambert(半蘭伯特)

          半蘭伯特光照模型是蘭伯特光照模型的一個變體,它改變了對光線反射的解釋,使得在光線與法線成 90 度角時,反射強(qiáng)度為 0.5 而非 0 ,從而使陰影部分不那么暗,這里做了下優(yōu)化增加了 diffuseWrap 的參數(shù),用  pow(NL * diffuseWrap + (1.-diffuseWrap),2.0)  代替  pow(NL *0.5 +00.5,2.) 。此光照模型常用于卡通或非真實(shí)感渲染。

          396b35f31813a0cccfd7d383ebafadf1.webp

          相比蘭伯特模型,半蘭伯特模型的陰影部分不那么暗(下圖左側(cè)),我們也可以通過 diffuseWrap 去控制暗部的陰影強(qiáng)度。

          af9f958c111c0c893c9c0cf82ca3daec.webp

          實(shí)現(xiàn)代碼如下:

              
              
                void HalfLambert(inout vec4 diffuseColor,in vec3 normal)
          {
             vec3 N = normalize(normal);
             vec3 L = normalize(cc_mainLitDir.xyz * -1.0);
             float NL = max(dot(N, L), 0.0);
             vec3 diffuse = pow(NL * diffuseWrap + (1.-diffuseWrap),2.0) * (diffuseColor.rgb * cc_mainLitColor.xyz);
             vec3 ambient = cc_ambientGround.rgb * diffuseColor.rgb * cc_ambientSky.w;
             diffuseColor.rgb = ambient + diffuse;
          }


          Blinn-Phong(布林-馮)

          Blinn-Phong 光照模型是 Phong 光照模型的改進(jìn)版,它引入了 "半向量" 的概念,使得鏡面高光的計(jì)算更加高效。適用于模擬有光澤的物體表面。

          305e47eebab47fe98002ba754fb4cd7b.webp

          實(shí)現(xiàn)代碼如下:

              
              
                void void blinnPhong(inout vec4 diffuseColor,in vec3 normal)
          {
             vec3 N = normalize(normal);
             vec3 L = normalize(cc_mainLitDir.xyz * -1.0);
             float NL = max(dot(N, L), 0.0);
             vec3 diffuse = NL * diffuseColor.rgb * cc_mainLitColor.xyz;
             vec3 position;
             HIGHP_VALUE_FROM_STRUCT_DEFINED(position, v_position);
             vec3 cameraPosition = cc_cameraPos.xyz / cc_cameraPos.w;
             vec3 V = normalize(cameraPosition- position);
             vec3 H = normalize(L + V);
             float specularFactor = pow(max(0.0, dot(H,N)), bpParams.x*50.);
             vec3 specular = (specularFactor * cc_ambientSky.rgb * cc_mainLitColor.xyz);
             float shadowCtrl = 1.0;
             #if CC_RECEIVE_SHADOW && CC_SHADOW_TYPE == CC_SHADOW_MAP
               if (NL > 0.0) {
               #if CC_DIR_LIGHT_SHADOW_TYPE == CC_DIR_LIGHT_SHADOW_CASCADED
                 shadowCtrl = CCCSMFactorBase(position, N, v_shadowBias);
               #endif
               #if CC_DIR_LIGHT_SHADOW_TYPE == CC_DIR_LIGHT_SHADOW_UNIFORM
                 shadowCtrl = CCShadowFactorBase(CC_SHADOW_POSITION, N, v_shadowBias);
               #endif
               }
             #endif
             diffuse = (diffuse + specular) * (shadowCtrl);
          }


          Toon(卡通著色)

          卡通模型或稱 Cel-Shading,它通過將光照強(qiáng)度量離散化為幾個等級,模擬出手繪動畫的效果。適用于卡通或者藝術(shù)風(fēng)格的渲染。

          63583249269ba15e6a2beb2953ade693.webp

          實(shí)現(xiàn)代碼如下:

              
              
                void ToonShading (inout vec4 diffuseColor,in vec3 normal) {
             vec3 position;
             HIGHP_VALUE_FROM_STRUCT_DEFINED(position, v_position);
             vec3 V = normalize(cc_cameraPos.xyz - position);
             vec3 N = normalize(normal);
             vec3 L = normalize(-cc_mainLitDir.xyz);
             float NL = 0.5 * dot(N, L) + 0.5;
             float NH = 0.5 * dot(normalize(V + L), N) + 0.5;
             vec3 lightColor = cc_mainLitColor.rgb * (cc_mainLitColor.w * shadeParams.x);
             float shadeFeather = shadeParams.y;
             float shadeCtrl = mix(1., (1.-shadeParams.z), clamp(1.0 + (shadeParams.x - shadeFeather - NL) / shadeFeather, 0.0, 1.0));
             shadeCtrl *= mix(1., (1.-shadeParams.z*0.5), clamp(1.0 + (shadeParams.w - shadeFeather - NL) / shadeFeather, 0.0, 1.0));
             float specularWeight = 1.0 - pow(specularParams.x, 5.0);
             float specularMask = 1.0-smoothstep( NH, NH+ specularParams.y, specularWeight + EPSILON_LOWP);
             float shadowCtrl = 1.0;
             #if CC_RECEIVE_SHADOW && CC_SHADOW_TYPE == CC_SHADOW_MAP
               if (NL > 0.0) {
               #if CC_DIR_LIGHT_SHADOW_TYPE == CC_DIR_LIGHT_SHADOW_CASCADED
                 shadowCtrl = CCCSMFactorBase(position, N, v_shadowBias+0.1);
               #endif
               #if CC_DIR_LIGHT_SHADOW_TYPE == CC_DIR_LIGHT_SHADOW_UNIFORM
                 shadowCtrl = CCShadowFactorBase(CC_SHADOW_POSITION, N, v_shadowBias+0.1);
               #endif
               }
             #endif
             float diffuseCtrl = (shadowCtrl+specularMask*specularParams.z)*shadeCtrl;
             vec3 envColor = cc_ambientGround.rgb*cc_ambientSky.w;
             diffuseColor.rgb *= (envColor + (lightColor*diffuseCtrl));
           }

          我們還可以通過邊緣光(Rim Light)實(shí)現(xiàn)不同風(fēng)格化渲染風(fēng)格。

          19dbed939b59021c306a8cd21b8c2fc6.webp

          實(shí)現(xiàn)代碼如下:

              
              
                    #if USE_RIM_LIGHT
                 float fRim = (1.0 - dot(v_view_normal,vec3(0,0,1.0))) * rimColor.w;
                 color.rgb = mix(color.rgb,rimColor.rgb,fRim);
             #endif


          PBR vs. Blinn-Phong

          PBR - Physically Based Rendering, 是最新的光照模型,它試圖更真實(shí)地模擬光線與物體表面的相互作用,包括漫反射和鏡面反射。PBR 模型通常包括能量守恒和菲涅耳等效等物理原理,適用于模擬真實(shí)世界的渲染。

          PBR 是幾乎是所有現(xiàn)代標(biāo)準(zhǔn)圖形引擎默認(rèn)的光照模型,但 PBR 由于涉及過多的公式計(jì)算和貼圖采樣,它的 Shader 代碼非常復(fù)雜,對用戶設(shè)備算力要求較高。對這塊有興趣的同學(xué)可以直接查看 Cocos 引擎最新版本的內(nèi)置 Shader 源碼。

          https://github.com/cocos/cocos-engine/tree/develop/editor/assets/chunks

          但在很多情況下,我們不用 PBR 也能渲染出可接受的效果。

          如下圖所示:

          a9975655c6ac47eb95e25ca110f66fd6.webp

          左:Blinn-Phong,右:PBR

          不同的光照模型適用于不同的渲染風(fēng)格,可以根據(jù)具體的需求和場景來選擇使用。例如,無光照模型適合廣告牌或者地面指引,蘭伯特光照模型適合無光澤的表面,卡通模型適合卡通或手繪風(fēng)格的渲染,而 PBR 則適合模擬真實(shí)世界的高質(zhì)量渲染。


          常用術(shù)語

          在 Shader 中,無論我們使用哪一種光照模型,都有一些通用的技術(shù)和術(shù)語,它們各自承擔(dān)著不同的功能和目的。以下是一些概念的解釋:

          顏色(Color):這通常是一個 RGBA 值,表示一個像素的基本顏色。R、G、B分別代表紅色、綠色和藍(lán)色,A 代表透明度。這些值通常在 0 到 1 之間。

          fff4eeb60667b0b87022e58ea302482f.webp

          Albedo Map:不帶任何光照信息的顏色貼圖,主要表示物體表面的固有顏色,不受光照影響,通常用在 PBR 光照模型中。

          92f4be1ca8631e9d367014bf451eb298.webp

          Diffuse Map:漫反射貼圖,可能會攜帶一些光照信息,比如 AO,Shading 等。一般用于傳統(tǒng)光照模型。

          Alpha Test:Alpha 測試是一種通過比較像素的 Alpha 值和預(yù)設(shè)閾值來決定是否丟棄像素的技術(shù)。這種技術(shù)常常用于實(shí)現(xiàn)透明和半透明效果。

          實(shí)現(xiàn)代碼如下:

              
                  #if USE_ALPHA_TEST
               if (color.ALPHA_TEST_CHANNEL < colorScaleAndCutoff.w) discard;
             #endif

          Normal Map:Normal Mapping 是一種用于模擬表面細(xì)節(jié)的技術(shù)。它使用一張貼圖來存儲向量,這個向量描述了表面在每一點(diǎn)上的法線方向,使得物體表面看起來有更多的細(xì)節(jié)。

          8e7160c077bcc9bcafd8c5455f87cb20.webp

          Emissive Map:Emissive Map 是一種紋理貼圖,用于表示物體在沒有外部光照的情況下自發(fā)的顏色和亮度。

          Fog:霧是一種用于模擬大氣效果的技術(shù),它可以使遠(yuǎn)離觀察者的物體看起來更模糊,顏色也會向霧的顏色過渡。

          Image-Based Lighting (IBL):IBL 是一種使用環(huán)境反射貼圖來模擬環(huán)境光照的技術(shù)。它可以產(chǎn)生更真實(shí)的反射和光照效果。

          實(shí)現(xiàn)代碼如下:

              
              
                   #if CC_USE_IBL && USE_IBL
               vec3 cameraPosition = cc_cameraPos.xyz / cc_cameraPos.w;
               vec3 V = normalize(cameraPosition- position);
               vec3 env = vec3(1.);
               vec3 R = normalize(reflect(-V, N));
               vec3 rotationDir = RotationVecFromAxisY(R.xyz, cc_surfaceTransform.z, cc_surfaceTransform.w);
               vec4 envmap = fragTextureLod(cc_environment, rotationDir, bpParams.y * (cc_ambientGround.w - 1.0));
               #if CC_USE_IBL == IBL_RGBE
                 env = unpackRGBE(envmap);
               #else
                 env = SRGBToLinear(envmap.rgb);
               #endif
               diffuse = mix(env, diffuse, bpParams.x);
             #endif
             vec3 ambient = cc_ambientGround.rgb * diffuseColor.rgb * cc_ambientSky.w;
             diffuseColor.rgb = ambient + diffuse;
           }

          Rim Light: 邊緣光是一種模擬物體邊緣被背光照亮的效果的技術(shù),可以增加3D模型的立體感。

          作用原理

          上面提到的這些技術(shù),步驟通常在 Fragment Shader 中以以下的順序進(jìn)行:

          1. 顏色:首先,你需要知道物體的基本顏色,這通常通過讀取Albedo貼圖來實(shí)現(xiàn)。

          2. Normal Map:然后,你可以應(yīng)用Normal Map來改變物體表面的法線,從而模擬出更多的細(xì)節(jié)。

          3. 光照計(jì)算:接著,你可以進(jìn)行光照計(jì)算,這通常包括環(huán)境光、漫反射光、鏡面反射光等的計(jì)算。在計(jì)算過程中,你可能會用到Rim Light來增加邊緣的亮度。

          4. IBL:然后,你可以根據(jù)全景圖片來計(jì)算IBL,使得環(huán)境的反射和陰影效果更真實(shí)。 4a40723f2f5d8f0bec2265a868c2608e.webp5. Emissive Map:然后,你可以加上Emissive Map,使物體能在沒有光源的情況下發(fā)光。

          6. Alpha Test:最后,你可以進(jìn)行Alpha測試,根據(jù)測試結(jié)果決定是否丟棄像素。

          7. Fog:在所有的顏色和光照計(jì)算完畢后,你可以應(yīng)用Fog效果,使遠(yuǎn)離觀察者的物體顏色向霧的顏色過渡。

          be8a10f769e26bc81f7626ff18e8767f.webp


          性能分析

          由于 PC 的 GPU 運(yùn)算能力和帶寬都比較強(qiáng)大,在處理這些光照模型時候,性能幾乎相差不大。手機(jī)中的高端機(jī)型也不會受很大影響,只有少數(shù)低端機(jī)上 PBR 性能會弱于 Lambert。

          同時我們觀察到,使用陰影和描邊(Outline)會使得頂點(diǎn)數(shù)翻倍,這是性能下降的主要原因,這主要有以下兩個原因:

          陰影生成:陰影通常是通過生成陰影映射(Shadow Map)來實(shí)現(xiàn)的。在這個過程中,場景需要從光源的視角進(jìn)行一次額外的渲染。這意味著每個頂點(diǎn)需要被再次處理和光柵化,使得頂點(diǎn)數(shù)翻倍。

          ed9a2524208ddf9a96f24cb9a08eee49.webp

          描邊生成額外的幾何體:在原始模型的基礎(chǔ)上生成一個稍大的版本,然后渲染這個大版本的反面,形成描邊效果。這種方法會導(dǎo)致頂點(diǎn)數(shù)翻倍,因?yàn)槟銓?shí)際上是渲染了兩個模型。

          e1caec433965a3596ac2d46cb694a205.webp

          通常我們可以通過開啟  GPU Instancing  來提升游戲性能。

          8721fa18b32853352fff2ea84f921dea.webp

          如果模型有骨骼動畫,建議啟用烘焙動畫來配合  GPU Instancing  使用。

          dbe06369c00ebb914d559fb6b1d56699.webp

          所以建議大家還是基于游戲風(fēng)格去選擇光照模型,實(shí)測下來,PBR 性能弱的主要原因是開啟了 IBL。

          源碼免費(fèi)獲取

          針對 Blinn-Phong,Lambert 模型內(nèi)也寫了簡化版的 IBL,大家可以從 Cocos Store 下載全套源碼包進(jìn)行了解:

          fb7998f04c652b4403a9f23154d8ccf9.webp

          地址 https://store.cocos.com/app/detail/5256

          點(diǎn)擊【閱讀原文】可快速跳轉(zhuǎn),免費(fèi)的喲。


          關(guān)注 Cocos 引擎官方公眾號,你會變得更強(qiáng)!

          瀏覽 49
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  男女超碰| 哪里可以看AV片 | 操逼视频免费国产 | 99热这里都是精品 | 欧美一级婬片A片免费老牛 |