DirectX11 光照实现

光照实现

1. 光照结构体

在LightHelper.h文件中,我们定义了一些的结构体来表示平行光、点光或聚光灯。
结构体成员组成如下:

1.Ambient:由光源发射的环境光的数量。

2.Diffuse:由光源发射的漫反射光的数量。

3.Specular:由光源发射的高光的数量。

4.Direction:灯光方向。

5.Position:灯光位置。

6.Range:光照范围(离开光源的距离大于这个值的点不会被照亮)。

7.Attenuation:按照(a0、a1和a2)的顺序存储3个衰减常量。衰减常量只用于点和聚光灯,用于控制光强随距离衰减的程度。

8.Spot:该指数用于控制聚光灯的圆锥体区域大小;这个值只用于聚光灯。

“pad”变量的必要性和“packing”格式已经在上一篇博文中讨论过了,如果不知道什么意思,请务必回顾上一篇博文。

下面是各种光源的结构体定义:

struct DirectionalLight
{
    DirectionalLight() { ZeroMemory(this, sizeof(this)); }

    XMFLOAT4 Ambient;
    XMFLOAT4 Diffuse;
    XMFLOAT4 Specular;
    XMFLOAT3 Direction;
    float Pad; // 占位最后一个float,这样我们就可以设置光源数组了。
};

struct PointLight
{
    PointLight() { ZeroMemory(this, sizeof(this)); }

    XMFLOAT4 Ambient;
    XMFLOAT4 Diffuse;
    XMFLOAT4 Specular;

    // 打包到4D矢量: (Position, Range)
    XMFLOAT3 Position;
    float Range;

    // 打包到4D矢量: (A0, A1, A2, Pad)
    XMFLOAT3 Att;
    float Pad; // 占位最后一个float,,这样我们就可以设置光源数组了。
};

struct SpotLight
{
    SpotLight() { ZeroMemory(this, sizeof(this)); }

    XMFLOAT4 Ambient;
    XMFLOAT4 Diffuse;
    XMFLOAT4 Specular;

    // 打包到4D矢量: (Position, Range)
    XMFLOAT3 Position;
    float Range;

    // 打包到4D矢量: (Direction, Spot)
    XMFLOAT3 Direction;
    float Spot;

    // 打包到4D矢量: (Att, Pad)
    XMFLOAT3 Att;
    float Pad; // 占位最后一个float,,这样我们就可以设置光源数组了。
};

定义在LightHelper.fx文件中的结构体镜像了上面的结构体:

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;
};

2. 实现平行光

下面的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避免动态分支
    [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;
    }
}

其中,使用的HLSL内置函数有:dot、reflect、pow和max,它们分别用来计算向量点积、向量反射、乘方和取最大值。读者可以在附录B中找到大部分HLSL内置函数的描述,以及有关HLSL语法的快速入门。另外还有一件事情需要注意,那就是当两个向量使用*运算符相乘时,实际执行的是分量乘法。

注意:PC上的HLSL函数总是内联的,调用函数或传递参数不会有性能损失。

3. 实现点光

下面的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避免动态分支
    [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;
}

4. 实现聚光灯

下面的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);
     
    // Range test.
    if( d > L.Range )
        return;
         
    // 规范化光照矢量
    lightVec /= d;
     
    // 计算环境光
    ambient = mat.Ambient * L.Ambient; 
 
    // 计算漫反射和镜面光,所提供的表面是在该光线的位置射出的直线。
 
    float diffuseFactor = dot(lightVec, normal);
 
    // Flatten避免动态分支
    [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;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值