饭后温柔

汉堡与老干妈同嚼 有可乐味
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

一个有趣的模拟光照的shader(类似法线贴图)

Posted on 2013-10-30 04:25  饭后温柔  阅读(16318)  评论(2编辑  收藏  举报

  最近使用unity,碰到到一个很有趣的例子.场景无光线,却模拟出了光照,效果挺好.其思路与法线贴图原理异曲同工.

  原作者提供的效果印象深刻.

  模型除了使用原来的diffuse贴图外,还用到了一张模拟记录了"光照"信息的贴图(见机器人头上的贴图).这一点与法线贴图是一致的.

  这个方法比较简单,也比较死.思路很巧.

  分析一下贴图,有效范围基本是一个圆形.以前在学习法线贴图时,就遇到过法线投射在贴图的情景(http://www.cnblogs.com/flytrace/p/3387748.html).当各个方向的法线投影到一个正平面时,它形成一个圆.

  法线贴图本质就是为了预先保存法线信息.那几乎就可以引申出来了,我们也可以保存一个冒充的法线(光线)的信息,根据这个信息我们可以还原一个"伪"的光照过程.这个贴图就反过来利用了这一点.将模型的法线转换到model-view空间,再假设法线被投影到一个正平面(贴图上).于是法线的投影点可换算为贴图的uv坐标.此时在该点我们保存一个颜色值,可以与模型的diffuse贴图颜色进行计算,模拟出光照的效果.  

                struct v2f
                {
                    float4 pos    : SV_POSITION;
                    float2 uv     : TEXCOORD0;
                    float2 cap    : TEXCOORD1;
                };
                uniform float4 _MainTex_ST;
                
                v2f vert (appdata_base v)
                {
                    v2f o;
                    o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
                    o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                    
                    half2 capCoord;
                    capCoord.x = dot(UNITY_MATRIX_IT_MV[0].xyz,v.normal);
                    capCoord.y = dot(UNITY_MATRIX_IT_MV[1].xyz,v.normal);
                    o.cap = capCoord * 0.5 + 0.5;
                    
                    return o;
                }
                
                uniform sampler2D _MainTex;
                uniform sampler2D _MatCap;
                
                fixed4 frag (v2f i) : COLOR
                {
                    fixed4 tex = tex2D(_MainTex, i.uv);
                    fixed4 mc = tex2D(_MatCap, i.cap);
                    
                    return (tex + (mc*2.0)-1.0);
                }

  capCoord.x = dot(UNITY_MATRIX_IT_MV[0].xyz,v.normal);

  capCoord.y = dot(UNITY_MATRIX_IT_MV[1].xyz,v.normal);

  上边2行是这里比较关键的一步,把法线转换到view空间.参看(http://www.cnblogs.com/flytrace/p/3379816.html)说明了原理.总之法线不能直接使用model-view矩阵转换到view空间.

  o.cap = capCoord * 0.5 + 0.5;

  上边是把法线值转换到纹理区间[0,1],之后在fragment shader通过这个值去贴图里查找对应的颜色值.

  这种手法似乎叫MatCap shader.这里是些卡通效果,其他地方有更真实的.