DirectX11 With Windows SDK--07 添加光照與經常使用幾何模型、光柵化狀態

前言

這一章項目涉及到的內容很是多,你須要瞭解:html

  1. 光照模型
  2. 常量緩衝區打包規則
  3. 幾何模型
  4. 光柵化狀態

DirectX11 With Windows SDK完整目錄git

Github項目源碼github

除此以外你還須要瞭解下面內容:編程

章節內容
深刻理解HLSL常量緩衝區打包規則

歡迎加入QQ羣: 727623616 能夠一塊兒探討DX11,以及有什麼問題也能夠在這裏彙報。數組

顏色向量

一個4D的顏色向量,一般狀況下會表示爲(red, green, blue, alpha),每一個份量的取值範圍爲[0.0f, 1.0f]。對於紅綠藍份量,用0.0f表示該沒有該份量的顏色,用1.0f表示該份量的顏色達到飽和;對於alpha份量,用0.0f表示該份量徹底透明,用1.0f表示該份量徹底不透明。app

對於8位色來講,每種份量的顏色亮度能夠表達出256種,但使用浮點數會大大浪費存儲空間。在內存要求苛刻的狀況下,咱們可使用32位的數據類型,其中rgba各佔8位,若須要映射到浮點數向量,則對應關係爲f(x) = x / 255.0f,其中x爲整數存儲法,表示範圍爲[0,255]f(x)爲浮點存儲法,表示範圍爲[0.0f, 1.0f]ide

在頭文件DirectXColors.h中,咱們能夠看到定義了一些4D的顏色向量,位於名稱空間DirectX::Colors。在這裏貼出來供你們參考:函數

