HLSL效果框架-多光源效果

高級着色器語言(HLSL)難就難在它是運行在GPU,CPU上的,編寫調試都很麻煩.

效果框架簡化了很多操作,先列出着色器的代碼,重點部分都用中文註釋

着色器語言文件爲:,代碼爲:

//着色器處理未知數量光源數目 未知光源種類 的 混合效果
//HLSL裏一共支持256個float4,一個int最後也轉成float4,float4x3和float3x3佔內存一樣,最終都變成float4
//因爲HLSL的限制條件非常零散也非常多,所以,用起來還是很吃力的,在不懂的情況下,
//最終指令不能超過500多條貌似,運行在顯卡中程序,所以能省則省,能精簡則精簡!!!

//
//結構體 注意:爲了優化結構,把一些相關參數整合到了一起
//
struct PointLight	//點光源 結構
{
	float4 Diffuse;		//漫反射的顏色r,g,b,a
	float4 Specular;	//鏡面高光的顏色r,g,b,a
	float4 Position;	//光源位置x,y,z,w沒用到	
	float4 RangeAttenuation;//範圍,恆定衰減,光強,二次衰減
};

struct DirectLight	//方向光 結構
{
	float4 Diffuse;		//漫反射的顏色r,g,b,a
	float4 Specular;	//鏡面高光的顏色r,g,b,a
	float4 Direction;	//方向x,y,z,高光
};

struct SpotLight	//聚光燈 結構
{
	float4 Diffuse;		//漫反射的顏色r,g,b,a
	float4 Specular;	//鏡面高光的顏色r,g,b,a
	float4 Position;	//光源位置x,y,z,w沒用到	
	float4 Direction;	//方向x,y,z,w沒用到	
	float4 RangeAttenuation;//範圍,恆定衰減,光強,二次衰減
	float4 FalloffThetaPhi;	//從內錐到外錐之間的強度衰減,y沒用到,內錐的弧度,外錐的弧度
};

//接收光源的數量
int g_NumPLs;	//程序裏定義了幾個點光源?
int g_NumDLs;	//程序裏定義了幾個方向光源?
int g_NumSLs;	//程序裏定義了幾個方向光源?

PointLight		g_PLs[10];	//定義最多支持10個點光源
DirectLight		g_DLs[10];	//定義最多支持10個方向光源
SpotLight		g_SLs[10];	//定義最多支持10個聚光燈光源

//環境光(對於多燈光的場合,對每一個燈光循環進行這些運算(環境光除外))
//最後加上環境光(假設只有一個環境光)

matrix g_WorldMatrix;//世界矩陣
float4 g_ViewPos;//觀察點(相機)
matrix WVPMatrix;	//世界-觀察-投影矩陣

float AmbAmount;	//環境光的強弱程度,介於0-1之間
float4 AmbCol = { 1.0f, 1.0f, 1.0f, 1.0f };//環境光的顏色,默認白色

//--------------------------------------------------------------------------
//				頂點着色器(注意看下面,我把頂點着色器的輸出頂點位置和法線,當作像素着色器的輸入了!!! 
//							TEXCOORD這類寄存器也沒啥用,用來保存頂點和法線數據最合適不過了,最多好像是15個)
//--------------------------------------------------------------------------
struct VS_INPUT		//輸入結構
{
	float4 position : POSITION;//輸入:座標
	float3 normal : NORMAL;	//輸入:法線
};
struct VS_OUTPUT	//輸出結構
{
	float4 position : POSITION;
	float4 vtpos : TEXCOORD0;//傳入像素着色器用,TEXCOORD表示寄存器名字,(實際上保存的數據當像素着色器的輸入座標用,用TEXCOORD,一方面是多,二方面沒啥用)
	float3 normal : TEXCOORD1;
};

