HLSL效果框架-多光源效果

来源:互联网 发布:java商城项目经验 编辑:程序博客网 时间:2024/05/29 18:42

高级着色器语言(HLSL)难就难在它是运行在GPU,CPU上的,编写调试都很麻烦.

效果框架简化了很多操作,先列出着色器的代码,重点部分都用中文注释

着色器语言文件为:,代码为:

//着色器处理未知数量光源数目 未知光源种类 的 混合效果//HLSL里一共支持256个float4,一个int最后也转成float4,float4x3和float3x3占内存一样,最终都变成float4//因为HLSL的限制条件非常零散也非常多,所以,用起来还是很吃力的,在不懂的情况下,//最终指令不能超过500多条貌似,运行在显卡中程序,所以能省则省,能精简则精简!!!////结构体 注意:为了优化结构,把一些相关参数整合到了一起//struct PointLight//点光源 结构{float4 Diffuse;//漫反射的颜色r,g,b,afloat4 Specular;//镜面高光的颜色r,g,b,afloat4 Position;//光源位置x,y,z,w没用到float4 RangeAttenuation;//范围,恒定衰减,光强,二次衰减};struct DirectLight//方向光 结构{float4 Diffuse;//漫反射的颜色r,g,b,afloat4 Specular;//镜面高光的颜色r,g,b,afloat4 Direction;//方向x,y,z,高光};struct SpotLight//聚光灯 结构{float4 Diffuse;//漫反射的颜色r,g,b,afloat4 Specular;//镜面高光的颜色r,g,b,afloat4 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;//程序里定义了几个方向光源?PointLightg_PLs[10];//定义最多支持10个点光源DirectLightg_DLs[10];//定义最多支持10个方向光源SpotLightg_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;//光源到顶点的方向和大小.光源→顶点posfloat 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_TEAPOT0#define MESH_SPHERE1#define MESH_TORUS2#define MESH_CYLINDER3typedef 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 };//Diffusem_PointLight[0].Specular = { 1.0f, 1.0f, 1.0f, 1.0f };//Specularm_PointLight[0].Position = { -6.0f, 2.0f, 2.0f, 0.0f };//Positionm_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);//方向光数量:1m_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 nowm_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;//最后要乘以材质的吸收系数(这里没乘)
   }  

(下面的效果图都没有修改这一个错误,自行修改)

测试二: 两个点光源:

测试三: 方向光:

测试四:聚光灯:

测试五:火力全开:

(亮瞎眼了~...)



0 0
原创粉丝点击