基于法线的边缘检测

http://blog.youkuaiyun.com/xoyojank/article/details/3422631

边缘高亮效果中我提到过两种方法, 各有优缺点吧

 

图像空间域的边缘检测效果比较好, 中间没有多余的线条. 缺点是PS中计算比较慢

第二种把模型"放大"(其实是变胖)的做法, 可以在VS中完成, 不需要额外的RenderTarget, 适合低端显卡使用, 适应性好. 不如果模型法线信息不对的话, 会造成画面错乱. 实际使用时可以根据W值(不用Z深度)来画出远近粗细一样的线条

 

这次提到的基于法线的方法, 其实跟2D的空间域边缘检测很相似, 如果要求结果是绘制物体的线条图而不仅仅是一个边缘轮廓时, 它就派上用场了. (还是要用PS去算, 实际使用时要注意性能问题)

 

基本的渲染流程(2 pass):

第一个pass用于生成法线图到一张RenderTarget上, 第二个pass跟据这张法线图来做边缘检测.

实际使用时可以采用Multi-RenderTarget来加速

 

法线信息要在pixel shader里进行向量化, 不然会在一些面上出块很淡的颜色. 如果对质量要求不高, 可以在VS中进行向量化.

RenderTarget要Clear成单位化的值, 我用的(0,0,1), 即纯蓝色

  1. /*********************VS*********************/  
  2. float4x4 matViewProjection;  
  3.   
  4. struct VS_INPUT   
  5. {  
  6.    float4 Position : POSITION0;  
  7.    float3 Normal   : NORMAL0;  
  8. };  
  9.   
  10. struct VS_OUTPUT   
  11. {  
  12.    float4 Position : POSITION0;  
  13.    float3 Normal   : TEXCOORD0;  
  14. };  
  15.   
  16. VS_OUTPUT vs_main( VS_INPUT Input )  
  17. {  
  18.    VS_OUTPUT Output;  
  19.   
  20.    Output.Position = mul( Input.Position, matViewProjection );  
  21.    Output.Normal = mul( Input.Normal, matViewProjection );  
  22.      
  23.    return( Output );  
  24.      
  25. }  
  26. /*********************PS*********************/  
  27. float4 ps_main(float3 normal : TEXCOORD0) : COLOR0  
  28. {     
  29.    return(float4(normalize(normal), 1.0f));  
  30. }  

注意法线图的格式是浮点数格式, 我用的是D3DFMT_A16B16G16R16F(因为法线有负值, 你也可以自己压缩到[0,1]再解开)

 

有了这张法线图就很好办了, 对每个像素计算它与周围像素的法线夹角余弦值的和, 再取反(1-degree), 这样就能计算出来边缘了

依据就是边缘处的法线夹角比较大, 余弦值更接近0甚至为负值.

  1. sampler TexNormal;  
  2. float2 fInverseViewportDimensions;  
  3.   
  4. float2 PixelKernel[4] =  
  5. {  
  6.     { 0,  1},  
  7.     { 1,  0},  
  8.     { 0, -1},  
  9.     {-1,  0}  
  10. };  
  11.   
  12. float4 ps_main(float2 texCoord : TEXCOORD0) : COLOR0  
  13. {  
  14.    float4 origin = tex2D(TexNormal, texCoord);  
  15.    float3 sum = 0;  
  16.    for (int i = 0; i < 4; i++)  
  17.    {  
  18.       float2 texel = texCoord + PixelKernel[i] * fInverseViewportDimensions;  
  19.       sum += saturate(1.0f - dot(origin.xyz, tex2D(TexNormal, texel).xyz));  
  20.    }  
  21.      
  22.    return float4(sum, 1.0f);  
  23. }  

最终效果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值