关闭

Unity Shader入门教程(三)自制光照模型

137人阅读 评论(0) 收藏 举报

光照模型的概念目前还不明晰,因为笔者也是一个初学者,所以请小心对待笔者介绍的内容。笔者认为光照模型是规定光照算法的模型,比如说前面提到的Lambert光照模型,规定了材质表面的光线的表达式为

环境光+散射光+反射高光+放射光

 

我们通过一组小实验来说明如何自定义光照模型。

1.1步:新建一个shader,两个material

其中最后一个是采用了标准的diffuse

 

MEasyNormalMappingMaterial)采用了EasyNormalMappingshader)。

 

1.2步:

 

▼代码开始
Shader "Custom/EasyNormalMapping" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200
        
        CGPROGRAM
        #pragma surface surf CustomDiffuse
        //还记得吗?这一句的语法是:#pragma surface 光线处理函数 光照模型

        sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
        };

        fixed4 _Color;

        //这个surf非常简单,就是取出了主纹理罢了
        void surf (Input IN, inout SurfaceOutput o) {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }

        /*这里有很多注意事项:
        a:下面的LightingCustomDiffuse是CUstomDiffuse这个光照模型的计算光照的实现。
        b:声明此光照计算实现函数时,名称必须要按照:Lighting<光照模型名称>,此处即LightingCustomDiffuse
        c:光照模型首先经过surf方法得到表面颜色,然后利用LightingCustomDiffuse计算光线,最后呈现在屏幕上
        d:这里采用了一个典型的形参列表:(表面颜色 SurfaceOutput s, 光线的来源方向 fixed3 lightDir, 光衰减的系数 fixed atten)
        请注意 lightDir是指从表面上的研究点到光源的方向,也就是指向光源方向,而不是入射方向(是入射方向的反方向)
        e:返回一个四维的小数,其实是指返回的光线
        (其它的形参列表可以看:http://blog.csdn.net/henger_/article/details/52689080*/
        inline float4 LightingCustomDiffuse(SurfaceOutput s, fixed3 lightDir, fixed atten)
        {
            //都做了些什么呢?首先是表面法向量和光线方向的点乘,此数目越靠近1,散射越明亮(是不是很符合常理!)
            float difLight = max(0, dot(s.Normal, lightDir));
            //col表示结果的颜色
            float4 col;
            
            //这里_LightColor0是由Unity根据场景中的光源得到的光源的光色,具体来由不清楚,简单说就是光源的颜色。
            //这个公式表明:散射光色 = 表面的反射系数 * 光源 * 散射强度 * 衰减系数(有许多博文说这里要乘以2)
            col.rgb = s.Albedo*_LightColor0.rgb*(difLight*atten);

            col.a = s.Alpha;
            return col;
        }

        ENDCG
    }
    FallBack "Diffuse"
}

▲代码结束

 

1.3步:我们为两个材料都选上石头的纹理图,最后可以在预览图中看出,它们的效果完全一样。

 

 

 

总结:我们已经自定义了一个光照模型,这个光照模型和标准内置的diffuse模型效果一样,因为我们的实现原理是一样的。

本文参考了猫都能学会的Unity3D Shader入门指南(二)

 

第二组实验

halfLambert模型:

将上面的代码稍修改一下,得到HalfLambert模型,这是一个在低光线条件下增亮的做法,是将光强系数 * 0.5+0.5的做法,非常简单。

 

inline float4 LightingCustomDiffuse(SurfaceOutput s, fixed3 lightDir, fixed atten)

{

float difLight = max(0, dot(s.Normal, lightDir));

float4 col;

 

//只增加这一行

difLight = difLight * 0.5 + 0.5;

 

col.rgb = s.Albedo*_LightColor0.rgb*(difLight*atten);

col.a = s.Alpha;

return col;

}

下图中左边应用了HalfLambert模型。

 

第三组实验:实现积雪效果

3.1步:新建一个Snowshader,新建一个名为MSnow的材质。

3.2步:Snow的代码如下:

 

