3D图形学编程基础-基于Direct3D11-学习记录(二)光照模型的实现

来源:互联网 发布:linux查看服务列表 编辑:程序博客网 时间:2024/05/01 01:10

为了让绘制出的物体更接近真实世界,此章引入光照,光照章节不少都是概念论述以及少量的计算方式

和光照有关几个小概念,1,光照:方向,强度,衰弱      2.物体材质:光滑度,折射率,反射率和发光度等;3.顶点法线:我们处理物体被光照是按顶点处理的(绘制物体是按顶点绘制的)顶点法线求法:取所有共享此顶点的面的法线的平均值  

在directx中我们需要知道顶点的法线,法线决定了光照射平面的角度。光线会被应用到每个顶点,并且根据面法线和光照方向的点积去调,光线颜色的强度。那么怎么计算顶点法线呢?下面的图介绍的很清楚了:

   

  

第一部分:

物体受到光照时要考虑的三种现象,

1.环境光(ambient)

现实中在有光情况下就会产生颜色,物体对每种颜色的吸收程度不同,所以产生不同的颜色,环境光公式:

Cambient =Iambient *Mambient(I为照射到物体的光照,M是物体对每种光的吸收率)

2.漫反射光(diffuse)

物体对光照产生的漫反射效果,实际情况中由于物体见面的不光滑,漫反射效果是不一样的,我们这里假设漫反射效果一样:

Cdiffuse = Idiffuse *Mdiffuse * cosθ(I为光照,M是漫反射率,θ是光源方向和顶线法向量夹角)当θ小于0时,没有漫反射

3.镜面反射(specular)

物体对光照产生的镜面反射效果

Cspecular =Ispecular *Mspecular * cosθ(I为光照,M是镜面反射率,θ是反射后光方向和观察点方向夹角) 当θ小于0时,没有镜面反射

第二部分:

光源类型:

1.平行光:模拟太阳光照效果,光只有方向,而且方向不变,没有衰减。

因为平行光没有衰减,计算平行光效果只需要将环境光,漫反射和镜面反射相加即可

Idir =Cambient+Cdiffuse+Cspecular

C++中的平行光定义