VS_OUTPUT VS_Main(VS_INPUT input)
{
	VS_OUTPUT output = (VS_OUTPUT)0;
	output.vtpos = input.position;//模型本地空間座標
	output.position = mul(input.position, WVPMatrix);//輸出:世界-觀察-投影變幻後的座標
	output.normal = input.normal;
	return output;
}

//--------------------------------------------------------------------------
//				像素着色器(COLOR0是必須輸出的,其他的可作任何用途)
//--------------------------------------------------------------------------
struct PS_INPUT		//輸入
{
	float4 vtpos : TEXCOORD0;//頂點位置(接受頂點着色器的輸出,要對應)
	float3 normal : TEXCOORD1;
};
struct PS_OUTPUT	//輸出像素顏色
{
	float4 Color : COLOR0;	//像素着色器最終輸出計算好的顏色
};
PS_OUTPUT PS_Main(PS_INPUT input)
{
	PS_OUTPUT output = (PS_OUTPUT)0;

	float4 worldpos = mul(input.vtpos, g_WorldMatrix);//頂點在世界空間中的座標,即乘以世界矩陣
	float4 colRes;//用於保存最終的顏色 = E環境光+自身光+E(點光+方向光+聚光); 其中點,方向,聚光 又包含漫反射,鏡面反射
	float4 ambient = { 0.0f, 0.0f, 0.0f, 1.0f };//總環境光
	float4 self = { 0.0f, 0.0f, 0.0f, 1.0f };//自發光
	float4 diffuse = { 0.0f, 0.0f, 0.0f, 1.0f };//總漫反射光
	float4 specular = { 0.0f, 0.0f, 0.0f, 1.0f };//總鏡面反射

	ambient = AmbCol * AmbAmount;//求出環境光

	
	for (int i = 0; i < g_NumPLs; i++)//點光源
	{
		float3 dirSize = input.vtpos.xyz - g_PLs[i].Position.xyz;//光源到頂點的方向和大小.光源→頂點pos
		float distance = length(dirSize);//距離
		float3 dir = normalize(dirSize);//單位化方向
		if (distance < g_PLs[i].RangeAttenuation.x)//頂點在燈光範圍內
		{
			//求出漫反射
			float difusefactor = dot(dir.xyz, input.normal.xyz);
			if (difusefactor < 0)
			{
				difusefactor = 0;
			}
			float distanceDec = 1.0f - 1.0f / g_PLs[i].RangeAttenuation.x * distance;
			//漫反射 = diffuse*漫反射因子*距離衰減
			diffuse.xyz += g_PLs[i].Diffuse.xyz * difusefactor * distanceDec;//最後要乘以材質的吸收係數(這裏沒乘)

			//下面求鏡面反射(鏡面反射的算法自己去百度找吧...)
			float3 DirectionToView = normalize(worldpos.xyz - g_ViewPos.xyz);  //(同在世界空間中!)
			float3 VectorToLight = normalize(input.vtpos.xyz - g_PLs[i].Position.xyz);
				//計算反射光
			float3 reflectanceRay = 2 * dot(input.normal.xyz, VectorToLight.xyz)*input.normal.xyz - VectorToLight.xyz;
			float specfactor = pow(abs(dot(reflectanceRay, DirectionToView)), g_PLs[i].RangeAttenuation.z);
				//鏡面光累加
			specular.xyz += g_PLs[i].Specular.xyz * specfactor;//最後要乘以材質的吸收係數(這裏沒乘)
		}
		//else在光線外,無此光照影響
	}
	for (int j = 0; j < g_NumDLs; j++)//方向光源
	{
		//漫反射
		float3 dir = normalize(g_DLs[j].Direction.xyz);//單位化方向(光的方向)
		float difusefactor = dot(dir.xyz, input.normal.xyz);
		if (difusefactor < 0)
		{
			difusefactor = 0;
		}
		diffuse.xyz += g_DLs[j].Diffuse.xyz * difusefactor;//最後要乘以材質的吸收係數(這裏沒乘)

		//鏡面反射
		float3 DirectionToView = normalize(worldpos.xyz - g_ViewPos.xyz);//觀察點→頂點  (同在世界空間中!)
		//dir 已有
			//計算反射光
		float3 reflectanceRay = 2 * dot(input.normal.xyz, dir.xyz)*input.normal.xyz - dir.xyz;
		float specfactor = pow(abs(dot(reflectanceRay, DirectionToView)), g_DLs[j].Direction.w);
			//鏡面光累加
		specular.xyz += g_DLs[j].Specular.xyz * specfactor;//最後要乘以材質的吸收係數(這裏沒乘)
	}
	

	for (int k = 0; k < g_NumSLs; k++)//聚光燈
	{
		float disdec = 0.0f;//距離衰減
		float raddec = 0.0f;//角度衰減

		float distance = length(g_SLs[k].Position.xyz - worldpos.xyz);//光源到頂點的距離
		float3 xconeDirection = normalize(g_SLs[k].Direction.xyz);//聚光燈方向
		float3 ltvdir = normalize(g_SLs[k].Position.xyz - worldpos.xyz);//光到頂點的方向
		float cosx = abs(dot(xconeDirection, ltvdir));//夾角的餘弦值

		float cosPhi = cos(g_SLs[k].FalloffThetaPhi.w / 2.0f);
		float cosTheta = cos(g_SLs[k].FalloffThetaPhi.z / 2.0f);

		//距離衰減因子
		float sss = 1.0f / g_SLs[k].RangeAttenuation.x;

		if (cosx>cosTheta)//本影(角度衰減: 不衰減)
		{
			raddec = 1.0f;

			if (distance*cosx <g_SLs[k].RangeAttenuation.x)//在射程範圍內
			{
				disdec = 1.0f - 1.0f / g_SLs[k].RangeAttenuation.x*distance*cosx;//距離衰減(線性衰減)
			}
		}

		if (cosx < cosTheta)//本影外(角度衰減: 急劇衰減 )
		{
			float v1 = cosx - cosPhi;
			float v2 = cosTheta - cosPhi;
			float v3 = v1 / v2;
			raddec = pow(abs(v3), g_SLs[k].FalloffThetaPhi.x);

			if (distance*cosx <g_SLs[k].RangeAttenuation.x)//在射程範圍內(無光)
			{
				disdec = 1.0f - 1.0f / g_SLs[k].RangeAttenuation.x*distance*cosx; //距離衰減(線性衰減)
			}
		}

		if (cosx<cosPhi)//光錐外
		{
			raddec = 0.0f;
		}

		//計算漫反射
		float difusefactor = dot(ltvdir.xyz, input.normal.xyz);
		if (difusefactor < 0.0f)
		{
			difusefactor = 0.0f;
		}
		diffuse.xyz += g_SLs[k].Diffuse.xyz * raddec * disdec * difusefactor;//最後要乘以材質的吸收係數(這裏沒乘)

		//計算鏡面反射
		float3 DirectionToView = normalize(worldpos.xyz - g_ViewPos.xyz);//觀察點→頂點  (同在世界空間中!)
		float3 VectorToLight = normalize(input.vtpos.xyz - g_SLs[k].Position.xyz);
			//計算反射光
		float3 reflectanceRay = 2 * dot(input.normal.xyz, VectorToLight.xyz)*input.normal.xyz - VectorToLight.xyz;
		float specfactor = pow(abs(dot(reflectanceRay, DirectionToView)), g_SLs[k].RangeAttenuation.z);
			//鏡面光累加
		specular.xyz += g_SLs[k].Specular.xyz * raddec * disdec * specfactor;//最後要乘以材質的吸收係數(這裏沒乘)
	}
	
	output.Color.w = 1.0f;//因爲output被初始化爲(PS_OUTPUT)0,w也初始化爲0了.
	output.Color.xyz = ambient.xyz + self.xyz + diffuse.xyz + specular.xyz;//把所有種類的光都累加起來
	return output;
}

