Unity shader学习笔记(三)光照模型

发表于2018-04-28
评论0 1.9k浏览

想免费获取内部独家PPT资料库?观看行业大牛直播?点击加入腾讯游戏学院游戏程序行业精英群

711501594
光照模型

光照模型是一个公式,使用这个公式计算物体某个坐标点上的光照效果。
光照模型有很多种,例如:标准光照模型,半兰伯特光照模型,Blinn光照模型,Phong光照模型和Blinn-Phong光照模型

标准光照模型里面,会进入到视野中的的光主要有下面四种光
1.自发光 emissive(像火把)
2.高光反射 specular(像镜子的光反射)
3.漫反射 diffuse(像木头的光反射)
4.环境光 ambient

光照效果可以放在顶点函数里面处理,即逐顶点关照处理,也可以放在片元函数中处理,叫做逐片元(像素)光照处理

所有的物体光照计算都会与物体的发现相关,法线是指始终垂直于某平面的虚线或者某曲线上一点的切线的直线
法线如图:

漫反射光的计算方式:
直射光颜色 * max(0,cos(x))(x代表光和法线的夹角)

max的作用是使得光与物体法线夹角的值始终大于0,0的时候物体表面颜色即为黑色。
向量的夹角的cos值也可以通过向量进行点积得到 所以计算方式也可以写成 直射光颜色 * max(0,表面法线方向单位向量*直射光方向单位向量)
SubShader{
            Pass{
                Tags{"LightMode" = "ForwardBase"}
                CGPROGRAM
            #include "Lighting.cginc"  
        //unity系统定义好的类  可以使用一些内置的变量来简化计算操作  
        //_LightColor0              该变量表示第一个直射光的颜色
        //_WorldSpaceLightPos0      该变量表示第一个直射光的位置(世界坐标下)
        //UNITY_LIGHTMODEL_AMBIENT  该变量表示系统的环境光     #pragma vertex vert
        #pragma fragment frag
                struct a2v {
            float4 vertex : POSITION; //系统自动将顶点坐标传入vertex
            float3 normal : NORMAL;  //系统自动将法线传入    变量normal
        };
        struct v2f {
            float4 position:SV_POSITION; 
            fixed3 color : COLOR;
        };
        v2f vert(a2v x) {
            v2f f;
            //UNITY_MATRIX_MVP 这个矩阵用来把一个坐标从模型空间转换到剪裁空间
            f.position = mul(UNITY_MATRIX_MVP, x.vertex);
            //对于每个顶点来说,光的位置其实就是光的方向
            //normalize 该方法是用来将一个向量转换成单位向量  当前直射光的空间是在世界空间
            fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
        fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;              //目前的normal变量还是模型空间下的坐标。需要将其转换为和直射光相同空间下的坐标
            //_World2Object 这个矩阵用来把一个方向从世界空间转换到模型空间  方法为mul(_World2Object,x.vertex)
            //如果方法写成  mul(x.vertex,_World2Object) 则将一个方向从模型空间转换到世界空间
            //_World2Object是一个4x4的矩阵,但是当前我们需要使用的是3x3的矩阵,因此使用float3x3来对矩阵进行强制转换
            fixed3 normalDir = normalize(mul(x.normal, (float3x3)unity_WorldToObject));
            //暂时没有考虑alpha属性
            //max  该方法是用来取得参数中最大的一个
            //dot  该方法是用来获取两个向量的点积
            fixed3 diffuse = _LightColor0.rgb*max(dot(lightDir, normalDir),0)*_Diffuse.rgb;//取得漫反射的颜色           f.color = diffuse;
            return f;//系统会自动根据结构体中变量的语义来进行取值
        } 
        fixed4 frag(v2f f) :SV_TARGET{
            //将计算得到的颜色值输出到视野中的物体上
            return fixed4(f.color, 1);
        }
            ENDCG
        }
    }

如果想在inspector面板进行调控
那么在属性中定义变量
Properties{
        _Diffuse("Diffuse Color",Color) = (1,1,1,1)
    }

在pass中重新声明变量
fixed4 _Diffuse;

最后应用的结果如图

unity中两种颜色叠加 如果是光亮进行融合的话 直接两种颜色相乘即可
颜色亮度基本无变化
只要将
fixed3 diffuse = _LightColor0.rgb*max(dot(lightDir, normalDir),0);更改为fixed3 diffuse = _LightColor0.rgb*max(dot(lightDir,ormalDir),0)*_Diffuse.rgb; 即可
如果是两种光互相叠加 那么将两种颜色相加获得新的颜色 会使得颜色亮度增加 相加过程中颜色值会趋近于(1,1,1,1)
例如:如果加上环境光的话f.color = diffuse; 更改为 f.color = diffuse + ambient;

上面的渲染方法是逐顶点渲染,如果要将代码修改为逐像素渲染,那么只需要将计算方法的位置进行修改。
代码如下:
SubShader{
            Pass{
                Tags{"LightMode" = "ForwardBase"}
                CGPROGRAM
        #include "Lighting.cginc"  
        //unity系统定义好的类  可以使用一些内置的变量来简化计算操作  
        //_LightColor0              该变量表示第一个直射光的颜色
        //_WorldSpaceLightPos0      该变量表示第一个直射光的位置(世界坐标下)
        //UNITY_LIGHTMODEL_AMBIENT  该变量表示系统的环境光
        #pragma vertex vert
        #pragma fragment frag
        fixed4 _Diffuse;
        struct a2v {
            float4 vertex : POSITION; //系统自动将顶点坐标传入vertex
            float3 normal : NORMAL;   //系统自动将法线传入   变量normal
        };
        struct v2f {
            float4 position:SV_POSITION; 
            fixed3 worldNormalDir : COLOR;
        };
        v2f vert(a2v x) {
            v2f f;
            //UNITY_MATRIX_MVP 这个矩阵用来把一个坐标从模型空间转换到剪裁空间
            f.position = mul(UNITY_MATRIX_MVP, x.vertex);
            //法线的计算仍然在顶点函数中计算 ,将结果传递给片元函数
            f.worldNormalDir = normalize(mul(x.normal, (float3x3)unity_WorldToObject));
            //系统会自动根据结构体中变量的语义来进行取值
            return f;
        }
        fixed4 frag(v2f f) :SV_TARGET{
            fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
            fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
            fixed3 normalDir = f.worldNormalDir;
            //暂时没有考虑alpha属性
            fixed3 diffuse = _LightColor0.rgb*max(dot(lightDir, normalDir),0)*_Diffuse.rgb;
            fixed3 tempColor = diffuse + ambient;
            //将计算得到的颜色值输出到视野中的物体上
            return fixed4(tempColor, 1);
        }
            ENDCG
        }
    }

最后的效果如图

但是我们会发现物体的背光面是个全黑的颜色,现实生活中物体的背光面并不是全黑的,而是可以看到物体的大概形状。要实现此种现象,那么就需要别的光照模型来实现。
来自:https://blog.csdn.net/u013106366/article/details/53313141

原文链接

著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

游戏学院公众号二维码
腾讯游戏学院
微信公众号

提供更专业的游戏知识学习平台