Direct3D 12——灯光——点光

一个与点光源(point light)比较贴切的现实实例是灯泡,它能以球面向各个方向发出光线。特别地,对于任意点P,由位置Q处点光源发出的光线,总有一束会传播至此点。像之前一样,我们定 义光向量与光传播的方向相反,即光向量的方向是由点P指向点光源Q:
在这里插入图片描述
Direct3D 12——灯光——点光_第1张图片
其实点光与平行光之间唯一的区别就是光向量的计算方一对于点光来说,光向量随着目标点的 不同而改变;对于平行光而言,光向量则保持不变。

衰减

在物理学中,光强会根据平方反比定律(inverse squared law )而随着距离函数发生衰减。也就是说, 距离光源d处的某点光强为:
在这里插入图片描述
其中,I0为距离光源d=1处的光强。如果根据物理学来设置光light value),并且辅之以HDR (high dynamic range,高动态范围)光照与色调映射(tonemapping,也可写作tone mapping,或译作色调 贴图)技术,那效果定是极好的。然而,我们在这里却要使用一种更为简单的公式,并将它应用于演示 代码之中。这便是我们所用的线性衰减(falloff)
在这里插入图片描述
下图中所描绘的就是此线性衰减函数的图像。
saturate函数会将它的参数限定在[0,1]范围内:
Direct3D 12——灯光——点光_第2张图片
在距离d未达falloffStart之前,衰 减因子会一直令光量值保持最大强度(1.0)。当距离d到达falioffEnd时,衰减因子
便会线性衰减至0.0
Direct3D 12——灯光——点光_第3张图片
注意,衰减并不会影响 环境光项,这是因为环境光项模拟的是向四处反弹后照 射到目标物体的间接光。
在使用上述衰减函数的时候,若某点到光源的距离大 于或等于falioffEnd则不会受到光照。这便为我们提供了 一种极有用的光照优化手段:在着色器程序中,如果一个点超出了光照的有效范围,那么就可采用动态 分支(dynamic branching )跳过此处的光照计算并提前返回。

实现

//实现了一种线性衰减因子的计算方法,可将其应用于点光源于聚光灯
float CalcAttenuation(float d, float falloffStart, float falloffEnd)
{
    // Linear falloff.
    return saturate((falloffEnd-d) / (falloffEnd - falloffStart));
}

//代替菲涅尔方程的石里克近似。此函数基于光向量L于表面法线n之间的夹角,并根据菲涅尔效应近似地计算出以n为法线的表面所反射光的百分比
// Schlick gives an approximation to Fresnel reflectance (see pg. 233 "Real-Time Rendering 3rd Ed.").
// R0 = ( (n-1)/(n+1) )^2, where n is the index of refraction.
float3 SchlickFresnel(float3 R0, float3 normal, float3 lightVec)
{
    float cosIncidentAngle = saturate(dot(normal, lightVec));

    float f0 = 1.0f - cosIncidentAngle;
    float3 reflectPercent = R0 + (1.0f - R0)*(f0*f0*f0*f0*f0);

    return reflectPercent;
}

//计算反射到观察者眼中的光量,该值为漫反射光量于镜面反射光量的总和
float3 BlinnPhong(float3 lightStrength, float3 lightVec, float3 normal, float3 toEye, Material mat)
{
    //m由光泽度推导而来,而光泽度则根据粗糙度求得
    const float m = mat.Shininess * 256.0f;
    float3 halfVec = normalize(toEye + lightVec);

    float roughnessFactor = (m + 8.0f)*pow(max(dot(halfVec, normal), 0.0f), m) / 8.0f;
    float3 fresnelFactor = SchlickFresnel(mat.FresnelR0, halfVec, lightVec);

    float3 specAlbedo = fresnelFactor*roughnessFactor;

    //尽管我们进行的是LDR(low dynamic range .低动态范围)渲染,但spec(镜面反射)公式得到
    // 的结果仍会超出范围[0,1],因此现将其按比例缩小一些
    // Our spec formula goes outside [0,1] range, but we are 
    // doing LDR rendering.  So scale it down a bit.
    specAlbedo = specAlbedo / (specAlbedo + 1.0f);

    return (mat.DiffuseAlbedo.rgb + specAlbedo) * lightStrength;
}

//点光
float3 ComputePointLight(Light L, Material mat, float3 pos, float3 normal, float3 toEye)
{
    //自表面指向光源的向量 
    float3 lightVec = L.Position - pos;

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

    //范围检测
    if(d > L.FalloffEnd)
        return 0.0f;

    //对光向量进行规范化处理
    lightVec /= d;

    //通过朗伯余弦定律按比例降低光强
    // Scale light down by Lambert's cosine law.
    float ndotl = max(dot(lightVec, normal), 0.0f);
    float3 lightStrength = L.Strength * ndotl;

    //根据距离计算光的衰减 
    float att = CalcAttenuation(d, L.FalloffStart, L.FalloffEnd);
    lightStrength *= att;

    return BlinnPhong(lightStrength, lightVec, normal, toEye, mat);
}

你可能感兴趣的:(Direct3D12,3d,图形渲染)