▼代码开始
Shader "Custom/Snow" {
    Properties{
        _MainTex("Albedo (RGB)", 2D) = "white" {}
        _Bump("Bump", 2D) = "bump"{}
        _Snow("Snow Level", Range(0,1)) = 0.2
        _SnowColor("Snow Color", Color) =  (1,1,1,1)
        _SnowDirection("Snow Direction", Vector) = (0,1,0)
        //注意U3D中的Y轴是反重力方向的,而不是Z轴。这和UE4不一样。
    }
        SubShader{
        Tags{ "RenderType" = "Opaque" }
        LOD 300

        CGPROGRAM
        #pragma surface surf Lambert
        
        sampler2D _MainTex;
        sampler2D _Bump;
        struct Input {
            float2 uv_MainTex;
            float2 uv_Bump;
            float3 worldNormal; INTERNAL_DATA
                /*什么意思?这表示当前点在世界中的法方向,我们采用;INTERNAL_DATA来标记这个特殊的输入*/
                //更为详细的参考:https://docs.unity3d.com/Manual/SL-SurfaceShaders.html
        };
        float _Snow;
        float4 _SnowColor;
        float4 _SnowDirection;

        void surf(Input IN, inout SurfaceOutput o) {
            half4 c = tex2D(_MainTex, IN.uv_MainTex);
            o.Normal = UnpackNormal(tex2D(_Bump, IN.uv_Bump));

            //解释:输入IN表示输入的点(包含一个特殊的输入,就是世界坐标下的法方向)
            //WorldNormalVector(IN, o.Normal)表示IN点以及它的法向量在世界坐标下的向量表达,
            //和雪的方向的点击,和阈值作比较
            //插值函数:lerp(a,b,c)表示(b-a)*c+a
            if ( dot( WorldNormalVector(IN, o.Normal), _SnowDirection.xyz) > lerp(1, -1, _Snow)) {
                o.Albedo = _SnowColor.rgb;//取雪色
            }
            else {
                o.Albedo = c.rgb;//取原色
            }
            o.Alpha = c.a;
        }
        ENDCG
    }
        FallBack "Diffuse"
}

▲代码结束

3.3步:注意配上法向量和主纹理。

效果:

 

 

第四组实验:本组实验修改了顶点,也就是对模型进行了顶点的修改。此前的操作都是在贴图上进行的,顶点没有发生过改动。本组实验强调修改顶点。

第4.1步:在刚刚的Snow(Shader)代码中稍微修改,如下:

▼代码开始
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'

