• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            永遠(yuǎn)也不完美的程序

            不斷學(xué)習(xí),不斷實(shí)踐,不斷的重構(gòu)……

            常用鏈接

            統(tǒng)計(jì)

            積分與排名

            好友鏈接

            最新評論

            Shader示例

            上一章里,我們詳細(xì)討論了HLSL著色語言的各方面。但并沒有實(shí)際展示如何編寫shader。雖然本書不是關(guān)于如何編寫shader的,但還是有必要編寫幾個簡單的shader,幫你深入了解HLSL。此外,在學(xué)習(xí)effect framework時,我們還會用到這些例子來闡述一些核心概念。

             

                   記住,對創(chuàng)建一個完整的shader來說,不僅僅是編寫shader代碼,還包括用適當(dāng)?shù)恼Z義符設(shè)置一系列渲染狀態(tài)和變量。當(dāng)然,由于目前你還缺乏編寫完整shader的一些知識,所以,這里只討論前者:也就是頂點(diǎn)和像素著色程序代碼。本書的后面會對這些代碼進(jìn)行擴(kuò)展。

             

            最簡單的Shader

             

                   對于把物體渲染到屏幕上來說,有幾個基本的步驟是必須完成的。首先,需要接收輸入頂點(diǎn)的位置(頂點(diǎn)在世界坐標(biāo)中的位置)并把它們轉(zhuǎn)變?yōu)槠聊蛔鴺?biāo)。通常使用world-view-projection矩陣來完成這個任務(wù),它包含了把頂點(diǎn)從局部坐標(biāo)映射為最終屏幕坐標(biāo)的所有信息。現(xiàn)在開始,我們假設(shè)已經(jīng)有這樣一個矩陣變量,并且名稱為view_proj_matrix

             

                   先來定義一個把數(shù)據(jù)從頂點(diǎn)著色器傳遞給像素著色器的結(jié)構(gòu)。我們把這個結(jié)構(gòu)稱為VS_OUTPUT,當(dāng)然,也可以是任何你喜歡的名字。目前,只需要把頂點(diǎn)位置數(shù)據(jù)添加到這個結(jié)構(gòu)中。

             

            struct VS_OUTPUT

            {

                 float4 Pos: POSITION;

            };

             

                   你應(yīng)該注意到我們把POSITION語義連接到了Pos變量上,它將告訴effect系統(tǒng)如何把這個變量傳遞到像素著色器中。我會在下一章講解語義。現(xiàn)在只差頂點(diǎn)著色器代碼了。頂點(diǎn)著色器接收頂點(diǎn)位置,并使用view_proj_matrix矩陣對它進(jìn)行變換,可以用內(nèi)建的mul函數(shù)來完成這一步計(jì)算。我們把頂點(diǎn)著色器代碼放到一個名為vs_main的函數(shù)中:

             

            VS_OUTPUT vs_main ( float4 inPos : POSITION)

            {

                 VS_OUTPUT Out;

                 // output a transformed and projected vertex position

               Out.Pos = mul ( view_proj_matrix , inPos);

                 return Out;

            }

             

                   這里同樣使用了POSITION語義修飾輸入?yún)?shù)inPos。它將告訴頂點(diǎn)著色器把幾何體數(shù)據(jù)流信息映射為這個參數(shù)的輸入值。接下來進(jìn)入完成這個簡單shader的第二步。現(xiàn)在你知道了頂點(diǎn)在屏幕上的位置,可以定義頂點(diǎn)的顏色了。最簡單的方法就是把所有頂點(diǎn)的顏色都設(shè)置為一個常量。通常像素著色器將返回一個float4類型的值來表示當(dāng)前像素在屏幕上的顏色值,float4分量分別表示紅色,綠色,藍(lán)色和alpha值。我們把像素著色器代碼放到一個名為ps_main的函數(shù)中:

             

            float4 ps_main ( void ) : COLOR

            {

                 //Output constant color

                 float4 Color;

                 color[0] = color[3] = 1.0; // red and alpha on

                 color[1] = color[2] = 0.0;// Green and Blue off

                 return color;

            }

             

                   這幾乎是最簡單的代碼了,注意我們用COLOR語義修飾了函數(shù)的返回值,它將告訴編譯器把函數(shù)返回值作為當(dāng)前像素的顏色值。

             

            著色

             

                   我們已經(jīng)有渲染物體所需的最基本代碼了,如何把紋理映射到幾何體上,讓物體看起來更加真實(shí)呢?對于需要使用紋理的shader來說,需要有一個sampler類型的全局變量。在后面的章節(jié)中,我會教你如何使用語義和effect framework來設(shè)置紋理狀態(tài)。目前我們假設(shè)已經(jīng)設(shè)置了好了紋理狀態(tài):

             

                   sampler Texture0;

             

                   使用紋理之前,還需要知道知道對紋理的哪一部份進(jìn)行采樣映射,因此,每個像素都必須有相應(yīng)的紋理坐標(biāo)。一般情況下,紋理坐標(biāo)將作為幾何體信息的一部分輸入到頂點(diǎn)著色器中,經(jīng)由頂點(diǎn)著色器計(jì)算處理之后,傳入到像素著色器中。通常使用TEXCOORDx語義來修飾作為參數(shù)傳遞的紋理坐標(biāo)。這個語義將會告訴硬件如何在頂點(diǎn)和像素著色器之間交換數(shù)據(jù)。以下是修改之后的頂點(diǎn)著色器代碼:

             

            struct VS_OUTPUT

            {

                    float4 Pos :     POSITION;

                    float2 Txr1:    TEXCOORD0;

            }

             

            VS_OUTPUT vs_main(

                    float4 inPos : POSITION;

                    float2 Txr1 : TEXCOORD0)

            {

                    VS_OUTPUT Out;

                    //Output our transformed and projected vertex position and texture coordinate

                    Out.Pos = mul ( view_proj_matrix, inPos);

                    Out.Txr1 = Txr1;

                    returen Out;

            }

             

                   像素著色器也同樣簡單。在創(chuàng)建了sampler變量之后,可以使用HLSL的內(nèi)建函數(shù)tex2D來對紋理進(jìn)行采樣,代碼如下:

             

            sampler Texture0;

            float4 ps_main(

                 float4 inDiffuse : COLOR0,

                 float2 inTxr1 : TEXCOORD0) : COLOR0

            {

                 //Output the color taken from our texture

                 return tex2D ( Texture0, inTxr1);

            }

             

            添加光照

             

                   雖然添加了紋理的對象看起來不錯,但顯然還不夠真實(shí)。在增加場景真實(shí)度的過程中,很重要的一步就是為對象添加光照。真實(shí)世界中,從太陽到燈泡,充滿了各種光。沒有了光線,就什么都看不到了。

             

                   雖然光照本身是一個相當(dāng)復(fù)雜的主題,但在計(jì)算機(jī)圖形領(lǐng)域中,光通常被簡化為幾種基本類型:

             

            l         環(huán)境光(Ambient lighting:場景中所有光源經(jīng)過多次放射和折射之后,對場景總亮度貢獻(xiàn)的近似模擬。通常用它來減少場景中所需光源的數(shù)量,模擬出多光源下的照明效果。環(huán)境光通常是一個常量,對所有物體的作用效果都一樣。

            l         漫反射光(Diffuse lighting):材質(zhì)的微觀粗糙表面將導(dǎo)致在有所方向上均勻的反射入射光線。在任何角度接收到的反射光線強(qiáng)度都是相同的。

            l         鏡面高光( Specular lighting):當(dāng)材質(zhì)表面相當(dāng)光滑,粗糙度很低時,將以一種非均勻的方式反射光線。對鏡面高光來說,光線強(qiáng)度不但與入射光角度有關(guān),和觀察者的角度也有關(guān)。

             

            除了知道光線如何影響物體之外,你還需要如何對光源本身分類。雖然光總是由某個表面發(fā)出,比如太陽或燈泡表面,但你也可以把它們看作來自某個方向或某個點(diǎn)。

             

            光照技術(shù)中,方向光是最簡單的類型。它們沒有位置信息,并且假設(shè)所有光線之間都是平行的,指向同一個方向。哪一種光源是這樣的呢?現(xiàn)實(shí)中并沒有這樣的光源。方向光是假設(shè)光源離物體無限遠(yuǎn)時,照射到物體上的光線將近似于平行而得出的。

             

            方向光最好的例子就是陽光。如果把太陽看作一個離地球上億千米的點(diǎn)光源,那么當(dāng)陽光到達(dá)地球表面時已經(jīng)近似于平行了,完全可以看作是方向光。

             

            此外沒有位置信息表示方向光不隨距離而衰減。對方向光來說,要考慮的因素只有兩個:方向和光的顏色。看到這里你可能會問光線是如何影響物體表面的。如圖所示,光線照射到物體表面的強(qiáng)度只與入射光線和表面法線的角度有關(guān)。

                   知道了這些基礎(chǔ)知識,就可以用入射光的方向矢量和表面法線的點(diǎn)積以及燈光的顏色因子計(jì)算出物體表面上任意一點(diǎn)的光照強(qiáng)度和顏色。這讓我們得出了以下代碼:

             

            Color = Light_Color * saturate ( dot ( Light_Direction, inNormal ) );

             

                   注意在上面的代碼中我們使用了saturate函數(shù)。它保證對于背對光線的面來說,獲得的光照強(qiáng)度不會為負(fù)值。當(dāng)然,你也可以使用clamp函數(shù),但是對于把值限制在01之間來說,saturate函數(shù)要更加高效。

             

                   一般來說,場景中大多數(shù)的光都來自于燈泡,火炬或類似的光源。仔細(xì)觀察一下這類光源,它們通常由一個很小的有限點(diǎn)發(fā)出,并且位于場景中的某個特定位置。簡化一下,你可以把這些光源都看作場景中的一個點(diǎn),這就是點(diǎn)光源。

             

                   對這類光源來說,光線呈放射狀發(fā)出。這意味著只要物體和光源的距離相等,那么無論在哪個方向,所受到的影響都相同。由于表面的光照強(qiáng)度與光線和物體表面法線之間的關(guān)系有關(guān),因此我們所要做的第一步就是計(jì)算出光線的方向。顯然,對于表面上的任意點(diǎn)來說,光線方向就等于從當(dāng)前點(diǎn)的位置指向光源位置的矢量。對點(diǎn)光源來說,隨角度的衰減值如下:

             

            //compute the normalized light direction vector and use it to determine the angular light attenuation

            float3 Light_Direction = normal ( inPos – Light_Position);

            float AngleAttn = saturate ( dot ( inNormal, Light_Direction) );

             

                   此外對于點(diǎn)光源來說,還需要考慮它在距離上的衰減。自然,需要計(jì)算光源到當(dāng)前點(diǎn)的距離,使用如下代碼:

             

            float Distance = length ( inPos – Light_Position);

             

                   通常情況下,點(diǎn)光源的衰減因子隨距離的平方成反比。但是為了獲得很多的可控性,可以調(diào)整公式,讓衰減和距離的二次多項(xiàng)式成反比,代碼如下:

             

            //compute distance based attenuation. this is defined as

            // attenuatin = 1 / ( a + b*distance + c * disctance * distance)

            float DistAttn = saturate( 1 / ( LightAttenuation.x + LightAttenuation.y * Dist +

            LightAttenuation.z * Dist));

             

            現(xiàn)在把前面的代碼集成到頂點(diǎn)著色器中吧:

             

            struct VS_OUTPUT

            {

                    float4 Pos:      POSITION;

                    float2 TexCoord: TEXCOORD0;

                    float2 Color:    COLOR0;

            };

             

            float4 Light_PointDiffuse( float3 VertPos, float3 VertNorm, float3 LightPos, float4 LightColor,

                                                       float4 LightAttenuation)

            {

                    //determine the distance from the light o the vertex and the direction

                    float3 LightDir = LightPos – VertPos;

                    float Dist = length(LightDir);

                    LightDir = LightDir / Dist; 

                    //Compute distance based attenuation.

                    float DistAttn = saturate( 1 / ( LightAttenuation.x + LightAttenuation.y * Dist +

                           LightAttenuation.y * Dist*Dist));

                    //comopute angle based attenuation

                    float AngleAttn = saturate ( dot (VertNorm, LightDir));

                    // Computer the final lighting

                    return LightColor * DistAttn * AngleAttn;

            }

             

            VS_OUTPUT vs_main( float4 inPos: POSITION,

                    float3 inNormal: NORMAL,

                    float2 inTxr : TEXCOORD0)

            {

                    VS_OUTPUT Out;   

                    Out.Pos = mul ( view_proj_matrix, inPos);

                    Out.TexCoord = inTxr;

                    float4 Color = Light_PointDiffuse ( inPos, inNormal, Light_Position, Light1_Color, Light_Attenuation)

                    Out.Color = Color;

                    return Our;

            }

             

                   我把計(jì)算點(diǎn)光源光照的代碼單獨(dú)放到了Light_PointDiffuse函數(shù)中,因此,當(dāng)場景中有多個點(diǎn)光源時,你可以復(fù)用這段代碼。當(dāng)然,我們在后面的章節(jié)會有這樣的例子。

            posted on 2008-08-08 08:53 狂爛球 閱讀(1863) 評論(0)  編輯 收藏 引用


            只有注冊用戶登錄后才能發(fā)表評論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            日韩乱码人妻无码中文字幕久久| 996久久国产精品线观看| 婷婷久久综合九色综合九七| 性做久久久久久久久久久| 久久久久亚洲av综合波多野结衣| jizzjizz国产精品久久| 国产一区二区三精品久久久无广告 | 久久免费香蕉视频| 亚洲国产精品久久电影欧美| 久久久国产精品福利免费| 亚洲日本va午夜中文字幕久久 | 99久久中文字幕| 伊人精品久久久久7777| www久久久天天com| 久久经典免费视频| 成人a毛片久久免费播放| 亚洲乱码中文字幕久久孕妇黑人| 大香网伊人久久综合网2020| 亚洲国产精品无码久久久秋霞2| 精品久久久久久无码国产 | 国产精品美女久久久久av爽| 亚洲色婷婷综合久久| 精品久久久久中文字幕一区| 国产成人久久激情91| 亚洲国产精品成人久久| 久久婷婷午色综合夜啪| 日产久久强奸免费的看| 国内精品久久久久久久久电影网| 99精品久久精品| 狠狠色丁香久久综合婷婷| 亚洲精品无码久久千人斩| 国产成人精品综合久久久| 亚洲中文字幕伊人久久无码| 亚洲精品无码久久毛片| 久久精品免费大片国产大片| 久久免费香蕉视频| 中文字幕亚洲综合久久菠萝蜜| 亚洲国产天堂久久综合| 18禁黄久久久AAA片| 人人狠狠综合久久88成人| 精品无码久久久久国产|