入门图形学:光照模型(三)

       紧接上一篇:https://blog.csdn.net/yinhun2012/article/details/80912620

       之前两篇博客,我们学习了一般光照模型对物体颜色的作用公式,如下:

       surfaceColor = emissive + ambient + diffuse + specular;

         这次我们就来实际在图形引擎中用shader cg实现一下,看下具体效果,效果图如下:

        

        这里我做了一个放射光为鲜红色的高亮材质球,其中fragment函数是自己实现的通用光照模型计算,cg代码如下:

        


Shader "Unlit/GeneralLightUnlitShader"
{
     Properties
    {
         _EmissiveColor( "emissive", Color) = ( 1, 1, 1, 1)

         _AmbientColor( "embient", Color) = ( 1, 1, 1, 1)
         _AmbientFactor( "e_factor", Color) = ( 1, 1, 1, 1)

         _LightColor( "light", Color) = ( 1, 1, 1, 1)
         _DiffuseFactor( "d_factor", Color) = ( 1, 1, 1, 1)

         _SpecularFactor( "s_factor", Color) = ( 1, 1, 1, 1)
         _SpecShininess( "s_shininess", Range( 10, 100)) = 10
    }
     SubShader
    {
         Tags { "RenderType"= "Opaque" }
         LOD 100

         Pass
        {
             CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

             struct app2vert
            {
                 float4 vertex : POSITION;
                 float3 normal : NORMAL;
            };

             struct vert2frag
            {
                 float4 pos : SV_POSITION;
                 float3 worldNormal : TEXCOORD0;
                 float3 worldPos : TEXCOORD1;
            };

             float3 _EmissiveColor;       /*自己定义的放射颜色*/

             float3 _AmbientColor;        /*自己定义的环境光颜色,一般我们都是用unityLighting中的自带字段*/
             float4 _AmbientFactor;       /*自己定义的环境反射系数,为什么用一个color字段储存呢?博客中我会谈到*/    

             float3 _LightColor;          /*自己定义的太阳光颜色,当然这个我们也是一般使用unityLighting中自带的场景中directionlight的颜色了*/
             float4 _DiffuseFactor;       /*自己定义的Diffuse漫反射系数,float4值*/

             float4 _SpecularFactor;      /*自己定义的Sepcular镜面反射系数,float4值*/
             float _SpecShininess;        /*自己定义的specular的强度指数*/

            vert2frag vert (app2vert av)
            {
                vert2frag vf;
                 //使用mvp矩阵将顶点位置变换到投影空间位置
                vf.pos = mul( UNITY_MATRIX_MVP,av.vertex);
                 //使用M矩阵将法线位置变换到模型空间法线位置的单位向量
                vf.worldNormal = normalize( mul( UNITY_MATRIX_M,av.normal));
                 //使用M矩阵将顶点位置变换到模型空间位置的单位向量
                vf.worldPos = normalize( mul( UNITY_MATRIX_M,av.vertex).xyz);
                 return vf;
            }
            
             fixed4 frag (vert2frag vf) : SV_Target
            {
                 //根据lighting.cginc中的字段准备一些参数
                 float3 unity_buildin_ambient_light_color = UNITY_LIGHTMODEL_AMBIENT.xyz;                                 /*unity自带的环境光颜色*/
                 //float3 unity_buildin_ambient_light_color = _AmbientColor;                                             /*或者自己指定ambientColor*/
                 float3 unity_buildin_world_p2sun_dir = normalize(- WorldSpaceLightDir( float4(vf.worldPos, 1)));            /*unity自己的光线入射方向计算相反的的p点到太阳的朝向单位向量*/
                 float3 unity_buildin_light_color = _LightColor0.rgb;                                                     /*unity自带的入射光颜色*/
                 //float3 unity_buildin_light_color = _LightColor;                                                       /*或者自己指定lightColor*/
                 float3 unity_buildin_eye2p_dir = normalize( _WorldSpaceCameraPos.xyz - vf.worldPos.xyz);                  /*unity自带的eye眼睛到p点朝向单位向量*/  
                 float3 h_dir_or_call_VplusL_dir = normalize(unity_buildin_eye2p_dir + unity_buildin_world_p2sun_dir);    /*计算出的H向量的单位向量*/            
                 /*surfaceColor = emissive + ambient + diffuse + specular根据这个公式一步一步来计算*/
                 //先计算emissive
                 float3 emissive = _EmissiveColor;
                 //再计算ambient
                 float3 ambient = _AmbientFactor.rgb * unity_buildin_ambient_light_color;
                 /* 或者float3 ambient = _AmbientFactor.rgb * _AmbientColor; */
                 //再计算diffuse
                 float3 diffuse = _DiffuseFactor.rgb * unity_buildin_light_color * max( dot(vf.worldNormal,unity_buildin_world_p2sun_dir), 0);
                 //再计算specular
                 float3 specular = _SpecularFactor.rgb * unity_buildin_light_color * pow( max( dot(vf.worldNormal,h_dir_or_call_VplusL_dir), 0),_SpecShininess);
                 //最终计算surfaceColor
                 fixed4 sCol = fixed4(emissive + ambient + diffuse + specular, 1.0);
                 return sCol;
            }
             ENDCG
        }
    }
}

        这里我来着重解释一下cg代码中可能导致小伙伴们看不懂的地方。

        一.properties定义字段讲解。

        我依次将放射颜色(_EmissiveColor),环境光颜色(_AmbientColor),材质环境反射系数(_AmbientFactor),场景光源颜色(_LightColor),漫反射系数(_DiffuseFactor),镜面反射系数(_SpecularFactor),镜面反射强度指数(_SpecShininess)人为定义完毕。

        这里需要特别注意的是,我将系数都定义为了Color的float4类型,这是为什么呢?这些系数难道不应该是一个float类型么?

        其实是这样考虑的我们获取颜色rgb(float3)*系数(float4或者float)后分两种情况:

        ①.rgb(float3) * 系数(float4).xyz这种乘法得到的结果是float3(rgb.x*系数.x,rgb.x*系数.y,rgb.x*系数.z)

        ②.rgb(float3) * 系数(float) = float3(rgb.x*系数,rgb.x*系数,rgb.x*系数)

        第一种细节更加丰富,因为rgb颜色的每个分量分别乘上了不同的系数分量值,并不像第二种一样分量的系数不变,第二种就类似于一个等比例系数,太固定死板了。

        二.vert顶点函数讲解

        vert顶点函数中的输入参数app2vert结构体,这个就是UnityCG.cginc文件中提供的unitybuildin字段,我们可以直接定义成输入参数结构体拿来用,这些都是unity帮我计算储存好的,此时我就使用了vertex顶点坐标和normal顶点法向量(当然注意,这是最原始的数据)

        随后,在顶点函数中,我依次用矩阵处理了

       ①.UNITY_MATRIX_MVP去处理顶点变换到裁剪空间

      ②.UNITY_MATRIX_M去处理法向量到3D模型空间(这个法向量无所谓平移或者不平移,你可以用matrix3x3或者4x4的M矩阵去处理都行)并且单位化它,以便后续使用

       ③.UNITY_MATRIX_M去处理顶点坐标到3D模型空间坐标,后面也需要用

        ps:如果现在还是不懂这些矩阵运算意义的同学,建议从我博客第一篇看起。

       三.frag片段函数讲解

       frag片段函数就开始我们最关键的光照模型数学计算讲解了。

       首先我们准备一些参数,如下:

       ①.float3 unity_buildin_ambient_light_color = UNITY_LIGHTMODEL_AMBIENT.xyz;

         这个字段是unity buildin定义字段,记录的是当前环境光颜色,可以通过editor - window-lighting-settings中的Environment Lighting手动设置。

        ②.float3 unity_buildin_world_p2sun_dir = normalize(-WorldSpaceLightDir(float4(vf.worldPos,1)));

        现根据unity buildin的获取光线射线朝向向量的函数WorldSpaceLightDir获取光线朝向向量-L,然后p2sun就是将其反过来,就是照射点P到太阳光源的朝向L,同时单位化。

        ③.float3 unity_buildin_light_color = _LightColor0.rgb;

        一目了然,这就是unity buildin的入射光线颜色值

        ④.float3 unity_buildin_eye2p_dir = normalize(_WorldSpaceCameraPos.xyz - vf.worldPos.xyz);

        这个就是计算的观察者眼睛到照射点P的朝向向量,根据场景中camera在3D建模空间xyz坐标-顶点在3D建模空间中xyz坐标。

        ⑤.float3 h_dir_or_call_VplusL_dir = normalize(unity_buildin_eye2p_dir + unity_buildin_world_p2sun_dir);

        计算出H单位向量的值,这个知道吧?就是V+L,这里不懂为何的请返回上一篇看我写的镜面反射数学计算。

        接下来就简单了,依次计算出:emissive、ambient、diffuse、specular,然后计算出surfaceColor。

        这里基本是相当详细的讲解了cg代码的每一个步骤的作用,我相信小伙伴能看懂,demo我就不放了要分,这个cg代码就是最核心的东西,有兴趣自己动手实现看看。


        

        

猜你喜欢

转载自blog.csdn.net/yinhun2012/article/details/80924102