Shader "Custom/Snow" {
    Properties{
        _MainTex("Albedo (RGB)", 2D) = "white" {}
        _Bump("Bump", 2D) = "bump"{}
        _Snow("Snow Level", Range(0,1)) = 0.2
        _SnowColor("Snow Color", Color) =  (1,1,1,1)
        _SnowDirection("Snow Direction", Vector) = (0,1,0)
        //注意U3D中的Y轴是反重力方向的,而不是Z轴。这和UE4不一样。
            _SnowDepth("Snow Depth", Range(0,0.3))=0.1
            //改动1
    }
        SubShader{
        Tags{ "RenderType" = "Opaque" }
        LOD 300

        CGPROGRAM
        #pragma surface surf Lambert vertex:vert
        //改动2:在末尾添加vertex:Fun表示顶点模型将修改,修改的方法为Fun
        sampler2D _MainTex;
        sampler2D _Bump;
        struct Input {
            float2 uv_MainTex;
            float2 uv_Bump;
            float3 worldNormal; INTERNAL_DATA
                /*什么意思?这表示当前点在世界中的法方向,我们采用;INTERNAL_DATA来标记这个特殊的输入*/
                //更为详细的参考:https://docs.unity3d.com/Manual/SL-SurfaceShaders.html
        };
        float _Snow;
        float4 _SnowColor;
        float4 _SnowDirection;
        float _SnowDepth;
        //改动3

        void surf(Input IN, inout SurfaceOutput o) {
            half4 c = tex2D(_MainTex, IN.uv_MainTex);
            o.Normal = UnpackNormal(tex2D(_Bump, IN.uv_Bump));

            //解释:输入IN表示输入的点(包含一个特殊的输入,就是世界坐标下的法方向)
            //WorldNormalVector(IN, o.Normal)表示IN点以及它的法向量在世界坐标下的向量表达,
            //和雪的方向的点击,和阈值作比较
            //插值函数:lerp(a,b,c)表示(b-a)*c+a
            if ( dot( WorldNormalVector(IN, o.Normal), _SnowDirection.xyz) > lerp(1, -1, _Snow)) {
                o.Albedo = _SnowColor.rgb;//取雪色
            }
            else {
                o.Albedo = c.rgb;//取原色
            }
            o.Alpha = c.a;
        }

        //改动4:vert函数处理顶点,对于每一个顶点v,进行如下修改。inout appdata_full v是顶点的固有写法。
        void vert(inout appdata_full v) {
            /*需求:我们希望vert函数将“法方向和雪方向近似相同的所有顶点都抬高一些”
            数学做法:
            1:将雪方向(也就是010)转换到物体坐标系中;
                这是什么意思?就是以物体来看,雪的方向是什么,如果物体是倒置的(脑子朝下的),那么物体看来的雪方向应该是0,-1,0,
                如果物体时脑子靠一侧的,那么物体看的雪的方向将会是水平方向的(它是这样认为的),例如1,0,0,

                如何将世界里的雪方向以物体的视角来看待呢?数学上只需要
                M(world2object的转换矩阵) * 雪的世界方向 = 雪的物体系下的方向
                
                【语法上】
                1.a:unity_ObjectToWorld是内置的一个矩阵,表示此物体到世界的转换矩阵(也就是UE4中的Transform)
                1.b:transpose(P)表示将P转置
                1.c:mul(P,v)表示矩阵P左乘向量v即Pv

            2:上述得到的物体看待的雪方向记为sn,那么sn和此顶点的法方向的点乘,点乘值越大,表示越是容易积雪的情况
                如果大于阈值,那么顶点的xyz在积雪方向上增加一定的量,此处的积雪方向是指法+雪的方向
            */

            //写法A:
            float4 sn = mul(transpose(unity_ObjectToWorld), _SnowDirection);
            
            //写法B:sn = _SnowDirection;
            /*写法A是正确的,写法B是错误的,它没有将雪的方向转换为物体看待的描述*/

            if (dot(v.normal, sn.xyz) >= lerp(1, -1, (_Snow * 2) / 3)) {
                v.vertex.xyz += (sn.xyz + v.normal) * _SnowDepth;
            }
        }


        ENDCG
    }
        FallBack "Diffuse"
}

▲代码结束

 

效果可以看出:下面的球中,容易积雪的顶点被抬高了许多。

appdata_full拥有如下成员:

struct appdata_full {

    float4 vertex : POSITION;

    float4 tangent : TANGENT;

    float3 normal : NORMAL;

    float4 texcoord : TEXCOORD0;

    float4 texcoord1 : TEXCOORD1;

    float4 texcoord2 : TEXCOORD2;

    float4 texcoord3 : TEXCOORD3;

    fixed4 color : COLOR;

    UNITY_VERTEX_INPUT_INSTANCE_ID

};

以上顶点的信息中,前四个分别是:点的位置,点的正切方向(也就是定点的正切空间的指向,正切空间就是表面指向外的方向,也就是物理世界中严格的概念“法线方向”),点的法线方向(规定的法线方向,以正切空间为坐标系),纹理信息,以及最后的Color表示顶点的颜色。

 

——201772816:21:45 小江村儿的文杰 zouwj5@qq.com

 

0
0
查看评论

Unity实现标准光照模型的Shader代码

本文展示实现标准光照模型(只关心场景里最亮的那一个平行光光源)下的光照效果。即自发光(Emissive)、环境光(Ambient)、漫反射(Diffuse)、高光(Specular)这四种光效的叠加,其中高光有两种实现方式Phong和Blinn-Phong,选择调用方法即可。 下面介绍面板参数: ...
  • lzdidiv
  • lzdidiv
  • 2017-03-17 12:57
  • 460

