The Complete Effect and HLSL Guide_12(转载)

转载 2007年10月12日 12:47:00

转自http://blog.csdn.net/soilwork/archive/2007/01/14/1482661.aspx

The Complete Effect and HLSL Guide(十二) 

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

       为了计算高光,我们需要一个称为中间(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);

}

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

 

HLSL的基础语法

HLSL的基本语法 1 数据类型 1.1   标量类型 1. bool: True or false .Note that the HLSL provides the true and fals...
  • zhuhuangtianzi
  • zhuhuangtianzi
  • 2014年12月03日 22:23
  • 3002

HLSL内置函数,及HLSL与GLSL的对应函数

HLSL函数列表 本表来自网络,我对说明做了些修改。 Name Syntax Description abs abs(x) 返回x的绝对值。对x的每个元素都会独立计算一次。Abs...
  • eloudy
  • eloudy
  • 2017年05月06日 14:15
  • 1321

学习HLSL以来的一点心得(一):调试

HLSL看似复杂,其实真正使得其法的话,要比固定流水线来的更加容易.加之可以随心所欲的控制顶点和像素,要比固定渲染线中枯燥无味的反复调用那些状态函数精彩的多.所以我建议正在埋头苦学D3D的同学们一定不...
  • zhaobangyu
  • zhaobangyu
  • 2017年02月13日 16:36
  • 1159

D3D11之HLSL常见问题和调试方法总结

用D3D11有一小段时间了,感觉在以前的教程章节也是散乱的写关于HLSL的总结,不过以前的我感觉有点不太对,这里我重新整理一下子 第一,HLSL错误类型。 HLSL错误两类两大类: 第一,加载sh...
  • qq_29523119
  • qq_29523119
  • 2016年10月21日 01:11
  • 1065

HLSL Shader编程基础总结

基本前提概念     Shader是一种映射到GPU硬件汇编语言上的高级语言,Shader中的数据类型是要声明类型和用途的,用途其实就是和着色器寄存器关联,输入位置寄存器,输出位置寄存器,输出颜...
  • Blues1021
  • Blues1021
  • 2015年07月27日 23:07
  • 1981

正式宣布DXBC2GLSL,HLSL字节码到GLSL的编译器

转载请注明出处为KlayGE游戏引擎,本文的永久链接为http://www.klayge.org/?p=2826 三年前,我就曾经计划过一个KlayGE的长期研发子项目,D3D11 HL...
  • pizi0475
  • pizi0475
  • 2015年11月05日 19:14
  • 1006

HLSL效果框架-多光源效果

高级着色器语言(HLSL)难就难在它是运行在GPU,CPU上的,编写和调试都很麻烦. 用效果框架简化了很多操作,先列出着色器的代码,重点部分都用中文注释了 着色器语言文件为:,代码为: //着色器处理...
  • lunweiwangxi3
  • lunweiwangxi3
  • 2015年04月21日 14:35
  • 1200

HLSL注意事项

Visual Studio可以新建.hlsl文件 如新建VertexShader 在上图中我们还会看到有HLSL标头文件,该文件用于放公用的自定义函数 如: 标头文件的后缀...
  • u010897187
  • u010897187
  • 2015年01月14日 20:55
  • 882

D3D9学习笔记(九) effect (hlsl)

effect其实整合了shader和render state的控制两大部分内容 9.1 effect文件基本框架 part1 :shader state包括全局变量 shader数据结构定...
  • wangbuji
  • wangbuji
  • 2017年02月21日 10:35
  • 442

【DirectX11】第四篇 Effects框架

本文为转载文章,这里为原文链接。本文索引:一 什么是Effects 二 Effect文件结构 1 technique11 2 pass 三 如何编译着色器 四 如何创建Effect对象 五 如何使Ef...
  • xueyedie1234
  • xueyedie1234
  • 2016年05月09日 19:50
  • 3166
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:The Complete Effect and HLSL Guide_12(转载)
举报原因:
原因补充:

(最多只允许输入30个字)