本系列主要参考《Unity Shaders and Effects Cookbook》一书(感谢原书作者),同时会加上一点个人理解或拓展。

这里是本书所有的插图。这里是本书所需的代码和资源(当然你也可以从官网下载)。

========================================== 分割线 ==========================================

终于到了Diffuse Shading一章的最后一篇了!回忆一下,在上一篇中,一共学习了两种改善漫反射光照的方法:一种是完全根据感性认识,使用Half Lembert方法改变了光照值区间,使得物体颜色整体提亮;一种是通过一张渐变图,来控制光照值。

这两种方法都只考虑到了入射光线和反射点所在的平面法向量的夹角,但是想象实际生活中我们观察一个物体,即便是在相同的光照下观察物体的同一点,如果我们观察位置有所改变,看到的结果也会不一样。因此,这一篇中,我们将引入这一新的参数:view direction——观察点方向。

为了达到这一目的,我们使用一张二维的渐变图来代替之前的一维渐变图(因为在之前的方法中,我们仅使用一个参数就决定了该图的采样位置),使用两个参数来决定采样的真正的UV坐标:一个参数由入射光线和平面法向量计算而得,一个由观察点方向和平面法向量计算而得。

BRDF

BRDF是bidirectional reflectance distribution function的简写。这名字很长,翻译过来就是双向反射分布函数。简单说,就是考虑光线是如何从一个入射角度(the light direction)在一个不透明平面上反射到某一个观察者的眼睛(the view direction)里的。

准备工作

  1. 还是需要上一篇结束时的代码:
    Shader "Custom/RampDiffuse" {
    Properties {
    _EmissiveColor ("Emissive Color", Color) = (1,1,1,1)
    _AmbientColor ("Ambient Color", Color) = (1,1,1,1)
    _MySliderValue ("This is a Slider", Range(0,10)) = 2.5
    _RampTex ("Ramp Texture", 2D) = "white"{}
    }
    SubShader {
    Tags { "RenderType"="Opaque" }
    LOD 200 CGPROGRAM
    #pragma surface surf BasicDiffuse float4 _EmissiveColor;
    float4 _AmbientColor;
    float _MySliderValue;
    sampler2D _RampTex; struct Input
    {
    float2 uv_MainTex;
    }; void surf (Input IN, inout SurfaceOutput o)
    {
    float4 c;
    c = pow((_EmissiveColor + _AmbientColor), _MySliderValue);
    o.Albedo = c.rgb;
    o.Alpha = c.a;
    } inline float4 LightingBasicDiffuse (SurfaceOutput s, fixed3 lightDir, fixed atten)
    {
    float difLight = max(0, dot (s.Normal, lightDir));
    float hLambert = difLight * 0.5 + 0.5;
    float3 ramp = tex2D(_RampTex, float2(hLambert)).rgb; float4 col;
    col.rgb = s.Albedo * _LightColor0.rgb * (ramp);
    col.a = s.Alpha;
    return col;
    } ENDCG
    }
    FallBack "Diffuse"
    }
  2. 除此之外,我们还需要一张二维渐变图:

    大小为512*512:首先从左下角开始一个渐变色,直到图片右上角;再开始另一个渐变色,从左上角开始一直到图片中间;最后再开始一个渐变色,从右下角直到中间。

