龙书和SDK光照的例子也看到几个了,今天看《GPU 编程与CG 语言之阳春白雪下里巴人》系统的学了学,也能写写shader练练手了。1个月前让我抱着一本图形学书死啃的话,我肯定读不出来个所以然,因为那时才刚刚学完固定流水线,对3D还处于混沌的状态。今天学习光照模型,所以特意翻开了
图形学书查看资料,惊奇的发现,我实践中学到的大部分不甚明了的知识,在书中都有解答。现在明白了,为什么
图形学是基本功,过段时间我得找本好点的图形学圣经,好好看看了。光是畏惧严谨的原理阐述和数学知识是不行得。

还有,学了shader这么些天,看倒是看得明白,今天自己写简单的shader,发现问题真是不少,看来真是纸上得来终觉浅,绝知此事要躬行啊。

编程,就是要多动手实验,测试,才能练出真功夫。

 

1。Lambert光照模型,也就是漫反射光照模型。它考虑的是ambient光和diffuse光对物体的综合影响。

下面是我写的shader程序:

 

 

代码 
  
matrix WorldMatrix;
matrix ViewMatrix;
matrix ProjMatrix;
matrix WorldMatrix_IT;      // 
    世界矩阵的逆再转置,为了把顶点法向量变换到世界空间 
    
     float3 g_LightPos  
    = 
     float3( 
    - 
    3 
    ,  
    3 
    ,  
    - 
    10 
    );
float3 g_ambient      = 
     float3( 
    0.2f 
    ,  
    0.2f 
     , 
    0.2f 
    );
float3 g_LightColor      = 
     float3( 
    1.0f 
    ,  
    1.0f 
    ,  
    1.0f 
    );



     struct 
     VS_INPUT
{
 float4 inPos : POSITION;
 float4 inNormal : NORMAL;
};

     struct 
     VS_OUTPUT
{
 float4 oPos : POSITION;
 float4 color : COLOR0;
};

     // 
    顶点着色器入口函数 
    
     VS_OUTPUT VS_MAIN ( VS_INPUT In )
{
 VS_OUTPUT Out      = 
     ( VS_OUTPUT) 
    0 
    ;
 float4 worldPos      = 
     mul(In.inPos, WorldMatrix);
 Out.oPos      = 
     mul(worldPos, ViewMatrix);
 Out.oPos      = 
     mul(Out.oPos, ProjMatrix);

 float3 N      = 
     mul( In.inNormal, WorldMatrix_IT).xyz;
 N      = 
     normalize(N);

     // 
    计算入射光方向  
    
      float3 L  
    = 
     normalize( g_LightPos  
    - 
     worldPos.xyz);
     // 
    计算漫反射光强 
    
      float3 diffuse  
    = 
     g_LightColor  
    * 
     saturate (dot( L, N) );
     // 
    计算总光强 
    
      Out.color.xyz  
    = 
     diffuse  
    + 
     g_ambient;
 Out.color.w      = 
    1 
    ;
     return 
     Out;
}

float4 PS_MAIN( VS_OUTPUT In) : color0
{
     return 
     In.color;
}

technique LightAndTexture
{
 pass P0
 {
 VertexShader      = 
     compile vs_2_0 VS_MAIN( );
 PixelShader      = 
     compile ps_2_0 PS_MAIN( );
 }
}

 

效果图:

深度学习图像光照合成_IT

 

2。Phong光照模型,这个模型计算了镜面高光(Specular):当入射光照射在一些光滑的表面时,在反射角的一定区域内,会

形成很强的光亮,因为反射光反射了入射光的大部分光强。这时候,当观察角度接近反射角时,就会看到物体表面的高光。

所以镜面反射的光强与反射光和视线的夹角相关。其计算公式为:

                          

深度学习图像光照合成_IT_02