//--------------------------------------------------------------------------
//				框架效果
//--------------------------------------------------------------------------
technique MulLights
{
	pass P0
	{
		vertexShader = compile vs_3_0 VS_Main();//頂點着色器
		pixelshader = compile ps_3_0 PS_Main();//像素着色器
	}
}

值得注意的是,環境光也可以有好幾個(這裏假設只有一個),另外,光照的最終疊加效果公式爲:

最終光照顏色 = 所有的環境光 * 材質吸收指數 + 自發光 + (點光源 + 方向光源+聚光燈) * (漫反射 + 鏡面反射) *材質吸收指數        

(自己推算的公式,可能有誤,歡迎批評指出)


然後是應用程序中的代碼:

變量定義:

D3DXMATRIX m_ProjMatrix; //投影矩陣
 D3DXMATRIX m_ViewMatrix; //觀察矩陣

 //四個模型
 ID3DXMesh*    m_Meshes[4];
 D3DXMATRIX    m_WorldMatrices[4];
 D3DXVECTOR4    m_MeshColors[4];
 D3DMATERIAL9   m_Material;

 //點光源
 PointLight    m_PointLight[10];
 DirectLight   m_DirectLight[10];
 SpotLight    m_SpotLight[10];

 //-----------頂點着色器相關------------------
 D3DXHANDLE   m_WVPMatrixHandle;//世界-觀察-投影矩陣句柄
 //-----------效果框架-----------------
 ID3DXEffect*   m_NLightEffect = 0;
 //-----------------句柄------------------------
 D3DXHANDLE    m_TecniMulLightsHandle = 0; //技術句柄
 D3DXHANDLE    m_AmbAmountHandle = 0;  //環境光係數句柄
  //各種光源句柄
 D3DXHANDLE    m_PointLHandle = 0;   //點光源句柄
 D3DXHANDLE    m_DirectLightHandle = 0; //方向光句柄
 D3DXHANDLE    m_SpotLightHandle = 0;  //聚光燈句柄
  //各種光源數量句柄
 D3DXHANDLE    m_NumPointLightHandle = 0; //點光源個數句柄
 D3DXHANDLE    m_NumDirectLightHandle = 0; //方向光個數句柄
 D3DXHANDLE    m_NumSpotLightHandle = 0; //聚光燈個數句柄

 D3DXHANDLE    m_ViewPosHandle = 0;  //觀察點句柄
 D3DXHANDLE    m_WorldMatrixHandle = 0; //世界矩陣句柄

