学习了下简单的光照模型

龙书和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( );
}
}

 

效果图:


 

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

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

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

                          

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

 

效果图:

 

 

 

 

 

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

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

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

                             

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

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

 

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

 

4.Cook-Torrance光照模型。

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

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

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

 

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

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

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

 

5.Bank BRDF光照模型。

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

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

BRDF 数据。

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

          

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

 

效果图:

 

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

posted @ 2010-11-01 21:42  mavaL  阅读(10861)  评论(1编辑  收藏  举报