struct DirectionalLight{    DirectionalLight() { ZeroMemory(this, sizeof(this)); }    XMFLOAT4 Ambient;//每种光都对应3中处理效果,1.环境光    XMFLOAT4 Diffuse;//2.漫反射光      XMFLOAT4 Specular;//3.镜面光    XMFLOAT3 Direction;//光照方向,平行光自己得属性    float Pad;// 用于与HLSL中“4D向量”对齐规则匹配,然而并不知道干什么用,个人理解是为了凑成4维向量方便计算};

.fx文件中的平行光定义

struct DirectionalLight  {      float4 Ambient;//环境光      float4 Diffuse;//漫反射      float4 Specular;//镜面光      float3 Direction;//平行光方向      float pad;  }

2.点光源:模拟灯光效果,有光源,有范围,有衰减

点光源有衰减,要计算衰减效果,超出范围就没有光照效果

设a是常量衰减因子,b是一次衰减因子,c是二次衰减因子,d是光源到物体距离

衰减因子计算方法 K = 1/(a+b*d+c*d*d) //d的2次方不会打

点光源效果计算方法:Ipoint = Idir*K//平行光乘以衰减系数

C++中点光源定义:

struct PointLight{    PointLight() { ZeroMemory(this, sizeof(this)); }    XMFLOAT4 Ambient;//环境光    XMFLOAT4 Diffuse;//漫反射    XMFLOAT4 Specular;//镜面反射    // 点光源自己属性    XMFLOAT3 Position;//光源位置    float Range;     //光照范围    XMFLOAT3 Att;  // 衰减系数(参数a,b,c,d)        float Pad;};

.fx文件中的点光源定义

struct PointLight {      float4 Ambient;     float4 Diffuse;     float4 Specular;      float3 Position;//光源位置     float Range;//范围     float3 Att;//衰减系数     float pad;};

3.聚光灯:类似手电筒,光照体分为两个部分,一个内椎体一个外锥体。内椎体光照随距离增大而衰减,外锥体随角度和距离增大都衰减

聚光灯效果衰减算法:设内椎体角度a,外椎体角度b,角度衰减系数S,光线和聚光灯照射方向夹角θ

1.K = [(cosθ - cos(b/2))/(cos(a/2)-cos(b/2))]的S次方(不会打S次方)(S一般设为1) //这是在内锥形和外锥形之间算法

2.当角度小于内椎体时,计算方法和点光源一样

3.当角度大于外锥体时,不计算

C++中的聚光灯定义

struct SpotLight{    SpotLight() { ZeroMemory(this, sizeof(this)); }    XMFLOAT4 Ambient;    XMFLOAT4 Diffuse;    XMFLOAT4 Specular;     XMFLOAT3 Position;//光照位置    float Range;//光照范围    XMFLOAT3 Direction;//光照方向    float Spot;//光照角度衰减系数       XMFLOAT3 Att;//距离衰减系数    float Pad; };

.fx中的聚光灯定义
<pre name="code" class="cpp"> struct SpotLight {     float4 Ambient;     float4 Diffuse;     float4 Specular;      float3 Position;     float Range;      float3 Direction;     float Spot;      float3 Att;     float pad;};


第二部分:材质

C++中的定义

<pre name="code" class="cpp">struct Material{    Material() { ZeroMemory(this, sizeof(this)); }    XMFLOAT4 Ambient;//环境光反射率    XMFLOAT4 Diffuse;//漫反射反射率    XMFLOAT4 Specular;//镜面反射反射率    XMFLOAT4 Reflect;//镜面光反射系数(随夹角影响的)};


以下是处理三种光源的具体代码

1.平行光

float4 ComputeDirectionalLight(Material mat,        <span style="white-space:pre"></span>//材质                            DirectionalLight L,    <span style="white-space:pre"></span>//平行光                            float3 normal,        <span style="white-space:pre"></span>//顶点法线                            float3 toEye,        <span style="white-space:pre"></span>//顶点到眼睛的向量                            out float4 ambient,    <span style="white-space:pre"></span>//计算结果:环境光                            out float4 diffuse,    <span style="white-space:pre"></span>//计算结果:漫反射光                            out float4 spec)    <span style="white-space:pre"></span>//计算结果:镜面反射光{    // 结果初始化为0<span style="white-space:pre"></span>这三个是输出值    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);    // 光线方向<span style="white-space:pre"></span>光线方向和光照方向相反    float3 lightVec = -L.Direction;    // 环境光直接计算    ambient = mat.Ambient * L.Ambient;    // 计算漫反射系数    //光线、法线方向归一化    //HLSL内置函数dot计算向量点积    float diffuseFactor = dot(lightVec, normal);    // 顶点背向光源不再计算    if (diffuseFactor > 0.0f)    {        //计算漫反射光        diffuse = diffuseFactor * mat.Diffuse * L.Diffuse;        //计算高光
<pre name="code" class="cpp"><span style="white-space:pre"></span>//reflect求反射向量的 反射光专用        float3 v = reflect(-lightVec, normal);        float specFactor = pow(max(dot(v, toEye), 0.0f), mat.Specular.w);
spec = specFactor * mat.Specular * L.Specular; }

    return ambient+diffuse+spec;}
2.点光源

float4 ComputePointLight(Material mat,        <span style="white-space:pre"></span>//材质                         PointLight L,        <span style="white-space:pre"></span>//点光源                         float3 pos,            //顶点位置                         float3 normal,        <span style="white-space:pre"></span>//顶点法线                         float3 toEye,        <span style="white-space:pre"></span>//顶点到眼睛的向量                         out float4 ambient, <span style="white-space:pre"></span>//计算结果:环境光                         out float4 diffuse, <span style="white-space:pre"></span>//计算结果:漫反射光                         out float4 spec)    <span style="white-space:pre"></span>//计算结果:镜面反射{     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);      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;
</pre><pre name="code" class="cpp">     return<span style="white-space:pre"></span>ambient+diffuse+spec;}
3.计算聚光灯

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);      //距离大于光照方向不再计算     if (d > L.Range)         return;      //归一化光照方向     lightVec /= d;      //计算环境光     ambient = mat.Ambient * L.Ambient;           //计算漫反射系数     float diffuseFactor = dot(lightVec, normal);      if (diffuseFactor > 0.0f)    {         float3 v = reflect(-lightVec, normal);
//pow乘方函数         float specFactor = pow(max(dot(v, toEye), 0.0f), mat.Specular.w);         //漫反射光         diffuse = diffuseFactor * mat.Diffuse * L.Diffuse;         //高光         spec = specFactor * mat.Specular * L.Specular;     }      //聚光衰减系数角度衰减系数spot     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;
     return ambient+diffuse+spec;
} 

4.   .fx文件

cbuffer cbPerFrame{    DirectionalLight <span style="white-space:pre"></span>gDirLight;<span style="white-space:pre"></span>//平行光属性描述    PointLight <span style="white-space:pre"></span>gPointLight;<span style="white-space:pre"></span>//点光源属性描述    SpotLight <span style="white-space:pre"></span>gSpotLight;<span style="white-space:pre"></span>//聚光灯属性描述    float3 <span style="white-space:pre"></span>gEyePosW;            <span style="white-space:pre"></span>//观察点坐标};cbuffer cbPerObject{    float4x4 gWorld;    float4x4 gWorldInvTranspose;<span style="white-space:pre"></span>//世界矩阵的逆矩阵的转置    float4x4 gWorldViewProj;    Material gMaterial;};struct VertexIn{    float3 PosL    : POSITION;    <span style="white-space:pre"></span>//顶点坐标    float3 NormalL : NORMAL;    <span style="white-space:pre"></span>//顶点法线};struct VertexOut{    float4 PosH    : SV_POSITION;    <span style="white-space:pre"></span>//投影后的坐标    float3 PosW    : POSITION;        <span style="white-space:pre"></span>//世界变换后的坐标    float3 NormalW : NORMAL;        <span style="white-space:pre"></span>//世界变换后的顶点法线};
//进行坐标变换处理VertexOut VS(VertexIn vin){    VertexOut vout;
    //局部坐标变换为世界坐标,变换后的顶点法线    vout.PosW = mul(float4(vin.PosL, 1.0f), gWorld).xyz;    vout.NormalW = mul(vin.NormalL, (float3x3)gWorldInvTranspose);    //投影变换<span style="white-space:pre"></span>mul函数 向量相乘   然而这个函数使用起来貌似有问题  具体什么问题可以网上查下    vout.PosH = mul(float4(vin.PosL, 1.0f), gWorldViewProj);    return vout;}float4 PS(VertexOut pin) : SV_Target{
    float4 finalColor;    //插值运算有可能使法线不再单位化,重新单位化法线    pin.NormalW = normalize(pin.NormalW);    //顶点到观察点向量,归一化    float3 toEyeW = normalize(gEyePosW - pin.PosW);    //初始化颜色值全部为0    float4 ambient = float4(0.0f, 0.0f, 0.0f, 0.0f);    float4 diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f);    float4 spec = float4(0.0f, 0.0f, 0.0f, 0.0f);    //每个光源计算后得到的环境光、漫反射光、高光    float4 A, D, S;    //每个光源计算后将ADS更新到最终结果中    finalColor = ComputeDirectionalLight(gMaterial, gDirLight, pin.NormalW, toEyeW, A, D, S);    finalColor += ComputePointLight(gMaterial, gPointLight, pin.PosW, pin.NormalW, toEyeW, A, D, S);    finalColor += ComputeSpotLight(gMaterial, gSpotLight, pin.PosW, pin.NormalW, toEyeW, A, D, S);    //最终颜色透明度使用漫反射光的    finalColor.a = gMaterial.diffuse.a;    return finalColor;}
//此函数可以绑定着色器接口并绘制,C++中绘制只需要调用此函数就好technique11 LightTech{    pass P0    {        SetVertexShader(CompileShader(vs_5_0, VS()));        SetGeometryShader(NULL);        SetPixelShader(CompileShader(ps_5_0, PS()));    }}





0 0
原创粉丝点击