实现

  1. 首先,给我们的光照函数LightingBasicDiffuse添加新的参数viewDir,表示观察方向:
    inline float4 LightingBasicDiffuse (SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten)

    这个参数将会由Unity内部提供,来得到当前摄像机的观察位置到观察点的方向向量。

  2. 与计算入射光线和平面法向量类似,计算观察方向和平面法向量的夹角余弦值:
    float rimLight = max(0, dot (s.Normal, viewDir));
  3. 使用Half Lambert方法改善rimLight的值(后面我们会对比一下如果不这么做会有什么区别):
    float rim_hLambert = rimLight * 0.5 + 0.5;
  4. 最后,使用新的计算结果在_RampTex中采样:
    float3 ramp = tex2D(_RampTex, float2(dif_hLambert, rim_hLambert)).rgb;
  5. 完成的代码如下:
            inline float4 LightingBasicDiffuse (SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten)
    {
    float difLight = max(0, dot (s.Normal, lightDir));
    // Add this line
    float rimLight = max(0, dot (s.Normal, viewDir));
    // Modify this line
    float dif_hLambert = difLight * 0.5 + 0.5;
    // Add this line
    float rim_hLambert = rimLight * 0.5 + 0.5;
    // Modify this line
    float3 ramp = tex2D(_RampTex, float2(dif_hLambert, rim_hLambert)).rgb; float4 col;
    col.rgb = s.Albedo * _LightColor0.rgb * (ramp);
    col.a = s.Alpha;
    return col;
    }
  6. 最后得到的渲染结果如下:

解释

当使用了观察方向这个参数后,我们可以创建一个非常简单的衰减渲染结果。下图显示了观察方向和平面法向量进行dot运算后的结果:
而通过一张二维渐变图,我们可以考虑两个方向对我们观察结果的影响:

思考

在原书中,实际上在计算两个方向的dot值后,都没有对其和0值取max。而且,在得到rimLight后,也没有采用Half Lambert方法对其优化。实践是检验真理的唯一方法!我们最后就来看一下,究竟有什么区别。这次除了正面,我们还会看一下侧面观察效果有什么不同。

两个方向都不考虑max、不使用Half Lambert

代码如下:
        inline float4 LightingBasicDiffuse (SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten)
{
float difLight = dot (s.Normal, lightDir);
float rimLight = dot (s.Normal, viewDir);
float3 ramp = tex2D(_RampTex, float2(difLight, rimLight)).rgb; float4 col;
col.rgb = s.Albedo * _LightColor0.rgb * (ramp);
col.a = s.Alpha;
return col;
}

渲染结果如下:

上面两张图的特点有:两条明显的分割线,分割成了三块,有两块(第一张图中苹果的左边两个区域)的明暗变化是错误的。一条分割线是由入射光线引起的,在计算入射光线方向和法线的点乘时出现了负数,导致由最暗突变到了最亮。一条分割线是由观察角度引起的,道理类似。

分别对两个方向使用max

对入射光线方向的计算结果使用max操作:
float difLight = max (0, dot (s.Normal, lightDir));

结果如下:

结果是分界线仍然存在,但背光面的明暗变化对了。
对观察方向的结果使用max操作:
float rimLight = max (0, dot (s.Normal, viewDir));

结果如下:

通过和上面一种情况观察,可以发现max操作主要改善了正负交界处明暗的不正常变化的情况,防止了一些奇葩情况的出现,例如第一种情况下明暗的不正常变化。因此,如果你确保观察方向和入射方向都非常恰好的话,很有可能发现渲染结果没有变化。

分别使用Half Lambert修正

首先对入射光线的计算结果进行Half Lambert修正:
        inline float4 LightingBasicDiffuse (SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten)
{
float difLight = max (0, dot (s.Normal, lightDir));
float rimLight = max (0, dot (s.Normal, viewDir));
float dif_hLambert = difLight * 0.5 + 0.5;
float3 ramp = tex2D(_RampTex, float2(dif_hLambert, rimLight)).rgb; float4 col;
col.rgb = s.Albedo * _LightColor0.rgb * (ramp);
col.a = s.Alpha;
return col;
}

结果如下:

这也是原书所得到的结果。可以发现,使用了Half Lambert修正后,除了整体变亮以外,由于入射光线产生的分割线也消失了,在背光面(即原来max操作后为0的区域)现在也有了合理而连续的颜色变化。
而由于观察方向计算结果还未修正,因此上面的侧向观察图中,仍旧没有合理的渐变结果。
在用Half Lambert继续对观察方向计算结果进行修正,即上文中的代码后,结果如下:

结束语

关于漫反射光照模型的学习,基本告一段落。下面一章里,主要会学习如何使用材质贴图来进行渲染!

