首页 > 其他分享 >读UnityShader入门精要第六章-Unity中的基础光照

读UnityShader入门精要第六章-Unity中的基础光照

时间:2022-09-03 16:37:12浏览次数:57  
标签:vertex fixed3 Unity UnityShader v2f worldNormal 精要 Diffuse 光照

1.我们如何看到这个世界

1.1  光源

  光是由光源发出的,在实时渲染中,光源被当成一个没有体积的点.

  在光学中,使用辐照度(irradiance)来量化光.当光打在一个平面上时,平面接收到的光的辐照度由光本身的辐照度乘上光纤方向和平面的夹角的余弦值得到,因此最终使用点积来求辐照度.

1.2  吸收和散射

  光线到达物体表面后,会产生两种结果:散射(scattering)和吸收(absorption).

  散射改变光的方向.如果散射后光进入了物体内部,这种散射称为折射(refraction)或透射(transmission);如果散射后光射向物体外部,这种散射称为反射(reflection).对于不透明物体,经过折射后的光线还会继续与物体内部的颗粒相交,另一些则被物体吸收.同时,光线经过物体的反射后的光线将具有和入射光线不同的分布和颜色.

  在光照模型中,高光反射(specular)部分表示物体表面是如何反射光线的,漫反射(diffuse)部分则表示有多少光线会被折射\吸收和散射出表面.出射度(exitance)描述了出射光线和入射光线的方向和数量的比值.

1.3  着色

  着色(shadering):根据材质属性(如漫反射属性等)\光源信息(如光源方向\辐照度等),使用一个等式去计算沿某个观察方向的出射度的过程.这个等式也称为光照模型(Lighting Model).

1.4  BRDF光照模型

  BRDF(Bidirectional Reflectance Distribution Function):用于描述一个表面是如何和光照进行交互的方程.

2.标准光照模型

  标准光照模型只关心直接光照,即那些直接从光源发射出来照射到物体表面后,经过物体表面的一次反射直接进入摄像机的光线.

  标准光照模型将进入摄像机的光线分为4部分:

  1)自发光(emissive)

  2)高光反射(specular)

  3)漫反射(diffuse)

  4)环境光(ambient)

2.1  环境光

  在真实的世界中,物体除了被直接光照照亮外,还会被间接光照(在多个物体之间进行反射后进入摄像机的光线)照亮.由于是多次反射的光线,这类光照很难计算,因此使用环境光模拟间接光照.

  环境光就是一个全局变量,即场景中的所有物体都使用这个环境光.

2.2  自发光

  光线可以不经过反射直接由光源进入摄像机,这种类型的光称为自发光.自发光的计算不难,但是需要注意的是,自发光和光源不同,自发光并不会照亮周围的表面.

