青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

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

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

常用鏈接

統(tǒng)計(jì)

積分與排名

好友鏈接

最新評(píng)論

HLSL初級(jí)教程(轉(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é)語(yǔ)

參考資料

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

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

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

 

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)來(lái)解決。

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


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

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

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

下面是一個(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;

}

 

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

1.3.1全局變量

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

matrix WVPMatrix;

vector color;

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

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

       代碼中并沒有對(duì)全局變量進(jìn)行初始化,這是因?yàn)槲覀儗?duì)全局變量的初始化過程將在應(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)語(yǔ)法表示一個(gè)語(yǔ)義,冒號(hào)后面的標(biāo)志符用來(lái)指定變量的用途,如

vector position : POSITION;

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

本節(jié)所說(shuō)的輸入輸出其實(shí)是指著色器代碼和編譯器、GPU之間的通信,和應(yīng)用程序是無(wú)關(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)用程序中對(duì)HLSL著色器的使用分為以下步驟:

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

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

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

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

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

 

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接口對(duì)其進(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對(duì)著色器中的變量進(jìn)行賦值

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

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

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

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

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

        matrix WVPMatrix;

vector color;

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

D3DXHANDLE WVPMatrixHandle          = 0;

D3DXHANDLE ColorHandle              = 0;

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

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

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

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

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

        BasicConstTable->SetDefaults(g_pd3dDevice);

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

HRESULT SetXXX(

  LPDIRECT3DDEVICE9 pDevice,

  D3DXHANDLE hConstant,

  XXX value

);

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

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

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

              g_pd3dDevice->SetVertexShader(BasicShader);

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

在渲染過程中,我們?cè)O(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;

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

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

   

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

    //通過句柄對(duì)著色器中的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è)置為黃色,如果讀者在渲染過程中不斷變換對(duì)著色器變量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說(shuō)明了管線中頂點(diǎn)著色器替換的部件。

 

圖2.1

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

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

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

到現(xiàn)在為止,我們已經(jīng)使用自由頂點(diǎn)格式(flexible vertex format,F(xiàn)VF)來(lái)描述頂點(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é),相對(duì)于頂點(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。換句話說(shuō),normal分量以Vertex的第12個(gè)字節(jié)為開始。

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

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ù)是高級(jí)的,因此我們使用默認(rèn)值,標(biāo)識(shí)為D3DDECLMETHOD_DEFAULT。

²        Usage——指定已計(jì)劃的對(duì)頂點(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è)形狀。對(duì)我們而言,這些形狀就是Mesh網(wǎng)格模型。漸變網(wǎng)格模型的處理就是以時(shí)間軸為基準(zhǔn),逐漸地改變網(wǎng)格模型頂點(diǎn)的坐標(biāo),從一個(gè)網(wǎng)格模型的形狀漸變到另外一個(gè)。請(qǐng)看圖2.3:

 


 
圖2.3

       我們?cè)诔绦蛑惺褂脙蓚€(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)格模型中對(duì)應(yīng)頂點(diǎn)1的坐標(biāo)為B(Bx,By,Bz),要計(jì)算漸變過程中時(shí)間點(diǎn)t所對(duì)應(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í)刻對(duì)應(yīng)的坐標(biāo)C為:

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

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

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

 

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

       程序中,我們?cè)O(shè)定一個(gè)頂點(diǎn)對(duì)應(yīng)兩個(gè)數(shù)據(jù)流,這兩個(gè)數(shù)據(jù)流分別包含了源網(wǎng)格模型的數(shù)據(jù)和目標(biāo)網(wǎng)格模型的數(shù)據(jù)。渲染過程中,我們?cè)谥骼锔鶕?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理對(duì)于兩個(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)最終位置信息,并對(duì)頂點(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類型,我們?cè)赟calar.x中存儲(chǔ)了S值,Scalar.y中存儲(chǔ)的則是(1-S)值

vector Scalar;

 

//輸入

struct VS_INPUT

{

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

    vector position : POSITION;

    vector normal   : NORMAL;

float2 uvCoords : TEXCOORD;

//對(duì)應(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的值

