• <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ì)

            積分與排名

            好友鏈接

            最新評論

            HLSL初級教程(轉(zhuǎn))

            目錄
            前言

            1.HLSL入門

                   1.1什么是著色器

                   1.2什么是HLSL

                   1.3怎么寫HLSL著色器

                   1.4怎么用HLSL著色器

            2.頂點(diǎn)著色器

                   2.1可編程數(shù)據(jù)流模型

                   2.2頂點(diǎn)聲明

                   2.3用頂點(diǎn)著色器實(shí)現(xiàn)漸變動(dòng)畫

            3.像素著色器

                   3.1多紋理化

                   3.2多紋理效果的像素著色器

                   3.3應(yīng)用程序

            4.HLSL Effect(效果框架)

                   4.1Effect代碼結(jié)構(gòu)

                   4.2用Effect實(shí)現(xiàn)多紋理化效果

            結(jié)語

            參考資料

            前言
                   本教程針對HLSL(High Level Shading Language)初學(xué)者,從應(yīng)用的角度對HLSL、頂點(diǎn)著色器、像素著色器和Effect效果框架進(jìn)行了介紹,教程中去掉了對HLSL語法等一些細(xì)節(jié)內(nèi)容的討論,力求幫助讀者盡可能快地理解HLSL編程的概念,掌握HLSL編程的方法。

                   教程中部分闡述直接引用了其他文檔,這是因?yàn)檫@些文檔表述之精要,已經(jīng)達(dá)到了不能更改的地步,這里表示感謝。

                   本文檔版權(quán)為作者所有,非商業(yè)用途可免費(fèi)使用,轉(zhuǎn)載請注明出處。

             

            1.HLSL入門
            1.1什么是著色器

            DirectX使用管道技術(shù)(pipeline)進(jìn)行圖形渲染,其構(gòu)架如下:

             

            圖1.1 Direct3D Graphics Pipeline

            之前我們使用管道的步驟如下:

            1.       設(shè)定頂點(diǎn)、圖元、紋理等數(shù)據(jù)信息;

            2.       設(shè)定管道狀態(tài)信息;

            ²        渲染狀態(tài)

            通過SetRenderState方法設(shè)定渲染狀態(tài);

            另外,使用以下方法設(shè)置變換、材質(zhì)和光照:

                          SetTransform

                          SetMaterial

            SetLight

                          LightEnable

            ²        取樣器狀態(tài)

            通過SetSamplerState方法設(shè)定取樣器狀態(tài);

            ²        紋理層狀態(tài)

            通過SetTextureStageState設(shè)定紋理層狀態(tài);

            3.       渲染;

            這部分交由D3D管道按照之前的設(shè)定自行完成,這部分操作是D3D預(yù)先固定的,所以這種管道技術(shù)被稱為固定功能管道(fixed function pipeline);

             

            固定功能管道給我們編程提供了一定的靈活性,但是仍有很多效果難以通過這種方式實(shí)現(xiàn),比如:

            1.       在渲染過程中,我們要求y坐標(biāo)值大于10的頂點(diǎn)要被繪制到坐標(biāo)值(0,0,0)的地方,在之前的固定功能管道中,頂點(diǎn)被繪制的位置是在第1步即被設(shè)定好的,不可能在渲染過程中進(jìn)行改變,所以是不可行的;

            2.       謀頂點(diǎn)在紋理貼圖1上映射為點(diǎn)A,在紋理貼圖2上映射為點(diǎn)B,我們要求該頂點(diǎn)顏色由A、B共同決定,即:

            定點(diǎn)顏色 = A點(diǎn)色彩值*0.7 + B點(diǎn)色彩值*0.3

               這在固定管道編程中也是不可行的。

            以上兩個(gè)問題都可以由可編程管道(pragrammable pipeline)來解決。

                   可編程管線允許用戶自定義一段可以在GPU上執(zhí)行的程序,代替固定管道技術(shù)中的Vertex Processing和Pixel Processing階段(參照圖1.1),從而在使我們在編程中達(dá)到更大的靈活性。其中替換Vertex Processing的部分叫做Vertex Shader(頂點(diǎn)著色器),替換Pixel Proccessing的部分叫做Pixel Shader(像素著色器),這就是我們所說的著色器Shader。


            1.2什么是HLSL
            Direct8.x中,著色器是通過低級著色匯編語言來編寫的,這樣的程序更像是匯編式的指令集合,由于其效率低、可讀性差、版本限制等缺點(diǎn),迫切要求出現(xiàn)一門更高級的著色語言。到了Direct3D9,HLSL(High Level Shading Language,高級渲染語言)應(yīng)運(yùn)而生了。

            HLSL的語法非常類似于C和C++,學(xué)習(xí)起來是很方便的。

            1.3怎么寫HLSL著色器
            我們可以直接把HLSL著色器代碼作為一長串字符串編寫進(jìn)我們的應(yīng)用程序源文件中,但是,更加方便和模塊化的方法是把著色器的代碼從應(yīng)用程序代碼中分離出來。因此,我們將著色器代碼單獨(dú)保存為文本格式,然后在應(yīng)用程序中使用特定函數(shù)將其加載進(jìn)來。

            下面是一個(gè)完整的HLSL著色器程序代碼,我們把它保存在BasicHLSL.txt中。該著色器完成頂點(diǎn)的世界變換、觀察變換和投影變幻,并將頂點(diǎn)顏色設(shè)定為指定的顏色。

            //

            // BasicHLSL.txt

            //

             

            //

            // Global variable

            //

             

            matrix WVPMatrix;

            vector color;

             

            //

            // Structures

            //

             

            struct VS_INPUT

            {

                   vector position : POSITION;

            };

             

            struct VS_OUTPUT

            {

                   vector position : POSITION;

                   vector color : COLOR;

            };

             

            //

            // Functions

            //

             

            VS_OUTPUT SetColor(VS_INPUT input)

            {

                   VS_OUTPUT output = (VS_OUTPUT)0;

                  

                   output.position = mul(input.position, WVPMatrix);     

                   output.color = color;

                  

                   return output;

            }

             

            下面就針對上述代碼講解一下HLSL著色器程序的編寫:

            1.3.1全局變量

                   代碼中聲明了兩個(gè)全局變量:

            matrix WVPMatrix;

            vector color;

                   變量WVPMatrix是一個(gè)矩陣類型,它包含了世界、觀察、投影的合矩陣,用于對頂點(diǎn)進(jìn)行坐標(biāo)變換;

                   變量color是一個(gè)向量類型,它用于設(shè)定頂點(diǎn)顏色;

                   代碼中并沒有對全局變量進(jìn)行初始化,這是因?yàn)槲覀儗θ肿兞康某跏蓟^程將在應(yīng)用程序中進(jìn)行,全局變量在應(yīng)用程序中賦值而在著色器程序中使用,這是應(yīng)用程序和著色器通信的關(guān)鍵所在。具體賦值過程將在后續(xù)部分講述。

            1.3.2輸入輸出

            ²        輸入輸出結(jié)構(gòu)

            程序中定義了兩個(gè)輸入輸出結(jié)構(gòu)VS_INPUT和VS_OUTPUT

            struct VS_INPUT

            {

                   vector position : POSITION;

            };

             

            struct VS_OUTPUT

            {

                   vector position : POSITION;

                   vector color : COLOR;

            };

            自定義的結(jié)構(gòu)可以采用任意名稱,結(jié)構(gòu)不過是一種組織數(shù)據(jù)的方式,并不是強(qiáng)制的,你也可以不使用,而將本程序的輸入改為:

                   vector position : POSITION;

            ²        標(biāo)志符

            用于輸入輸出的變量采用用一種特殊的聲明方式:

            Type VariableName : Semantic

                   這個(gè)特殊的冒號(hào)語法表示一個(gè)語義,冒號(hào)后面的標(biāo)志符用來指定變量的用途,如

            vector position : POSITION;

                   其中,POSITION標(biāo)志符表明該變量表示頂點(diǎn)位置,另外還有諸如COLOR、NORMAL等很多表示其他意義的標(biāo)志符。

            本節(jié)所說的輸入輸出其實(shí)是指著色器代碼和編譯器、GPU之間的通信,和應(yīng)用程序是無關(guān)的,所以這些變量不需要在應(yīng)用程序中進(jìn)行賦值,標(biāo)志符告訴編譯器各個(gè)輸入輸出變量的用途(頂點(diǎn)位置、法線、顏色等),這是著色器代碼和編譯器、GPU之間通信的關(guān)鍵。

            1.3.3入口函數(shù)

                   程序中還定義了一個(gè)函數(shù)SetColor:

            OUTPUT SetColor(INPUT input)

            {

                   VS_OUTPUT output = (VS_OUTPUT)0;

                  

                   output.position = mul(input.position, WVPMatrix);     

                   output.color = color;

                  

                   return output;

            }

            1.       該函數(shù)以input和output類型作為輸入輸出;

            2.       使全局變量WVPMatrix和input.position相乘,以完成頂點(diǎn)的世界、觀察、投影變換,并把結(jié)果賦值到output.position;

            output.position = mul(input.position, WVPMatrix);

            3.       將全局變量color的值賦給output.color;

            output.color = color;

            4.       在同一個(gè)著色器代碼文件中,可以有多個(gè)用戶自定義函數(shù),因此在應(yīng)用程序中需要指定一個(gè)入口函數(shù),相當(dāng)于windows程序的WinMain函數(shù),本程序只包含SetColor一個(gè)函數(shù)而且它將被做為入口函數(shù)使用。

             

            1.3.4總結(jié)

                   至此,一個(gè)HLSL著色器編寫完畢,渲染過程中,當(dāng)一個(gè)頂點(diǎn)被送到著色器時(shí):

            1.       全局變量WVPMatrix、color將在應(yīng)用程序中被賦值;

            2.       入口函數(shù)SetColor被調(diào)用編譯器根據(jù)標(biāo)志符將頂點(diǎn)信息填充到VS_INPUT中的各個(gè)字段;

            3.       SetColor函數(shù)中,首先定義一個(gè)VS_OUTPUT信息,之后根據(jù)WVPMatrix和color變量完成頂點(diǎn)的坐標(biāo)變換和顏色設(shè)定操作,最后函數(shù)返回VS_OUTPUT結(jié)構(gòu);

            4.       編譯器將會(huì)再次根據(jù)標(biāo)志符把返回的VS_OUTPUT結(jié)構(gòu)中的各字段映射為頂點(diǎn)相應(yīng)的信息。

            5.       頂點(diǎn)被送往下一個(gè)流程接受進(jìn)一步處理。

            上述過程中,全局變量在應(yīng)用程序中賦值而在著色器程序中使用,這是應(yīng)用程序和著色器通信的關(guān)鍵所在;標(biāo)志符告訴編譯器各個(gè)輸入輸出變量的用途(頂點(diǎn)位置、法線、顏色等),這是著色器代碼和編譯器、GPU之間通信的關(guān)鍵。個(gè)人認(rèn)為這是著色器中最為精義的地方:)


            1.4怎么用HLSL著色器

            應(yīng)用程序中對HLSL著色器的使用分為以下步驟:

            1.       加載(稱為編譯更為妥當(dāng))著色器代碼;

            2.       創(chuàng)建(頂點(diǎn)/像素)著色器;

            3.       對著色器中的變量進(jìn)行賦值,完成應(yīng)用程序和著色器之間的通信。

            4.       把著色器設(shè)定到渲染管道中;

            本例使用的著色器是一個(gè)頂點(diǎn)著色器,因此我們將通過頂點(diǎn)著色器的使用來講解著色器的使用過程,像素著色器的使用過程與此大同小異,二者之間僅有些微差別。

             

            1.4.1聲明全局變量

            IDirect3DVertexShader9* BasicShader = 0; //頂點(diǎn)著色器指針

             

            ID3DXConstantTable* BasicConstTable = 0; //常量表指針

            D3DXHANDLE WVPMatrixHandle          = 0;

            D3DXHANDLE ColorHandle              = 0;

             

            ID3DXMesh* Teapot                   = 0; //指向程序中D3D茶壺模型的指針

             

            1.4.2編譯著色器

            通過D3DXCompileShaderFromFile函數(shù)從應(yīng)用程序外部的文本文件BasicHLSL.txt中編譯一個(gè)著色器:

            //編譯后的著色器代碼將被放在一個(gè)buffer中,可以通過ID3DXBuffer接口對其進(jìn)行訪問,之后的著色器將從這里創(chuàng)建

                   ID3DXBuffer* shaderBuffer      = 0;

                   //用于接受錯(cuò)誤信息

                   ID3DXBuffer* errorBuffer       = 0;

             

                   //編譯著色器代碼

                   D3DXCompileShaderFromFile("BasicHLSL.txt", //著色器代碼文件名

                                                               0,

                                                               0,

                                                               "SetColor", //入口函數(shù)名稱

                                                               "vs_1_1", //頂點(diǎn)著色器版本號(hào)

                                                               D3DXSHADER_DEBUG,// Debug模式編譯      

                                                               &shaderBuffer, //指向編譯后的著色器代碼的指針

                                                               &errorBuffer,

                                                               &BasicConstTable); //常量表指針

            1.4.3創(chuàng)建著色器

                   應(yīng)用程序通過CreateVertexShader創(chuàng)建一個(gè)頂點(diǎn)著色器,注意使用了上一步得到的shaderBuffer:

                   g_pd3dDevice->CreateVertexShader((DWORD*)shaderBuffer->GetBufferPointer(), &BasicShader);

            1.4.3對著色器中的變量進(jìn)行賦值

            1.3.4節(jié)說到著色器的全局變量在應(yīng)用程序中賦值而在著色器程序中使用,這是應(yīng)用程序和著色器通信的關(guān)鍵所在,這里就具體說明賦值過程。

            著色器中的全局變量在編譯后都被放在一個(gè)叫常量表的結(jié)構(gòu)中,我們可以使用ID3DXConstantTable接口對其進(jìn)行訪問,參照1.4.1中編譯著色器函數(shù)D3DXCompileShaderFromFile的最后一個(gè)參數(shù),該參數(shù)即返回了指向常量表的指針。

            對一個(gè)著色器中變量進(jìn)行賦值的步驟如下:

            1.       通過變量名稱得到指向著色器變量的句柄;

            還記得在BasicHLSL.x著色器文件中我們聲明的兩個(gè)全局變量嗎:

                    matrix WVPMatrix;

            vector color;

                          我們在應(yīng)用程序中相應(yīng)的聲明兩個(gè)句柄:

            D3DXHANDLE WVPMatrixHandle          = 0;

            D3DXHANDLE ColorHandle              = 0;

                          然后通過變量名得到分別得到對應(yīng)的兩個(gè)句柄:

                                 WVPMatrixHandle = BasicConstTable->GetConstantByName(0, "WVPMatrix");

                                 ColorHandle = BasicConstTable->GetConstantByName(0, "color");

            2.       通過句柄對著色器變量進(jìn)行賦值;

            我們可以先設(shè)置各變量為默認(rèn)值:

                    BasicConstTable->SetDefaults(g_pd3dDevice);

            之后,可以使用ID3DXConstantTable::SetXXX函數(shù)對各個(gè)變量進(jìn)行賦值:

            HRESULT SetXXX(

              LPDIRECT3DDEVICE9 pDevice,

              D3DXHANDLE hConstant,

              XXX value

            );

            其中XXX代表變量類型,例如Matrix類型的變量就要使用SetMatrix函數(shù)賦值,而Vector類型的則要使用SetVector來賦值。

            1.4.4把著色器設(shè)定到渲染管道中

                   這里我們使用SetVertexShader方法把頂點(diǎn)著色器設(shè)定到渲染管道中:

                          g_pd3dDevice->SetVertexShader(BasicShader);

            1.4.5整個(gè)渲染過程如下

            在渲染過程中,我們設(shè)定頂點(diǎn)的變換坐標(biāo)和顏色值,渲染代碼如下:

                g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,

            D3DCOLOR_XRGB(153,153,153), 1.0f, 0 );

                //開始渲染

                g_pd3dDevice->BeginScene();

             

                //得到世界矩陣、觀察矩陣和投影矩陣

                D3DXMATRIX matWorld, matView, matProj;

                g_pd3dDevice->GetTransform(D3DTS_WORLD, &matWorld);

                g_pd3dDevice->GetTransform(D3DTS_VIEW, &matView);

                g_pd3dDevice->GetTransform(D3DTS_PROJECTION, &matProj);

             

                D3DXMATRIX matWVP = matWorld * matView * matProj;

                //通過句柄對著色器中的WVPMatrix變量進(jìn)行賦值

                BasicConstTable->SetMatrix(g_pd3dDevice, WVPMatrixHandle, &matWVP);

               

                D3DXVECTOR4 color(1.0f, 1.0f, 0.0f, 1.0f);

                //通過句柄對著色器中的color變量進(jìn)行賦值,這里我們賦值為黃色

                BasicConstTable->SetVector(g_pd3dDevice, ColorHandle, &color);

             

                //把頂點(diǎn)著色器設(shè)定到渲染管道中

                g_pd3dDevice->SetVertexShader(BasicShader);

             

                //繪制模型子集

                Teapot->DrawSubset(0);

             

                //渲染完畢

                g_pd3dDevice->EndScene();

                g_pd3dDevice->Present(NULL, NULL, NULL, NULL);

             

                   編譯運(yùn)行程序,運(yùn)行效果如圖1.2所示,這里我們將頂點(diǎn)顏色設(shè)置為黃色,如果讀者在渲染過程中不斷變換對著色器變量color的賦值,你將會(huì)得到一個(gè)色彩不斷變幻的D3D茶壺。

            D3DXVECTOR4 color(1.0f, 1.0f, 0.0f, 1.0f); //讀者可以嘗試改變顏色值

            BasicConstTable->SetVector(g_pd3dDevice, ColorHandle, &color);

             

            圖1.2 著色器效果

             

            2.頂點(diǎn)著色器
            頂點(diǎn)著色器(vertex shader)是一個(gè)在顯卡的GPU上執(zhí)行的程序,它替換了固定功能管道(fixed function pipeline)中的變換(transformation)和光照(lighting)階段(這不是百分之百的正確,因?yàn)轫旤c(diǎn)著色器可以被Direct3D運(yùn)行時(shí)(Direct3D runtime)以軟件模擬,如果硬件不支持頂點(diǎn)著色器的話)。圖2.1說明了管線中頂點(diǎn)著色器替換的部件。

             

            圖2.1

            由于頂點(diǎn)著色器是我們(在HLSL中)寫的一個(gè)自定義程序,因此我們在圖形效果方面獲得了極大的自由性。我們不再受限于Direct3D的固定光照算法。此外,應(yīng)用程序操縱頂點(diǎn)位置的能力也有了多樣性,例如:布料仿真,粒子系統(tǒng)的點(diǎn)大小操縱,還有頂點(diǎn)混合/變形。此外,我們的頂點(diǎn)數(shù)據(jù)結(jié)構(gòu)更自由了,并且可以在可編程管線中包含比在固定功能管線中多的多的數(shù)據(jù)。

            正如作者所在群的公告所說,“拍照不在于你對相機(jī)使用的熟練程度,而是在于你對藝術(shù)的把握。”之前的介紹使讀者對著色器的編寫和使用都有了一定的了解,下面我們將把重心從介紹如何使用著色器轉(zhuǎn)到如何實(shí)現(xiàn)更高級的渲染效果上來。

             2.1可編程數(shù)據(jù)流模型

            DirectX 8.0引入了數(shù)據(jù)流的概念,可以這樣理解數(shù)據(jù)流(圖2.2):

             

            圖2.2

            ·       一個(gè)頂點(diǎn)由n個(gè)數(shù)據(jù)流組成。

            ·       一個(gè)數(shù)據(jù)流由m個(gè)元素組成。

            ·       一個(gè)元素是[位置、顏色、法向、紋理坐標(biāo)]。

            程序中使用IDirect3DDevice9::SetStreamSource方法把一個(gè)頂點(diǎn)緩存綁定到一個(gè)設(shè)備數(shù)據(jù)流。


            2.2頂點(diǎn)聲明
            該小節(jié)對頂點(diǎn)聲明的描述絕大多數(shù)都取自翁云兵的《著色器和效果》,該文對頂點(diǎn)聲明的描述是我所見到最詳盡最透徹的,這里向作者表示敬意:)

            到現(xiàn)在為止,我們已經(jīng)使用自由頂點(diǎn)格式(flexible vertex format,F(xiàn)VF)來描述頂點(diǎn)結(jié)構(gòu)中的各分量。但是,在可編程管線中,我們的頂點(diǎn)數(shù)據(jù)可以包含比用FVF所能表達(dá)的多的多的數(shù)據(jù)。因此,我們通常使用更具表達(dá)性的并且更強(qiáng)有力的頂點(diǎn)聲明(vertex declaration)。

            注意:我們?nèi)匀豢梢栽诳删幊坦芫€中使用FVF——如果我們的頂點(diǎn)格式可以這樣描述。不管怎樣,這只是為了方便,因?yàn)镕VF會(huì)在內(nèi)部被轉(zhuǎn)換為一個(gè)頂點(diǎn)聲明。

            2.2.1 描述頂點(diǎn)聲明

            我們將一個(gè)頂點(diǎn)聲明描述為一個(gè)D3DVERTEXELEMENT9結(jié)構(gòu)的數(shù)組。D3DVERTEXELEMENT9數(shù)組中的每個(gè)元素描述了一個(gè)頂點(diǎn)的分量。所以,如果你的頂點(diǎn)結(jié)構(gòu)有三個(gè)分量(例如:位置、法線、顏色),那么其相應(yīng)的頂點(diǎn)聲明將會(huì)被一個(gè)含3個(gè)元素的D3DVERTEXELEMENT9結(jié)構(gòu)數(shù)組描述。

            D3DVERTEXELEMENT9結(jié)構(gòu)定義如下:

            typedef struct _D3DVERTEXELEMENT9 {

                 BYTE Stream;

                 BYTE Offset;

                 BYTE Type;

                 BYTE Method;

                 BYTE Usage;

                 BYTE UsageIndex;

            } D3DVERTEXELEMENT9;

            ²        Stream——指定關(guān)聯(lián)到頂點(diǎn)分量的流;

            ²        Offset——偏移,按字節(jié),相對于頂點(diǎn)結(jié)構(gòu)成員的頂點(diǎn)分量的開始。例如,如果頂點(diǎn)結(jié)構(gòu)是:

            struct Vertex

            {

                 D3DXVECTOR3 pos;

                 D3DXVECTOR3 normal;

            };

            ……pos分量的偏移是0,因?yàn)樗堑谝粋€(gè)分量;normal分量的偏移是12,因?yàn)閟izeof(pos) == 12。換句話說,normal分量以Vertex的第12個(gè)字節(jié)為開始。

            ²        Type——指定數(shù)據(jù)類型。它可以是D3DDECLTYPE枚舉類型的任意成員;完整列表請參見文檔。常用類型如下:

            D3DDECLTYPE_FLOAT1——浮點(diǎn)數(shù)值

            D3DDECLTYPE_FLOAT2——2D浮點(diǎn)向量

            D3DDECLTYPE_FLOAT3——3D浮點(diǎn)向量

            D3DDECLTYPE_FLOAT4——4D浮點(diǎn)向量

            D3DDECLTYPE_D3DCOLOR—D3DCOLOR類型,它擴(kuò)展為RGBA浮點(diǎn)顏色向量(r, g, b, a),其每一分量都是歸一化到區(qū)間[0, 1]了的。

            ²        Method——指定網(wǎng)格化方法。我們認(rèn)為這個(gè)參數(shù)是高級的,因此我們使用默認(rèn)值,標(biāo)識(shí)為D3DDECLMETHOD_DEFAULT。

            ²        Usage——指定已計(jì)劃的對頂點(diǎn)分量的使用。例如,它是否準(zhǔn)備用于一個(gè)位置向量、法線向量、紋理坐標(biāo)等,有效的用途標(biāo)識(shí)符(usage identifier)是D3DDECLUSAGE枚舉類型的:

            typedef enum _D3DDECLUSAGE {

                 D3DDECLUSAGE_POSITION     = 0,  // Position.

                 D3DDECLUSAGE_BLENDWEIGHTS = 1,  // Blending weights.

                 D3DDECLUSAGE_BLENDINDICES = 2,  // Blending indices.

                 D3DDECLUSAGE_NORMAL       = 3,  // Normal vector.

                 D3DDECLUSAGE_PSIZE        = 4,  // Vertex point size.

                 D3DDECLUSAGE_TEXCOORD     = 5,  // Texture coordinates.

                 D3DDECLUSAGE_TANGENT      = 6,  // Tangent vector.

                 D3DDECLUSAGE_BINORMAL     = 7,  // Binormal vector.

                 D3DDECLUSAGE_TESSFACTOR   = 8,  // Tessellation factor.

                 D3DDECLUSAGE_POSITIONT    = 9,  // Transformed position.

                 D3DDECLUSAGE_COLOR        = 10, // Color.

                 D3DDECLUSAGE_FOG          = 11, // Fog blend value.

                 D3DDECLUSAGE_DEPTH        = 12, // Depth value.

                 D3DDECLUSAGE_SAMPLE       = 13  // Sampler data.

            } D3DDECLUSAGE;

            其中,D3DDECLUSAGE_PSIZE類型用于指定一個(gè)頂點(diǎn)的點(diǎn)的大小。它用于點(diǎn)精靈,因此我們可以基于每個(gè)頂點(diǎn)控制其大小。一個(gè)D3DDECLUSAGE_POSITION成員的頂點(diǎn)聲明意味著這個(gè)頂點(diǎn)已經(jīng)被變換,它通知圖形卡不要把這個(gè)頂點(diǎn)送到頂點(diǎn)處理階段(變形和光照)。

            ²        UsageIndex——用于標(biāo)識(shí)多個(gè)相同用途的頂點(diǎn)分量。這個(gè)用途索引是位于區(qū)間[0, 15]間的一個(gè)整數(shù)。例如,假設(shè)我們有三個(gè)用途為D3DDECLUSAGE_NORMAL的頂點(diǎn)分量。我們可以為第一個(gè)指定用途索引為0,為第二個(gè)指定用途索引為1,并且為第三個(gè)指定用途索引為2。按這種方式,我們可以通過其用途索引標(biāo)識(shí)每個(gè)特定的法線。

             

            例:假設(shè)我們想要描述的頂點(diǎn)格式由兩個(gè)數(shù)據(jù)流組成,第一個(gè)數(shù)據(jù)流包含位置、法線、紋理坐標(biāo)3個(gè)分量,第二個(gè)數(shù)據(jù)流包含位置和紋理坐標(biāo)2個(gè)分量,頂點(diǎn)聲明可以指定如下:

            D3DVERTEXELEMENT9 decl[] =

            {

            //第一個(gè)數(shù)據(jù)流,包含分量位置、法線、紋理坐標(biāo)

            { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT,D3DDECLUSAGE_

            POSITION, 0 },

            { 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_

            NORMAL, 0 },

            { 0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_

            TEXCOORD, 0 },

             

            //第一個(gè)數(shù)據(jù)流,包含分量位置、紋理坐標(biāo)

            { 1, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_

            POSITION, 1 },

            { 1, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_

            NORMAL, 1 },

            D3DDECL_END()

            };

            D3DDECL_END宏用于初始化D3DVERTEXELEMENT9數(shù)組的最后一個(gè)頂點(diǎn)元素。

             

            2.2.2創(chuàng)建頂點(diǎn)聲明

            CreateVertexDeclaration函數(shù)用于創(chuàng)建頂點(diǎn)聲明,decl為指向上一小節(jié)定義的D3DVERTEXELEMENT9數(shù)組的指針,函數(shù)返回IDirect3DVertexDeclaration9指針g_Decl;

            IDirect3DVertexDeclaration9   *g_Decl = NULL;

            g_pd3dDevice->CreateVertexDeclaration(decl ,&g_Decl);

            2.2.3設(shè)置頂點(diǎn)聲明

                   g_pd3dDevice->SetVertexDeclaration(g_Decl);

             

                   至此,可編程數(shù)據(jù)流模型、頂點(diǎn)聲明介紹完畢,在下面的例子中讀者將會(huì)有更連貫的理解。

             
            2.3用頂點(diǎn)著色器實(shí)現(xiàn)漸變動(dòng)畫
            2.3.1漸變動(dòng)畫(Morphing)

                   Morphing漸變是20世紀(jì)90年代出現(xiàn)的一種革命性的計(jì)算機(jī)圖形技術(shù),該技術(shù)使得動(dòng)畫序列平滑且易于處理,即使在低檔配置的計(jì)算機(jī)系統(tǒng)上也能正常運(yùn)行。

                   漸變是指隨時(shí)間的變化把一個(gè)形狀改變?yōu)榱硪粋€(gè)形狀。對我們而言,這些形狀就是Mesh網(wǎng)格模型。漸變網(wǎng)格模型的處理就是以時(shí)間軸為基準(zhǔn),逐漸地改變網(wǎng)格模型頂點(diǎn)的坐標(biāo),從一個(gè)網(wǎng)格模型的形狀漸變到另外一個(gè)。請看圖2.3:

             


             
            圖2.3

                   我們在程序中使用兩個(gè)網(wǎng)格模型——源網(wǎng)格模型和目標(biāo)網(wǎng)格模型,設(shè)源網(wǎng)格模型中頂點(diǎn)1的坐標(biāo)為A(Ax,Ay,Az),目標(biāo)網(wǎng)格模型中對應(yīng)頂點(diǎn)1的坐標(biāo)為B(Bx,By,Bz),要計(jì)算漸變過程中時(shí)間點(diǎn)t所對應(yīng)的頂點(diǎn)1的坐標(biāo)C(Cx,Cy,Cz),我們使用如下方法:

                   T為源網(wǎng)格模型到目標(biāo)網(wǎng)格模型漸變所花費(fèi)的全部時(shí)間,得到時(shí)間點(diǎn)t占整個(gè)過程T的比例為:

                   S = t / T

            那么頂點(diǎn)1在t時(shí)刻對應(yīng)的坐標(biāo)C為:

                   C = A * (1-S)+ B * S

                   這樣,在渲染過程中我們根據(jù)時(shí)間不斷調(diào)整S的值,就得到了從源網(wǎng)格模型(形狀一)到目標(biāo)網(wǎng)格模型(形狀二)的平滑過渡。

                   接下來將在程序里使用頂點(diǎn)著色器實(shí)現(xiàn)我們的漸變動(dòng)畫。

             

            2.3.2漸變動(dòng)畫中的頂點(diǎn)聲明

                   程序中,我們設(shè)定一個(gè)頂點(diǎn)對應(yīng)兩個(gè)數(shù)據(jù)流,這兩個(gè)數(shù)據(jù)流分別包含了源網(wǎng)格模型的數(shù)據(jù)和目標(biāo)網(wǎng)格模型的數(shù)據(jù)。渲染過程中,我們在著色器里根據(jù)兩個(gè)數(shù)據(jù)流中的頂點(diǎn)數(shù)據(jù)以及時(shí)間值確定最終的頂點(diǎn)信息。

            個(gè)數(shù)據(jù)流包含分量如下:

            源網(wǎng)格模型數(shù)據(jù)流:頂點(diǎn)位置、頂點(diǎn)法線、紋理坐標(biāo);

            目標(biāo)網(wǎng)格模型數(shù)據(jù)流:頂點(diǎn)位置、頂點(diǎn)法線;

            注意目標(biāo)網(wǎng)格模型數(shù)據(jù)流沒有包含紋理坐標(biāo),因?yàn)榧y理對于兩個(gè)網(wǎng)格模型都是一樣的,所以僅使用源網(wǎng)格模型的紋理就可以了。

            頂點(diǎn)聲明指定如下:

            D3DVERTEXELEMENT9 decl[] =

            {

            //源網(wǎng)格模型數(shù)據(jù)流,包含分量位置、法線、紋理坐標(biāo)

            { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT,D3DDECLUSAGE_

            POSITION, 0 },

            { 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_

            NORMAL, 0 },

            { 0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_

            TEXCOORD, 0 },

             

            //目標(biāo)網(wǎng)格模型數(shù)據(jù)流,包含分量位置、紋理坐標(biāo)

            { 1, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_

            POSITION, 1 },

            { 1, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_

            NORMAL, 1 },

            D3DDECL_END()

            };

             

            2.3.3漸變動(dòng)畫中的頂點(diǎn)著色器

            下面給出頂點(diǎn)著色器源碼,代碼存儲(chǔ)于vs.txt中,該頂點(diǎn)著色器根據(jù)源網(wǎng)格模型數(shù)據(jù)流和目標(biāo)網(wǎng)格模型數(shù)據(jù)流中的信息以及時(shí)間標(biāo)尺值計(jì)算出頂點(diǎn)最終位置信息,并對頂點(diǎn)做了坐標(biāo)變換和光照處理。代碼中給出了詳細(xì)的注釋,幫助讀者理解。

            //全局變量

            //世界矩陣、觀察矩陣、投影矩陣的合矩陣,用于頂點(diǎn)的坐標(biāo)變換

            matrix WVPMatrix;

             

            //光照方向

            vector LightDirection;

            //存儲(chǔ)2.3.1小節(jié)提到的公式S = t / T中的時(shí)間標(biāo)尺S值

            //注意到Scalar是一個(gè)vector類型,我們在Scalar.x中存儲(chǔ)了S值,Scalar.y中存儲(chǔ)的則是(1-S)值

            vector Scalar;

             

            //輸入

            struct VS_INPUT

            {

                   //對應(yīng)源網(wǎng)格模型數(shù)據(jù)流中的頂點(diǎn)分量:位置、法線、紋理坐標(biāo)

                vector position : POSITION;

                vector normal   : NORMAL;

            float2 uvCoords : TEXCOORD;

            //對應(yīng)目標(biāo)網(wǎng)格模型數(shù)據(jù)流中的頂點(diǎn)分量:位置、法線

                vector position1 : POSITION1;

                vector normal1   : NORMAL1;

            };

             

            //輸出

            struct VS_OUTPUT

            {

                vector position : POSITION;

                vector diffuse  : COLOR;

                float2 uvCoords : TEXCOORD;

            };

             

            //入口函數(shù)

            VS_OUTPUT Main(VS_INPUT input)

            {

                VS_OUTPUT output = (VS_OUTPUT)0;

               

                   //頂點(diǎn)最終位置output.position取決于源網(wǎng)格模型數(shù)據(jù)流中位置信息input.position和目標(biāo)網(wǎng)格模型數(shù)據(jù)流中位置信息input.position1以及時(shí)間標(biāo)尺Scalar的值

                   //對應(yīng)2.3.1小節(jié)中的公式C = A * (1-S)+ B * S

                output.position = input.position*Scalar.x + input.position1*Scalar.y;

                   //頂點(diǎn)坐標(biāo)變換操作

                output.position = mul(output.position, WVPMatrix);

               

                   //計(jì)算頂點(diǎn)最終法線值

                vector normal = input.normal*Scalar.x + input.normal1*Scalar.y;

                   //逆光方向與法線的點(diǎn)積,獲得漫射色彩

                output.diffuse = dot((-LightDirection), normal);

               

                   //存儲(chǔ)紋理坐標(biāo)

                output.uvCoords = input.uvCoords;

               

                return output;

            }

                   以上是本例用到的頂點(diǎn)著色器,在接下來的應(yīng)用程序中,我們將給三個(gè)著色器全局變量賦值:

            ²        WVPMatrix;

            世界矩陣、觀察矩陣、投影矩陣的合矩陣,用于頂點(diǎn)的坐標(biāo)變換;

            ²        LightDirection

            光照方向;

            ²        Scalar

            存儲(chǔ)2.3.1小節(jié)提到的公式S = t / T中的時(shí)間標(biāo)尺S值;

            注意到Scalar是一個(gè)vector類型,我們在Scalar.x中存儲(chǔ)了S值,Scalar.y中存儲(chǔ)的則是(1-S)值;

             

            2.3.4應(yīng)用程序

            我們在應(yīng)用程序中執(zhí)行以下操作:

            ·         加載兩個(gè)兩個(gè)Mesh模型:源網(wǎng)格模型,目標(biāo)網(wǎng)格模型;

            ·         創(chuàng)建、設(shè)置頂點(diǎn)聲明;

            ·         創(chuàng)建、設(shè)置頂點(diǎn)著色器;

            ·         為著色器全局賦值;

            ·         把兩個(gè)Mesh模型數(shù)據(jù)分別綁定到兩個(gè)數(shù)據(jù)流中;

            ·         渲染Mesh模型;

            下面是應(yīng)用程序代碼:

            /*********************聲明變量*****************/

            //兩個(gè)指向LPD3DXMESH的指針,分別用于存儲(chǔ)源網(wǎng)格模型和目標(biāo)網(wǎng)格模型;

            LPD3DXMESH                      g_SourceMesh;

            LPD3DXMESH                                     g_TargetMesh;

             

            //頂點(diǎn)聲明指針

            IDirect3DVertexDeclaration9   *g_Decl = NULL;

             

            //頂點(diǎn)著色器

            IDirect3DVertexShader9        *g_VS   = NULL;

            //常量表

            ID3DXConstantTable* ConstTable = NULL;

             

            //常量句柄

            D3DXHANDLE WVPMatrixHandle          = 0;

            D3DXHANDLE ScalarHandle                  = 0;

            D3DXHANDLE LightDirHandle                = 0;

            /***************程序初始化*****************/

            //加載源、目標(biāo)網(wǎng)格模型

            Load_Meshes();

             

            //頂點(diǎn)聲明

            D3DVERTEXELEMENT9 MorphMeshDecl[] =

            {

                   //1st stream is for source mesh - position, normal, texcoord

                   { 0,  0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },

                   { 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL,   0 },

                   { 0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },

             

                   //2nd stream is for target mesh - position, normal

                   { 1,  0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 1 },

                   { 1, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL,   1 },

                   D3DDECL_END()

            };

             

            //創(chuàng)建頂點(diǎn)著色器

            ID3DXBuffer* shader      = NULL;

            ID3DXBuffer* errorBuffer  = NULL;

            D3DXCompileShaderFromFile("vs.txt",

                                                        0,

                                                        0,

                                                     "Main", // entry point function name

                                                    "vs_1_1",

                                                       D3DXSHADER_DEBUG,

                                                     &shader,

                                                        &errorBuffer,

                                                      &ConstTable);

                  

            if(errorBuffer)

            {

                   ::MessageBox(0, (char*)errorBuffer->GetBufferPointer(), 0, 0);

                   ReleaseCOM(errorBuffer);

            }

             

            //創(chuàng)建頂點(diǎn)著色器

            g_pd3dDevice->CreateVertexShader((DWORD*)shader->GetBufferPointer(), &g_VS);

             

            //創(chuàng)建頂點(diǎn)聲明

            g_pd3dDevice->CreateVertexDeclaration(MorphMeshDecl ,&g_Decl);

             

            //得到各常量句柄

            WVPMatrixHandle = ConstTable->GetConstantByName(0, "WVPMatrix");

            ScalarHandle = ConstTable->GetConstantByName(0, "Scalar");

            LightDirHandle = ConstTable->GetConstantByName(0, "LightDirection");

             

            //為著色器全局變量LightDirection賦值

            ConstTable->SetVector(g_pd3dDevice, LightDirHandle, &D3DXVECTOR4(0.0f, -1.0f, 0.0f, 0.0f));

            //設(shè)置各著色器變量為默認(rèn)值

            ConstTable->SetDefaults(g_pd3dDevice);

            /*******************渲染*******************/

            g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,

            D3DCOLOR_XRGB(153,153,153), 1.0f, 0 );

            g_pd3dDevice->BeginScene();

             

            //為著色器全局變量WVPMatrix賦值

            D3DXMATRIX matWorld, matView, matProj;

            g_pd3dDevice->GetTransform(D3DTS_WORLD, &matWorld);

            g_pd3dDevice->GetTransform(D3DTS_VIEW, &matView);

            g_pd3dDevice->GetTransform(D3DTS_PROJECTION, &matProj);

            D3DXMATRIX matWVP;

            matWVP = matWorld * matView * matProj;

             

            ConstTable->SetMatrix(g_pd3dDevice, WVPMatrixHandle, &matWVP);

             

            //為著色器全局變量Scalar賦值,注意程序中獲取時(shí)間標(biāo)尺值Scalar的方法

            float DolphinTimeFactor = (float)(timeGetTime() % 501) / 250.0f;

            float Scalar =

            (DolphinTimeFactor<=1.0f)?DolphinTimeFactor:(2.0f-DolphinTimeFactor);

            ConstTable->SetVector(g_pd3dDevice,ScalarHandle,&D3DXVECTOR4(1.0f-Scalar, Scalar, 0.0f, 0.0f));

             

            //設(shè)置頂點(diǎn)著色器和頂點(diǎn)聲明

            g_pd3dDevice->SetVertexShader(g_VS);

            g_pd3dDevice->SetVertexDeclaration(g_Decl);

             

            //綁定目標(biāo)網(wǎng)格模型的定點(diǎn)緩存到第二個(gè)數(shù)據(jù)流中

            IDirect3DVertexBuffer9 *pVB = NULL;

            g_TargetMesh->GetVertexBuffer(&pVB);

            g_pd3dDevice->SetStreamSource(1, pVB, 0,

            D3DXGetFVFVertexSize(g_TargetMesh->GetFVF()));

            ReleaseCOM(pVB);

             

            //綁定源網(wǎng)格模型的頂點(diǎn)緩存到第一個(gè)數(shù)據(jù)流中

            g_SourceMesh->GetVertexBuffer(&pVB);

            g_pd3dDevice->SetStreamSource(0, pVB, 0,

            D3DXGetFVFVertexSize(g_TargetMesh->GetFVF()));

            ReleaseCOM(pVB);

             

            //繪制Mesh網(wǎng)格模型

            DrawMesh(g_SourceMesh, g_pMeshTextures0, g_VS, g_Decl);

             

            g_pd3dDevice->EndScene();

            g_pd3dDevice->Present( NULL, NULL, NULL, NULL );

             

            2.3.5對應(yīng)用程序的一點(diǎn)說明

            程序中我們使用SetStreamSource方法把源網(wǎng)格模型和目標(biāo)網(wǎng)格模型中的頂點(diǎn)緩存分別綁定到兩個(gè)設(shè)備數(shù)據(jù)流,但是Direct3D對數(shù)據(jù)流中的數(shù)據(jù)的真正引用只有在調(diào)用諸如DrawPrimitive、DrawIndexedPrimitive之類的繪制方法時(shí)才發(fā)生,因此在繪制Mesh網(wǎng)格模型時(shí)我們不能再使用傳統(tǒng)的DrawSubmit方法,而是使用了DrawIndexedPrimitive,下面就如何調(diào)用DrawIndexedPrimitive繪制Mesh模型進(jìn)行說明,該部分內(nèi)容和HLSL著色器關(guān)系不大,在這里列出僅僅是為了大家理解程序的完整性,讀者完全可以跳過本節(jié)不看。

                   使用DrawIndexedPrimitive繪制Mesh模型的步驟如下:

                   1. 加載網(wǎng)格模型后使用OptimizeInPlace方法對Mesh進(jìn)行優(yōu)化;

                   2. 一旦優(yōu)化了網(wǎng)格模型,你就可以查詢ID3DXMesh對象,得到一個(gè)D3DXATTRIBUTERANGE數(shù)據(jù)類型的數(shù)組,我們稱之為屬性列表,該數(shù)據(jù)類型被定義如下:

                          typedef struct_D3DXATTRIBUTERANGE{

                                 DWORD AttribId; //子集編號(hào)

                                 DWORD FaceStart; //這兩個(gè)變量用于圈定本子集中的多邊形

                                 DWORD FaceCount;

                                 DWORD VertexStart; //這兩個(gè)變量用于圈定本子集中的頂點(diǎn)

                                 DWORD VertexCount;

                          } D3DXATTRIBUTERANGE;

                          我們屬性列表中的每一項(xiàng)都代表一個(gè)被優(yōu)化后Mesh的一個(gè)子集,D3DXATTRIBUTERANGE結(jié)構(gòu)的各字段描述了該子集的信息。

            1.       得到屬性數(shù)據(jù)后,我們就調(diào)用DrawIndexedPrimitive方法可以精美地渲染子集了。

             

                   下面是繪制Mesh模型的程序代碼:

                   在Load_Meshes()函數(shù)的最后,我們使用OptimizeInPlace方法對源網(wǎng)格模型和目標(biāo)網(wǎng)格模型進(jìn)行優(yōu)化,其他加載材質(zhì)和紋理的操作和之前一樣,相信大家能夠理解:

                   …

                   //優(yōu)化源網(wǎng)格模型

                   g_SourceMesh->OptimizeInplace(D3DXMESHOPT_ATTRSORT, NULL, NULL, NULL, NULL);

                   …

                   //優(yōu)化目標(biāo)網(wǎng)格模型

            g_TargetMesh->OptimizeInplace(D3DXMESHOPT_ATTRSORT, NULL, NULL, NULL, NULL);

                   …

             

                   在Draw_Mesh()函數(shù)中,渲染模型,注意程序是如何配合屬性表調(diào)用DrawIndexedPrimitive方法進(jìn)行繪制的:

                   …

                  

                   //分別得到指向Mesh模型頂點(diǎn)緩存區(qū)和索引緩存區(qū)的指針

                   IDirect3DVertexBuffer9 *pVB = NULL;

                   IDirect3DIndexBuffer9 *pIB  = NULL;

                   pMesh->GetVertexBuffer(&pVB);

                   pMesh->GetIndexBuffer(&pIB);

             

                   //得到Mesh模型的屬性列表

                   DWORD NumAttributes;

                   D3DXATTRIBUTERANGE *pAttributes = NULL;

                   pMesh->GetAttributeTable(NULL, &NumAttributes);

                   pAttributes = new D3DXATTRIBUTERANGE[NumAttributes];

                   pMesh->GetAttributeTable(pAttributes, &NumAttributes);

             

                   //設(shè)置頂點(diǎn)著色器和頂點(diǎn)聲明

                   g_pd3dDevice->SetVertexShader(pShader);

                   g_pd3dDevice->SetVertexDeclaration(pDecl);

             

                   //設(shè)置數(shù)據(jù)流

                   g_pd3dDevice->SetStreamSource(0, pVB, 0, D3DXGetFVFVertexSize(pMesh->GetFVF()));

                   g_pd3dDevice->SetIndices(pIB);

             

                   //遍歷屬性列表并配合其中的信息調(diào)用DrawIndexPrimitive繪制各個(gè)子集

                   for(DWORD i=0;i<NumAttributes;i++)

                   {

                          if(pAttributes[i].FaceCount)

                          {

                                 //Get material number

                                 DWORD MatNum = pAttributes[i].AttribId;

             

                                 //Set texture

                                 g_pd3dDevice->SetTexture(0, pTextures[MatNum]);

             

                                 //Draw the mesh subset

                                 g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0,

                                                                                          pAttributes[i].VertexStart,

                                                                                          pAttributes[i].VertexCount,

                                                                                          pAttributes[i].FaceStart * 3,

                                                                                          pAttributes[i].FaceCount);

                          }

                   }

                  

                   //Free resources

                   ReleaseCOM(pVB);

                   ReleaseCOM(pIB);

                   delete [] pAttributes;

             

                   …

             

                   編譯運(yùn)行程序,效果如圖2.4所示,你將看到屏幕上白色的海豚上下翻騰,同時(shí)感受到頂點(diǎn)著色器為渲染效果所帶來的巨大改善。

             

             

             

             

            圖2.4

             

            3.像素著色器
                   像素著色器是在對每個(gè)像素進(jìn)行光柵化處理期間在圖形卡的GPU上執(zhí)行的程序。(不像頂點(diǎn)著色器,Direct3D不會(huì)以軟件模擬像素著色器的功能。)它實(shí)際上替換了固定功能管線的多紋理化階段(the multitexturing stage),并賦予我們直接操縱單獨(dú)的像素和訪問每個(gè)像素的紋理坐標(biāo)的能力。這種對像素和紋理坐標(biāo)的直接訪問使我們可以達(dá)成各種特效,例如:多紋理化(multitexturing)、每像素光照(per pixel lighting)、景深(depth of field)、云狀物模擬(cloud simulation)、焰火模擬(fire simulation)、混雜陰影化技巧(sophisticated shadowing technique)。

                   像素著色器的編寫、使用和頂點(diǎn)著色器大同小異,有了之前的基礎(chǔ),不用太過于詳細(xì)的介紹相信讀者也能理解,下面使用像素著色器實(shí)現(xiàn)多紋理化。

             

            3.1多紋理化
                   簡單的說,多紋理化就是使用多個(gè)紋理貼圖混合后進(jìn)行渲染,如圖3.1,渲染過程中,從紋理1和紋理2中分別采樣,得到的顏色值依據(jù)一定規(guī)則進(jìn)行組合得到紋理3,這就是多紋理化。

             

            圖3.1

             

            3.2多紋理效果的像素著色器
                   下面是像素著色器的代碼,該代碼存儲(chǔ)于ps.txt中,該像素著色器根據(jù)輸入的兩套紋理坐標(biāo)對對應(yīng)的紋理貼圖進(jìn)行采樣,根據(jù)一定比例Scalar混合后輸出像素顏色。

            //全局變量

             

            //存儲(chǔ)顏色混合的比例值s,其中

            //Scalar.x = s

            //Scalar.y = 1-s

            vector Scalar;

             

            //紋理

            texture Tex0;

            texture Tex1;

             

            //紋理采樣器

            sampler Samp0 =

            sampler_state

            {

                Texture = <Tex0>;

                MipFilter = LINEAR;

                MinFilter = LINEAR;

                MagFilter = LINEAR;

            };

             

            sampler Samp1 =

            sampler_state

            {

                Texture = <Tex1>;

                MipFilter = LINEAR;

                MinFilter = LINEAR;

                MagFilter = LINEAR;

            };

             

            //輸入兩套紋理坐標(biāo)

            struct PS_INPUT

            {

                   float2 uvCoords0 : TEXCOORD0;

                   float2 uvCoords1 : TEXCOORD1;

            };

             

            //輸出像素顏色

            struct PS_OUTPUT

            {

                   float4 Color : COLOR0;

            };

             

            //入口函數(shù)

            PS_OUTPUT PS_Main(PS_INPUT input)

            {

                   PS_OUTPUT output = (PS_OUTPUT)0;

                   //分別對兩個(gè)紋理進(jìn)行采樣按照比例混合后輸出顏色值

                   output.Color = tex2D(Samp0, input.uvCoords0)*Scalar.x + tex2D(Samp1, input.uvCoords1)*Scalar.y;

                   return output;

            }

                   整個(gè)程序很容易理解,程序中涉及到著色器的紋理和采樣,是我們第一次接觸的內(nèi)容,下面給于說明。

            3.2.1HLSL采樣器和紋理

                   和vector、matrix一樣,采樣器sample和紋理texture也是HLSL語言的一種類型,HLSL著色器使用采樣器對指定紋理進(jìn)行采樣,得到采樣后的顏色值以供處理。

                   它們的用法如下:

                   //聲明一個(gè)紋理變量

                   texture g_texture;

             

                   //定義采樣器

                   sampler g_samp =

                   sampler_state

                   {

                          //關(guān)聯(lián)到紋理

                   Texture = <g_texture>;

                   //設(shè)置采樣器狀態(tài)

                       MipFilter = LINEAR;

                       MinFilter = LINEAR;

                       MagFilter = LINEAR;

                   };

             

                   //調(diào)用HLSL內(nèi)置函數(shù)tex2D取得顏色值,參數(shù)一為采樣器,參數(shù)二為紋理坐標(biāo)

                   vector Color = tex2D(g_samp, uvCoords);

                   更多HLSL采樣器和紋理的內(nèi)容請參見DirectX文檔。

             

                   以上是本例用到的像素著色器,在接下來的應(yīng)用程序中,我們將給三個(gè)著色器全局變量賦值:

            ²        Scalar

                          存儲(chǔ)顏色混合的比例值s,其中Scalar.x = s, Scalar.y = 1-s;

            ²        Samp0

                          第一層紋理采樣器;

            ²        Samp1

                          第二層紋理采樣器;

                   像素著色器的輸入結(jié)構(gòu)中我們設(shè)定了一個(gè)頂點(diǎn)對應(yīng)兩套紋理坐標(biāo),讀者可以留意一下應(yīng)用程序中對應(yīng)的頂點(diǎn)格式的定義。

             


            3.3應(yīng)用程序

                   程序中我們首先創(chuàng)建一個(gè)四邊形,然后使用像素著色器進(jìn)行紋理混合后對其進(jìn)行渲染。下面是應(yīng)用程序代碼:

            /*********************頂點(diǎn)格式定義*****************/

            struct CUSTOMVERTEX

            {

                   //定點(diǎn)位置坐標(biāo)

                   float x,y,z;

                   //兩套紋理坐標(biāo);

                   float tu0, tv0;

                   float tu1, tv1;

            };

            #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_TEX2)

            /*********************聲明變量*****************/

            //頂點(diǎn)著色器

            LPDIRECT3DPIXELSHADER9 pixelShader   = 0;

            //常量表

            ID3DXConstantTable* pixelConstTable  = 0;

             

            //常量句柄

            D3DXHANDLE ScalarHandle              = 0;

            D3DXHANDLE Samp0Handle                = 0;

            D3DXHANDLE Samp1Handle                = 0;

             

            //常量描述結(jié)構(gòu)

            D3DXCONSTANT_DESC Samp0Desc;

            D3DXCONSTANT_DESC Samp1Desc;

             

            //四邊形頂點(diǎn)緩存

            LPDIRECT3DVERTEXBUFFER9 quadVB  = NULL;

            //兩個(gè)紋理

            LPDIRECT3DTEXTURE9 quadTexture0 = NULL;

            LPDIRECT3DTEXTURE9 quadTexture1 = NULL;

             

            /********************初始化應(yīng)用程序*****************/

            //創(chuàng)建四邊形頂點(diǎn)模型

            CUSTOMVERTEX quad[] =

            //  x      y      z    tu0   tv0   tu1   tv1

            {{-3.0f, -3.0f, 10.0f, 0.0f, 1.0f, 0.0f, 1.0f},

            { -3.0f,  3.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f},

            {  3.0f, -3.0f, 10.0f, 1.0f, 1.0f, 1.0f, 1.0f},

            {  3.0f,  3.0f, 10.0f, 1.0f, 0.0f, 1.0f, 0.0f}};

             

            //創(chuàng)建頂點(diǎn)緩存

            void *ptr = NULL;

            g_pd3dDevice->CreateVertexBuffer(sizeof(quad),

                                                                     D3DUSAGE_WRITEONLY,

                                                                     0,

                                                                     D3DPOOL_MANAGED,

                                                                     &quadVB,

                                                                     NULL);

            quadVB->Lock(0, 0, (void**)&ptr, 0);

            memcpy((void*)ptr, (void*)quad, sizeof(quad));

            quadVB->Unlock();

             

            //創(chuàng)建紋理

            D3DXCreateTextureFromFile(g_pd3dDevice, "porpcart.jpg", &quadTexture0);

            D3DXCreateTextureFromFile(g_pd3dDevice, "luoqi.jpg", &quadTexture1);

             

            //檢測系統(tǒng)是否支持像素著色器

            D3DCAPS9 caps;

            g_pd3dDevice->GetDeviceCaps(&caps);

            if(caps.PixelShaderVersion < D3DPS_VERSION(1, 1))

            {

                   MessageBox(0, "NotSupport Pixel Shader - FAILED", 0, 0);

                   exit(0);

            }

             

            //創(chuàng)建像素著色器

            ID3DXBuffer* codeBuffer        = 0;

            ID3DXBuffer* errorBuffer       = 0;

             

            HRESULT hr = D3DXCompileShaderFromFile("ps.txt",

                                                                              0,

                                                                              0,

                                                                              "PS_Main", // entry point function name

                                                                              "ps_1_1",

                                                                              D3DXSHADER_DEBUG,

                                                                              &codeBuffer,

                                                                              &errorBuffer,

                                                                              &pixelConstTable);

             

            // output any error messages

            if(errorBuffer)

            {

                   MessageBox(0, (char*)errorBuffer->GetBufferPointer(), 0, 0);

                   ReleaseCOM(errorBuffer);

            }

             

            if(FAILED(hr))

            {

                   MessageBox(0, "D3DXCompileShaderFromFile() - FAILED", 0, 0);

                   return false;

            }

             

             

            hr = g_pd3dDevice->CreatePixelShader((DWORD*)codeBuffer->GetBufferPointer(), &pixelShader);

             

            if(FAILED(hr))

            {

                   MessageBox(0, "CreatePixelShader - FAILED", 0, 0);

                   return false;

            }

             

            ReleaseCOM(codeBuffer);

            ReleaseCOM(errorBuffer);

             

            //得到各常量句柄

            ScalarHandle = pixelConstTable->GetConstantByName(0, "Scalar");

            Samp0Handle = pixelConstTable->GetConstantByName(0, "Samp0");

            Samp1Handle = pixelConstTable->GetConstantByName(0, "Samp1");

             

            //得到對著色器變量Samp0、Samp0的描述

            UINT count;

            pixelConstTable->GetConstantDesc(Samp0Handle, & Samp0Desc, &count);

            pixelConstTable->GetConstantDesc(Samp1Handle, & Samp1Desc, &count);

             

            //設(shè)定各著色器變量為初始值

            pixelConstTable->SetDefaults(g_pd3dDevice);

            /********************渲染*****************/

            g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(153,153,153), 1.0f, 0 );

            g_pd3dDevice->BeginScene();

             

            //為著色器全局變量Scalar賦值

            D3DXVECTOR4 scalar(0.5f, 0.5f, 0.0f, 1.0f);

            pixelConstTable->SetVector(g_pd3dDevice, ScalarHandle, &scalar);

             

            //設(shè)置像素著色器

            g_pd3dDevice->SetPixelShader(pixelShader);

             

            //設(shè)置定點(diǎn)格式、綁定數(shù)據(jù)流

            g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);

            g_pd3dDevice->SetStreamSource(0, quadVB, 0, sizeof(CUSTOMVERTEX));

             

            //設(shè)置第一、二層紋理

            g_pd3dDevice->SetTexture(Samp0Desc.RegisterIndex, quadTexture0);

            g_pd3dDevice->SetTexture(Samp1Desc.RegisterIndex, quadTexture1);

             

            //繪制圖形

            g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);

             

            g_pd3dDevice->EndScene();

            g_pd3dDevice->Present(NULL, NULL, NULL, NULL);

             

                   程序中像素著色器的使用和之前頂點(diǎn)著色器的使用無二,只是設(shè)置著色器中紋理采樣器變量Samp0、Samp1和設(shè)定著色器其他變量稍有不同:

                1. 首先通過變量名稱得到變量句柄:

                          Tex0Handle = pixelConstTable->GetConstantByName(0, " Samp0");

                          Tex1Handle = pixelConstTable->GetConstantByName(0, " Samp1");

                2. 然后通過句柄得到對變量的描述:

                          UINT count;

                          pixelConstTable->GetConstantDesc(Samp0Handle, & Samp0Desc, &count);

                          pixelConstTable->GetConstantDesc(Samp1Handle, & Samp1Desc, &count);

                3.最后通過SetTexture配合所得到的描述信息設(shè)置紋理:

                          g_pd3dDevice->SetTexture(Samp0Desc.RegisterIndex, quadTexture0);

                          g_pd3dDevice->SetTexture(Samp1Desc.RegisterIndex, quadTexture1);

             

                   編譯運(yùn)行程序,運(yùn)行效果如圖3.2,這里我們將顏色混合比例設(shè)置為0.5,如果讀者在渲染過程中不斷變換對著色器變量Scalar的賦值,你將會(huì)得到一個(gè)混合度不斷變換的多紋理效果。

                   D3DXVECTOR4 scalar(0.5f, 0.5f, 0.0f, 1.0f); //讀者可以嘗試改變混合采用的比例值

                   pixelConstTable->SetVector(g_pd3dDevice, ScalarHandle, &scalar);


             

            紋理一

             

            紋理二

             

            混合后紋理三

            圖3.2

            4.HLSL Effect(效果框架)
                   進(jìn)行到這里,讀者可能會(huì)覺得使用著色器多少有些繁瑣,Effect(效果框架)被提出以解決這些問題。作為一種方法,Effect簡化了使用著色器的操作;作為一個(gè)框架,Effect把頂點(diǎn)著色器和像素著色器有機(jī)地組織了起來。


            4.1Effect代碼結(jié)構(gòu)

                   一個(gè)Effect效果代碼的結(jié)構(gòu)如下:

                   //effect

                   technique T0

                   {

                       pass P0

                       {

                           ...

                          }

                   }

             

                   technique T1

                   {

                       pass P0

                       {

                           ...

                          }

                       pass P1

                       {

                           ...

                          }

                   }

             

                   ...

                   technique Tn

                   {

                       pass P0

                       {

                           ...

                          }

                   }

                   首先理解三個(gè)術(shù)語effect(效果)、technique(技術(shù))、pass(過程),所幸這三個(gè)術(shù)語從字面意思上就能得到很好的詮釋。

                   要實(shí)現(xiàn)一種效果effect,可以使用多種技術(shù)technique,而每種技術(shù)中可能使用多個(gè)過程pass進(jìn)行渲染,這樣就構(gòu)成了上述effect包含多個(gè)technique,technique又包含多個(gè)pass的代碼結(jié)構(gòu)。

                   理解了代碼結(jié)構(gòu),effect知識(shí)就已經(jīng)掌握了大半,下面我們直接使用一個(gè)程序?qū)嵗龑ffect進(jìn)行介紹。

             

            4.2用Effect實(shí)現(xiàn)多紋理化效果

                   前面我們介紹了一個(gè)使用像素著色器實(shí)現(xiàn)的多紋理化,這里用Effect框架重新給于實(shí)現(xiàn),讀者可以比較兩者之間的異同,體會(huì)Effect框架給我們帶來了哪些方面的改善。

            4.2.1著色器

                   下面是著色器代碼,該代碼存儲(chǔ)于Effect.txt中,代碼中包含了一個(gè)頂點(diǎn)著色器和一個(gè)像素著色器和一個(gè)Effect效果框架。

            //---------------------------------------------

            //          頂點(diǎn)著色器

            //---------------------------------------------

            matrix WVPMatrix;

             

            struct VS_INPUT

            {

                vector position : POSITION;

                float2 uvCoords0 : TEXCOORD0;

                float2 uvCoords1 : TEXCOORD1;

            };

             

            struct VS_OUTPUT

            {

                vector position : POSITION;

                float2 uvCoords0 : TEXCOORD0;

                float2 uvCoords1 : TEXCOORD1;

            };

             

            VS_OUTPUT VS_Main(VS_INPUT input)

            {

                   VS_OUTPUT output = (VS_OUTPUT)0;

                  

                   output.position = mul(input.position, WVPMatrix);

                  

                   output.uvCoords0 = input.uvCoords0;

                   output.uvCoords1 = input.uvCoords1;

                  

                   return output;

            }

             

            //---------------------------------------------

            //          像素著色器

            //---------------------------------------------

            vector Scalar;

             

            texture Tex0;

            texture Tex1;

             

            sampler Samp0 =

            sampler_state

            {

                Texture = <Tex0>;

                MipFilter = LINEAR;

                MinFilter = LINEAR;

                MagFilter = LINEAR;

            };

             

            sampler Samp1 =

            sampler_state

            {

                Texture = <Tex1>;

                MipFilter = LINEAR;

                MinFilter = LINEAR;

                MagFilter = LINEAR;

            };

             

            struct PS_INPUT

            {

                   float2 uvCoords0 : TEXCOORD0;

                   float2 uvCoords1 : TEXCOORD1;

            };

             

            struct PS_OUTPUT

            {

                   float4 Color : COLOR0;

            };

             

            PS_OUTPUT PS_Main(PS_INPUT input)

            {

                   PS_OUTPUT output = (PS_OUTPUT)0;

                   output.Color = tex2D(Samp0, input.uvCoords0)*Scalar.x + tex2D(Samp1, input.uvCoords1)*Scalar.y;

                   return output;

            }

             

            //---------------------------------------------

            //          效果框架

            //---------------------------------------------

            technique T0

            {

                   pass P0

                   {

                          vertexShader = compile vs_1_1 VS_Main();

                          pixelShader = compile ps_1_1 PS_Main();

                   }

            }

                   注意程序中是如何使用效果框架將頂點(diǎn)著色器和像素著色器組織起來的:

                   pass P0

                   {

                          //著色器類型        版本號(hào) 入口函數(shù)名稱

                          vertexShader = compile vs_1_1 VS_Main();

                          pixelShader = compile ps_1_1 PS_Main();

                   }

                   也可以直接將著色代碼寫在pass過程中,相關(guān)用法請讀者參看DirectX文檔:

                   pass P0

                   {

                          //這里書寫著色器代碼

                          …

                   }

                   有了之前的基礎(chǔ),著色器代碼讀者應(yīng)該很容易理解,下面具體介紹如何在應(yīng)用程序中使用Effect。

            4.2.2應(yīng)用程序

            /*********************頂點(diǎn)格式定義*****************/

            struct CUSTOMVERTEX

            {

                   //定點(diǎn)位置坐標(biāo)

                   float x,y,z;

                   //兩套紋理坐標(biāo);

                   float tu0, tv0;

                   float tu1, tv1;

            };

            #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_TEX2)

            /*********************聲明變量*****************/

            //Effect效果指針

            ID3DXEffect *g_pEffect              = 0;

             

            //常量句柄

            D3DXHANDLE WVPMatrixHandle          = 0;

            D3DXHANDLE ScalarHandle             = 0;

            D3DXHANDLE Tex0Handle               = 0;

            D3DXHANDLE Tex1Handle               = 0;

            D3DXHANDLE TechHandle               = 0;

             

            //四邊形頂點(diǎn)緩存

            LPDIRECT3DVERTEXBUFFER9 quadVB  = NULL;

            //兩個(gè)紋理

            LPDIRECT3DTEXTURE9 quadTexture0 = NULL;

            LPDIRECT3DTEXTURE9 quadTexture1 = NULL;

             

            /********************初始化應(yīng)用程序*****************/

            //定義四邊頂點(diǎn)模型

            CUSTOMVERTEX quad[] =

            //  x      y      z    tu0   tv0   tu1   tv1

            {{-3.0f, -3.0f, 10.0f, 0.0f, 1.0f, 0.0f, 1.0f},

            { -3.0f,  3.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f},

            {  3.0f, -3.0f, 10.0f, 1.0f, 1.0f, 1.0f, 1.0f},

            {  3.0f,  3.0f, 10.0f, 1.0f, 0.0f, 1.0f, 0.0f}};

             

            //設(shè)置頂點(diǎn)緩存

            void *ptr = NULL;

            g_pd3dDevice->CreateVertexBuffer(sizeof(quad),

                                                                     D3DUSAGE_WRITEONLY,

                                                                     0,

                                                                     D3DPOOL_MANAGED,

                                                                     &quadVB,

                                                                     NULL);

            quadVB->Lock(0, 0, (void**)&ptr, 0);

            memcpy((void*)ptr, (void*)quad, sizeof(quad));

            quadVB->Unlock();

             

            //創(chuàng)建紋理

            D3DXCreateTextureFromFile(g_pd3dDevice, "chopper.bmp", &quadTexture0);

            D3DXCreateTextureFromFile(g_pd3dDevice, "Bleach.jpg", &quadTexture1);

             

            //檢測像素著色器是否被支持

            D3DCAPS9 caps;

            g_pd3dDevice->GetDeviceCaps(&caps);

            if(caps.PixelShaderVersion < D3DPS_VERSION(1, 1))

            {

                   MessageBox(0, "NotSupport Pixel Shader - FAILED", 0, 0);

                   exit(0);

            }

             

            //創(chuàng)建Effect效果

            ID3DXBuffer* errorBuffer       = 0;

             

            HRESULT hr = D3DXCreateEffectFromFile(g_pd3dDevice,

                                                                             "Effect.txt",

                                                                             0,

                                                                             0,

                                                                             D3DXSHADER_DEBUG,

                                                                             0,

                                                                             &g_pEffect,

                                                                             &errorBuffer);

             

            // output any error messages

            if(errorBuffer)

            {

                   MessageBox(0, (char*)errorBuffer->GetBufferPointer(), 0, 0);

                   ReleaseCOM(errorBuffer);

                   exit(0);

            }

             

            if(FAILED(hr))

            {

                   MessageBox(0, "D3DXCreateEffectFromFile() - FAILED", 0, 0);

                   return false;

            }

             

            //得到各常量句柄

            WVPMatrixHandle = g_pEffect->GetParameterByName(0, "WVPMatrix");

            ScalarHandle = g_pEffect->GetParameterByName(0, "Scalar");

            Tex0Handle = g_pEffect->GetParameterByName(0, "Tex0");

            Tex1Handle = g_pEffect->GetParameterByName(0, "Tex1");

             

            //得到技術(shù)technique T0的句柄

            TechHandle = g_pEffect->GetTechniqueByName("T0");

             

            //設(shè)置紋理,注意這里設(shè)置紋理的方式比之前像素著色器簡便很多

            g_pEffect->SetTexture(Tex0Handle, quadTexture0);

            g_pEffect->SetTexture(Tex1Handle, quadTexture1);

            /********************渲染*****************/

            g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(153,153,153), 1.0f, 0 );

            g_pd3dDevice->BeginScene();

             

            //為著色器變量WVPMatrix賦值

            D3DXMATRIX matWorld, matView, matProj;

            g_pd3dDevice->GetTransform(D3DTS_WORLD, &matWorld);

            g_pd3dDevice->GetTransform(D3DTS_VIEW, &matView);

            g_pd3dDevice->GetTransform(D3DTS_PROJECTION, &matProj);

             

            D3DXMATRIX matWVP = matWorld * matView * matProj;

            g_pEffect->SetMatrix(WVPMatrixHandle, &matWVP);

             

            //為著色器全局變量Scalar賦值

            D3DXVECTOR4 scalar(0.5f, 0.5f, 0.0f, 1.0f);

            g_pEffect->SetVector(ScalarHandle, &scalar);

             

            //設(shè)置定點(diǎn)格式、綁定數(shù)據(jù)流

            g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);

            g_pd3dDevice->SetStreamSource(0, quadVB, 0, sizeof(CUSTOMVERTEX));

             

            //注意下面使用effect框架進(jìn)行渲染的方法

            //設(shè)置要使用的技術(shù)

            g_pEffect->SetTechnique(TechHandle);

             

            //遍歷技術(shù)中包含的所有過程進(jìn)行多次渲染

            UINT numPasses = 0;

            g_pEffect->Begin(&numPasses, 0);

            for(UINT i = 0; i<numPasses; ++i)

            {

                   //開始過程

                   g_pEffect->BeginPass(i);

                   //繪制圖形

                   g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);

                   //結(jié)束過程

                   g_pEffect->EndPass();

            }

            //結(jié)束使用技術(shù)

            g_pEffect->End();

             

            g_pd3dDevice->EndScene();

            g_pd3dDevice->Present(NULL, NULL, NULL, NULL);

                   以上是應(yīng)用程序中使用Effect框架的代碼,可以看到Effect在著色器加載、著色器變量賦值、頂點(diǎn)著色器和像素著色器配合使用等方面做出了簡化,這里只是個(gè)簡單的例子,當(dāng)讀者深入學(xué)習(xí)Effect的時(shí)候,會(huì)了解到更多Effect框架為著色器編程提供的方便。

                   編譯運(yùn)行程序,運(yùn)行效果如圖4.1所示,這和第三章使用像素著色器實(shí)現(xiàn)的多紋理化效果是一樣的。

             

            紋理一

             

            紋理二

             

            混合后紋理三

            圖4.1


            本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/chpdirect1984/archive/2007/12/02/1911622.aspx

            posted on 2009-07-09 18:10 狂爛球 閱讀(19461) 評論(0)  編輯 收藏 引用 所屬分類: 圖形編程

            青草影院天堂男人久久| 久久久亚洲欧洲日产国码二区| 久久精品国产精品亚洲人人| 亚洲v国产v天堂a无码久久| 亚洲中文久久精品无码| 久久综合久久综合久久综合| 日韩AV毛片精品久久久| 婷婷综合久久中文字幕蜜桃三电影 | 亚洲午夜久久久久妓女影院| 久久久久AV综合网成人| 久久www免费人成看国产片| 伊人久久一区二区三区无码| 国内精品久久国产大陆| 久久综合鬼色88久久精品综合自在自线噜噜 | 国产精品久久影院| 最新久久免费视频| 久久免费视频观看| 97精品依人久久久大香线蕉97 | 思思久久精品在热线热| 精品午夜久久福利大片| 久久天天躁狠狠躁夜夜不卡| 亚洲嫩草影院久久精品| 色婷婷久久综合中文久久蜜桃av | 久久成人国产精品一区二区| 久久久一本精品99久久精品88| 久久久久亚洲?V成人无码| 精品久久久久中文字幕日本| 伊人热热久久原色播放www| 精品国产热久久久福利| 99久久国语露脸精品国产| 久久久国产视频| 久久久久国产成人精品亚洲午夜| 97久久精品国产精品青草| 精品国产乱码久久久久久人妻| 欧美无乱码久久久免费午夜一区二区三区中文字幕 | 色综合久久精品中文字幕首页| 伊人久久精品无码av一区| 亚洲精品国精品久久99热| 久久国产一片免费观看| 国产三级精品久久| 91久久精品视频|