Unity Shader 光照模型(基础公式和代码实现)

标准光照模型只关心直接光照(direct light)。它把进入摄像机的光照分为4个部分:

自发光(emissive),这部分用于给定一个方向时,物体表面会向这个方向产生多少的光,当没有使用全局光照时,自发光物体不会照亮周围物体,只是本身看起来更亮而已。
高光反射(specular),这个部分用于描述当光线从光源照到物体表面时,物体镜面反射产生的光。
漫反射(diffuse)这个部分是光线从光源照到物体表面时,物体向各个方向产生的光。
环境光(ambient)这个部分用来描述其他间接的光。

1 漫反射

1.1 基本公式:

漫反射可使用兰伯特定律(Lamberts Law),即反射光线的强度与表面法线和光源方向之间的夹角的余弦成正比。计算公式如下:

       
           
其中LC(Light Color)是光照颜色和强度,MD(Material Diffuse)是材质的漫反射颜色,N(Normal)是表面法线向量,L(Light)是光源的单位矢量。max函数是为了防止法线和光源点乘结果为负,可防止物体被后面的光照照亮。
漫反射还可以通过一种兰伯特定律的视觉加强模型半兰伯特光照模型来计算,半兰伯特光照模型没有使用max函数来防止法线和光源方向的点乘为负的情况,而是对其结果进行了一个X倍的缩放再加上一个Y大小的偏移大多数情况下,x和y为0.5。计算公式如下:
       
       
       
这样就可以把法线和光源方向的点乘的结果范围从[-1, 1]映射到[0, 1]。就是兰伯特光照模型中,对物体背面的结果会映射到同一个值,即0;而在半兰伯特光照模型中,背面也是有明暗变换,会映射到不同的值。

1.2  Unity shader实现

使用SufaceShader  实现兰伯特漫反射Diffuse

Shader "Example/Diffuse Texture" {
        Properties {
            _MainTex ("Texture", 2D) = "white" {}
        }
        SubShader {
        Tags { "RenderType" = "Opaque" }
        CGPROGRAM
          #pragma surface surf SimpleLambert

          half4 LightingSimpleLambert (SurfaceOutput s, half3 lightDir, half atten) {
              half NdotL = dot (s.Normal, lightDir);
              half4 c;
              c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten);
              c.a = s.Alpha;
              return c;
          }
         struct Input {
            float2 uv_MainTex;
        };

        sampler2D _MainTex;

        void surf (Input IN, inout SurfaceOutput o) {
            o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
        }
        ENDCG
        }
        Fallback "Diffuse"
    }

注释:

很多实例用的是float,这里根据最新的unity官方代码example, 使用half,shader中有三个基本类型, float(32位高精度浮点数)  half (16位中精度浮点数)  fixed(11位低精度浮点数)

half3 lightDir是光线的方向,half atten表示光衰减的系数。在计算光照的代码中,我们先将输入的s的法线值(在Normal mapping中的话这个值已经是法线图中的对应量了)和输入光线进行点积(dot函数是CG中内置的数学函数,希望你还记得,可以参考这里)。点积的结果在-1至1之间,这个值越大表示法线与光线间夹角越小,这个点也就应该越亮。之后使用max来将这个系数结果限制在0到1之间,是为了避免负数情况的存在而导致最终计算的颜色变为负数,输出一团黑,一般来说这是我们不愿意看到的。接下来我们将surf输出的颜色与光线的颜色_LightColor0.rgb(由Unity根据场景中的光源得到的,它在Lighting.cginc中有声明)进行乘积,然后再与刚才计算的光强系数和输入的衰减系数相乘,最后得到在这个光线下的颜色输出

