Clayman的专栏

It's all about XNA & GPU Programming

用户操作
[即时聊天] [发私信] [加为好友]
孙凌峰ID:soilwork
191780次访问,排名382好友0人,关注者12
soilwork的文章
原创 85 篇
翻译 15 篇
转载 0 篇
评论 329 篇
clayman的公告
嘿嘿 ^o^....
最近评论
jym5596337:我也不知道我怎么就走到了你的路上来了呵呵...
太晕了,我们专业就学的C#... 那我就凑合着用它学习MDX喽,但到了2008你的这个时期,感觉形式有点尴尬,以前的人说 在中国搞软件是 前有微软,后有盗版。
现在是 前有XNA后有C++ ... MDX 学习资料太太太难找了。。太太太少了。 师兄给介绍下你学历路途中的资料目录咯。。。我好找来学习咯。。 感谢哦感谢。。
jym5596337:好象没有继续哇 ...
shapin:ATI的网站有个支持HLSL语法高亮的vs插件,可以支持其他版本,只要修改相应的那个注册表就行
flip:To linxv :
貼圖座標有用投影嗎?
linxv:我用了你上面的基本技术,确实实现了纹理扰动,非常感谢!但是有一个问题是那张被扰动的图会跟着摄象机动,不知道是什么原因!
文章分类
收藏
    相册
    blogs
    David Weller
    nVidia Developer blog
    Rico Mariani
    Shawn Hargreaves
    XNA Team blog
    XNA资源
    XNA Creators Club
    ZBuffer
    Ziggyware XNA Resources
    中国XNA开发网
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 《The Complete Effect and HLSL Guide》翻译连载(十二)收藏

    新一篇: content pipeline without volume texture support???!!! | 旧一篇: Shaderey――非真实渲染

    The Complete Effect and HLSL Guide(十二) 

     本文版权归原作者所有,仅供个人学习使用,请勿转载,勿用于任何商业用途。
    由于本人水平有限,难免出错,不清楚的地方请大家以原著为准。欢迎大家和我多多交流。
    翻译:clayman
    Blog:
    http://blog.csdn.net/soilwork
    clayman_joe@yahoo.com.cn 
     

       我们已经有了个一个漫反射点光源着色器,现在应该考虑光照方程中的高光部分了。和漫反射相比,镜面高光最大的区别就是光照亮度不但与光线到表面的角度有关,还和观察者的角度有关。

           为了计算高光,我们需要一个称为中间(half)矢量的值。这个矢量其实是观察矢量和灯光矢量的中间值。为了计算观察矢量需要把观察点变换到模型空间。我们假设观察点的位置位于( 0,0,10)。把它变换到模型空间之后,加上顶点位置,就是最终的观察矢量:

     

           EyeVector = -normal(mul ( inv_view_matrix, float4(0,0,10,1) + inPos);

     

           EyeVector与光源矢量混合到一起,然后进行标准化,就是中间矢量。由于EyeVectorLightDir都是标准矢量,所以中间矢量相当于二者的均值(A + B/ 2

     

           HalfVect = normalize ( LightDir – EyeVetor);

           (译注:更直观的做法是在世界坐标下计算EyeVector,然后用LightDir EyeVetor计算HalfVect)

           有了中间矢量,把它和表面法线的点积进行m次幂运算,就能算出光源基于角度的光线强度。幂运算时的指数相当于物体的镜面指数,值越大,高光区域就越小也越明亮。下面的代码假设镜面指数为32

     

    struct VS_OUTPUT

    {

           float4 Pos:      POSITION;

           float2 texCoord: TEXCOORD0;

           float2 Color: COLOR;

    };

     

    float4 Light_PointSpecular( float3 VertPos, float3 VertNorm,float3 LightPos,

                  float4 LightColor, float4 Lightattenuation, float3 EyeDir)

    {

           //Determine the Distance from the light to the vertex and the direction

           float3 LightDir = LightPos - VertPos;

           float Dis = length(lightDir);

           LightDir = LightDir / Dist;   

           //Computer half vector

           float3 HalfVect = normalize( LightDir = EyeDir);     

           //Compute distance based attenuation. this is defined as:

           //Attenuation = 1/(LA.x + LA.y * Dist + LA * Dist * Dist)

           float DistAttn = saturate( 1 / ( LightAttenuation.x + LightAttenuation.y * Dist +

                         LightAttenuation.z * Dist * Dist));

           float SpecularAttn = pow(saturate ( dot(VertNorm,HalfVect)),32);      

           //Compute finfal lighting

           return LightColor * DistAttn * SpecularAttn;

    }

     

    VS_OUTPUT vs_main( float4 inPos:POSITION,float3 inNormal:NORMAL,float2 inTxr:TEXCOORD0)

    {

           VS_OUTPUT Out;

           //compute the projected positon and send out the texture coordinates

           Out.Pos = mul(View_proj_matrix,inPos);

           Out.TexCoord = inTxr;      

           //Output the ambient Color

           float4 Color = Light_Ambient;

           //Determine the eye vector

           float3 EyeVector = -normlaize(mul(inv_view_matrix,float4(0,0,10,1)) + inPos);      

           //computer the light contribution

           Color = Light_PointSpecular(inPos,inNormal,Light1_Position,Light1_Color,Lihgt_Attenuation,

                  EyeVector);     

           //Output Final Color

           Out.Color = Color;

           return Out

    }

     

           当考虑光照时,大部分人都认为逐顶点的光照足够好了。对于镶嵌度较高的模型来说是这样,但对某些复杂或的精度模型来说却不一定。出现这种效果的原因是顶点间颜色插值的方式。

           当逐顶点照亮对象时,将为每个顶点计算一次光照颜色,然后在通过顶点在多边形所覆盖的区域对像素颜色进行线形插值。现实中,光照值取决于光线角度,表面法线,和观察点(对于镜面高光来说)。与逐像素对所有光照元素进行单独插值,再计算光照相比,直接对顶点颜色进行插值所得的结果通常不够精确,特别是对面积较大的多边形来说。

    (左图为per-pixel lighting,右图为per-vertex

          

           上图显示了逐像素和逐顶点光照的差别。当处理高精度多边形模型时,由于每个多边形所覆盖的区域很小,因此插值之后每个像素的误差也很小,所以逐顶点光照可以工作的很好。而当处理低模时,这种误差就变的很大了。

           使用逐像素光照的另一个好处是可以在渲染时添加并不存在的表面细节。通过bump mapnormal map,可以在像素级别让原本平坦的表面表现出近似的凹凸效果。

           再回过头来看看漫反射光线,你需要决定哪些光照元素可以插值之后传送到像素着色器,哪些元素必须逐像素计算。从头到尾,决定漫反射光照的就是表面法线和光线矢量的点积。

           这个点积定义了光照的强度,但是对它进行插值的结果通常不正确。因此这一步计算应该移动到像素着色器中,进行逐像素计算。

           表面法线和光矢量是计算点积的要素。通常矢量间的插值是正确的。因此可以在顶点着色器计算他们的值,并传递到像素着色器中,然后进行最终的点积计算。

     

    注意


    虽然对法线的插值是正确的,但是插值之后的向量将不再是标准向量。为了对此进行校正,需要在像素着色器中对法线重新进行标准化,可以使用内建的normalize函数。

          

           为了把光照计算移动到像素着色器中,需要先添加两个变量到顶点着色器的输出结构中。可以使用TEXCOORD1TEXCOORD2语义把这些值传递到像素着色器。代码如下:

     

    struct VS_OUTPUT

    {

            float4 Pos : POSITION;

            float2 TexCoord: TEXCOORD0;

            float3 Normal: TEXCOORD1;

            float3 LightDir: TEXCOORD2;

    }

     

           接下来修改顶点着色器代码。目前我们只考虑单光源光照的情况,所以先删除原先的光照函数把其中的代码复制到vs_main函数中。记住,我们后面回讨论多光源的情况。

           你可能已经注意到我们还没有讨论基于距离的衰减值应该在哪里计算。虽然对距离的插值结果不是完全正确,但由于光线的变化有足够容差范围,距离上的微小差别并不会给结果带来明显变化,所以不需要逐像素计算。

           衰减值只是一个标量,为了把它传递给像素着色器,而又不浪费一个额外的寄存器来传值,可以把LightDir改为一个float4的矢量,把衰减值作为这个矢量的w分量。修改后的顶点着色器如下:

     

    VS_OUTPUT vs_main(float4 inPos:POSITION,float3 inNormal: NORMAL,

           float2 inTxr: TEXCOORD0)

    {

           VS_OUTPUT Out; 

           Out.Pos = mul( view_proj_matrix, inPos);

           Out.TexCoord = inTxr;

           Out.Normal = inNormal;   

           //Computer and move the light direction to the pixel shader

           float4 LightDir;

           LightDir.xyz = Light1_Position - inPosition;

           float Dist = length(LightDir.xyz);

           LightDir.xyz = LightDir.xyz / Dist;    

           LightDir.w = saturate( 1 / ( LightAttenuation.x + LightAttenuation.y * Dist +

                         LightAttenuation.y * Dist*Dist));  

           //Output the light direction

           Out.LightDir = lightDir;

           return Out;

    }

     

           在像素着色器中,只需要接收参数,计算点击和颜色就可以了。为了方便,把光照计算单独放到一个名为Light_PointDiffuse的函数中。本质上来说,这里计算光照的方法和在顶点着色器中是一样的。

     

    float4 Light_PointDiffuse(float4 LightDir, float3 Normal,Float4 LightColor)

    {

           //compute dot product of N and L

           float AngleAttn = saturate(dot(Normal,LightDir.xyz));

           //compute final lighting

           return LightColor * LightDir.w * AngleAttn;

    }

     

    float4 ps_main(float3 inNormal:TEXCOORD1,

           float4 inLightDir : TEXCOORD2) : COLOR

    {

           //Compter the lighting contribution for this single light

           return Light_PointDiffuse(inLightDir,inNormal,Light_Color);

    }

     

           很简单,对吧!接下来编写镜面高光像素着色器。整个步骤基本上和我们刚才编写漫反射像素着色器的方法差不多。这一个shader的核心是法线和中间矢量的点积,需要把它们移动到像素着色器中。这意味着需要在顶点着色器中计算光照矢量,中间矢量。先修改顶点着色器的输出结构。

     

    struct VS_OUTPUT

    {

           float4 pos: POSITION;

           float2 TexCoord: TEXCOORD0;

           float3 Normal : TEXCOORD1;

           float4 LightDir: TEXCOORD2;

           float3 HalfVect: TEXCOORD3;

    }

     

           在顶点着色器中,需要计算以下值:表面法线,光照矢量,中间矢量和基于距离的衰减值。只需要把先前编写的镜面高光着色器代码稍作修改就行了。

    VS_OUTPUT vs_main(float4 inPos:POSITION,float3 inNormal:NORMAL,

                  float2 inTxr: TEXCOORD0)

    {

           VS_OUT Out;
       
       Out.Pos = mul(view_proj_matrix, inPos);
           Out.TexCoord = inTxr;
           float4 LightDir;

     

           LightDir.xyz = Light1_Position - inPos;

           flaot Dist = length(LightDir.xyz);

           LightDir.xyz = LightDir.xyz / Dist;

     

           LightDir.w = saturate( 1 / ( LightAttenuation.x + LightAttenuation.y * Dist +  LightAttenuation.y * Dist*Dist));    

           //computer eye vector and the half vector

           float3 EyeVector = -normalize(mul(inv_view_matrix,float4(0,0,10,1))+inPos);

           Out.HalfVect = normalize(LightDir - EyeVector);    

           //Output normal and light direction

           Out.Normal = inNormal;

           Out.LightDir = LightDir; 

           return Out;

    }

     

           对像素着色器来说,同样把计算高光的代码作为一个单独的函数:

     

    float4 Light_PointSpecular(float3 Normal,flaot3 HalfVect,float4 LightDir,float4 LightColor)

    {

           float SpecularAttn = pow(saturate(dot(Normal,HalfVect)),32);

           return LightColor * LightDir.w * SpecularAttn;

    }

     

    float4 ps_main(float3 inNormal:TEXCOORD1,float4 LightDir:TEXCOORD2,

                  float3 HalfVect:TEXCOORD3):COLOR

    {

           //simply route teh vertex color to the output

           return Light_PointSpecular(inNormal,HalfVect,LightDir,Light1_Color);

    }

    ~~~~~~~~未完待续~~~~~~~~~~~~~~~~

     

    发表于 @ 2007年01月14日 11:49:00|评论(loading...)|编辑

    新一篇: content pipeline without volume texture support???!!! | 旧一篇: Shaderey――非真实渲染

    评论

    #rc 发表于2007-04-04 22:11:05  IP: 202.114.106.*
    第一行代码少一个右括号
    #rc 发表于2007-04-04 22:18:07  IP: 202.114.106.*
    基本上看完了。呵呵
    该更新了,我等着看新的啊。
    #soilwork 发表于2007-04-08 23:27:32  IP: 58.60.213.*
    to rc:
    确实好久没有更新了,最近太忙。另外如果后面要写的话,我也不想直接翻译了,有时候自己写起来还要快些^_^
    #clamp 发表于2007-07-26 17:12:54  IP: 60.2.146.*
    这篇的图看不见了
    #GPU通用计算 发表于2008-01-10 10:18:40  IP: 202.118.75.*
    还会继续往下翻译或者你自己写么,下面的effect部分我非常期待,你翻译的这本书现在又翻译版的发行了吗,或者同主题的讲HLSL和effect或者通用计算的书籍你知道的有中文版的不错的书籍么,希望能得到你的推荐,谢谢
    #GPU通用计算 发表于2008-01-10 10:22:32  IP: 202.118.75.*
    能否给我发些关于HLSL和effect的电子版的资料,我想收藏起来好好研究,本人基础比较差,我的邮箱是mly219@163.com
    发表评论  


    登录
    Csdn Blog version 3.1a
    Copyright © clayman