ks为材质的镜面反射系数, ns 是高光指数,V 表示从顶点到视点的观察方
向, R 代表反射光方向。
高光指数反映了物体表面的光泽程度。ns越大,反射光越集中,当偏离反射
方向时,光线衰减的越厉害,只有当视线方向与反射光线方向非常接近时才能看
到镜面反射的高光现象,此时,镜面反射光将会在反射方向附近形成亮且小的光
斑; ns越小,表示物体越粗糙,反射光分散,观察到的光斑区域小,强度弱。

 

将镜面反射与漫反射,环境光一起使用,能使得物体更具有真实感。

书上说:镜面反射的高光颜色应该等于光源的颜色,物体的的材质颜色应该使用在漫反射和环境光上。

SDK里面那个specular.fx写得很漂亮,可以用来学习。不过注意,SDK里面的顶点法向量变换到世界空间全是错了的,应该乘以世界矩阵的逆的转置.

 

 

下面是我写的shader,很恼火的是,开始看着画面亮亮的一团,根本看不出来高光区,怀疑是模型太简陋,又怀疑是光源离模型太近,

后来到处改,发现把diffuse光强改小点,才能看出高光。真是发晕。

 

这里我澄清了自己一直的一个迷惑,物体的颜色似乎既可以可以由灯光颜色决定,也可以由D3DMATERIAL9中的Emissive成员决定(它表示

物体自身发出光的颜色,即没有光照也能看到的颜色),还可以由物体材质决定。其实在shader中计算顶点最终颜色时,几个算式包含的内容已经说明了

这个问题。我们设定光源颜色为白色:float3(1.0, 1.0, 1.0),然后假设光照只由散射光组成。然后我们想让物体材质在散射光下反射红色(1.0, 0.0, 0.0),

那么直接给光源颜色乘以float3(1.0, 0.0, 0.0)。注意了,这个乘法就相当于在直接对光源颜色做过滤啊!所以把D3DMATERIAL9中的几个分量理解成材质颜色是不对的,而应该是反射颜色百分比。这就越说越绕了,其实这个计算过程非常符合光照的物理过程:光源发出灯光,在材质表面反射,材质决定吸收灯光的什么分量和反射什么分量,而材质本身是不具有颜色的!在现实世界中,我们看到的物体颜色不是属于物体的,而是光与其作用后反射的。就这么个问题,初中就学了,现在还混淆得不清楚,真是头痛!

 

//计算漫反射光强
 float3 diffuse = g_LightColor * saturate (dot( L, N) ) * float3(1.0f, 0.0f, 0.0f) + g_ambient;  
 
     
代码   
matrix WorldMatrix;
matrix ViewMatrix;
matrix ProjMatrix;
matrix WorldMatrix_IT;      //     世界矩阵的逆再转置,为了把顶点法向量变换到世界空间 
    
     float3 g_LightPos      = 
     float3(  
    - 
    2 
    ,  
    - 
    5 
    ,  
    - 
    10 
    );
float3 g_ambient      =      float3( 
    0.3f 
    ,  
    0.3f 
     , 
    0.3f 
    );
float3 g_LightColor      =      float3( 
    1.0f 
    ,  
    1.0f 
    ,  
    1.0f 
    );
     float      g_shininess  
    = 
    20.0f 
    ;
