Shader 逐顶点和逐像素光照

转自:http://blog.youkuaiyun.com/pizi0475/article/details/6968070


逐顶点光照

所谓逐顶点光照,简单地说就是在vetext shader中计算光照颜色,该过程将为每个顶点计算一次光照颜色,然后在通过顶点在多边形所覆盖的区域对像素颜色进行线形插值。现实中,光照值取决于光线角度,表面法线,和观察点(对于镜面高光来说)。具体实现时的shader代码如下:

[cpp]  view plain copy
  1. //相关全局变量  
  2. shared float4x4 matWorldViewProj;  
  3. shared float4x4 matWorld;  
  4. shared float3 lightPosition;  
  5. shared float4 ambientLightColor;  
  6. shared float4 diffuseLightColor;  
  7. shared float4 specularLightColor;  
  8. shared float3 cameraPosition;   
  9.   
  10. //VertexShader输出结构  
  11. struct VertexShaderOutput  
  12. {  
  13.     float4 Position : POSITION;  
  14.     float4 Color : COLOR0;  
  15. };  
  16.   
  17. //PixelShader输入结构,只接受从VertexShader传来的颜色  
  18. struct PixelShaderInput  
  19. {  
  20.     float4 Color: COLOR0;   
  21. };  
  22.   
  23. VertexShaderOutput VertexDiffuseAndPhong(float3 position : POSITION,float3 normal : NORMAL )   
  24. {  
  25.     VertexShaderOutput output;   
  26.       
  27.     //transform the input position to the output   
  28.     output.Position = mul(float4(position, 1.0), matWorldViewProj);   
  29.     float3 worldNormal = mul(normal, matWorld);   
  30.     float4 worldPosition = mul(float4(position, 1.0), matWorld);  
  31.     worldPosition = worldPosition / worldPosition.w;   
  32.     float3 directionToLight = normalize(lightPosition - worldPosition.xyz);   
  33.     float diffuseIntensity = saturate( dot(directionToLight, worldNormal));   
  34.     float4 diffuse= diffuseLightColor * diffuseIntensity;   
  35.     float3 reflectionVector = normalize(reflect(-directionToLight, worldNormal));   
  36.     float3 directionToCamera = normalize(cameraPosition - worldPosition.xyz);   
  37.     float4 specular = specularLightColor * pow(saturate(dot(reflectionVector,   
  38.     directionToCamera)), 20);   
  39.     output.Color = specular + diffuse + ambientLightColor;   
  40.     output.Color.a = 1.0;  
  41.      //return the output structure   
  42.      return output;   
  43. }  
  44.   
  45. float4  SimplePixelShader(PixelShaderInput input) : COLOR  
  46. {  
  47.     return input.Color;   
  48. }  
  49.   
  50. technique PerVertexDiffuseAndPhong  
  51. {  
  52.     pass P0  
  53.     {  
  54.         //set the VertexShader state to the vertex shader function  
  55.         VertexShader = compile vs_2_0 VertexDiffuseAndPhong();  
  56.         //set the PixelShader state to the pixel shader function  
  57.         PixelShader = compile ps_2_0 SimplePixelShader();  
  58.     }  
  59. }   


由以上代码可见,各像素的颜色计算都是在VertexShader中实现的。程序截图如下:

逐顶点光照

当考虑光照时,大部分人都认为逐顶点光照已经足够好了。对于镶嵌度较高的模型来说是这样,但对某些多边形较少的模型来说却不一定。比如这个示例,球的多边形较少,可以明显看出棱角分明,高光效果也不理想。直接对顶点颜色进行插值所得的结果通常不够精确,特别是对面积较大的多边形来说。当处理高精度多边形模型时,由于每个多边形所覆盖的区域很小,因此插值之后每个像素的误差也很小,所以逐顶点光照可以工作的很好。而当处理低模时,这种误差就变的很大了。

逐像素光照

逐像素光照是对所有光照元素进行单独插值,简单地说就是在pixelshader中计算颜色。具体实现时的shader代码如下:

[cpp]  view plain copy
  1. //全局变量  
  2. shared float4x4 matWorldViewProj;  
  3. shared float4x4 matWorld;   
  4. shared float3 cameraPosition;  
  5. shared float3 lightPosition;  
  6. shared float4 ambientLightColor;  
  7. shared float4 diffuseLightColor;   
  8. shared float4 specularLightColor;   
  9.   
  10. struct VertexShaderOutputPerPixelDiffuse  
  11. {  
  12.     float4 Position : POSITION;  
  13.     float3 WorldNormal : TEXCOORD0;  
  14.     float3 WorldPosition : TEXCOORD1;  
  15. };  
  16.   
  17. struct PixelShaderInputPerPixelDiffuse  
  18. {  
  19.     float3 WorldNormal : TEXCOORD0;   
  20.     float3 WorldPosition : TEXCOORD1;   
  21. };  
  22.   
  23. VertexShaderOutputPerPixelDiffuse PerPixelDiffuseVS( float3 position : POSITION, float3 normal : NORMAL )   
  24. {  
  25.     VertexShaderOutputPerPixelDiffuse output;   
  26.     //transform the input position to the output   
  27.     output.Position = mul(float4(position, 1.0), matWorldViewProj);   
  28.     output.WorldNormal = mul(normal, matWorld);   
  29.     float4 worldPosition =  mul(float4(position, 1.0), matWorld);   
  30.     output.WorldPosition = worldPosition / worldPosition.w;   
  31.       
  32.     //return the output structure   
  33.     return output;  
  34. }  
  35.   
  36. float4 DiffuseAndPhongPS(PixelShaderInputPerPixelDiffuse input) : COLOR  
  37. {  
  38.     //calculate per-pixel diffuse   
  39.     float3 directionToLight = normalize(lightPosition - input.WorldPosition);   
  40.     float diffuseIntensity = saturate( dot(directionToLight, input.WorldNormal));   
  41.     float4 diffuse = diffuseLightColor * diffuseIntensity;   
  42.       
  43.     //calculate Phong components per-pixel   
  44.     float3 reflectionVector = normalize(reflect(-directionToLight, input.WorldNormal));   
  45.     float3 directionToCamera = normalize(cameraPosition - input.WorldPosition);   
  46.     //calculate specular component float4 specular = specularLightColor *   
  47.     pow(saturate(dot(reflectionVector, directionToCamera)), 20);   
  48.       
  49.     //all color components are summed in the pixel shader   
  50.     float4 color = specular + diffuse + ambientLightColor;   
  51.     color.a = 1.0;   
  52.     return color;   
  53. }   
  54.   
  55. technique PerPixelDiffuseAndPhong  
  56. {  
  57.     pass P0  
  58.     {  
  59.         VertexShader = compile vs_2_0 PerPixelDiffuseVS();   
  60.         PixelShader = compile ps_2_0 DiffuseAndPhongPS();  
  61.     }  
  62. }   


由上面两段代码对比可知,算法实际上是一样的,只不过颜色的计算过程一个放在VertexShader中,而另一个放在PixelShader中。程序截图如下,源代码中可以通过按空格键切换两种效果,逐像素光照效果好:

逐像素光照

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

当然,由于逐像素的计算量要比逐顶点要大,所以请根据具体情况灵活选择,如果你使用BasicEffect,那么默认是使用逐顶点光照,你必须添加basicEffect.PreferPerPixelLighting=true才能开启逐像素光照。

最后以上大部分文字来自于clayman博客中的The Complete Effect and HLSL Guide的第十二章,在此感谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值