       //對(duì)應(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)著色器,在接下來(lái)的應(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類型,我們?cè)赟calar.x中存儲(chǔ)了S值,Scalar.y中存儲(chǔ)的則是(1-S)值;

 

2.3.4應(yīng)用程序

我們?cè)趹?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對(duì)應(yīng)用程序的一點(diǎn)說(shuō)明

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

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

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

       2. 一旦優(yōu)化了網(wǎng)格模型,你就可以查詢ID3DXMesh對(duì)象,得到一個(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方法對(duì)源網(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)著色器為渲染效果所帶來(lái)的巨大改善。

 

 

 

 

圖2.4

 

3.像素著色器
       像素著色器是在對(duì)每個(gè)像素進(jìn)行光柵化處理期間在圖形卡的GPU上執(zhí)行的程序。(不像頂點(diǎn)著色器,Direct3D不會(huì)以軟件模擬像素著色器的功能。)它實(shí)際上替換了固定功能管線的多紋理化階段(the multitexturing stage),并賦予我們直接操縱單獨(dú)的像素和訪問每個(gè)像素的紋理坐標(biāo)的能力。這種對(duì)像素和紋理坐標(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多紋理化
       簡(jiǎn)單的說(shuō),多紋理化就是使用多個(gè)紋理貼圖混合后進(jìn)行渲染,如圖3.1,渲染過程中,從紋理1和紋理2中分別采樣,得到的顏色值依據(jù)一定規(guī)則進(jìn)行組合得到紋理3,這就是多紋理化。

 

圖3.1

 

3.2多紋理效果的像素著色器
       下面是像素著色器的代碼,該代碼存儲(chǔ)于ps.txt中,該像素著色器根據(jù)輸入的兩套紋理坐標(biāo)對(duì)對(duì)應(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;

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

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

       return output;

}

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

3.2.1HLSL采樣器和紋理

       和vector、matrix一樣,采樣器sample和紋理texture也是HLSL語(yǔ)言的一種類型,HLSL著色器使用采樣器對(duì)指定紋理進(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)容請(qǐng)參見DirectX文檔。

 

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

²        Scalar

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

²        Samp0

              第一層紋理采樣器;

²        Samp1

              第二層紋理采樣器;

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

 


3.3應(yīng)用程序

       程序中我們首先創(chuàng)建一個(gè)四邊形,然后使用像素著色器進(jìn)行紋理混合后對(duì)其進(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);

 

//檢測(cè)系統(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");

 

//得到對(duì)著色器變量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)著色器的使用無(wú)二,只是設(shè)置著色器中紋理采樣器變量Samp0、Samp1和設(shè)定著色器其他變量稍有不同:

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

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

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

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

              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,如果讀者在渲染過程中不斷變換對(duì)著色器變量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簡(jiǎn)化了使用著色器的操作;作為一個(gè)框架,Effect把頂點(diǎn)著色器和像素著色器有機(jī)地組織了起來(lái)。


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ù)語(yǔ)effect(效果)、technique(技術(shù))、pass(過程),所幸這三個(gè)術(shù)語(yǔ)從字面意思上就能得到很好的詮釋。

       要實(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ū)嵗龑?duì)effect進(jìn)行介紹。

 

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

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

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)著色器和像素著色器組織起來(lái)的:

       pass P0

       {

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

              vertexShader = compile vs_1_1 VS_Main();

              pixelShader = compile ps_1_1 PS_Main();

       }

       也可以直接將著色代碼寫在pass過程中,相關(guān)用法請(qǐng)讀者參看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);

 

//檢測(cè)像素著色器是否被支持

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è)置紋理的方式比之前像素著色器簡(jiǎn)便很多

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)著色器和像素著色器配合使用等方面做出了簡(jiǎn)化,這里只是個(gè)簡(jiǎn)單的例子,當(dāng)讀者深入學(xué)習(xí)Effect的時(shí)候,會(huì)了解到更多Effect框架為著色器編程提供的方便。

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

 

紋理一

 

紋理二

 

混合后紋理三