#define MESH_TEAPOT		0
#define MESH_SPHERE		1
#define MESH_TORUS		2
#define MESH_CYLINDER	3

typedef D3DXVECTOR4 float4;	//這裏typedef成float4,就能與着色器裏面一致,不需要改代碼了!

struct PointLight
{
	float4 Diffuse;		//漫反射的顏色
	float4 Specular;	//鏡面高光的顏色
	float4 Position;	//光源位置	
	float4 RangeAttenuation;//範圍,恆定衰減,鏡面光強弱,二次衰減
};

struct DirectLight
{
	float4 Diffuse;		//漫反射的顏色
	float4 Specular;	//鏡面高光的顏色
	float4 DirectionPow;	//方向x,y,z,高光
};

struct SpotLight
{
	float4 Diffuse;		//漫反射的顏色
	float4 Specular;	//鏡面高光的顏色
	float4 Position;	//光源位置	
	float4 Direction;	//方向
	float4 RangeAttenuation;//範圍,恆定衰減,鏡面光強弱,二次衰減
	float4 FalloffThetaPhi;	//從內錐到外錐之間的強度衰減,NULL,內錐的弧度,外錐的弧度
};

//setup: main函數中調用一次
	void Setup()
	{
		HRESULT hr = 0;

		//創建模型們: 茶壺 圓球 圓環 圓桶
		D3DXCreateTeapot(m_d3dDevice->GetD3DDevice(), &m_Meshes[MESH_TEAPOT], NULL);
		D3DXCreateSphere(m_d3dDevice->GetD3DDevice(), 1.0f, 20, 20, &m_Meshes[MESH_SPHERE], NULL);
		D3DXCreateTorus(m_d3dDevice->GetD3DDevice(), 0.5f, 1.0f, 20, 20, &m_Meshes[MESH_TORUS], NULL);
		D3DXCreateCylinder(m_d3dDevice->GetD3DDevice(), 0.5f, 0.5f, 2.0f, 20, 20, &m_Meshes[MESH_CYLINDER], NULL);
		//模型們的在世界中的矩陣:
		D3DXMatrixTranslation(&m_WorldMatrices[MESH_TEAPOT], 0.0f, 2.0f, 0.0f);
		D3DXMatrixTranslation(&m_WorldMatrices[MESH_SPHERE], 0.0f, -2.0f, 0.0f);
		D3DXMatrixTranslation(&m_WorldMatrices[MESH_TORUS], -3.0f, 0.0f, 0.0f);
		D3DXMatrixTranslation(&m_WorldMatrices[MESH_CYLINDER], 3.0f, 0.0f, 0.0f);
		//模型們的顏色:
		m_MeshColors[MESH_TEAPOT] = D3DXVECTOR4(1.0f, 1.0f, 1.0f, 1.0f);
		m_MeshColors[MESH_SPHERE] = D3DXVECTOR4(1.0f, 1.0f, 1.0f, 1.0f);
		m_MeshColors[MESH_TORUS] = D3DXVECTOR4(1.0f, 1.0f, 1.0f, 1.0f);
		m_MeshColors[MESH_CYLINDER] = D3DXVECTOR4(1.0f, 1.0f, 1.0f, 1.0f);

		//
		//創建兩個點光源
			//m_PointLight[0]
		m_PointLight[0].Diffuse = { 1.0f, 0.0f, 0.0f, 1.0f };//Diffuse
		m_PointLight[0].Specular = { 1.0f, 1.0f, 1.0f, 1.0f };//Specular
		m_PointLight[0].Position = { -6.0f, 2.0f, 2.0f, 0.0f };//Position
		m_PointLight[0].RangeAttenuation = { 15.0f, 1.0f, 50.0f, 0.0f };//Range,Attenuation0,鏡面光強弱,Attenuation2
			//m_PointLight[1]
		m_PointLight[1].Diffuse = { 0.0f, 1.0f, 0.0f, 1.0f };
		m_PointLight[1].Specular = { 1.0f, 1.0f, 1.0f, 1.0f };
		m_PointLight[1].Position = { 6.0f, 2.0f, 3.0f, 0.0f };
		m_PointLight[1].RangeAttenuation = { 15.0f, 0.8f, 50.0f, 0.0f };
		//創建一個方向光
			//m_DirectLight[0]
		m_DirectLight[0].Diffuse = { 0.5f, 0.5f, 0.5f, 1.0f };
		m_DirectLight[0].Specular = { 0.5f, 0.5f, 0.5f, 1.0f };
		m_DirectLight[0].DirectionPow = { 0.0f, 0.0f, -1.0f, 10.0f };//(z軸正方向)
		//創建一個聚光燈
			//m_SpotLight[0]
		m_SpotLight[0].Diffuse = { 1.0f, 1.0f, 1.0f, 1.0f };
		m_SpotLight[0].Specular = { 1.0f, 1.0f, 1.0f, 1.0f };
		m_SpotLight[0].Position = { 0.0f, 1.8f, -5.0f, 0.0f };
		m_SpotLight[0].Direction = { 0.0f, 0.0f, 1.0f, 0.0f };
		m_SpotLight[0].FalloffThetaPhi = { 3.5f, 0.0f, 0.04f, 0.18f };//衰減,-,內角(弧度半角),外角(弧度半角)
		m_SpotLight[0].RangeAttenuation = { 10.0f, 0.0f, 20.0f, 0.0f };//範圍,衰減1,高光,衰減2
		
 
		ID3DXBuffer* errorBuffer = 0;
		hr = D3DXCreateEffectFromFile(m_d3dDevice->GetD3DDevice(),
			L"D:\\nlight.fx",
			0,
			0,
			D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY,
			0,
			&m_NLightEffect,
			&errorBuffer);
		// 輸出錯誤信息:
		if (errorBuffer)
		{
			string str = (char*)errorBuffer->GetBufferPointer();
			MessageBox(NULL, Common::StringToWString(str).c_str(), L"ERROR", MB_OK);
			//safe_release<ID3DXBuffer*>(error_buffer);
		}
		if (FAILED(hr))
		{
			MessageBox(NULL, L"D3DXCreateEffectFromFile() - FAILED", L"ERROR", MB_OK);
		} 

		//獲取hlsl中常量句柄
			//得到技術technique MulLights的句柄
		m_TecniMulLightsHandle = m_NLightEffect->GetTechniqueByName("MulLights");
			//各種光源的個數
		m_NumPointLightHandle = m_NLightEffect->GetParameterByName(NULL, "g_NumPLs");
		m_NumDirectLightHandle = m_NLightEffect->GetParameterByName(NULL, "g_NumDLs");
		m_NumSpotLightHandle = m_NLightEffect->GetParameterByName(NULL, "g_NumSLs");
			//獲取光源種類的句柄
		m_AmbAmountHandle = m_NLightEffect->GetParameterByName(NULL, "AmbAmount");//環境光因子句柄	
		m_PointLHandle = m_NLightEffect->GetParameterByName(NULL, "g_PLs");//點光源句柄
		m_DirectLightHandle = m_NLightEffect->GetParameterByName(NULL, "g_DLs");//方向光句柄
		m_SpotLightHandle = m_NLightEffect->GetParameterByName(NULL, "g_SLs");//聚光燈句柄
			//獲取觀察點,矩陣句柄
		m_ViewPosHandle = m_NLightEffect->GetParameterByName(NULL, "g_ViewPos");
		m_WorldMatrixHandle = m_NLightEffect->GetParameterByName(NULL, "g_WorldMatrix");
		m_WVPMatrixHandle = m_NLightEffect->GetParameterByName(NULL, "WVPMatrix");//世界觀察投影矩陣

		//設置常量句柄
			//傳光源數據進去
		m_NLightEffect->SetRawValue(m_PointLHandle, m_PointLight, 0,sizeof(PointLight)*2);//傳點光源
		m_NLightEffect->SetRawValue(m_DirectLightHandle, m_DirectLight, 0, sizeof(DirectLight)* 1);//傳方向光
		m_NLightEffect->SetRawValue(m_SpotLightHandle, m_SpotLight, 0, sizeof(SpotLight)* 1);//傳聚光燈

		m_NLightEffect->SetFloat(m_AmbAmountHandle, 0.0f);
		m_NLightEffect->SetInt(m_NumPointLightHandle, 2);//點光源數量:2 (調節個數可方便調試)
		m_NLightEffect->SetInt(m_NumDirectLightHandle, 1);//方向光數量:1
		m_NLightEffect->SetInt(m_NumSpotLightHandle, 1);//聚光燈數量:1

		//設置投影矩陣
		RECT rt;
		GetClientRect(Application::GetInstance()->GetWnd(), &rt);
		D3DXMatrixPerspectiveFovLH(&m_ProjMatrix, D3DX_PI / 4.0f, (float)rt.right / rt.bottom, 1.0f, 1000.0f);

		//暫時未設置紋理
		m_d3dDevice->GetD3DDevice()->SetRenderState(D3DRS_LIGHTING, false);//關閉燈光,即不用固定管線
	}


	//display: 在消息循環中調用,時時更新
	void Display(float timeDelta)
	{
		static float angle = (3.0f * D3DX_PI) / 2.0f;
		static float height = 0.0f;

		if (GetAsyncKeyState(VK_LEFT) & 0x8000f)
			angle -= 0.5f * timeDelta;

		if (GetAsyncKeyState(VK_RIGHT) & 0x8000f)
			angle += 0.5f * timeDelta;

		if (GetAsyncKeyState(VK_UP) & 0x8000f)
			height += 5.0f * timeDelta;

		if (GetAsyncKeyState(VK_DOWN) & 0x8000f)
			height -= 5.0f * timeDelta;

		//求出觀察矩陣
		D3DXVECTOR3 position(cosf(angle) * 7.0f, height, sinf(angle) * 7.0f);
		D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
		D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
		D3DXMatrixLookAtLH(&m_ViewMatrix, &position, &target, &up);
		D3DXVECTOR4 vps = { cosf(angle) * 7.0f, height, sinf(angle) * 7.0f, 0.0f };
		m_NLightEffect->SetVector(m_ViewPosHandle, &vps);

		// render now
		m_d3dDevice->GetD3DDevice()->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x99999999, 1.0f, 0);
		m_d3dDevice->GetD3DDevice()->BeginScene();
		
		//設置技術
		m_NLightEffect->SetTechnique(m_TecniMulLightsHandle);

		D3DXMATRIX WVPMatrix;
		
		for (int i = 0; i < 4; i++)
		{
			WVPMatrix = m_WorldMatrices[i] * m_ViewMatrix * m_ProjMatrix;
			
			m_NLightEffect->SetMatrix(m_WorldMatrixHandle, &m_WorldMatrices[i]);
			m_NLightEffect->SetMatrix(m_WVPMatrixHandle, &WVPMatrix);
			UINT numPass = 0;
			m_NLightEffect->Begin(&numPass, 0);
			for (UINT j = 0; j < numPass; ++j)
			{
				//開始過程
				m_NLightEffect->BeginPass(j);	//在begin和end中間不建議設置着色器變量
				m_Meshes[i]->DrawSubset(0);
				//結束過程
				m_NLightEffect->EndPass();
			}
			//結束使用技術
			m_NLightEffect->End();  
		}
		m_d3dDevice->GetD3DDevice()->EndScene();
		m_d3dDevice->GetD3DDevice()->Present(NULL, NULL, NULL, NULL);
	}