Unity Shader自定义光照模型

前几天去一家公司面试,因为简历上写着“能写简单的Shader”,就被问了一个问题:怎么自定义一个光照模型? 直接懵逼了,隐隐约约能联想到的什么漫反射光+镜面反射光+环境光...难道是这个?光照模型就是#pragam surface surf Lambert里面的那个Lambert呗?这不都是写好的吗...
  • u010133610
  • u010133610
  • 2016-08-14 23:38
  • 1658

Unity Shader-Phong光照模型与Specular

简介 学完了兰伯特光照模型,再来学习一个更加高级一点的光照模型-Phong光照模型。兰伯特光照模型中主要就是漫反射,而实际上,光除了漫反射,还有镜面反射。一些金属类型的材质,往往表现出一种高光效果,用兰伯特模型是模拟不出来的,所以就有了Phong模型。Phong模型主要有三部分构成,第一部分是上...
  • puppet_master
  • puppet_master
  • 2016-12-09 13:34
  • 2593

Unity Shader 五 光照模型

Unity光照模型入门简介
  • qq_18293659
  • qq_18293659
  • 2016-10-23 13:14
  • 197

【Unity Shaders】Lighting Models —— 光照模型之Lit Sphere

写在前面 照亮的球体(Lit Sphere,翻译过来很怪)类型的光照模型是一种非常有趣的基于图像的光照。实际上,我们可以使用一张2D贴图来完整地烘焙我们的光照。你可以得到Zbrush这个软件实现的相同效果。如果你对Zbrush的MatCaps(Material Captures)很熟悉,那...
  • candycat1992
  • candycat1992
  • 2014-05-20 22:08
  • 12058

Unity shader教程-第四课:自定义光照模型(方程)

Unity的自定义光照模型的使用。Unity默认的光照模型非常有限,在现实的使用中各种不同的材质和环境需要各种各样的光照模型,本篇介绍怎么在Unity shader中使用自定义的光照模型。
  • TinyHum
  • TinyHum
  • 2015-02-02 13:09
  • 2158

Unity Shader-兰伯特光照模型与Diffuse Shader

一.简介 学了一段时间shader,然而一直在玩后处理,现在终于下定决心钻研一下真正的带光照的shader。从Diffuse到Specular,到Bump等等。一个游戏的画面好坏,很大程度上取决于光照和贴图。有了光,游戏世界才显得真实,所以,首先我们先来看一下现在游戏中最简单的几种光照模型。 ...
  • puppet_master
  • puppet_master
  • 2016-12-01 13:02
  • 3877

Shader学习笔记(三)自定义光照模型,经典光照模型Lambert与HalfLambert

Unity Shader自定义光照模型,经典光照模型Lambert与HalfLambert
  • henger_
  • henger_
  • 2016-09-28 11:33
  • 930

Unity Shader基础的使用 基础、法线贴图及光照模型代码的注释

版本:unity 5.3.4  语言:C#   这边主要参考了猫也能学会Shader的文章。   话说之前是不是说暂时不研究Shader?没事哈,不要在意那么多细节,要的就是莽,疯狂加点就行了。   这边主要是把猫大写的Shader仔细注释了一下,以便以后查阅...
  • u012632851
  • u012632851
  • 2016-10-31 10:14
  • 1104

【Unity Shaders】学习笔记之为创建Half Lambert光照模型(六)

一、创建Half Lambert光照模型    Half Lambert(半兰伯特)是由Value公式提出的技术,是一种用于在低光照区域照亮物体的技术。它基于提高了材质和物体表面周围的漫反射光照。    二、如何操作    我们继续使用...
  • liulongling
  • liulongling
  • 2016-06-20 19:54
  • 2500
    个人资料
    • 访问:12228次
    • 积分:298
    • 等级:
    • 排名:千里之外
    • 原创:0篇
    • 转载:105篇
    • 译文:0篇
    • 评论:1条
    文章存档