2.3  漫反射

  漫反射用于对那些被物体表面随机散射到各个方向的辐照度进行建模.漫反射的观察角度不重要,因为漫反射完全随机.

  漫反射光照符合兰伯特定理(Lambert`s law):

  这个公式代表漫反射结果的颜色等于光照颜色乘以漫反射表面材质颜色乘以漫反射表面辐照度.注意,漫反射表面辐照度不能为负值,这样可以避免表面被来自于后方的光照亮.

2.4  高光反射

  这里的高光反射是一种经验模型,并不能完全符合真实世界中的高光反射现象.

2.5  逐像素还是逐顶点

  光照模型如果在片元着色器中计算,我们称为逐像素光照(per-pixel lighting);光照模型如果在顶点着色器中计算,我们就称为逐顶点光照(per-vertex lighting).

  在逐像素光照中,每个像素都会先得到像素的法线,然后进行光照模型的计算,这种计算称为Phong着色.

  在逐顶点光照中,每个顶点上会计算光照,然后像素的光照会通过顶点进行插值得到,这称为高洛德着色.由于片元内部像素的光照是由顶点插值得到的,因此光照模型中有非线性的计算时会出现问题,而且片元内部像素的颜色总是暗于顶点处的最高颜色值.

 3.在UnityShader中实现漫反射光照模型

3.1  逐顶点光照

Shader "Custom/DiffuseVertexLevel"
{
    Properties
    {
        //用于漫反射光照模型的颜色
        _Diffuse("Diffuse", Color) = (1,1,1,1)
    }
        SubShader
    {
        Pass{
            //定义光照模式
            Tags{ "LightMode" = "ForwardBase" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "Lighting.cginc"//使用Unity的内置变量

            fixed4 _Diffuse;//光照模型颜色
            
            //顶点着色器输入
            struct a2v {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            //顶点着色器输出
            struct v2f {
                float4 pos : SV_POSITION;
                fixed3 color : COLOR;
            };

            //顶点着色器
            v2f vert(a2v v) {
                v2f o;
                //从物体局部空间转化到裁剪空间中
                o.pos = UnityObjectToClipPos(v.vertex);
                //获取环境光部分
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                //世界空间下的法向量
                fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
                //世界空间下的光源方向
                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                //计算漫反射部分,saturate函数将值限定在0-1之间
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight));

                //最终颜色为环境光加上漫反射光颜色
                o.color = ambient + diffuse;

                return o;
            }

            fixed4 frag(v2f i) : SV_Target{
                return fixed4(i.color, 1.0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

3.2  逐像素光照

Shader "Custom/DiffusePixelLevel"
{
    Properties
    {
        //用于漫反射光照模型的颜色
        _Diffuse("Diffuse", Color) = (1,1,1,1)
    }
        SubShader
    {
        Pass{
            //定义光照模式
            Tags{ "LightMode" = "ForwardBase" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "Lighting.cginc"//使用Unity的内置变量

            fixed4 _Diffuse;//光照模型颜色
            
            //顶点着色器输入
            struct a2v {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            //顶点着色器输出
            struct v2f {
                float4 pos : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
            };

            v2f vert(a2v v) {
                v2f o;
                //从物体局部空间转化到裁剪空间中
                o.pos = UnityObjectToClipPos(v.vertex);
                //法向量也从局部空间转化到裁剪空间中
                o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);

                return o;
            }

            //片元着色器
            fixed4 frag(v2f i) : SV_Target{

                //获取环境光部分
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                //世界空间下的法向量
                fixed3 worldNormal = normalize(i.worldNormal);
                //世界空间下的光源方向
                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                //计算漫反射部分,saturate函数将值限定在0-1之间
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight));

                //计算最终颜色
                fixed3 color = ambient + diffuse;

                return fixed4(color, 1.0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

3.3  两种光照模型的效果对照

  左侧为逐顶点光照的效果,右侧为逐像素光照的效果.

4.在UnityShader中实现高光反射光照模型

4.1  逐顶点光照

Shader "Custom/SpecularVertexLevel"
{
    Properties
    {
        _Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
        _Specular ("Specular", Color) = (1, 1, 1, 1)//高光颜色
        _Gloss ("Gloss", Range(8.0, 256)) = 20//高光大小
    }
    SubShader
    {
        Pass {
            Tags { "LightMode"="ForwardBase" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc"

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

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

            struct v2f {
                float4 pos : SV_POSITION;
                float3 color : COLOR;
            };

            v2f vert(a2v v) {
                v2f o;
                
                o.pos = UnityObjectToClipPos(v.vertex);
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));

                //计算世界坐标下反射方向
                fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
                //计算世界坐标下视角方向
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz);

                //计算反射部分颜色
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);

                //计算得到最终的颜色
                o.color = ambient + diffuse + specular;
                
                return o;
            }

            fixed4 frag(v2f i) : SV_Target {
                return fixed4(i.color, 1.0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

4.2  逐像素光照

Shader "Custom/SpecularPixelLevel"
{
    Properties
    {
        _Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
        _Specular ("Specular", Color) = (1, 1, 1, 1)//高光颜色
        _Gloss ("Gloss", Range(8.0, 256)) = 20//高光大小
    }
    SubShader
    {
        Pass {
            Tags { "LightMode"="ForwardBase" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc"

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

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

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

            v2f vert(a2v v) {
                v2f o;
                
                o.pos = UnityObjectToClipPos(v.vertex);
                o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                
                return o;
            }

            fixed4 frag(v2f i) : SV_Target {
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));

                //计算世界坐标下反射方向
                fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
                //计算世界坐标下视角方向
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);

                //计算反射部分颜色
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);

                return fixed4(ambient + diffuse + specular, 1.0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

4.3  Blimm-Phong光照模型

  对逐像素光照模型中计算高光反射的代码稍作修改就是Blimm-Phong光照模型:

Shader "Custom/BlinnPhong"
{
    Properties
    {
        _Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
        _Specular ("Specular", Color) = (1, 1, 1, 1)//高光颜色
        _Gloss ("Gloss", Range(8.0, 256)) = 20//高光大小
    }
    SubShader
    {
        Pass {
            Tags { "LightMode"="ForwardBase" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc"

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

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

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

            v2f vert(a2v v) {
                v2f o;
                
                o.pos = UnityObjectToClipPos(v.vertex);
                o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                
                return o;
            }

            fixed4 frag(v2f i) : SV_Target {
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));

                //计算世界坐标下视角方向
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
                fixed3 halfDir = normalize(worldLightDir + viewDir);

                //计算反射部分颜色
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal, halfDir)), _Gloss);

                return fixed4(ambient + diffuse + specular, 1.0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

4.4  3种高光反射光照对比

  左中右分别为逐顶点光照模型\逐片元光照模型\Blinn-Phong光照模型.

标签:vertex,fixed3,Unity,UnityShader,v2f,worldNormal,精要,Diffuse,光照
From: https://www.cnblogs.com/movin2333/p/16648044.html

相关文章

  • 第 20 天:在 Unity 中为 Sprite 设置动画
    第20天:在Unity中为Sprite设置动画客观的:我们如何在我们的统一项目中为我们的精灵提供动画?在我的GalaxyShooter项目中,拥有sprite非常棒,并且都提供了真实的......
  • unityshader_01
    第一章渲染流水线1.1渲染流水线1.1.1现实中流水线在工业上,流水线被广泛应用在装配线上。假设,老王有一个生产洋娃娃的工厂,一个洋娃娃的生产流程可以分为4个步骤:在流水线......
  • unity prefab的新用法
    预制件是一个可重用的构建块,你可以把各种类型的对象统一成预制件。蓝色立方体是预制件,灰色的是游戏对象。用浏览器自带的翻译软件看的,可能会有一些细节问题......嵌套的......
  • 如何将Unity3D的工程文件安装到quest中
    本文用于学习笔记,参考链接  作者:倥偬盎离https://www.bilibili.com/read/cv12205073/出处:bilibili一、需要提前安装的软件1.oculus手机应用下载 使用手册上面提供......
  • Unity — 带有专业提示的 Cinemachine 系统
    Unity—带有专业提示的Cinemachine系统今天,我将解释Unity中电影机系统的使用。正如我在互联网上的几个教程和主题中看到的那样,由于缺乏真实案例,给出的解释还不够。......
  • Unity踩坑记录(持续更新)
    1.利用UsePass可以帮助投影pass合批。相同网格、不同材质、不同shader,只要这些shader使用UsePass引用了同一个pass,并且材质的属性和宏都一样,那么这些物体的投影pass就可以......
  • 如何精简Unity中使用的字体文件
    http://t.zoukankan.com/lancidie-p-6285569.html ◆◆◆问题描述我们《极无双》项目中使用了两套字库。黑体字体来显示大部分既定文本、玩家昵称、对话等。隶书字体来......
  • CentOS CentOS是免费的、开源的、可以重新分发的开源操作系统 [1] ,CentOS(Community E
    CentOS_百度百科 https://baike.baidu.com/item/centos/498948?fr=aladdinCentOS是免费的、开源的、可以重新分发的开源操作系统 [1]  ,CentOS(CommunityEnterprise......
  • 如何将MMD镜头导入Unity
    最近懒得K动作和镜头了,想着把其他大佬做的动作和镜头拿来玩玩。虽然导入MMD动作的教程随便搜就能找到(MMD->Blender->FBX->Unity),但是关于导入镜头的教程好像特别少。网上......
  • 种类并查集 把find变成查索引 unity变成x是y的
    真假英雄http://oj.saikr.com/contest/20/problem/K在一个小镇上,很多人都患了一个精神病,他们都认为自己是“英雄”或者“反派”中间的一种,“英雄”觉得自己是正义的一方,......