// Standard colors (Red/Green/Blue/Alpha)
XMGLOBALCONST XMVECTORF32 AliceBlue            = { { { 0.941176534f, 0.972549081f, 1.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 AntiqueWhite         = { { { 0.980392218f, 0.921568692f, 0.843137324f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Aqua                 = { { { 0.000000000f, 1.000000000f, 1.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Aquamarine           = { { { 0.498039246f, 1.000000000f, 0.831372619f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Azure                = { { { 0.941176534f, 1.000000000f, 1.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Beige                = { { { 0.960784376f, 0.960784376f, 0.862745166f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Bisque               = { { { 1.000000000f, 0.894117713f, 0.768627524f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Black                = { { { 0.000000000f, 0.000000000f, 0.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 BlanchedAlmond       = { { { 1.000000000f, 0.921568692f, 0.803921640f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Blue                 = { { { 0.000000000f, 0.000000000f, 1.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 BlueViolet           = { { { 0.541176498f, 0.168627456f, 0.886274576f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Brown                = { { { 0.647058845f, 0.164705887f, 0.164705887f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 BurlyWood            = { { { 0.870588303f, 0.721568644f, 0.529411793f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 CadetBlue            = { { { 0.372549027f, 0.619607866f, 0.627451003f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Chartreuse           = { { { 0.498039246f, 1.000000000f, 0.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Chocolate            = { { { 0.823529482f, 0.411764741f, 0.117647067f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Coral                = { { { 1.000000000f, 0.498039246f, 0.313725501f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 CornflowerBlue       = { { { 0.392156899f, 0.584313750f, 0.929411829f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Cornsilk             = { { { 1.000000000f, 0.972549081f, 0.862745166f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Crimson              = { { { 0.862745166f, 0.078431375f, 0.235294133f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Cyan                 = { { { 0.000000000f, 1.000000000f, 1.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 DarkBlue             = { { { 0.000000000f, 0.000000000f, 0.545098066f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 DarkCyan             = { { { 0.000000000f, 0.545098066f, 0.545098066f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 DarkGoldenrod        = { { { 0.721568644f, 0.525490224f, 0.043137256f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 DarkGray             = { { { 0.662745118f, 0.662745118f, 0.662745118f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 DarkGreen            = { { { 0.000000000f, 0.392156899f, 0.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 DarkKhaki            = { { { 0.741176486f, 0.717647076f, 0.419607878f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 DarkMagenta          = { { { 0.545098066f, 0.000000000f, 0.545098066f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 DarkOliveGreen       = { { { 0.333333343f, 0.419607878f, 0.184313729f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 DarkOrange           = { { { 1.000000000f, 0.549019635f, 0.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 DarkOrchid           = { { { 0.600000024f, 0.196078449f, 0.800000072f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 DarkRed              = { { { 0.545098066f, 0.000000000f, 0.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 DarkSalmon           = { { { 0.913725555f, 0.588235319f, 0.478431404f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 DarkSeaGreen         = { { { 0.560784340f, 0.737254918f, 0.545098066f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 DarkSlateBlue        = { { { 0.282352954f, 0.239215702f, 0.545098066f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 DarkSlateGray        = { { { 0.184313729f, 0.309803933f, 0.309803933f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 DarkTurquoise        = { { { 0.000000000f, 0.807843208f, 0.819607913f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 DarkViolet           = { { { 0.580392182f, 0.000000000f, 0.827451050f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 DeepPink             = { { { 1.000000000f, 0.078431375f, 0.576470613f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 DeepSkyBlue          = { { { 0.000000000f, 0.749019623f, 1.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 DimGray              = { { { 0.411764741f, 0.411764741f, 0.411764741f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 DodgerBlue           = { { { 0.117647067f, 0.564705908f, 1.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Firebrick            = { { { 0.698039234f, 0.133333340f, 0.133333340f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 FloralWhite          = { { { 1.000000000f, 0.980392218f, 0.941176534f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 ForestGreen          = { { { 0.133333340f, 0.545098066f, 0.133333340f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Fuchsia              = { { { 1.000000000f, 0.000000000f, 1.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Gainsboro            = { { { 0.862745166f, 0.862745166f, 0.862745166f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 GhostWhite           = { { { 0.972549081f, 0.972549081f, 1.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Gold                 = { { { 1.000000000f, 0.843137324f, 0.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Goldenrod            = { { { 0.854902029f, 0.647058845f, 0.125490203f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Gray                 = { { { 0.501960814f, 0.501960814f, 0.501960814f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Green                = { { { 0.000000000f, 0.501960814f, 0.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 GreenYellow          = { { { 0.678431392f, 1.000000000f, 0.184313729f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Honeydew             = { { { 0.941176534f, 1.000000000f, 0.941176534f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 HotPink              = { { { 1.000000000f, 0.411764741f, 0.705882370f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 IndianRed            = { { { 0.803921640f, 0.360784322f, 0.360784322f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Indigo               = { { { 0.294117659f, 0.000000000f, 0.509803951f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Ivory                = { { { 1.000000000f, 1.000000000f, 0.941176534f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Khaki                = { { { 0.941176534f, 0.901960850f, 0.549019635f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Lavender             = { { { 0.901960850f, 0.901960850f, 0.980392218f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 LavenderBlush        = { { { 1.000000000f, 0.941176534f, 0.960784376f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 LawnGreen            = { { { 0.486274540f, 0.988235354f, 0.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 LemonChiffon         = { { { 1.000000000f, 0.980392218f, 0.803921640f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 LightBlue            = { { { 0.678431392f, 0.847058892f, 0.901960850f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 LightCoral           = { { { 0.941176534f, 0.501960814f, 0.501960814f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 LightCyan            = { { { 0.878431439f, 1.000000000f, 1.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 LightGoldenrodYellow = { { { 0.980392218f, 0.980392218f, 0.823529482f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 LightGreen           = { { { 0.564705908f, 0.933333397f, 0.564705908f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 LightGray            = { { { 0.827451050f, 0.827451050f, 0.827451050f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 LightPink            = { { { 1.000000000f, 0.713725507f, 0.756862819f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 LightSalmon          = { { { 1.000000000f, 0.627451003f, 0.478431404f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 LightSeaGreen        = { { { 0.125490203f, 0.698039234f, 0.666666687f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 LightSkyBlue         = { { { 0.529411793f, 0.807843208f, 0.980392218f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 LightSlateGray       = { { { 0.466666698f, 0.533333361f, 0.600000024f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 LightSteelBlue       = { { { 0.690196097f, 0.768627524f, 0.870588303f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 LightYellow          = { { { 1.000000000f, 1.000000000f, 0.878431439f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Lime                 = { { { 0.000000000f, 1.000000000f, 0.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 LimeGreen            = { { { 0.196078449f, 0.803921640f, 0.196078449f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Linen                = { { { 0.980392218f, 0.941176534f, 0.901960850f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Magenta              = { { { 1.000000000f, 0.000000000f, 1.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Maroon               = { { { 0.501960814f, 0.000000000f, 0.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 MediumAquamarine     = { { { 0.400000036f, 0.803921640f, 0.666666687f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 MediumBlue           = { { { 0.000000000f, 0.000000000f, 0.803921640f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 MediumOrchid         = { { { 0.729411781f, 0.333333343f, 0.827451050f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 MediumPurple         = { { { 0.576470613f, 0.439215720f, 0.858823597f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 MediumSeaGreen       = { { { 0.235294133f, 0.701960802f, 0.443137288f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 MediumSlateBlue      = { { { 0.482352972f, 0.407843173f, 0.933333397f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 MediumSpringGreen    = { { { 0.000000000f, 0.980392218f, 0.603921592f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 MediumTurquoise      = { { { 0.282352954f, 0.819607913f, 0.800000072f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 MediumVioletRed      = { { { 0.780392230f, 0.082352944f, 0.521568656f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 MidnightBlue         = { { { 0.098039225f, 0.098039225f, 0.439215720f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 MintCream            = { { { 0.960784376f, 1.000000000f, 0.980392218f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 MistyRose            = { { { 1.000000000f, 0.894117713f, 0.882353008f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Moccasin             = { { { 1.000000000f, 0.894117713f, 0.709803939f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 NavajoWhite          = { { { 1.000000000f, 0.870588303f, 0.678431392f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Navy                 = { { { 0.000000000f, 0.000000000f, 0.501960814f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 OldLace              = { { { 0.992156923f, 0.960784376f, 0.901960850f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Olive                = { { { 0.501960814f, 0.501960814f, 0.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 OliveDrab            = { { { 0.419607878f, 0.556862772f, 0.137254909f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Orange               = { { { 1.000000000f, 0.647058845f, 0.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 OrangeRed            = { { { 1.000000000f, 0.270588249f, 0.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Orchid               = { { { 0.854902029f, 0.439215720f, 0.839215755f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 PaleGoldenrod        = { { { 0.933333397f, 0.909803987f, 0.666666687f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 PaleGreen            = { { { 0.596078455f, 0.984313786f, 0.596078455f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 PaleTurquoise        = { { { 0.686274529f, 0.933333397f, 0.933333397f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 PaleVioletRed        = { { { 0.858823597f, 0.439215720f, 0.576470613f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 PapayaWhip           = { { { 1.000000000f, 0.937254965f, 0.835294187f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 PeachPuff            = { { { 1.000000000f, 0.854902029f, 0.725490212f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Peru                 = { { { 0.803921640f, 0.521568656f, 0.247058839f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Pink                 = { { { 1.000000000f, 0.752941251f, 0.796078503f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Plum                 = { { { 0.866666734f, 0.627451003f, 0.866666734f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 PowderBlue           = { { { 0.690196097f, 0.878431439f, 0.901960850f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Purple               = { { { 0.501960814f, 0.000000000f, 0.501960814f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Red                  = { { { 1.000000000f, 0.000000000f, 0.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 RosyBrown            = { { { 0.737254918f, 0.560784340f, 0.560784340f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 RoyalBlue            = { { { 0.254901975f, 0.411764741f, 0.882353008f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 SaddleBrown          = { { { 0.545098066f, 0.270588249f, 0.074509807f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Salmon               = { { { 0.980392218f, 0.501960814f, 0.447058856f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 SandyBrown           = { { { 0.956862807f, 0.643137276f, 0.376470625f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 SeaGreen             = { { { 0.180392161f, 0.545098066f, 0.341176480f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 SeaShell             = { { { 1.000000000f, 0.960784376f, 0.933333397f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Sienna               = { { { 0.627451003f, 0.321568638f, 0.176470593f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Silver               = { { { 0.752941251f, 0.752941251f, 0.752941251f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 SkyBlue              = { { { 0.529411793f, 0.807843208f, 0.921568692f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 SlateBlue            = { { { 0.415686309f, 0.352941185f, 0.803921640f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 SlateGray            = { { { 0.439215720f, 0.501960814f, 0.564705908f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Snow                 = { { { 1.000000000f, 0.980392218f, 0.980392218f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 SpringGreen          = { { { 0.000000000f, 1.000000000f, 0.498039246f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 SteelBlue            = { { { 0.274509817f, 0.509803951f, 0.705882370f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Tan                  = { { { 0.823529482f, 0.705882370f, 0.549019635f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Teal                 = { { { 0.000000000f, 0.501960814f, 0.501960814f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Thistle              = { { { 0.847058892f, 0.749019623f, 0.847058892f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Tomato               = { { { 1.000000000f, 0.388235331f, 0.278431386f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Transparent          = { { { 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f } } };
XMGLOBALCONST XMVECTORF32 Turquoise            = { { { 0.250980407f, 0.878431439f, 0.815686345f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Violet               = { { { 0.933333397f, 0.509803951f, 0.933333397f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Wheat                = { { { 0.960784376f, 0.870588303f, 0.701960802f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 White                = { { { 1.000000000f, 1.000000000f, 1.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 WhiteSmoke           = { { { 0.960784376f, 0.960784376f, 0.960784376f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 Yellow               = { { { 1.000000000f, 1.000000000f, 0.000000000f, 1.000000000f } } };
XMGLOBALCONST XMVECTORF32 YellowGreen          = { { { 0.603921592f, 0.803921640f, 0.196078449f, 1.000000000f } } };

若是顏色相關的運算在C++代碼層進行的話,則在最後須要調用XMVectorSaturate函數確保各個份量都控制在[0.0f, 1.0f]之間。佈局

法向量

法向量用於表述物體表面的朝向,它是單位向量,而且垂直於該表面。對於曲面上一點,一般描述的是曲面該點的切面所對應的法向量。在光照的計算中會常常用到該向量。可是咱們傳入的一般是頂點數據而不是面的數據,所以頂點結構體內還會包含法向量數據:測試

struct VertexPosNormalColor
{
    DirectX::XMFLOAT3 pos;
    DirectX::XMFLOAT3 normal;
    DirectX::XMFLOAT4 color;
    static const D3D11_INPUT_ELEMENT_DESC inputLayout[3];
};

對應的每一個輸入元素的描述爲:

const D3D11_INPUT_ELEMENT_DESC VertexPosNormalColor::inputLayout[3] = {
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0},
    { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0}
};

對於棱角分明的物體,如立方體,一共由12個三角形組成,2個三角形的4個頂點構成一個面。因爲該面的4個頂點要求法向量朝向一致,而一個頂點雖然與立方體的三個面鄰接,可是法向量只有一個,所以須要分化出3個包含不一樣法向量的頂點。最終用於繪製該立方體的頂點數就須要24個,是立方體頂點數的3倍!

而對於沒那麼棱角分明的物體,表面只是稍微有些不平坦的話,咱們能夠求出該點相鄰的全部面的平均法向量。

image

image

對於能夠用函數表示的曲面,如球,則能夠求出曲面一點對應切面的法向量。

對於函數:

\[f(x,y,z)≡0\]

則對應點切平面的法向量(非單位向量)爲:

\[\frac{\partial{f}}{\partial{x}}\vec{i}+\frac{\partial{f}}{\partial{y}}\vec{j}+\frac{\partial{f}}{\partial{z}}\vec{k}\]

最後通過標準化後即爲曲面該點對應的單位法向量。如球面方程:

\[f(x,y,z)=x^2+y^2+z^2-1≡0\]

最終對應的單位法向量爲:
\[\vec{n}=(x,y,z)\]

法向量的變換

這裏省略變換的證實,若一個物體的向量u與法向量n正交,當向量u通過了矩陣變換獲得了\(\mathbf{u'}=\mathbf{u}\mathbf{A}\)時,對應變換後的法向量應爲\(\mathbf{n'}=\mathbf{n}(\mathbf{A^{-1}})^{\mathbf{T}}\)

物體材質

光在照射到物體上時,因爲物體的材質特性會反射一部分光到人眼,最終咱們觀察到的物體顏色就是被反射的那部分光的顏色。不一樣的物體有不一樣的材質屬性,決定了各類顏色份量的反射係數是多少。其中紅綠藍每一個份量的取值範圍爲[0.0f, 1.0f]

在C++中的結構體表示爲:

// 物體表面材質
struct Material
{
    Material() { memset(this, 0, sizeof(Material)); }

    DirectX::XMFLOAT4 Ambient;
    DirectX::XMFLOAT4 Diffuse;
    DirectX::XMFLOAT4 Specular; // w = 鏡面反射強度
    DirectX::XMFLOAT4 Reflect;
};

在HLSL中則表示爲:

// 物體表面材質
struct Material
{
    float4 Ambient;
    float4 Diffuse;
    float4 Specular; // w = SpecPower
    float4 Reflect;
};

光的種類

環境光(Ambient Lighting)

環境光也能夠說是間接光照,即光線通過了屢次反射後最終被咱們的人眼所見。即使在一個密閉的暗室使用手電筒照射,你仍是能夠感覺到光充滿了整個房間。一樣,在陽光照射下,陰影部分也並非純粹的黑色,只是比其餘地方暗上許多而已。所以在設置環境光的時候,能夠考慮以你想呈現的陰影部分亮度進行設置。

在HLSL中,若環境光向量爲\(\mathbf{l_a}\) ,物體材質對環境光的反射向量爲\(\mathbf{m_a}\),最終環境光份量呈現的顏色爲:

\(\mathbf{A} = \mathbf{l_a} \otimes \mathbf{m_a}\)

這裏的乘號表示各個份量相乘,最終獲得的向量爲

(la.r * ma.r, la.g * ma.g, la.b * ma.b, la.a * ma.a)

漫反射光(Diffuse Lighting)

在現實生活中,咱們看到的光以漫反射光爲主。對於粗糙的表面,光照射在物體表面一點後反射的方向是不肯定的。咱們能夠近似認爲光線在照射到物體表面一點後會朝任意方向反射等量的光照,這樣咱們人眼不管在哪一個方向觀察該點,呈現的亮度應該是不會變化的(在沒有鏡面反射的基礎)。可是物體的亮度與光線照射的方向有所關係,好比當均勻光線垂直照射物體的時候,此時看到的物體表面是最亮的;而均勻光線不通過物體表面,與表面平行的時候,物體的表面此時幾乎是看不到的(此時可能仍有少許的光會到達物體表面,取決於光束的匯聚程度和與物體的距離)。畢竟光束不可能作到徹底同一個方向照射,仍會有少數的散射光。

朗伯余弦定理

既然光照方向與亮度有關係,咱們可使用朗伯余弦定理來表示這種現象,用L表示光反射後的單位方向向量,n表示平面單位法向量,則有:
\(k_d = max(\mathbf{L} \cdot \mathbf{n}, 0)\)

若漫反射光向量爲ld,物體材質對環境光的反射向量爲md,最終漫反射光份量呈現的顏色爲:
\(k_d * \mathbf{l_d} \otimes \mathbf{m_d} = k_d \mathbf{D}\)

鏡面反射光(Specular Lighting)

某些較爲光滑的平面能夠均勻地反射光照。鏡面反射光與人眼所在位置有聯繫,若人站在反射光的路徑上看反射點,能夠看到此時的點是最亮的,而後隨着人眼遠離反射光線的路徑,看到的鏡面反射光會愈來愈少,直至只能看到漫反射光和環境光的部分。

在HLSL中,若R爲光反射後的單位方向向量,toEye爲物體表面一點到人眼的單位方向向量,p爲鏡面係數(係數越大,表面越光滑,並且p的值必須大於等於1,不然會有很奇怪的效果),能夠用下面的公式來表達鏡面係數:

\(k_s=\begin{cases} max(\mathbf{R} \cdot \mathbf{toEye}, 0)^p, \mathbf{L} \cdot \mathbf{n} > 0\\ 0, \mathbf{L} \cdot \mathbf{n} <= 0\\ \end{cases}\)

若鏡面反射光向量爲ls,物體材質對鏡面光的反射向量爲ms,最終鏡面反射光份量呈現的顏色爲:

\(k_s * \mathbf{l_s} \otimes \mathbf{m_s} = k_s \mathbf{S}\)

HLSL常量緩衝區打包規則

這一部份內容已經轉移到下面的連接了:

http://www.javashuo.com/article/p-pxcbmqfe-dh.html

光照模型

平行光/方向光

平行光一般是一種全局光,它有一個固定的照射方向。

經平行光照射下物體表面一點的顏色能夠初步表示爲:
\(\mathbf{litColor}=\mathbf{A} + k_d \mathbf{D} + k_s \mathbf{S}\)
其中
\(k_d = max(\mathbf{L} \cdot \mathbf{n}, 0)\)
\(k_s=\begin{cases} max(\mathbf{R} \cdot \mathbf{toEye}, 0)^p, \mathbf{L} \cdot \mathbf{n} > 0\\ 0, \mathbf{L} \cdot \mathbf{n} <= 0\\ \end{cases}\)

在C++中,方向光的結構體表示爲:

struct DirectionalLight
{
    DirectionalLight() { memset(this, 0, sizeof(DirectionalLight)); }

    DirectX::XMFLOAT4 Ambient;
    DirectX::XMFLOAT4 Diffuse;
    DirectX::XMFLOAT4 Specular;
    DirectX::XMFLOAT3 Direction;
    float Pad; // 最後用一個浮點數填充使得該結構體大小知足16的倍數,便於咱們之後在HLSL設置數組
};

在HLSL則表示爲:

struct DirectionalLight
{
    float4 Ambient;
    float4 Diffuse;
    float4 Specular;
    float3 Direction;
    float Pad;
};

在HLSL中計算平行光/方向光的函數以下(光向量是與光照射方向相反的單位向量):

void ComputeDirectionalLight(Material mat, DirectionalLight L,
    float3 normal, float3 toEye,
    out float4 ambient,
    out float4 diffuse,
    out float4 spec)
{
    // 初始化輸出
    ambient = float4(0.0f, 0.0f, 0.0f, 0.0f);
    diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f);
    spec = float4(0.0f, 0.0f, 0.0f, 0.0f);

    // 光向量與照射方向相反
    float3 lightVec = -L.Direction;

    // 添加環境光
    ambient = mat.Ambient * L.Ambient;

    // 添加漫反射光和鏡面光
    float diffuseFactor = dot(lightVec, normal);

    // 展開,避免動態分支
    [flatten]
    if (diffuseFactor > 0.0f)
    {
        float3 v = reflect(-lightVec, normal);
        float specFactor = pow(max(dot(v, toEye), 0.0f), mat.Specular.w);

        diffuse = diffuseFactor * mat.Diffuse * L.Diffuse;
        spec = specFactor * mat.Specular * L.Specular;
    }
}

注意該函數的normaltoEye向量都爲單位向量,除此以外輸出了光的三種份量。

點光

點光是一種有源光照模型,肯定光源位置後,它會朝全部方向輻射光照。

image

在HLSL,若已知光源位置向量Q和照射點P,則單位光向量爲:

(Q - P) / length(Q - P)

光的衰弱

隨着距離的增加,光源照射到物體表面更遠一點時呈現的亮度更低。其中光的衰弱只對直接光照有效,對間接光(如環境光份量)沒有影響。

d爲光源到物體一點的距離,則有:

\(I(d) = \frac{I_{0}}{a_{0}+a_{1}d+a_{2}d^2}\)

若燈光距離物體太遠,計算到的衰弱因子接近於0.咱們可使用標量range來控制照射範圍,對距離過大的物體提早避免了漫射光和鏡面光的計算。

則經點光照射下物體表面一點的亮度能夠初步表示爲:
\(\mathbf{litColor}=\mathbf{A} + \frac{k_d \mathbf{D} + k_s \mathbf{S}}{a_{0}+a_{1}d+a_{2}d^2}\)

在C++中,點光能夠被表示爲:

// 點光
struct PointLight
{
    PointLight() { memset(this, 0, sizeof(PointLight)); }

    DirectX::XMFLOAT4 Ambient;
    DirectX::XMFLOAT4 Diffuse;
    DirectX::XMFLOAT4 Specular;

    // 打包成4D向量: (Position, Range)
    DirectX::XMFLOAT3 Position;
    float Range;

    // 打包成4D向量: (A0, A1, A2, Pad)
    DirectX::XMFLOAT3 Att;
    float Pad; // 最後用一個浮點數填充使得該結構體大小知足16的倍數,便於咱們之後在HLSL設置數組
};

在HLSL則表示爲:

// 點光
struct PointLight
{
    float4 Ambient;
    float4 Diffuse;
    float4 Specular;

    float3 Position;
    float Range;

    float3 Att;
    float Pad;
};

在HLSL中計算點光的函數以下

void ComputePointLight(Material mat, PointLight L, float3 pos, float3 normal, float3 toEye,
    out float4 ambient, out float4 diffuse, out float4 spec)
{
    // 初始化輸出
    ambient = float4(0.0f, 0.0f, 0.0f, 0.0f);
    diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f);
    spec = float4(0.0f, 0.0f, 0.0f, 0.0f);

    // 從表面到光源的向量
    float3 lightVec = L.Position - pos;

    // 表面到光線的距離
    float d = length(lightVec);

    // 燈光範圍測試
    if (d > L.Range)
        return;

    // 標準化光向量
    lightVec /= d;

    // 環境光計算
    ambient = mat.Ambient * L.Ambient;

    // 漫反射和鏡面計算
    float diffuseFactor = dot(lightVec, normal);

    // 展開以免動態分支
    [flatten]
    if (diffuseFactor > 0.0f)
    {
        float3 v = reflect(-lightVec, normal);
        float specFactor = pow(max(dot(v, toEye), 0.0f), mat.Specular.w);

        diffuse = diffuseFactor * mat.Diffuse * L.Diffuse;
        spec = specFactor * mat.Specular * L.Specular;
    }

    // 光的衰弱
    float att = 1.0f / dot(L.Att, float3(1.0f, d, d * d));

    diffuse *= att;
    spec *= att;
}

聚光燈

聚光燈也是一種有源光照模型,在肯定光源位置後,其照射區域能夠看做一個錐體,在同等照射距離下,越靠近照射中心,亮度越強。一樣隨着距離的增大,光照強度逐漸減弱。
image

在HLSL,若已知光源位置向量Q和照射點P,則單位光向量爲:

(Q - P) / length(Q - P)

若已知光向量L和照射強度d,以及光的匯聚程度spot,則能夠獲得光照強度因子:
\(k_{spot} = max(\mathbf{-L} \cdot \mathbf{d}, 0)^{spot}\)

一般spot的值越大,光束的匯聚程度越強。

則經聚光燈照射下物體表面一點的亮度能夠初步表示爲:
\(\mathbf{LitColor} = k_{spot}(\mathbf{A} + \frac{k_d \mathbf{D} + k_s \mathbf{S}}{a_{0}+a_{1}d+a_{2}d^2})\)

在C++中,聚光燈的結構體以下:

struct SpotLight
{
    SpotLight() { memset(this, 0, sizeof(SpotLight)); }

    DirectX::XMFLOAT4 Ambient;
    DirectX::XMFLOAT4 Diffuse;
    DirectX::XMFLOAT4 Specular;

    // 打包成4D向量: (Position, Range)
    DirectX::XMFLOAT3 Position;
    float Range;

    // 打包成4D向量: (Direction, Spot)
    DirectX::XMFLOAT3 Direction;
    float Spot;

    // 打包成4D向量: (Att, Pad)
    DirectX::XMFLOAT3 Att;
    float Pad; // 最後用一個浮點數填充使得該結構體大小知足16的倍數,便於咱們之後在HLSL設置數組
};

在HLSL中,則表示爲:

struct SpotLight
{
    float4 Ambient;
    float4 Diffuse;
    float4 Specular;

    float3 Position;
    float Range;

    float3 Direction;
    float Spot;

    float3 Att;
    float Pad;
};

在HLSL中計算聚光燈的函數以下

void ComputeSpotLight(Material mat, SpotLight L, float3 pos, float3 normal, float3 toEye,
    out float4 ambient, out float4 diffuse, out float4 spec)
{
    // 初始化輸出
    ambient = float4(0.0f, 0.0f, 0.0f, 0.0f);
    diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f);
    spec = float4(0.0f, 0.0f, 0.0f, 0.0f);

    // // 從表面到光源的向量
    float3 lightVec = L.Position - pos;

    // 表面到光源的距離
    float d = length(lightVec);

    // 範圍測試
    if (d > L.Range)
        return;

    // 標準化光向量
    lightVec /= d;

    // 計算環境光部分
    ambient = mat.Ambient * L.Ambient;


    // 計算漫反射光和鏡面反射光部分
    float diffuseFactor = dot(lightVec, normal);

    // 展開以免動態分支
    [flatten]
    if (diffuseFactor > 0.0f)
    {
        float3 v = reflect(-lightVec, normal);
        float specFactor = pow(max(dot(v, toEye), 0.0f), mat.Specular.w);

        diffuse = diffuseFactor * mat.Diffuse * L.Diffuse;
        spec = specFactor * mat.Specular * L.Specular;
    }

    // 計算匯聚因子和衰弱係數
    float spot = pow(max(dot(-lightVec, L.Direction), 0.0f), L.Spot);
    float att = spot / dot(L.Att, float3(1.0f, d, d * d));

    ambient *= spot;
    diffuse *= att;
    spec *= att;
}

HLSL代碼

在LightHelper.hlsli標頭文件中包含了上述全部的HLSL結構體以及三種光照模型的函數

而後Light_VS.hlsl和Light_PS.hlsl分別存放的是須要用到的頂點着色器和像素着色器

// Light.hlsli
#include "LightHelper.hlsli"

cbuffer VSConstantBuffer : register(b0)
{
    matrix g_World; 
    matrix g_View;  
    matrix g_Proj;  
    matrix g_WorldInvTranspose;
}

cbuffer PSConstantBuffer : register(b1)
{
    DirectionalLight g_DirLight;
    PointLight g_PointLight;
    SpotLight g_SpotLight;
    Material g_Material;
    float3 g_EyePosW;
    float g_Pad;
}


struct VertexIn
{
    float3 PosL : POSITION;
    float3 NormalL : NORMAL;
    float4 Color : COLOR;
};

struct VertexOut
{
    float4 PosH : SV_POSITION;
    float3 PosW : POSITION;     // 在世界中的位置
    float3 NormalW : NORMAL;    // 法向量在世界中的方向
    float4 Color : COLOR;
};
// Light_VS.hlsl
#include "Light.hlsli"

// 頂點着色器
VertexOut VS(VertexIn vIn)
{
    VertexOut vOut;
    matrix viewProj = mul(g_View, g_Proj);
    float4 posW = mul(float4(vIn.PosL, 1.0f), g_World);

    vOut.PosH = mul(posW, viewProj);
    vOut.PosW = posW.xyz;
    vOut.NormalW = mul(vIn.NormalL, (float3x3) g_WorldInvTranspose);
    vOut.Color = vIn.Color; // 這裏alpha通道的值默認爲1.0
    return vOut;
}
// Light_PS.hlsl
#include "Light.hlsli"

// 像素着色器
float4 PS(VertexOut pIn) : SV_Target
{
    // 標準化法向量
    pIn.NormalW = normalize(pIn.NormalW);

    // 頂點指向眼睛的向量
    float3 toEyeW = normalize(g_EyePosW - pIn.PosW);

    // 初始化爲0 
    float4 ambient, diffuse, spec;
    float4 A, D, S;
    ambient = diffuse = spec = A = D = S = float4(0.0f, 0.0f, 0.0f, 0.0f);

    ComputeDirectionalLight(g_Material, g_DirLight, pIn.NormalW, toEyeW, A, D, S);
    ambient += A;
    diffuse += D;
    spec += S;

    ComputePointLight(g_Material, g_PointLight, pIn.PosW, pIn.NormalW, toEyeW, A, D, S);
    ambient += A;
    diffuse += D;
    spec += S;

    ComputeSpotLight(g_Material, g_SpotLight, pIn.PosW, pIn.NormalW, toEyeW, A, D, S);
    ambient += A;
    diffuse += D;
    spec += S;

    float4 litColor = pIn.Color * (ambient + diffuse) + spec;
    
    litColor.a = g_Material.Diffuse.a * pIn.Color.a;
    
    return litColor;
}

這裏有兩個常量緩衝區,一個用於頂點着色階段,另外一個用於像素着色階段。

因爲這裏的頂點信息還包含了顏色,咱們只讓像素顏色受環境光和漫反射光的影響,以凸顯高光效果。

經常使用幾何模型

在頭文件Geometry.h中的名稱空間Geometry內包含了五個經常使用的3D模型:球、立方體、圓柱體、圓錐體和平面。調用它能夠生成模型數據以寫入頂點和索引緩衝區。

(2018/12/15更新)目前的幾何模型網格數據採用模板形式,能夠作到支持VertexPosVertexPosColorVertexPosTexVertexPosNormalTexVertexPosNormalColorVertexPosNormalTangentTex的頂點以及WORDDWORD索引的輸出

namespace Geometry
{
    // 網格數據
    template<class VertexType = VertexPosNormalTex, class IndexType = WORD>
    struct MeshData
    {
        std::vector<VertexType> vertexVec;  // 頂點數組
        std::vector<IndexType> indexVec;    // 索引數組

        MeshData()
        {
            // 需檢驗索引類型合法性
            static_assert(sizeof(IndexType) == 2 || sizeof(IndexType) == 4, "The size of IndexType must be 2 bytes or 4 bytes!");
            static_assert(std::is_unsigned<IndexType>::value, "IndexType must be unsigned integer!");
        }
    };

    // 建立球體網格數據,levels和slices越大,精度越高。
    template<class VertexType = VertexPosNormalTex, class IndexType = WORD>
    MeshData<VertexType, IndexType> CreateSphere(float radius = 1.0f, UINT levels = 20, UINT slices = 20,
        const DirectX::XMFLOAT4& color = { 1.0f, 1.0f, 1.0f, 1.0f });


    // 建立立方體網格數據
    template<class VertexType = VertexPosNormalTex, class IndexType = WORD>
    MeshData<VertexType, IndexType> CreateBox(float width = 2.0f, float height = 2.0f, float depth = 2.0f,
        const DirectX::XMFLOAT4& color = { 1.0f, 1.0f, 1.0f, 1.0f });

    // 建立圓柱體網格數據,slices越大,精度越高。
    template<class VertexType = VertexPosNormalTex, class IndexType = WORD>
    MeshData<VertexType, IndexType> CreateCylinder(float radius = 1.0f, float height = 2.0f, UINT slices = 20,
        const DirectX::XMFLOAT4& color = { 1.0f, 1.0f, 1.0f, 1.0f });



    // 建立只有圓柱體側面的網格數據,slices越大,精度越高
    template<class VertexType = VertexPosNormalTex, class IndexType = WORD>
    MeshData<VertexType, IndexType> CreateCylinderNoCap(float radius = 1.0f, float height = 2.0f, UINT slices = 20,
        const DirectX::XMFLOAT4& color = { 1.0f, 1.0f, 1.0f, 1.0f });

    // 建立一個覆蓋NDC屏幕的面
    template<class VertexType = VertexPosTex, class IndexType = WORD>
    MeshData<VertexType, IndexType> Create2DShow(const DirectX::XMFLOAT2& center, const DirectX::XMFLOAT2& scale = { 1.0f, 1.0f },
        const DirectX::XMFLOAT4& color = { 1.0f, 1.0f, 1.0f, 1.0f });
    template<class VertexType = VertexPosTex, class IndexType = WORD>
    MeshData<VertexType, IndexType> Create2DShow(float centerX = 0.0f, float centerY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f,
        const DirectX::XMFLOAT4& color = { 1.0f, 1.0f, 1.0f, 1.0f });

    // 建立一個平面
    template<class VertexType = VertexPosNormalTex, class IndexType = WORD>
    MeshData<VertexType, IndexType> CreatePlane(const DirectX::XMFLOAT3& center, const DirectX::XMFLOAT2& planeSize = { 10.0f, 10.0f },
        const DirectX::XMFLOAT2& maxTexCoord = { 1.0f, 1.0f }, const DirectX::XMFLOAT4& color = { 1.0f, 1.0f, 1.0f, 1.0f });
    template<class VertexType = VertexPosNormalTex, class IndexType = WORD>
    MeshData<VertexType, IndexType> CreatePlane(float centerX = 0.0f, float centerY = 0.0f, float centerZ = 0.0f,
        float width = 10.0f, float depth = 10.0f, float texU = 1.0f, float texV = 1.0f,
        const DirectX::XMFLOAT4& color = { 1.0f, 1.0f, 1.0f, 1.0f });
}

爲了可以保證支持前面提到的頂點類型,在內部實現中須要依賴VertexData產生完整的頂點數據,並根據頂點的類型進行鍼對性篩選保留,並輸出到對應的頂點類型。其中篩選的核心,一是要創建元素語義與VertexData對應內存區間關係,二是要肯定當前頂點須要插入的字節偏移位置。關於第一點,在下面的代碼中,咱們直接利用map創建映射關係

namespace Geometry
{
    namespace Internal
    {
        //
        // 如下結構體和函數僅供內部實現使用
        //

        struct VertexData
        {
            DirectX::XMFLOAT3 pos;
            DirectX::XMFLOAT3 normal;
            DirectX::XMFLOAT4 tangent;
            DirectX::XMFLOAT4 color;
            DirectX::XMFLOAT2 tex;
        };

        // 根據目標頂點類型選擇性將數據插入
        template<class VertexType>
        inline void InsertVertexElement(VertexType& vertexDst, const VertexData& vertexSrc)
        {
            static std::string semanticName;
            static const std::map<std::string, std::pair<size_t, size_t>> semanticSizeMap = {
                {"POSITION", std::pair<size_t, size_t>(0, 12)},
                {"NORMAL", std::pair<size_t, size_t>(12, 24)},
                {"TANGENT", std::pair<size_t, size_t>(24, 40)},
                {"COLOR", std::pair<size_t, size_t>(40, 56)},
                {"TEXCOORD", std::pair<size_t, size_t>(56, 64)}
            };

            for (size_t i = 0; i < ARRAYSIZE(VertexType::inputLayout); i++)
            {
                semanticName = VertexType::inputLayout[i].SemanticName;
                const auto& range = semanticSizeMap.at(semanticName);
                memcpy_s(reinterpret_cast<char*>(&vertexDst) + VertexType::inputLayout[i].AlignedByteOffset,
                    range.second - range.first,
                    reinterpret_cast<const char*>(&vertexSrc) + range.first,
                    range.second - range.first);
            }
        }
    }
// ...

上面的pair對應了VertexData指定語義的元素所在的內存區間。

而關於第二點,就是要肯定當前我要賦值的頂點元素目前處理到第幾個成員,其字節偏移量是多少。這些可使用頂點的成員佈局來實現,好比VertexPosNormalTex

struct VertexPosNormalTex
{
    DirectX::XMFLOAT3 pos;
    DirectX::XMFLOAT3 normal;
    DirectX::XMFLOAT2 tex;
    static const D3D11_INPUT_ELEMENT_DESC inputLayout[3];
};

const D3D11_INPUT_ELEMENT_DESC VertexPosNormalTex::inputLayout[3] = {
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};

最後就是頂點元素插入的實現:

// 根據目標頂點類型選擇性將數據插入
template<class VertexType>
inline void Geometry::InsertVertexElement(VertexType& vertexDst, const VertexData& vertexSrc)
{
    static std::string semanticName;
    for (size_t i = 0; i < ARRAYSIZE(VertexType::inputLayout); i++)
    {
        semanticName = VertexType::inputLayout[i].SemanticName;
        const auto& range = Geometry::semanticSizeMap.at(semanticName);
        memcpy_s(reinterpret_cast<char*>(&vertexDst) + VertexType::inputLayout[i].AlignedByteOffset,
            range.second - range.first,
            reinterpret_cast<const char*>(&vertexSrc) + range.first,
            range.second - range.first);
    }
}

Geometry::CreateBox函數--建立立方體

該函數會建立包含24個頂點的數組(立方體一個頂點重複3次,但法向量不相同),以及一個含36個索引的數組:

template<class VertexType, class IndexType>
inline MeshData<VertexType, IndexType> CreateBox(float width, float height, float depth, const DirectX::XMFLOAT4 & color)
{
    using namespace DirectX;

    MeshData<VertexType, IndexType> meshData;
    meshData.vertexVec.resize(24);


    Internal::VertexData vertexDataArr[24];
    float w2 = width / 2, h2 = height / 2, d2 = depth / 2;

    // 右面(+X面)
    vertexDataArr[0].pos = XMFLOAT3(w2, -h2, -d2);
    vertexDataArr[1].pos = XMFLOAT3(w2, h2, -d2);
    vertexDataArr[2].pos = XMFLOAT3(w2, h2, d2);
    vertexDataArr[3].pos = XMFLOAT3(w2, -h2, d2);
    // 左面(-X面)
    vertexDataArr[4].pos = XMFLOAT3(-w2, -h2, d2);
    vertexDataArr[5].pos = XMFLOAT3(-w2, h2, d2);
    vertexDataArr[6].pos = XMFLOAT3(-w2, h2, -d2);
    vertexDataArr[7].pos = XMFLOAT3(-w2, -h2, -d2);
    // 頂面(+Y面)
    vertexDataArr[8].pos = XMFLOAT3(-w2, h2, -d2);
    vertexDataArr[9].pos = XMFLOAT3(-w2, h2, d2);
    vertexDataArr[10].pos = XMFLOAT3(w2, h2, d2);
    vertexDataArr[11].pos = XMFLOAT3(w2, h2, -d2);
    // 底面(-Y面)
    vertexDataArr[12].pos = XMFLOAT3(w2, -h2, -d2);
    vertexDataArr[13].pos = XMFLOAT3(w2, -h2, d2);
    vertexDataArr[14].pos = XMFLOAT3(-w2, -h2, d2);
    vertexDataArr[15].pos = XMFLOAT3(-w2, -h2, -d2);
    // 背面(+Z面)
    vertexDataArr[16].pos = XMFLOAT3(w2, -h2, d2);
    vertexDataArr[17].pos = XMFLOAT3(w2, h2, d2);
    vertexDataArr[18].pos = XMFLOAT3(-w2, h2, d2);
    vertexDataArr[19].pos = XMFLOAT3(-w2, -h2, d2);
    // 正面(-Z面)
    vertexDataArr[20].pos = XMFLOAT3(-w2, -h2, -d2);
    vertexDataArr[21].pos = XMFLOAT3(-w2, h2, -d2);
    vertexDataArr[22].pos = XMFLOAT3(w2, h2, -d2);
    vertexDataArr[23].pos = XMFLOAT3(w2, -h2, -d2);

    for (UINT i = 0; i < 4; ++i)
    {
        // 右面(+X面)
        vertexDataArr[i].normal = XMFLOAT3(1.0f, 0.0f, 0.0f);
        vertexDataArr[i].tangent = XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f);
        vertexDataArr[i].color = color;
        // 左面(-X面)
        vertexDataArr[i + 4].normal = XMFLOAT3(-1.0f, 0.0f, 0.0f);
        vertexDataArr[i + 4].tangent = XMFLOAT4(0.0f, 0.0f, -1.0f, 1.0f);
        vertexDataArr[i + 4].color = color;
        // 頂面(+Y面)
        vertexDataArr[i + 8].normal = XMFLOAT3(0.0f, 1.0f, 0.0f);
        vertexDataArr[i + 8].tangent = XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f);
        vertexDataArr[i + 8].color = color;
        // 底面(-Y面)
        vertexDataArr[i + 12].normal = XMFLOAT3(0.0f, -1.0f, 0.0f);
        vertexDataArr[i + 12].tangent = XMFLOAT4(-1.0f, 0.0f, 0.0f, 1.0f);
        vertexDataArr[i + 12].color = color;
        // 背面(+Z面)
        vertexDataArr[i + 16].normal = XMFLOAT3(0.0f, 0.0f, 1.0f);
        vertexDataArr[i + 16].tangent = XMFLOAT4(-1.0f, 0.0f, 0.0f, 1.0f);
        vertexDataArr[i + 16].color = color;
        // 正面(-Z面)
        vertexDataArr[i + 20].normal = XMFLOAT3(0.0f, 0.0f, -1.0f);
        vertexDataArr[i + 20].tangent = XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f);
        vertexDataArr[i + 20].color = color;
    }

    for (UINT i = 0; i < 6; ++i)
    {
        vertexDataArr[i * 4].tex = XMFLOAT2(0.0f, 1.0f);
        vertexDataArr[i * 4 + 1].tex = XMFLOAT2(0.0f, 0.0f);
        vertexDataArr[i * 4 + 2].tex = XMFLOAT2(1.0f, 0.0f);
        vertexDataArr[i * 4 + 3].tex = XMFLOAT2(1.0f, 1.0f);
    }

    for (UINT i = 0; i < 24; ++i)
    {
        Internal::InsertVertexElement(meshData.vertexVec[i], vertexDataArr[i]);
    }

    meshData.indexVec = {
        0, 1, 2, 2, 3, 0,       // 右面(+X面)
        4, 5, 6, 6, 7, 4,       // 左面(-X面)
        8, 9, 10, 10, 11, 8,    // 頂面(+Y面)
        12, 13, 14, 14, 15, 12, // 底面(-Y面)
        16, 17, 18, 18, 19, 16, // 背面(+Z面)
        20, 21, 22, 22, 23, 20  // 正面(-Z面)
    };

    return meshData;
}

Geometry::CreateSphere函數--建立球體

因爲3D模型都是用三角形模擬的,這裏的球體若是想要效果更佳逼真,須要用到更多的三角形。球體的法向量如前面所述,使用微分法求出。在提供參數的時候,levels決定上下分多少層,slices決定一個水平圓切面的頂點數目。levelsslices越高,生成的頂點數、索引數都會越多。

template<class VertexType, class IndexType>
inline MeshData<VertexType, IndexType> CreateSphere(float radius, UINT levels, UINT slices, const DirectX::XMFLOAT4 & color)
{
    using namespace DirectX;

    MeshData<VertexType, IndexType> meshData;
    UINT vertexCount = 2 + (levels - 1) * (slices + 1);
    UINT indexCount = 6 * (levels - 1) * slices;
    meshData.vertexVec.resize(vertexCount);
    meshData.indexVec.resize(indexCount);

    Internal::VertexData vertexData;
    IndexType vIndex = 0, iIndex = 0;

    float phi = 0.0f, theta = 0.0f;
    float per_phi = XM_PI / levels;
    float per_theta = XM_2PI / slices;
    float x, y, z;

    // 放入頂端點
    vertexData = { XMFLOAT3(0.0f, radius, 0.0f), XMFLOAT3(0.0f, 1.0f, 0.0f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f), color, XMFLOAT2(0.0f, 0.0f) };
    Internal::InsertVertexElement(meshData.vertexVec[vIndex++], vertexData);

    for (UINT i = 1; i < levels; ++i)
    {
        phi = per_phi * i;
        // 須要slices + 1個頂點是由於 起點和終點需爲同一點,但紋理座標值不一致
        for (UINT j = 0; j <= slices; ++j)
        {
            theta = per_theta * j;
            x = radius * sinf(phi) * cosf(theta);
            y = radius * cosf(phi);
            z = radius * sinf(phi) * sinf(theta);
            // 計算出局部座標、法向量、Tangent向量和紋理座標
            XMFLOAT3 pos = XMFLOAT3(x, y, z), normal;
            XMStoreFloat3(&normal, XMVector3Normalize(XMLoadFloat3(&pos)));

            vertexData = { pos, normal, XMFLOAT4(-sinf(theta), 0.0f, cosf(theta), 1.0f), color, XMFLOAT2(theta / XM_2PI, phi / XM_PI) };
            Internal::InsertVertexElement(meshData.vertexVec[vIndex++], vertexData);
        }
    }

    // 放入底端點
    vertexData = { XMFLOAT3(0.0f, -radius, 0.0f), XMFLOAT3(0.0f, -1.0f, 0.0f),
        XMFLOAT4(-1.0f, 0.0f, 0.0f, 1.0f), color, XMFLOAT2(0.0f, 1.0f) };
    Internal::InsertVertexElement(meshData.vertexVec[vIndex++], vertexData);


    // 逐漸放入索引
    if (levels > 1)
    {
        for (UINT j = 1; j <= slices; ++j)
        {
            meshData.indexVec[iIndex++] = 0;
            meshData.indexVec[iIndex++] = j % (slices + 1) + 1;
            meshData.indexVec[iIndex++] = j;
        }
    }


    for (UINT i = 1; i < levels - 1; ++i)
    {
        for (UINT j = 1; j <= slices; ++j)
        {
            meshData.indexVec[iIndex++] = (i - 1) * (slices + 1) + j;
            meshData.indexVec[iIndex++] = (i - 1) * (slices + 1) + j % (slices + 1) + 1;
            meshData.indexVec[iIndex++] = i * (slices + 1) + j % (slices + 1) + 1;

            meshData.indexVec[iIndex++] = i * (slices + 1) + j % (slices + 1) + 1;
            meshData.indexVec[iIndex++] = i * (slices + 1) + j;
            meshData.indexVec[iIndex++] = (i - 1) * (slices + 1) + j;
        }
    }

    // 逐漸放入索引
    if (levels > 1)
    {
        for (UINT j = 1; j <= slices; ++j)
        {
            meshData.indexVec[iIndex++] = (levels - 2) * (slices + 1) + j;
            meshData.indexVec[iIndex++] = (levels - 2) * (slices + 1) + j % (slices + 1) + 1;
            meshData.indexVec[iIndex++] = (levels - 1) * (slices + 1) + 1;
        }
    }


    return meshData;
}

Geometry::CreateCylinderNoCap函數--建立圓柱體的側面

因爲後續項目要用到無上下圓面的圓柱體,故拆分出來單獨實現:

template<class VertexType, class IndexType>
inline MeshData<VertexType, IndexType> CreateCylinderNoCap(float radius, float height, UINT slices, const DirectX::XMFLOAT4 & color)
{
    using namespace DirectX;

    MeshData<VertexType, IndexType> meshData;
    UINT vertexCount = 2 * (slices + 1);
    UINT indexCount = 6 * slices;
    meshData.vertexVec.resize(vertexCount);
    meshData.indexVec.resize(indexCount);

    float h2 = height / 2;
    float theta = 0.0f;
    float per_theta = XM_2PI / slices;

    Internal::VertexData vertexData;

    // 放入側面頂端點
    for (UINT i = 0; i <= slices; ++i)
    {
        theta = i * per_theta;
        vertexData = { XMFLOAT3(radius * cosf(theta), h2, radius * sinf(theta)), XMFLOAT3(cosf(theta), 0.0f, sinf(theta)),
            XMFLOAT4(-sinf(theta), 0.0f, cosf(theta), 1.0f), color, XMFLOAT2(theta / XM_2PI, 0.0f) };
        Internal::InsertVertexElement(meshData.vertexVec[i], vertexData);
    }

    // 放入側面底端點
    for (UINT i = 0; i <= slices; ++i)
    {
        theta = i * per_theta;
        vertexData = { XMFLOAT3(radius * cosf(theta), -h2, radius * sinf(theta)), XMFLOAT3(cosf(theta), 0.0f, sinf(theta)),
            XMFLOAT4(-sinf(theta), 0.0f, cosf(theta), 1.0f), color, XMFLOAT2(theta / XM_2PI, 1.0f) };
        UINT vIndex = (slices + 1) + i;
        Internal::InsertVertexElement(meshData.vertexVec[vIndex], vertexData);
    }

    // 放入索引
    UINT iIndex = 0;

    for (UINT i = 0; i < slices; ++i)
    {
        meshData.indexVec[iIndex++] = i;
        meshData.indexVec[iIndex++] = i + 1;
        meshData.indexVec[iIndex++] = (slices + 1) + i + 1;

        meshData.indexVec[iIndex++] = (slices + 1) + i + 1;
        meshData.indexVec[iIndex++] = (slices + 1) + i;
        meshData.indexVec[iIndex++] = i;
    }


    return meshData;
}

Geometry::CreateCylinder函數--建立圓柱體

圓柱體側面能夠調用上面的函數,剩下的兩個圓蓋在這完成:

template<class VertexType, class IndexType>
inline MeshData<VertexType, IndexType> CreateCylinder(float radius, float height, UINT slices, const DirectX::XMFLOAT4 & color)
{
    using namespace DirectX;

    auto meshData = CreateCylinderNoCap<VertexType, IndexType>(radius, height, slices, color);
    UINT vertexCount = 4 * (slices + 1) + 2;
    UINT indexCount = 12 * slices;
    meshData.vertexVec.resize(vertexCount);
    meshData.indexVec.resize(indexCount);

    float h2 = height / 2;
    float theta = 0.0f;
    float per_theta = XM_2PI / slices;

    IndexType vIndex = 2 * (slices + 1), iIndex = 6 * slices;
    IndexType offset = 2 * (slices + 1);
    Internal::VertexData vertexData;

    // 放入頂端圓心
    vertexData = { XMFLOAT3(0.0f, h2, 0.0f), XMFLOAT3(0.0f, 1.0f, 0.0f),
                  XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f), color, XMFLOAT2(0.5f, 0.5f) };
    Internal::InsertVertexElement(meshData.vertexVec[vIndex++], vertexData);

    // 放入頂端圓上各點
    for (UINT i = 0; i <= slices; ++i)
    {
        theta = i * per_theta;
        vertexData = { XMFLOAT3(radius * cosf(theta), h2, radius * sinf(theta)), XMFLOAT3(0.0f, 1.0f, 0.0f),
                      XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f), color, XMFLOAT2(cosf(theta) / 2 + 0.5f, sinf(theta) / 2 + 0.5f) };
        Internal::InsertVertexElement(meshData.vertexVec[vIndex++], vertexData);
    }

    // 放入底端圓心
    vertexData = { XMFLOAT3(0.0f, -h2, 0.0f), XMFLOAT3(0.0f, -1.0f, 0.0f),
                  XMFLOAT4(-1.0f, 0.0f, 0.0f, 1.0f), color, XMFLOAT2(0.5f, 0.5f) };
    Internal::InsertVertexElement(meshData.vertexVec[vIndex++], vertexData);

    // 放入底部圓上各點
    for (UINT i = 0; i <= slices; ++i)
    {
        theta = i * per_theta;
        vertexData = { XMFLOAT3(radius * cosf(theta), -h2, radius * sinf(theta)), XMFLOAT3(0.0f, -1.0f, 0.0f),
                      XMFLOAT4(-1.0f, 0.0f, 0.0f, 1.0f), color, XMFLOAT2(cosf(theta) / 2 + 0.5f, sinf(theta) / 2 + 0.5f) };
        Internal::InsertVertexElement(meshData.vertexVec[vIndex++], vertexData);
    }



    // 逐漸放入頂部三角形索引
    for (UINT i = 1; i <= slices; ++i)
    {
        meshData.indexVec[iIndex++] = offset;
        meshData.indexVec[iIndex++] = offset + i % (slices + 1) + 1;
        meshData.indexVec[iIndex++] = offset + i;
    }

    // 逐漸放入底部三角形索引
    offset += slices + 2;
    for (UINT i = 1; i <= slices; ++i)
    {
        meshData.indexVec[iIndex++] = offset;
        meshData.indexVec[iIndex++] = offset + i;
        meshData.indexVec[iIndex++] = offset + i % (slices + 1) + 1;
    }

    return meshData;
}

Geometry::CreateConeNoCap函數--建立圓錐體的側面

該函數不包含底部的圓形面,注意圓錐的尖端由於各自所處的三角形法向量不一致,須要建立和底部圓相同的數目的頂點:

template<class VertexType, class IndexType>
    MeshData<VertexType, IndexType> CreateConeNoCap(float radius, float height, UINT slices, const DirectX::XMFLOAT4& color)
{
    using namespace DirectX;

    MeshData<VertexType, IndexType> meshData;
    meshData.vertexVec.resize(2 * slices);
    meshData.indexVec.resize(3 * slices);

    float h2 = height / 2;
    float theta = 0.0f;
    float per_theta = XM_2PI / slices;
    float len = sqrtf(height * height + radius * radius);
    UINT iIndex = 0;
    UINT vIndex = 0;
    Internal::VertexData vertexData;

    // 放入圓錐尖端頂點(每一個頂點包含不一樣的法向量和切線向量)
    for (UINT i = 0; i < slices; ++i)
    {
        theta = i * per_theta + per_theta / 2;
        vertexData = { XMFLOAT3(0.0f, h2, 0.0f), XMFLOAT3(radius * cosf(theta) / len, height / len, radius * sinf(theta) / len),
                      XMFLOAT4(-sinf(theta), 0.0f, cosf(theta), 1.0f), color, XMFLOAT2(0.5f, 0.5f) };
        Internal::InsertVertexElement(meshData.vertexVec[vIndex++], vertexData);
    }

    // 放入圓錐底面頂點
    for (UINT i = 0; i < slices; ++i)
    {
        theta = i * per_theta;
        vertexData = { XMFLOAT3(radius * cosf(theta), -h2, radius * sinf(theta)), XMFLOAT3(radius * cosf(theta) / len, height / len, radius * sinf(theta) / len),
                      XMFLOAT4(-sinf(theta), 0.0f, cosf(theta), 1.0f), color, XMFLOAT2(cosf(theta) / 2 + 0.5f, sinf(theta) / 2 + 0.5f) };
        Internal::InsertVertexElement(meshData.vertexVec[vIndex++], vertexData);
    }

    // 放入索引
    for (UINT i = 0; i < slices; ++i)
    {
        meshData.indexVec[iIndex++] = i;
        meshData.indexVec[iIndex++] = slices + (i + 1) % slices;
        meshData.indexVec[iIndex++] = slices + i % slices;
    }

    return meshData;
}

Geometry::CreateCone函數--建立圓錐體

template<class VertexType, class IndexType>
    MeshData<VertexType, IndexType> CreateCone(float radius, float height, UINT slices, const DirectX::XMFLOAT4& color)
{
    using namespace DirectX;
    auto meshData = CreateConeNoCap<VertexType, IndexType>(radius, height, slices, color);

    UINT vertexCount = 3 * slices + 1;
    UINT indexCount = 6 * slices;
    meshData.vertexVec.resize(vertexCount);
    meshData.indexVec.resize(indexCount);

    float h2 = height / 2;
    float theta = 0.0f;
    float per_theta = XM_2PI / slices;
    UINT iIndex = 3 * slices;
    UINT vIndex = 2 * slices;
    Internal::VertexData vertexData;

    // 放入圓錐底面頂點
    for (UINT i = 0; i < slices; ++i)
    {
        theta = i * per_theta;
        vertexData = { XMFLOAT3(radius * cosf(theta), -h2, radius * sinf(theta)), XMFLOAT3(0.0f, -1.0f, 0.0f),
                      XMFLOAT4(-1.0f, 0.0f, 0.0f, 1.0f), color, XMFLOAT2(cosf(theta) / 2 + 0.5f, sinf(theta) / 2 + 0.5f) };
        Internal::InsertVertexElement(meshData.vertexVec[vIndex++], vertexData);
    }
    vertexData = { XMFLOAT3(0.0f, -h2, 0.0f), XMFLOAT3(0.0f, -1.0f, 0.0f),
                  XMFLOAT4(-1.0f, 0.0f, 0.0f, 1.0f), color, XMFLOAT2(0.5f, 0.5f) };
    Internal::InsertVertexElement(meshData.vertexVec[vIndex++], vertexData);

    // 放入索引
    UINT offset = 2 * slices;
    for (UINT i = 0; i < slices; ++i)
    {
        meshData.indexVec[iIndex++] = offset + slices;
        meshData.indexVec[iIndex++] = offset + i % slices;
        meshData.indexVec[iIndex++] = offset + (i + 1) % slices;
    }

    return meshData;
}

剩下關於平面的建立能夠在項目源碼中閱讀。

光柵化狀態

Direct3D是基於狀態機的,咱們能夠經過修改這些狀態來修改渲染管線的當前行爲。有三種狀態值得咱們如今以及後續留意:

  1. 光柵化狀態(光柵化階段)
  2. 混合狀態(輸出合併階段)
  3. 深度/模板狀態(輸出合併階段)

光柵化階段儘管是不可編程的,但在該階段須要完成許多任務:

  1. 頂點着色器、幾何着色器完成了頂點輸出後,光柵化階段會負責對前面傳來的頂點數據,尤爲是對4D位置向量(SV_POSITION)進行透視除法,判斷頂點是否在NDC空間內。
  2. 其次,根據光柵化狀態來決定頂點順(逆)時針排布的三角形是否經過。
  3. 它還能指定額外的裁剪區域,即矩形區域外的三角形(或者部分)被裁剪掉,僅留下剩餘在矩形區域內的像素片元傳遞到像素着色器中進行處理。
  4. 根據三角形的三個頂點,光柵化就能夠經過線性插值法對頂點內的全部成員(如位置,顏色,法向量等)進行插值運算,並根據視口信息計算出位於三角形內的全部像素頂點以傳遞給像素着色器,也正由於如此你才能在第二章看到高洛德着色(Gouraud Shading)呈現的漸變效果。
  5. 因爲光柵化階段會進行視口變換,在像素着色器中,SV_POSITIONx份量和y份量都已經通過視口變換成爲最終的屏幕座標,且帶有小數點0.5,這是由於要取到像素的中心位置,即對於800x600的視口區域,實際上的屏幕座標取值範圍爲[0.5, 800.5]x[0.5, 600.5]z份量取值範圍爲[0, 1]。這一點讀者能夠修改像素着色器使得SV_POSITION與像素顏色結果有關聯,而後進入調試以驗證。

ID3D11Device::CreateRasterizerState方法--建立光柵化狀態

在建立光柵化狀態前,咱們須要先填充D3D11_RASTERIZER_DESC結構體來描述光柵化狀態:

typedef struct D3D11_RASTERIZER_DESC
{
    D3D11_FILL_MODE FillMode;          // 填充模式
    D3D11_CULL_MODE CullMode;          // 裁剪模式
    BOOL FrontCounterClockwise;        // 是否三角形頂點按逆時針排布時爲正面
    INT DepthBias;                     // 忽略
    FLOAT DepthBiasClamp;              // 忽略
    FLOAT SlopeScaledDepthBias;        // 忽略
    BOOL DepthClipEnable;              // 是否容許深度測試將範圍外的像素進行裁剪,默認TRUE
    BOOL ScissorEnable;                // 是否容許指定矩形範圍的裁剪,若TRUE,則須要在RSSetScissor設置像素保留的矩形區域
    BOOL MultisampleEnable;            // 是否容許多重採樣
    BOOL AntialiasedLineEnable;        // 是否容許反走樣線,僅當多重採樣爲FALSE時纔有效
}   D3D11_RASTERIZER_DESC;

對於枚舉類型D3D11_FILL_MODE有以下枚舉值:

枚舉值 含義
D3D11_FILL_WIREFRAME = 2 線框填充方式
D3D11_FILL_SOLID = 3 面填充方式

枚舉類型D3D11_CULL_MODE有以下枚舉值:

枚舉值 含義
D3D11_CULL_NONE = 1 無背面裁剪,即三角形不管處在視野的正面仍是背面都能看到
D3D11_CULL_FRONT = 2 對處在視野正面的三角形進行裁剪
D3D11_CULL_BACK = 3 對處在視野背面的三角形進行裁剪

光柵化建立的方法以下:

HRESULT ID3D11Device::CreateRasterizerState( 
    const D3D11_RASTERIZER_DESC *pRasterizerDesc,    // [In]光柵化狀態描述
    ID3D11RasterizerState **ppRasterizerState) = 0;  // [Out]輸出光柵化狀態

ID3D11DeviceContext::RSSetState方法--設置光柵化狀態

void ID3D11DeviceContext::RSSetState(
  ID3D11RasterizerState *pRasterizerState);  // [In]光柵化狀態,若爲nullptr,則使用默認光柵化狀態

默認光柵化狀態以下:

FillMode = D3D11_FILL_SOLID;
CullMode = D3D11_CULL_BACK;
FrontCounterClockwise = FALSE;
DepthBias = 0;
SlopeScaledDepthBias = 0.0f;
DepthBiasClamp = 0.0f;
DepthClipEnable = TRUE;
ScissorEnable = FALSE;
MultisampleEnable = FALSE;
AntialiasedLineEnable = FALSE;

繪製線框

繪製線框有兩種方式:

  1. 在輸入裝配階段,以圖元linelist的方式進行裝配,而後還修改索引緩衝區,使得每兩個索引對應一條線。
  2. 修改光柵化階段,以線框(wireframe)的方式進行像素點的標記。

顯然,第2種方式操做起來會更加容易一些。

首先咱們須要建立線框繪製的光柵化狀態:

// ******************
// 初始化光柵化狀態
//
D3D11_RASTERIZER_DESC rasterizerDesc;
ZeroMemory(&rasterizerDesc, sizeof(rasterizerDesc));
rasterizerDesc.FillMode = D3D11_FILL_WIREFRAME;
rasterizerDesc.CullMode = D3D11_CULL_NONE;
rasterizerDesc.FrontCounterClockwise = false;
rasterizerDesc.DepthClipEnable = true;
HR(m_pd3dDevice->CreateRasterizerState(&rasterizerDesc, m_pRSWireframe.GetAddressOf()));

而後繪製線框的時候只須要這樣調用:

m_pd3dImmediateContext->RSSetState(m_pRSWireframe.Get());

若是想要恢復正常的面繪製,上述傳入nullptr便可。

GameApp類

GameApp類的變化以下:

class GameApp : public D3DApp
{
public:

    struct VSConstantBuffer
    {
        DirectX::XMMATRIX world;
        DirectX::XMMATRIX view;
        DirectX::XMMATRIX proj;
        DirectX::XMMATRIX worldInvTranspose;
        
    };

    struct PSConstantBuffer
    {
        DirectionalLight dirLight;
        PointLight pointLight;
        SpotLight spotLight;
        Material material;
        DirectX::XMFLOAT4 eyePos;
    };



public:
    GameApp(HINSTANCE hInstance);
    ~GameApp();

    bool Init();
    void OnResize();
    void UpdateScene(float dt);
    void DrawScene();


private:
    bool InitEffect();
    bool InitResource();
    bool ResetMesh(const Geometry::MeshData<VertexPosNormalColor>& meshData);


private:
    ComPtr<ID3D11InputLayout> m_pVertexLayout;      // 頂點輸入佈局
    ComPtr<ID3D11Buffer> m_pVertexBuffer;           // 頂點緩衝區
    ComPtr<ID3D11Buffer> m_pIndexBuffer;            // 索引緩衝區
    ComPtr<ID3D11Buffer> m_pConstantBuffers[2];     // 常量緩衝區
    UINT m_IndexCount;                              // 繪製物體的索引數組大小

    ComPtr<ID3D11VertexShader> m_pVertexShader;     // 頂點着色器
    ComPtr<ID3D11PixelShader> m_pPixelShader;       // 像素着色器
    VSConstantBuffer m_VSConstantBuffer;            // 用於修改用於VS的GPU常量緩衝區的變量
    PSConstantBuffer m_PSConstantBuffer;            // 用於修改用於PS的GPU常量緩衝區的變量

    DirectionalLight m_DirLight;                    // 默認環境光
    PointLight m_PointLight;                        // 默認點光
    SpotLight m_SpotLight;                          // 默認匯聚光

    ComPtr<ID3D11RasterizerState> m_pRSWireframe;   // 光柵化狀態: 線框模式
    bool m_IsWireframeMode;                         // 當前是否爲線框模式
    
};

其中,LightHelper.h包含了前面描述的材質和三種光照模型的結構體

GameApp類中則新添加了幾個結構體和對應的一些成員。

GameApp::ResetMesh方法--從新設置要使用的模型

該項目爲了演示不一樣3D模型下的光照效果,建立了該方法用於從新設置要使用的模型,但在這裏物體表面的顏色默認都被設置爲白色。實現以下:

bool GameApp::ResetMesh(const Geometry::MeshData<VertexPosNormalColor>& meshData)
{
    // 釋放舊資源
    m_pVertexBuffer.Reset();
    m_pIndexBuffer.Reset();

    // 設置頂點緩衝區描述
    D3D11_BUFFER_DESC vbd;
    ZeroMemory(&vbd, sizeof(vbd));
    vbd.Usage = D3D11_USAGE_IMMUTABLE;
    vbd.ByteWidth = (UINT)meshData.vertexVec.size() * sizeof(VertexPosNormalColor);
    vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    vbd.CPUAccessFlags = 0;
    // 新建頂點緩衝區
    D3D11_SUBRESOURCE_DATA InitData;
    ZeroMemory(&InitData, sizeof(InitData));
    InitData.pSysMem = meshData.vertexVec.data();
    HR(m_pd3dDevice->CreateBuffer(&vbd, &InitData, m_pVertexBuffer.GetAddressOf()));

    // 輸入裝配階段的頂點緩衝區設置
    UINT stride = sizeof(VertexPosNormalColor); // 跨越字節數
    UINT offset = 0;                            // 起始偏移量

    m_pd3dImmediateContext->IASetVertexBuffers(0, 1, m_pVertexBuffer.GetAddressOf(), &stride, &offset);



    // 設置索引緩衝區描述
    m_IndexCount = (UINT)meshData.indexVec.size();
    D3D11_BUFFER_DESC ibd;
    ZeroMemory(&ibd, sizeof(ibd));
    ibd.Usage = D3D11_USAGE_IMMUTABLE;
    ibd.ByteWidth = m_IndexCount * sizeof(WORD);
    ibd.BindFlags = D3D11_BIND_INDEX_BUFFER;
    ibd.CPUAccessFlags = 0;
    // 新建索引緩衝區
    InitData.pSysMem = meshData.indexVec.data();
    HR(m_pd3dDevice->CreateBuffer(&ibd, &InitData, m_pIndexBuffer.GetAddressOf()));
    // 輸入裝配階段的索引緩衝區設置
    m_pd3dImmediateContext->IASetIndexBuffer(m_pIndexBuffer.Get(), DXGI_FORMAT_R16_UINT, 0);

    return true;
}

GameApp::InitResource方法的變化

該方法初始化模型後,就會初始化常量緩衝區的數據,以及一些光照模型和物體材質。矩陣須要注意轉置:

bool GameApp::InitResource()
{
    // ******************
    // 初始化網格模型
    //
    auto meshData = Geometry::CreateBox<VertexPosNormalColor>();
    ResetMesh(meshData);


    // ******************
    // 設置常量緩衝區描述
    //
    D3D11_BUFFER_DESC cbd;
    ZeroMemory(&cbd, sizeof(cbd));
    cbd.Usage = D3D11_USAGE_DYNAMIC;
    cbd.ByteWidth = sizeof(VSConstantBuffer);
    cbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    cbd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    // 新建用於VS和PS的常量緩衝區
    HR(m_pd3dDevice->CreateBuffer(&cbd, nullptr, m_pConstantBuffers[0].GetAddressOf()));
    cbd.ByteWidth = sizeof(PSConstantBuffer);
    HR(m_pd3dDevice->CreateBuffer(&cbd, nullptr, m_pConstantBuffers[1].GetAddressOf()));

    // ******************
    // 初始化默認光照
    // 方向光
    m_DirLight.ambient = XMFLOAT4(0.2f, 0.2f, 0.2f, 1.0f);
    m_DirLight.diffuse = XMFLOAT4(0.8f, 0.8f, 0.8f, 1.0f);
    m_DirLight.specular = XMFLOAT4(0.5f, 0.5f, 0.5f, 1.0f);
    m_DirLight.direction = XMFLOAT3(-0.577f, -0.577f, 0.577f);
    // 點光
    m_PointLight.position = XMFLOAT3(0.0f, 0.0f, -10.0f);
    m_PointLight.ambient = XMFLOAT4(0.3f, 0.3f, 0.3f, 1.0f);
    m_PointLight.diffuse = XMFLOAT4(0.7f, 0.7f, 0.7f, 1.0f);
    m_PointLight.specular = XMFLOAT4(0.5f, 0.5f, 0.5f, 1.0f);
    m_PointLight.att = XMFLOAT3(0.0f, 0.1f, 0.0f);
    m_PointLight.range = 25.0f;
    // 聚光燈
    m_SpotLight.position = XMFLOAT3(0.0f, 0.0f, -5.0f);
    m_SpotLight.direction = XMFLOAT3(0.0f, 0.0f, 1.0f);
    m_SpotLight.ambient = XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f);
    m_SpotLight.diffuse = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
    m_SpotLight.specular = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
    m_SpotLight.att = XMFLOAT3(1.0f, 0.0f, 0.0f);
    m_SpotLight.spot = 12.0f;
    m_SpotLight.range = 10000.0f;
    // 初始化用於VS的常量緩衝區的值
    m_VSConstantBuffer.world = XMMatrixIdentity();          
    m_VSConstantBuffer.view = XMMatrixTranspose(XMMatrixLookAtLH(
        XMVectorSet(0.0f, 0.0f, -5.0f, 0.0f),
        XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f),
        XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f)
    ));
    m_VSConstantBuffer.proj = XMMatrixTranspose(XMMatrixPerspectiveFovLH(XM_PIDIV2, AspectRatio(), 1.0f, 1000.0f));
    m_VSConstantBuffer.worldInvTranspose = XMMatrixIdentity();
    
    // 初始化用於PS的常量緩衝區的值
    m_PSConstantBuffer.material.ambient = XMFLOAT4(0.5f, 0.5f, 0.5f, 1.0f);
    m_PSConstantBuffer.material.diffuse = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
    m_PSConstantBuffer.material.specular = XMFLOAT4(0.5f, 0.5f, 0.5f, 5.0f);
    // 使用默認平行光
    m_PSConstantBuffer.dirLight = m_DirLight;
    // 注意不要忘記設置此處的觀察位置,不然高亮部分會有問題
    m_PSConstantBuffer.eyePos = XMFLOAT4(0.0f, 0.0f, -5.0f, 0.0f);

    // 更新PS常量緩衝區資源
    D3D11_MAPPED_SUBRESOURCE mappedData;
    HR(m_pd3dImmediateContext->Map(m_pConstantBuffers[1].Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedData));
    memcpy_s(mappedData.pData, sizeof(PSConstantBuffer), &m_VSConstantBuffer, sizeof(PSConstantBuffer));
    m_pd3dImmediateContext->Unmap(m_pConstantBuffers[1].Get(), 0);

    // ******************
    // 初始化光柵化狀態
    //
    D3D11_RASTERIZER_DESC rasterizerDesc;
    ZeroMemory(&rasterizerDesc, sizeof(rasterizerDesc));
    rasterizerDesc.FillMode = D3D11_FILL_WIREFRAME;
    rasterizerDesc.CullMode = D3D11_CULL_NONE;
    rasterizerDesc.FrontCounterClockwise = false;
    rasterizerDesc.DepthClipEnable = true;
    HR(m_pd3dDevice->CreateRasterizerState(&rasterizerDesc, m_pRSWireframe.GetAddressOf()));

    // ******************
    // 給渲染管線各個階段綁定好所需資源
    //

    // 設置圖元類型,設定輸入佈局
    m_pd3dImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    m_pd3dImmediateContext->IASetInputLayout(m_pVertexLayout.Get());
    // 將着色器綁定到渲染管線
    m_pd3dImmediateContext->VSSetShader(m_pVertexShader.Get(), nullptr, 0);
    // VS常量緩衝區對應HLSL寄存於b0的常量緩衝區
    m_pd3dImmediateContext->VSSetConstantBuffers(0, 1, m_pConstantBuffers[0].GetAddressOf());
    // PS常量緩衝區對應HLSL寄存於b1的常量緩衝區
    m_pd3dImmediateContext->PSSetConstantBuffers(1, 1, m_pConstantBuffers[1].GetAddressOf());
    m_pd3dImmediateContext->PSSetShader(m_pPixelShader.Get(), nullptr, 0);

    // ******************
    // 設置調試對象名
    //
    D3D11SetDebugObjectName(m_pVertexLayout.Get(), "VertexPosNormalTexLayout");
    D3D11SetDebugObjectName(m_pConstantBuffers[0].Get(), "VSConstantBuffer");
    D3D11SetDebugObjectName(m_pConstantBuffers[1].Get(), "PSConstantBuffer");
    D3D11SetDebugObjectName(m_pVertexShader.Get(), "Light_VS");
    D3D11SetDebugObjectName(m_pPixelShader.Get(), "Light_PS");

    return true;
}

GameApp::UpdateScene方法的變化

這裏排除掉鍵盤操做的代碼部分,重點須要觀察worldworldInvTranspose的賦值:

void GameApp::UpdateScene(float dt)
{
    static float phi = 0.0f, theta = 0.0f;
    phi += 0.0001f, theta += 0.00015f;
    XMMATRIX W = XMMatrixRotationX(phi) * XMMatrixRotationY(theta);
    m_VSConstantBuffer.world = XMMatrixTranspose(W);
    m_VSConstantBuffer.worldInvTranspose = XMMatrixInverse(nullptr, W); // 兩次轉置能夠抵消
    
    // ...
    
    // 更新常量緩衝區,讓立方體轉起來
    D3D11_MAPPED_SUBRESOURCE mappedData;
    HR(m_pd3dImmediateContext->Map(m_pConstantBuffers[0].Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedData));
    memcpy_s(mappedData.pData, sizeof(VSConstantBuffer), &m_VSConstantBuffer, sizeof(VSConstantBuffer));
    m_pd3dImmediateContext->Unmap(m_pConstantBuffers[0].Get(), 0);

    HR(m_pd3dImmediateContext->Map(m_pConstantBuffers[1].Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedData));
    memcpy_s(mappedData.pData, sizeof(PSConstantBuffer), &m_PSConstantBuffer, sizeof(PSConstantBuffer));
    m_pd3dImmediateContext->Unmap(m_pConstantBuffers[1].Get(), 0);

}

下面的程序按一、二、3分別對應切換平行光、點光、聚光,按Q、W、E、R分別對應切換立方體、球體、柱體和圓錐體,按S則切換面模式或線框模式:

練習題

粗體字爲自定義題目

  1. 嘗試修改本章Demo的光照,讓方向光只射出紅光,點光燈只射出綠光,聚光燈只射出藍光。
  2. 嘗試修改本章Demo所用到的材質,讓其只反射紅光。
  3. 嘗試修改本章Demo所用到的聚光燈,經過鼠標滾輪的形式,對光照匯聚強度增長/減小,範圍爲2-512,觀察效果。
  4. 嘗試修改本章Demo所用到的材質,看看若是鏡面反射強度的值小於1會發生什麼狀況。
  5. 嘗試用HLSL內建函數lit來改寫LightHelper.hlsli
  6. 實現一個函數用於建立膠囊(capsule)幾何體,須要指定上下半球的半徑(radius)、柱體部分高度(height)、球面三角形切片數(slices)和上下半球的層級數(levels),而且實現頂點要包含位置(position)、法向量(normal)和顏色信息(color)。

DirectX11 With Windows SDK完整目錄

Github項目源碼

歡迎加入QQ羣: 727623616 能夠一塊兒探討DX11,以及有什麼問題也能夠在這裏彙報。

相關文章
相關標籤/搜索