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

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

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

上一篇中,我们学了怎样在surface shader(这里即指surf函数)中使用自己定义的Properties变量。而在之前的学习中,我们实际上使用的都是Unity内置的Diffuse Lighting Model,即漫反射光照模型。这一次,我们将学习如何让Unity使用我们自己定义的光照模型进行渲染。

准备工作


  1. 使用上一篇结束时的shader代码即可。
    Shader "Custom/BasicDiffuse" {
    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
    }
    SubShader {
    Tags { "RenderType"="Opaque" }
    LOD 200
    CGPROGRAM
    #pragma surface surf Lambert //We need to declare the properties variable type inside of the
    //CGPROGRAM so we can access its value from the properties block.
    float4 _EmissiveColor;
    float4 _AmbientColor;
    float _MySliderValue; struct Input
    {
    float2 uv_MainTex;
    }; void surf (Input IN, inout SurfaceOutput o)
    {
    //We can then use the properties values in our shader
    float4 c;
    c = pow((_EmissiveColor + _AmbientColor), _MySliderValue);
    o.Albedo = c.rgb;
    o.Alpha = c.a;
    } ENDCG
    }
    FallBack "Diffuse"
    }

实现


  1. 将上述代码的第11行,即#pragma surface surf Lambert一行,改为如下代码:
    #pragma surface surf BasicDiffuse
  2. 向SubShader中添加如下函数(位置需在#pragma下面):
            inline float4 LightingBasicDiffuse (SurfaceOutput s, fixed3 lightDir, fixed atten)
    {
    float difLight = max(0, dot (s.Normal, lightDir));
    float4 col;
    col.rgb = s.Albedo * _LightColor0.rgb * (difLight * atten * 2);
    col.a = s.Alpha;
    return col;
    }
  3. 保存,进入Unity查看编译结果。
Unity编译成功后,你会发现Material并没有什么可视化变化。因为上面仅仅是将Unity自带的Surface Lighting Model——Lambert,换成了我们自定义的光照模型——BasicDiffuse。

解释


  1. "#pragma surface"将直接告诉Shader使用哪个光照模型用于计算。当我们最初创建了一个Shader时,Untiy为我们指定了一个默认的光照模型即Lambert(在Lighting.cginc中定义)。因此我们一开始可以使用这个默认的模型进行渲染。而现在,我们告诉Shader,嘿,使用一个名叫BasicDiffuse的光照模型给我渲染哦!
  2. 为了创建一个新的光照模型,我们需要声明一个新的光照模型函数。例如上面,我们声明了BasicDiffuse,并且定义了一个函数名叫LightingBasicDiffuse,如你所见,这两者之间的关系即为Lighting<自定义的光照模型名称>。下面有三种可供选择的光照模型函数:
    • half4 LightingName (SurfaceOutput s, half3 lightDir, half atten){}

      这个函数被用于forward rendering(正向渲染),但是不需要考虑view direction(观察角度)时。

    • half4 LightingName (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten){}

      这个函数被用于forward rendering(正向渲染),并且需要考虑view direction(观察角度)时。

    • half4 LightingName_PrePass (SurfaceOutput s, half4 light){}

      这个函数被用于需要使用defferred rendering(延迟渲染)时。

  3. 观察我们定义的光照模型函数。dot函数是一个Cg的内置数学函数,可以被用于比较空间中两个向量的方向。若两个参数向量均为单位向量(一般如此),-1表示两向量平行但方向相反,1表示两向量平行且方向相同,0表示两向量垂直。
  4. 为了完成光照模型函数,我们还使用了一个Unity提供的数据——类型为SurfaceOutput的变量s。我们将s.Albedo(从surf函数中输出)和_LightColor0.rgb(Unity提供)相乘,结果再乘以(difLight * atten * 2),最后作为颜色值进行输出。
到这里,可能大家还会对LightingBasicDiffuse的代码不理解。下面再谈一下我的理解。
  1. 首先是参数。s是上一个surf函数的输出。
            void surf (Input IN, inout SurfaceOutput o)
    {
    //We can then use the properties values in our shader
    float4 c;
    c = pow((_EmissiveColor + _AmbientColor), _MySliderValue);
    o.Albedo = c.rgb;
    o.Alpha = c.a;
    }

    由上可以看出,经过surf函数,计算输出了s的Albedo(反射率)和Alpha(透明度)。

    LightBasicDiffuse函数输出的将是一个surface上某一点的颜色值和透明度值。因此lightDir对应该点的光照方向。而atten表明了光线的衰减率。

  2. 光照模型函数中的第一行代码通过dot函数和max函数,计算到达该点的光照值(由于dot函数的两个参数都是单位向量,也可以理解成是入射光线的角度的余弦值,角度越大,余弦值越小,进入人眼的光线也就越少,物体看起来也就越暗)。由于光线有可能从相反射入,因此通过dot得到的值有可能是负数。如果不用max加以限制,之后将会得到非预期的效果,如全黑等。
  3. 接下来计算颜色值col。col的rgb由三个部分计算而得:第一个部分是surface本身的反射率,这很好理解,因为反射率越大,进入人眼的光线就越多,颜色也就越鲜亮;第二个是_LightColor0.rgb。_LightColor0是Unity内置变量,我们可以使用它得到场景中光源的颜色等;最后便是利用第一步中得到的光照值和衰减率的乘积。细心的童鞋可以发现,这里后面多乘了一个倍数2。按我的猜测,这里仅仅是根据需要自行修改的。例如,没有倍数2时,效果如下:



    乘以倍数2后效果如下:

结束语


更多关于Surface Shader光照模型函数参数的信息,可以参见Unity官方文档

呼呼,这次就到这里!

【Unity Shaders】Diffuse Shading——创建一个自定义的diffuse lighting model(漫反射光照模型)的更多相关文章

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

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

  2. 如何创建一个自定义jQuery插件

    简介 jQuery 库是专为加快 JavaScript 开发速度而设计的.通过简化编写 JavaScript 的方式,减少代码量.使用 jQuery 库时,您可能会发现您经常为一些常用函数重写相同的代 ...

  3. springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。为了区别不同的异常通常根据异常类型自定义异常类,这里我们创建一个自定义系统异常,如果controller、service、dao抛出此类异常说明是系统预期处理的异常信息。

    springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑. 1.1 异常处理思路 系统中异常包括两类:预期异常和运行时异常RuntimeEx ...

  4. 怎么在java中创建一个自定义的collector

    目录 简介 Collector介绍 自定义Collector 总结 怎么在java中创建一个自定义的collector 简介 在之前的java collectors文章里面,我们讲到了stream的c ...

  5. 使用 TypeScript,React,ANTLR 和 Monaco Editor 创建一个自定义 Web 编辑器(二)

    译文来源 欢迎阅读如何使用 TypeScript, React, ANTLR4, Monaco Editor 创建一个自定义 Web 编辑器系列的第二章节, 在这之前建议您阅读使用 TypeScrip ...

  6. Unity 3D使用GameObject创建一个简单的可移动物体

    于Unity 3D游戏的开发.游戏脚本需要3D模拟组合,该脚本将被写入阻力3D为了达到效果对象. 以下是一个小实例,使用Unity 3D实现一个可控制移动的小人.小人能够向前.向后.向左和向右移动. ...

  7. 如何创建一个自定义的`ErrorHandlerMiddleware`方法

    在本文中,我将讲解如何通过自定义ExceptionHandlerMiddleware,以便在中间件管道中发生错误时创建自定义响应,而不是提供一个"重新执行"管道的路径. 作者:依乐 ...

  8. 创建一个自定义的Application类

    由于每个应用程序必须创建一个Application对象,vs为开发人员提供了模板来减轻开发人员的重复工作.当使用vs创建一个WPF应用程序是,vs会自动创建一个app.xaml文件, <Appl ...

  9. 创建一个自定义名称的Ceph集群

    前言 这里有个条件,系统环境是Centos 7 ,Ceph 的版本为Jewel版本,因为这个组合下是由systemctl来进行服务控制的,所以需要做稍微的改动即可实现 准备工作 部署mon的时候需要修 ...

随机推荐

  1. javascript:void(0) 含义

    javascript:void(0) 含义 我们经常会使用到 javascript:void(0) 这样的代码,那么在 JavaScript 中 javascript:void(0) 代表的是什么意思 ...

  2. Scikit-learn:分类classification

    http://blog.csdn.net/pipisorry/article/details/53034340 支持向量机SVM分类 svm分类有多种不同的算法.SVM是非常流行的机器学习算法,主要用 ...

  3. Spark:相关错误总结

    http://blog.csdn.net/pipisorry/article/details/52916307 路径错误 spark FileNotFoundError: [Errno 2] No s ...

  4. ant编译mysql驱动

    修改驱动源码后需要重新编译构建,由于mysql编译需要两个jdk版本且还需要hibernate4和junit,这里记录下. 安装ant. 配置两个jdk,5和8.并修改build.xml配置,如下: ...

  5. 理解性能的奥秘——应用程序中慢,SSMS中快(5)——案例:如何应对参数嗅探

    本文属于<理解性能的奥秘--应用程序中慢,SSMS中快>系列 接上文:理解性能的奥秘--应用程序中慢,SSMS中快(4)--收集解决参数嗅探问题的信息 首先我们需要明白,参数嗅探本身不是问 ...

  6. Spark技术内幕:Shuffle Read的整体流程

    回忆一下,每个Stage的上边界,要么需要从外部存储读取数据,要么需要读取上一个Stage的输出:而下边界,要么是需要写入本地文件系统(需要Shuffle),以供childStage读取,要么是最后一 ...

  7. JAVA面向对象-----extends关键字

    继承使用extends关键字实现 1:发现学生是人,工人是人.显然属于is a 的关系,is a就是继承. 2:谁继承谁? 学生继承人,发现学生里的成员变量,姓名和年龄,人里边也都进行了定义.有重 复 ...

  8. 【NPR】卡通渲染

    写在前面 我的博客讲过好几篇卡通渲染了,比如[Unity Shader实战]卡通风格的Shader(一).[Unity Shader实战]卡通风格的Shader(二).[NPR]漫谈轮廓线的渲染.[S ...

  9. 剑指offer面试题5 从头到尾打印链表(java)

    注:(1)这里体现了java数据结构与C语言的不同之处 (2)栈的操作直接利用stack进行 package com.xsf.SordForOffer; import java.util.Stack; ...

  10. 剑指offer面试题4 替换空格(c)