float4 g_eyePos;


     struct      VS_INPUT
{
 float4 inPos : POSITION;
 float3 inNormal : NORMAL;
};

     struct      VS_OUTPUT
{
 float4 oPos : POSITION;
 float3 worldPos : texcoord0;
 float3 worldNormal : texcoord1;
};

     //     顶点着色器入口函数 
    
     VS_OUTPUT VS_MAIN ( VS_INPUT In )
{
 VS_OUTPUT Out      =      ( VS_OUTPUT) 
    0 
    ;
 float4 wPos      =      mul(In.inPos, WorldMatrix);

 Out.worldPos      =      wPos.xyz;
 Out.oPos      =      mul(wPos, ViewMatrix);
 Out.oPos      =      mul(Out.oPos, ProjMatrix);

 Out.worldNormal      =      normalize( mul( In.inNormal, WorldMatrix_IT ) );
     return      Out;
}

     //     像素着色器入口函数 
    
     float4 PS_MAIN( VS_OUTPUT In) : color0
{
 float3 N      =      In.worldNormal;
     //     计算入射光方向 
    
      float3 L      = 
     normalize( g_LightPos  
    - 
     In.worldPos );
     //     计算反射光方向 
    
      float3 R      = 
     reflect(  
    - 
    L, N );
     //     计算视线方向 
    
      float3 V      = 
     normalize( g_eyePos.xyz  
    - 
     In.worldPos );
     //     计算漫反射光强 
    
      float3 diffuse      = 
     g_LightColor  
    * 
     saturate (dot( L, N) )  
    + 
     g_ambient;
     //     计算镜面反射光强 
    
      float3 specular      = 
     g_LightColor  
    * 
     pow( saturate (dot( R, V) ), g_shininess );
     //     计算总光强 
    
      float4 color;
 color.xyz      =      diffuse  
    * 
     float3( 
    0.7f 
    ,  
    0.7f 
    ,  
    0.7f 
    )  
    + 
     specular ;  
    // 
    漫反射光强改小了,才看出来高光效果 
    
      color.w      = 
    1 
    ;
     return      color;
}

technique LightAndTexture
{
 pass P0
 {
 VertexShader      =      compile vs_2_0 VS_MAIN( );
 PixelShader      =      compile ps_2_0 PS_MAIN( );
 }
}


 

效果图:

 

 

深度学习图像光照合成_漫反射光_03

 

 

 

3.Blinn-phong光照模型。它是以Phong模型为基础的,效果是能让高光更加柔和,更平滑。其实这个模型的效果并不比Phong模型

 高级,我看的书上说:使用blinn-phong 进行光照渲染,在同样的高光系数下,高光领域覆盖范围较大,明暗界限不明显。所以它真实感

还没Phong模型强。但是这个模型运算速度要快些。因此在DX中默认的高光模型就是它。Blinn-phong光照模型公式为:

                             

深度学习图像光照合成_IT_04

公式与Phong模型公式不同的是,R点乘V变成了N点乘H。N是顶点法向量,H是所谓的半角向量(half way vector).它等于入射方向L和视线方向V

的中间向量。H有什么几何意义呢?我也还不知道..

 

代码就跟phong几乎一样了,就不贴了,看看效果图:

深度学习图像光照合成_深度学习图像光照合成_05

 

4.Cook-Torrance光照模型。

前面三个模型都是简单光照模型,它们没有考虑物体材质的细节,如粗糙平面,因此真实感不足。Cook-Torrance模型考虑了粗糙表面,它的漫反射光强

计算跟前面相同,只是高光计算部分不同。我们要认识到,既然要想模拟更为真实的高级光照,那么肯定要遵循物理世界的定律,模型的计算公式必定要

变复杂不少,还要加入很多物理参数。下面这个就是该光照模型的计算公式:

深度学习图像光照合成_光照模型_06

 

 其中:V为视线方向向量,H为半角向量,L为入射向量,阿尔法是N和H的夹角,m度量表面的粗糙程度,越粗糙m越大,f0为入射角度接近0时的Fresnel 反射系数。

看到这么大个公式都让人晕啊,我猜想它的实用性应该不强吧,能用在实时光照中吗?速度怎么样?

我也懒得动手实验了,这个模型就先了解到这里吧。。贴个书上的效果图:

深度学习图像光照合成_IT_07

 

5.Bank BRDF光照模型。

BRDF就是双向反射分布函数的意思,它描述了入射光线在某个反射角度的反射光的相对能量。所以给定不同的BRDF函数,就能实现不同的光照效果。