測試一: 1個點光源:

發現了一個錯誤!!!:黑色的地方沒有光照,居然也有鏡面反射了,oh,shit,網上的算法也不嚴謹嘛...

解決方法:稍微一思考,有光的地方纔有鏡面反射對吧?怎麼樣纔有光? 就是光線方向和麪法線的夾角要大於0,

即difusefactor = dot(光線,面法線)>0.0f; 即difusefactor>0.0f,所以,HLSL裏在計算鏡面光的時候多加一個

判斷:

//鏡面反射算法
   if (difusefactor>0.0f) //只有有光照到的地方纔有鏡面反射!!!
   {
    float3 DirectionToView = normalize(worldpos.xyz - g_ViewPos.xyz);  //(同在世界空間中!)
     float3 VectorToLight = normalize(input.vtpos.xyz - g_PLs[i].Position.xyz);
     //計算反射光
     float3 reflectanceRay = 2 * dot(input.normal.xyz, VectorToLight.xyz)*input.normal.xyz - VectorToLight.xyz;
     float specfactor = pow(abs(dot(reflectanceRay, DirectionToView)), g_PLs[i].RangeAttenuation.z);
    //鏡面光累加
    specular.xyz += g_PLs[i].Specular.xyz * specfactor;//最後要乘以材質的吸收係數(這裏沒乘)
   }  

(下面的效果圖都沒有修改這一個錯誤,自行修改)

測試二: 兩個點光源:

測試三: 方向光:

測試四:聚光燈:

測試五:火力全開:

(亮瞎眼了~...)



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章