• <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的,但還是有必要編寫幾個(gè)簡單的shader,幫你深入了解HLSL。此外,在學(xué)習(xí)effect framework時(shí),我們還會用到這些例子來闡述一些核心概念。

             

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

             

            最簡單的Shader

             

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

             

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

             

            struct VS_OUTPUT

            {

                 float4 Pos: POSITION;

            };

             

                   你應(yīng)該注意到我們把POSITION語義連接到了Pos變量上,它將告訴effect系統(tǒng)如何把這個(gè)變量傳遞到像素著色器中。我會在下一章講解語義。現(xiàn)在只差頂點(diǎn)著色器代碼了。頂點(diǎn)著色器接收頂點(diǎn)位置,并使用view_proj_matrix矩陣對它進(jìn)行變換,可以用內(nèi)建的mul函數(shù)來完成這一步計(jì)算。我們把頂點(diǎn)著色器代碼放到一個(gè)名為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ù)流信息映射為這個(gè)參數(shù)的輸入值。接下來進(jìn)入完成這個(gè)簡單shader的第二步。現(xiàn)在你知道了頂點(diǎn)在屏幕上的位置,可以定義頂點(diǎn)的顏色了。最簡單的方法就是把所有頂點(diǎn)的顏色都設(shè)置為一個(gè)常量。通常像素著色器將返回一個(gè)float4類型的值來表示當(dāng)前像素在屏幕上的顏色值,float4分量分別表示紅色,綠色,藍(lán)色和alpha值。我們把像素著色器代碼放到一個(gè)名為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來說,需要有一個(gè)sampler類型的全局變量。在后面的章節(jié)中,我會教你如何使用語義和effect framework來設(shè)置紋理狀態(tài)。目前我們假設(shè)已經(jīng)設(shè)置了好了紋理狀態(tài):

             

                   sampler Texture0;

             

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

            }

             

            添加光照

             

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

             

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

             

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

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

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

             

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

             

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

             

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

             

            此外沒有位置信息表示方向光不隨距離而衰減。對方向光來說,要考慮的因素只有兩個(gè):方向和光的顏色。看到這里你可能會問光線是如何影響物體表面的。如圖所示,光線照射到物體表面的強(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ì)觀察一下這類光源,它們通常由一個(gè)很小的有限點(diǎn)發(fā)出,并且位于場景中的某個(gè)特定位置。簡化一下,你可以把這些光源都看作場景中的一個(gè)點(diǎn),這就是點(diǎn)光源。

             

                   對這類光源來說,光線呈放射狀發(fā)出。這意味著只要物體和光源的距離相等,那么無論在哪個(gè)方向,所受到的影響都相同。由于表面的光照強(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)場景中有多個(gè)點(diǎn)光源時(shí),你可以復(fù)用這段代碼。當(dāng)然,我們在后面的章節(jié)會有這樣的例子。

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


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


            久久噜噜电影你懂的| 亚洲国产成人精品女人久久久| 青青草国产97免久久费观看| 久久电影网| 午夜天堂av天堂久久久| 精品免费tv久久久久久久| 欧美与黑人午夜性猛交久久久| 久久婷婷五月综合色奶水99啪| 国产精品久久久久久| 久久九九久精品国产| 国内精品久久久久伊人av| 亚洲国产成人精品女人久久久| 久久久久人妻一区二区三区 | 999久久久免费国产精品播放| 久久人人爽人人爽人人片AV麻豆| 7777久久久国产精品消防器材| 国内精品久久久人妻中文字幕| 亚洲伊人久久成综合人影院| 88久久精品无码一区二区毛片| 国产亚洲精品久久久久秋霞 | 久久久综合九色合综国产| 一本色道久久88综合日韩精品 | 伊人久久大香线蕉无码麻豆| 99久久久精品| 日日噜噜夜夜狠狠久久丁香五月| 狠狠色丁香婷婷综合久久来来去| 色婷婷久久综合中文久久蜜桃av | 久久精品三级视频| 久久夜色tv网站| 97久久久精品综合88久久| 久久综合香蕉国产蜜臀AV| 久久久一本精品99久久精品88| 久久精品国产色蜜蜜麻豆| 94久久国产乱子伦精品免费| 久久国产精品99久久久久久老狼| 精品久久亚洲中文无码| 亚洲色欲久久久久综合网| 伊人色综合久久天天网| 亚洲午夜无码AV毛片久久| 亚洲一级Av无码毛片久久精品| 国产精品99久久久久久宅男小说|