日期:2014-05-17  浏览次数:20843 次

Windows 8 Directx 开发学习笔记(九)材质定义及混合光照效果实现

在真实环境中,同一个物体在不同光源照射下的颜色并不一样,因为物体本身并没有颜色,而是它会反射不同颜色的光。物体对不同颜色光的吸收率、反射率,加上光泽度、透明度等其他物理属性组合在一起,定义了这个物体的材质。知道物体的材质,就能够方便地算出物体在不同光源照射下的颜色。这里简化山峰模型,统一使用陆地材质,水面则使用水材质,增加了平行光源、点光源和聚光灯三种光照模式,模拟一个更通用的山峰水波模型。实现流程和漫反射光实现基本一样。

首先编写着色器代码。平行光、点光和聚光灯均需要自己的数据结构和计算方法,可在一个头文件中进行定义。HLSL的头文件扩展名为hlsli,创建方法与创建C++头文件一样,其代码如下:

/***************************************************
/ LightBase.hlsli
/
/ 光源结构体和对应的光照计算方法。
/***************************************************/
 
struct DirectionalLight
{
    float4 Ambient;
    float4 Diffuse;
    float4 Specular;
    float3 Direction;
    float pad;
};
 
struct PointLight
{
    float4 Ambient;
    float4 Diffuse;
    float4 Specular;
 
    float3 Position;
    float Range;
 
    float3 Att;
    float pad;
};
 
struct SpotLight
{
    float4 Ambient;
    float4 Diffuse;
    float4 Specular;
 
    float3 Position;
    float Range;
 
    float3 Direction;
    float Spot;
 
    float3 Att;
    float pad;
};
 
struct Material
{
    float4 Ambient;
    float4 Diffuse;
    float4 Specular;
    float4 Reflect;
};
 
//---------------------------------------------------
// 计算平行光源照射产生的环境光,漫反射光和高光
//---------------------------------------------------
void ComputeDirectionalLight(Material mat, DirectionalLightL,
                             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);
 
    // V向量的方向与光线的方向相反.
    float3 lightVec =-L.Direction;
 
    // 计算环境光
    ambient =mat.Ambient * L.Ambient;
 
    // 计算漫反射光和高光
    // 如果V向量与法向量夹角小于零,无需计算
    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;
    }
}
 
//---------------------------------------------------
// 计算点光源照射产生的环境光,漫反射光和高光
//---------------------------------------------------
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;
 
    // 计算漫反射光和高光
    // 如果V向量与法向量夹角小于零,无需计算
    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;
}
 
//---------------------------------------------------
// 计算聚光灯光源照射产生的环境光,漫反射光和高光
//---------------------------------------------------
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;
      
    // 点光源到物体表面的距离,用于计算衰减