这些数学问题不深入研究是搞不懂的,我学学模型的公式,了解一些原理就是了。 具体的BRDF模型有:HTSG BRDF模型,它擅长模拟很多物理现象,
是现今最完整的BRDF 模型,但是同时需要昂贵的计算开销;Ward BRDF ,用于各向异性表面的经验模型有些复杂,并且需要从实际物体表面来获取

BRDF 数据。

有一个实现简单,速度快的Bank BRDF模型,它能模拟各向异性光照效果,其镜面反射部分的计算公式是:

          

深度学习图像光照合成_深度学习图像光照合成_08

ks 、ns 分别表示镜面反射系数和高光系数;L 表示入射光线方向、V 表示视线方向,T 表示顶点的切向量。计算T的一种方法是用N和V叉乘得到。

 

下面是代码:

 

代码   
matrix WorldMatrix;
matrix ViewMatrix;
matrix ProjMatrix;
matrix WorldMatrix_IT;      //     世界矩阵的逆再转置,为了把顶点法向量变换到世界空间     
     float3 g_LightPos      =      float3(      - 
    3 
    ,  
    0 
    ,  
    - 
    10 
    );
float3 g_ambient      =      float3(     0.3f     ,  
    0.3f 
     , 
    0.3f 
    );
float3 g_LightColor      =      float3(     1.0f     ,  
    1.0f 
    ,  
    1.0f 
    );
     float      g_shininess      =     20.0f 
    ;
float4 g_eyePos;

     //     顶点着色器入口函数     
     void      VS_MAIN ( float4 inPos : POSITION,
 float3 inNormal : NORMAL,
     out      float4 oPos : POSITION,
     out      float3 worldPos : texcoord0,
     out      float3 worldNormal : texcoord1 )
{
 float4 wPos      =      mul( inPos, WorldMatrix);

 worldPos      =      wPos.xyz;
 oPos      =      mul(wPos, ViewMatrix);
 oPos      =      mul(oPos, ProjMatrix);

 worldNormal      =      normalize( mul( inNormal, ( float3x3 )WorldMatrix_IT ) );
}

     //     像素着色器入口函数     
     float4 PS_MAIN( float3 worldPos : texcoord0,
 float3 worldNormal : texcoord1) : color0 
{
 float3 N      =      worldNormal;
     //     计算入射光方向     
      float3 L      =      normalize( g_LightPos      - 
     worldPos );
     //     计算视线方向     
      float3 V      =      normalize( g_eyePos.xyz      - 
     worldPos );
     //     计算漫反射光强     
      float3 diffuse      =      g_LightColor      * 
     saturate (dot( L, N) )  
    + 
     g_ambient;

 float3 specular      =      float3(     0.0f     ,  
    0.0f 
    ,  
    0.0f 
     );
     bool      back      =      ( dot( L, N ) )  
    && 
     ( dot( N, V )  
    > 
    0 
     );
     //     若不满足条件则高光为0     
     if     (back)
 {
     //     计算顶点切向量     
      float3 T      =      normalize( cross( N, V ) ); 
 float3 LT      =      dot( L, T );
 float3 VT      =      dot( V, T );
 float3 a      =      sqrt(      1     - 
     pow( LT,  
    2.0f 
     ) )  
    * 
     sqrt(  
    1 
    - 
     pow( VT,  
    2.0f 
     ) )  
    - 
     LT  
    * 
     VT;
 specular      =      pow( a, g_shininess );
 }
     //     计算总光强     
      float4 color;
 color.xyz      =      diffuse      *      float3( 
    0.6f 
    ,  
    0.6f 
    ,  
    0.6f 
    )  
    + 
     specular ;
 color.w      =     1     ;
     return      color;
}

technique LightAndTexture
{
 pass P0
 {
 VertexShader      =      compile vs_2_0 VS_MAIN( );
 PixelShader      =      compile ps_2_0 PS_MAIN( );
 }
}

 

效果图:

 

深度学习图像光照合成_深度学习图像光照合成_09

 可以很清楚地看出这种各向异性光照效果。