【Unity Shaders】Diffuse Shading——使用2D ramp texture来创建一个假的BRDF(双向反射分布函数)的更多相关文章

  1. 【Unity Shaders】Reflecting Your World —— 在Unity3D中创建一个简单的动态Cubemap系统

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

  2. 【Unity Shaders】Reflecting Your World —— 在Unity3D中创建Cubemaps

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

  3. 【Unity Shaders】概述及Diffuse Shading介绍

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

  4. 【Unity Shaders】学习笔记——SurfaceShader(四)用纹理改善漫反射

    [Unity Shaders]学习笔记——SurfaceShader(四)用纹理改善漫反射 转载请注明出处:http://www.cnblogs.com/-867259206/p/5603368.ht ...

  5. 【Unity Shaders】《Unity Shaders and Effects Cookbook》总结篇

    我的唠叨 不知不觉,从发表第一篇关于<Unity Shaders and Effects Cookbook>已经快十个月了.一开始的初衷就是学习笔记,毕竟将来回过头去看的时候,再看英文难免 ...

  6. 【Unity Shaders】Diffuse Shading——漫反射光照改善技巧

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

  7. 【Unity Shaders】Diffuse Shading——向Surface Shader添加properties

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

  8. 【Unity Shaders】Diffuse Shading——创建一个基本的Surface Shader

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

  9. 【Unity Shaders】Diffuse Shading——在Surface Shader中使用properties

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

随机推荐

  1. CRM客户关系管理系统(三)

    第四章.kingadmin开发设计 4.1.kingadmin设计 自定义admin注册model的写法 crm/admin.py class CustomerAdmin(admin.ModelAdm ...

  2. mybatis映射器配置细则

    前面三篇博客我们已经多次涉及到映射器的使用了,增删查基本上都用过一遍了,但是之前我们只是介绍了基本用法,实际上mybatis中映射器可以配置的地方还是非常多,今天我们就先来看看映射器还有哪些需要配置的 ...

  3. iOS开源照片浏览器框架SGPhotoBrowser的设计与实现

    简介 近日在制作一个开源加密相册时附带着设计了一个照片浏览器,在进一步优化后发布到了GitHub供大家使用,该框架虽然没有MWPhotoBrowser那么强大,但是使用起来更为方便,操作更符合常规相册 ...

  4. Lua判断OS并添加cpath

    Lua判断OS并添加cpath(金庆的专栏)Lua初始化时需要根据OS来设置package.cpath, 如果是Windows系统则添加 ?.dll, 否则添加 ?.so.不然加载错误后缀名的动态库会 ...

  5. Eclipse简介和使用技巧快捷方式

    1Eclipse简介和使用 IDE(Integrated Development Environment ): 集成开发环境,集合开发.运行.调试于一体的一个软件 Eclipse 是一个开放源代码的. ...

  6. Centos6.5 mysql安装

    cenos中安装软件使用yum进行安装,小米加步枪不如ak47. 第1步.yum安装mysql   yum -y install mysql-server 第2步.设置开机启动   chkconfig ...

  7. hive分组排序 取top N

    pig可以轻松获取TOP n.书上有例子 hive中比较麻烦,没有直接实现的函数,可以写udf实现.还有个比较简单的实现方法: 用row_number,生成排名序列号.然后外部分组后按这个序列号多虑, ...

  8. 早期Swift中Cocos2D初始化代码的重构

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 我们知道在早期的Swift中在子类里只能调用超类的design ...

  9. Hadoop:Hadoop单机伪分布式的安装和配置

    http://blog.csdn.net/pipisorry/article/details/51623195 因为lz的linux系统已经安装好了很多开发环境,可能下面的步骤有遗漏. 之前是在doc ...

  10. Unity UGUI图文混排源码(三) -- 动态表情

    这里是根据图文混排源码(二)进一步修改的,其他链接也不贴了,就贴一个链接就好了,第一次看这文章的同学可以先去看看其他几篇文章 Unity UGUI图文混排源码(二):http://blog.csdn. ...