有些例子中,公式会变成c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten*2);(关于difLight * atten * 2中为什么有个乘2,这是一个历史遗留问题,主要是为了进行一些光强补偿,可以参见这里的讨论


使用SufaceShader  实现半兰伯特  halfLambert

 Shader "Example/HalfDiffuse Texture" {
  Properties {
     _MainTex ("Texture", 2D) = "white" {}
    }
   SubShader {
    Tags { "RenderType" = "Opaque" }
    CGPROGRAM
    #pragma surface surf HlafLambert

    half4 LightingHalfLambert (SurfaceOutput s, half3 lightDir, half atten) {
        half NdotL = dot (s.Normal, lightDir);
        half diff = NdotL * 0.5 + 0.5;
        half4 c;
        c.rgb = s.Albedo * _LightColor0.rgb * (diff * atten);
        c.a = s.Alpha;
        return c;
    }

    struct Input {
        float2 uv_MainTex;
    };

    sampler2D _MainTex;
        void surf (Input IN, inout SurfaceOutput o) {
        o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
    }
    ENDCG
   Fallback "Diffuse" }
         

Half Lambert是由Valve创造的可以使物体在低光线条件下增亮的技术,最早被用于半条命(Half Life)中以避免在低光下物体的走形。简单说就是把光强系数先取一半,然后在加0.5,这样一来,原来光强0的点,现在对应的值变为了0.5,而原来是1的地方现在将保持为1。也就是说模型贴图的暗部被增强变亮了,而亮部基本保持和原来一样,防止过曝

2 高光反射(BillinPhong)

2.1 公式

Blinn思想就是不计算反射方向R。他计算了一个新的矢量H,是通过V和L取平均再归一得到的。即: 
        
        

 

specular =LC*MS * pow(max(0, N*H), gloss)

其中,V(View)是视角方向,LC(Light Color)是光照颜色和强度,MS(Material Spscular)是材质的高光反射颜色,  gloss 是材质的反光度(Shininess)

...ShaderLab code...
    CGPROGRAM
    #pragma surface surf SimpleSpecular

    half4 LightingSimpleSpecular (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten) {
        half3 h = normalize (lightDir + viewDir);

        half diff = max (0, dot (s.Normal, lightDir));

        float nh = max (0, dot (s.Normal, h));
        float spec = pow (nh, 48.0);

        half4 c;
        c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * spec) * atten;
        c.a = s.Alpha;
        return c;
    }

    struct Input {
        float2 uv_MainTex;
    };

    sampler2D _MainTex;

    void surf (Input IN, inout SurfaceOutput o) {
        o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
    }
    ENDCG
    ...ShaderLab code...

备注:上面的例子,没有乘MS,有些shader会乘上这个系数.

原文地址:https://www.cnblogs.com/jiwen/p/11263153.html

时间: 2024-08-08 10:59:11

Unity Shader 光照模型(基础公式和代码实现)的相关文章

Unity3D for VR 学习(9): Unity Shader 光照模型 (illumination model)

关于光照模型 所谓模型,一般是由学术算法发起, 经过大量实际数据验证而成的可靠公式 现在还记得2009年做TD-SCDMA移动通信算法的时候,曾经看过自由空间传播模型(Free space propagation Model),目的为了得出移动信号的传播损耗.当时是基于普通的PC实时运算,非常非常耗时–如北京五环内的传播模型渲染GIS图用了超过20分钟. 光照模型来源有2类: 一类是基于学术论文的算法,如Lambert模型.Phong模型. 另一类基于算法的变种–在实际生产实践中修正得到的模型,

Unity Shader 之 基础光照

摄像机是如何看这个世界的 游戏中摄像机所看到的世界与我们现实中所看到的几乎是一样的. 首先,光线从光源中发射出来. 然后,光线和场景中的一些物体相交(散射,吸收). 最后,摄像机吸收了一些光,产生一张图像. 光线与物体相交的结果有两个:散射(scattering)和吸收(absorption) 散射:只改变光线的方向,但不改变光线的密度和颜色,有两种方向:内部与外部,对应折射与反射. 折射(refraction):散射到物体内部,用漫反射(diffuse)模型来计算. 反射(reflection

[Unity Shader]光照模型对物体的假设

什么是光照模型 光照模型就是模拟光在物体间的传递过程,以确保物体可见表面每一点的亮度和颜色. 当光照射到一个物体表面时,光可能被吸收.反射或折射.反射和折射的光使物体可见.如果入射光全部被吸收,物体将不可见,称物体为黑体. 一个物体表面呈现的颜色是有物体表面向视线方向辐射的光能中各种波长的分布所确定的. 如果物体是不透明的,则物体表面呈现的颜色仅有其反射光决定,通常把反射光考虑成环境反射光.漫反射光和镜面反射光三个分量的组合. 环境反射光(Ambient Light) 环境反射光是由于邻近物体所

Unity Shader入门精要学习笔记 - 第3章 Unity Shader 基础

来源作者:candycat   http://blog.csdn.net/candycat1992/article/ 概述 总体来说,在Unity中我们需要配合使用材质和Unity Shader才能达到需要的效果.一个最常见的流程是. 1)创建一个材质 2)创建一个Unity Shader,并把它赋给上一步创建的材质 3)把材质赋给要渲染的对象 4)在材质面板中调整Unity Shader的属性,以得到满意的效果 下图显示了Unity Shader和材质是如何一起工作来控制物体的渲染的. Uni

【我的书】Unity Shader的书 — 目录(2016.1.29更新)

写在前面 感谢所有点进来看的朋友.没错,我目前打算写一本关于Unity Shader的书. 出书的目的有下面几个: 总结我接触Unity Shader以来的历程,给其他人一个借鉴.我非常明白学Shader的艰难,在群里也见了很多人提出的问题.我觉得学习Shader还是一件有规律可循的事情,但问题是中文资料难觅,而大家又不愿意去看英文...这对我有什么好处呢?强迫我对知识进行梳理,对细节问题把握更清楚. 第二个原因你懂的. 关于本书的定位问题: 面向Unity Shader初学者,但要: 有一定的

【我的书】Unity Shader的书 — 文件夹(2015.12.21更新)

写在前面 感谢全部点进来看的朋友.没错.我眼下打算写一本关于Unity Shader的书. 出书的目的有以下几个: 总结我接触Unity Shader以来的历程,给其它人一个借鉴.我非常明确学Shader的艰难,在群里也见了非常多人提出的问题. 我认为学习Shader还是一件有规律可循的事情,但问题是中文资料难觅,而大家又不愿意去看英文...这对我有什么优点呢?强迫我对知识进行梳理,对细节问题把握更清楚. 第二个原因你懂的. 关于本书的定位问题: 面向Unity Shader刚開始学习的人,但要

【我的书】Unity Shader的书 — 目录(2015.09.04更新)

写在前面 感谢所有点进来看的朋友.没错,我目前打算写一本关于Unity Shader的书. 出书的目的有下面几个: 总结我接触Unity Shader以来的历程,给其他人一个借鉴.我非常明白学Shader的艰难,在群里也见了很多人提出的问题.我觉得学习Shader还是一件有规律可循的事情,但问题是中文资料难觅,而大家又不愿意去看英文...这对我有什么好处呢?强迫我对知识进行梳理,对细节问题把握更清楚. 第二个原因你懂的. 关于本书的定位问题: 面向Unity Shader初学者,但要: 有一定的

【我的书】Unity Shader的书 — 目录(实时更新中)

写在前面 感谢所有点进来看的朋友.没错,我目前打算写一本关于Unity Shader的书. 出书的目的有下面几个: 总结我接触Unity Shader以来的历程,给其他人一个借鉴.我非常明白学Shader的艰难,在群里也见了很多人提出的问题.我觉得学习Shader还是一件有规律可循的事情,但问题是中文资料难觅,而大家又不愿意去看英文...这对我有什么好处呢?强迫我对知识进行梳理,对细节问题把握更清楚. 第二个原因你懂的. 关于本书的定位问题: 面向Unity Shader初学者,但要: 有一定的

Unity Shader入门精要学习笔记 - 第6章 开始 Unity 中的基础光照

转自冯乐乐的<Unity Shader入门精要> 通常来讲,我们要模拟真实的光照环境来生成一张图像,需要考虑3种物理现象. 首先,光线从光源中被发射出来. 然后,光线和场景中的一些物体相交:一些光线被物体吸收了,而另一些光线被散射到其他方向. 最后,摄像机吸收了一些光,产生了一张图像. 在光学中,我们使用辐照度来量化光.对于平行光来说,它的辐照度可通过计算在垂直于l的单位面积上单位时间内穿过的能量来得到.在计算光照模型时,我们需要知道一个物体表面的辐照度,而物体表面往往是和l不垂直的,我们可以