圖4.1


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

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

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            欧美一区二区三区婷婷月色| 这里只有精品电影| 久久久久久欧美| 欧美α欧美αv大片| 亚洲黄色天堂| 在线观看成人小视频| 亚洲欧美日韩精品综合在线观看| 久久亚洲一区| 久久蜜臀精品av| 国内成人在线| 久久gogo国模啪啪人体图| 久久不射2019中文字幕| 国产精品久久毛片a| 在线亚洲观看| 欧美一区在线视频| 国产性猛交xxxx免费看久久| 欧美一区二区观看视频| 久久亚洲国产成人| 亚洲国产精品福利| 欧美精品在线视频| 99这里只有精品| 欧美影院久久久| 欧美高清在线视频观看不卡| 欧美中文在线观看| 精品av久久707| 免费久久精品视频| 亚洲国产精品视频一区| 在线观看福利一区| 欧美激情精品久久久久久免费印度| 亚洲美女在线观看| 亚洲欧美在线一区二区| 国自产拍偷拍福利精品免费一| 久久美女艺术照精彩视频福利播放| 欧美国产亚洲精品久久久8v| 一区二区三区 在线观看视| 国产精品美腿一区在线看| 久久激情一区| 91久久精品国产91久久性色tv | 亚洲深夜激情| 亚洲欧美日韩另类| 在线成人欧美| 欧美日韩国产页| 亚洲欧美日韩一区在线| 欧美大片免费| 午夜久久tv| 亚洲激情一区二区| 国产精品美女| 久久在精品线影院精品国产| 亚洲最新视频在线播放| 久久影音先锋| 亚洲午夜视频在线| 在线观看不卡av| 国产精品日本| 欧美—级a级欧美特级ar全黄| 亚洲欧美在线网| 亚洲精品系列| 另类人畜视频在线| 午夜国产一区| 日韩视频一区二区三区在线播放免费观看 | 久久精品夜色噜噜亚洲a∨| 亚洲狠狠丁香婷婷综合久久久| 亚洲综合精品自拍| 亚洲人人精品| 红杏aⅴ成人免费视频| 欧美日韩美女在线观看| 久久久久久网站| 在线视频精品一区| 亚洲福利视频二区| 久久免费黄色| 香蕉久久一区二区不卡无毒影院 | 欧美国产日韩亚洲一区| 久久激情婷婷| 香蕉久久夜色精品国产| 在线视频欧美日韩精品| 亚洲电影免费| 欧美1区2区视频| 欧美中文字幕精品| 亚洲资源在线观看| 亚洲精品一二| 亚洲国产精品视频一区| 韩国精品主播一区二区在线观看| 国产精品久久久99| 欧美日韩二区三区| 欧美高清视频在线播放| 久久久噜噜噜久久狠狠50岁| 亚洲欧美日韩电影| 99热在线精品观看| 亚洲人www| 亚洲人成7777| 亚洲经典三级| 亚洲电影欧美电影有声小说| 欧美成人在线免费观看| 欧美~级网站不卡| 欧美jizzhd精品欧美巨大免费| 久久久久久久欧美精品| 久久九九久精品国产免费直播| 亚洲一级在线观看| 亚洲一区二区三区乱码aⅴ| 亚洲天堂久久| 亚洲午夜精品久久| 亚洲一区国产精品| 亚洲欧美精品一区| 亚洲男人的天堂在线aⅴ视频| 亚洲天堂av图片| 亚洲欧美日本在线| 欧美在线免费观看| 久久久久久电影| 久久精品日韩一区二区三区| 久久免费视频观看| 美女精品网站| 亚洲成色777777在线观看影院| 亚洲国产精品久久久久秋霞不卡| 亚洲狠狠婷婷| 一本色道久久综合亚洲精品小说 | 久久久精品动漫| 欧美在线播放视频| 久久久久久高潮国产精品视| 久久综合九色综合网站| 欧美黄网免费在线观看| 欧美四级在线观看| 国产欧美日韩视频在线观看| 国精品一区二区三区| **网站欧美大片在线观看| 亚洲免费观看高清在线观看| 亚洲性线免费观看视频成熟| 欧美一区亚洲| 麻豆久久婷婷| 日韩午夜激情| 国产欧美日韩麻豆91| 一区二区自拍| 亚洲人精品午夜| 亚洲综合视频1区| 久久久精品一区| 欧美成人日本| 一本久道综合久久精品| 久久成人18免费观看| 欧美福利一区二区三区| 国产精品试看| 亚洲国产成人porn| 亚洲一区二区视频| 久久亚洲私人国产精品va| 亚洲国产欧美日韩| 亚洲曰本av电影| 欧美freesex交免费视频| 国产精品第一页第二页第三页| 国产一区二区三区丝袜 | 国产综合久久久久久| 91久久精品国产91久久性色| 午夜视频一区| 亚洲国产精品高清久久久| 亚洲自拍偷拍麻豆| 欧美成人dvd在线视频| 国产色视频一区| 99国产成+人+综合+亚洲欧美| 欧美在线你懂的| 亚洲人成网站色ww在线| 久久精品亚洲乱码伦伦中文| 欧美日韩一区二区三区在线视频 | 亚洲欧美日韩一区二区| 免费美女久久99| 亚洲视频一二| 欧美国产国产综合| 韩国成人精品a∨在线观看| 亚洲少妇自拍| 欧美国产日韩亚洲一区| 欧美一区网站| 国产精品萝li| 亚洲视频高清| 亚洲欧洲美洲综合色网| 久久久蜜桃一区二区人| 国产精品资源| 亚洲欧美一区二区精品久久久| 亚洲国产一区二区三区a毛片 | 一区二区激情视频| 欧美激情1区| 亚洲激情啪啪| 欧美aa国产视频| 久久精品人人爽| 国产一区欧美| 久久er精品视频| 亚洲免费在线视频| 国产精品久久久久久av下载红粉| 日韩亚洲欧美中文三级| 亚洲国产裸拍裸体视频在线观看乱了中文 | 亚洲国产精品一区二区三区| 久久久av网站| 午夜日韩电影| 国产欧美二区| 欧美伊人久久久久久午夜久久久久| 99re在线精品| 欧美日韩一区二区欧美激情| 夜夜嗨av一区二区三区四区| 亚洲电影在线| 免费欧美网站| 日韩视频在线一区二区| 亚洲精品影视| 欧美偷拍另类| 欧美亚洲一区| 欧美影院一区| 在线观看视频一区|