首页 > 其他分享 >Unity Shader 基础光照(build)

Unity Shader 基础光照(build)

时间:2023-07-11 18:33:36浏览次数:45  
标签:vertex Shader fixed3 Unity build worldNormal Diffuse 光照 v2f

1.光学基础

  1.1 光源:  

    光线由光源发出,在实时渲染中,我们通常把光源当成一个没有体积的点。用l代表光的方向,用辐照度来量化光的强度。辐照度的意思表示垂直于l的单位面积上单位时间穿过的能量。如果光于平面不垂直,则辐照度为 cos S/l,S为光线与平面法线的夹角。

  1.2 吸收与散射:   

    光源与物体相交后通常会有两种结果:散射 和 吸收。

    散射:只改变光的方向,不改变光的密度和颜色; 吸收: 只改变光的方向和密度,不改变光的方向。散射又分为折射和反射两种。

    散射又分为两种,一种是直接被反射出来,另一种是经过内部折射或者内部吸收后翻出出来的光。我们把高光反射(specular)表示表面反射的光,漫反射(difuss)表示折射、吸收、散射出表面的光。

  1.3 着色:

    着色,指根据材质属性、光源信息、使用一个等式去计算沿着某个观察方向的初射度过程。我们把这个等式称为 光照模型(Lighting Model),不同的光照模型有不同的目的,例如一些描述粗糙物体表面,一些描述金属表面。

  1.4 BRDF 光照模型

    BRDF光照模型用来回答了当光线从某个方向照射到一个表面时,有多少光线被反射,反射的角度有哪些等问题。BRDF大多都是一个数学公式给定的。这些往往都是经验模型,你可以自己试一试,图形学第一定律“如果它看起来是对的,那么它就是对的”。

2.标准光照模型

  标准光照模型的基本方法是,把进入摄像机内的光线分成4个部分。每一个部分使用一种方法来计算它的贡献度:

    自发光(emissive),用于描述给定一个方向时,一个表面本身会向该方向发射多少辐射量。值得注意的是如果没有全局光照技术,这些自发光的表面并不会真的照亮周围的物体,而它本身看起来更亮了而已。

    高光反射(specular),这部分用于描述当前光线从光源照射到模型表面时,该表面完全镜面反射散射多少辐射量。

    漫反射(difuss),当光线从光源照射到模型表面时,该表面会向每个方向散射多少辐射量。

    环境光(ambient),描述其他所有的间接光线。

  逐顶点还是逐像素?

    通过基本光照模型,我们可以得到数学工作,那在哪开始计算这些光照模型呢?我们可以在片元着色器中计算,也叫做逐像素光照。或者是在顶点着色器中计算,也叫做逐顶点光照

    在逐像素光照中,我们会以每个像素为单位,得到它的法线,在片之间进行法线的差值计算,然后进行光照模型计算。逐顶点光照,我们会计算每个顶点的光照,然后会在渲染图元内部进行线性差值,最后输出成像素颜色。逐顶点光照计算量小于逐像素光照。

3.Unity中的环境光与自发光

  在光照模型中,环境光和自发光是最简单的,Unity中可以通过Window->Rendering->Lighting->Environment 中进行设置环境光。而由于绝大多数的物体是没有自发光特性的,因此大部分不用设置。如果需要的话只需要在片元着色器输出最后的颜色之前,把材质的自发光颜色添加到输出颜色上即可。

4.使用Unity Shader实现漫反射光照模型

  在基本光照模型中漫反射的计算公式是:

    Cdiffuse=(Clight*Mdiffuse)max(0,n*l);

  其中:Clight表示入射光颜色和强度,Mdiffuse为材质漫反射系数,n为法线,l为入射方向。

  1.逐顶点光照:

Shader "Untiy Book/Chapter6/DiffuseVertexLevel"
{
    Properties
    {
        //控制漫反射颜色
        _Diffuse("Diffuse",Color) = (1,1,1,1)
    }
    SubShader
    {
        Pass
        {
            //只有定义了正确的LigtMode,才能得到后续的一些光照变量
            Tags { "LightMode" = "ForwardBase" }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "Lighting.cginc"
            //为了使用在Properties定义的变量,要定义一个与之属性类型匹配的变量
            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(UnityObjectToWorldNormal(v.normal));
                // 得到光照强度和方向
                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                // 光的颜色强度*材质漫反射颜色 得到漫散射光照  saturate函数是Cg提供的可以班参数截取到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"
}

效果: 逐顶点的漫反射光照,对于细分程度高的物体可以有比较好的效果,对于细分程度低的模型,会看到背光面与向光面有一些齿轮。

   2.逐像素光照:

Shader "Untiy Book/Chapter6/DiffusePixelLevel"
{
    Properties
    {
        //控制漫反射颜色
        _Diffuse("Diffuse",Color) = (1,1,1,1)
    }
    SubShader
    {
        Pass
        {
            //只有定义了正确的LigtMode,才能得到后续的一些光照变量
            Tags { "LightMode" = "ForwardBase" }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "Lighting.cginc"
            //为了使用在Properties定义的变量,要定义一个与之属性类型匹配的变量
            fixed4 _Diffuse;
            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float4 pos:SV_POSITION;
                fixed3 worldNormal : TEXCOORD0;
            };

            v2f vert (a2v v)
            {
                v2f o;
                // 顶点着色器基本任务 将模型空间转为裁剪空间
                o.pos = UnityObjectToClipPos(v.vertex);
                // 顶点着色器不再计算,只需要将世界坐标法线给片元着色器
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                // 将顶点的法线从模型空间转为世界空间   归一化处理,防止结果为负数
                fixed3 worldNormal = normalize(i.worldNormal);
                // 得到光照强度和方向
                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                // 得到物体散射
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb*saturate(dot(worldNormal,worldLight));
                fixed3 color = ambient+diffuse;
                return fixed4(color,1.0);
            }
            ENDCG
        }
    }
    Fallback "Diffuse"
}

效果: 这样光线过渡就有了更加平滑的效果了。但问题又来了,在光线无法到达的区域,模型没有任何的明暗变化。

   3.半兰伯特模型

  这种模型是在开发半条命时提出的一种技术。其实也就是更改了光照模型的公式,如下:

  Cdiffuse=(Clight*Mdiffuse)(alpha(n*l)+beta);

  其中对n*l 乘以一个alpha倍再加上了 beta偏移,绝大多数情况下两个值都为0.5,这样保证了值不为0,且原有的0值处,现在为0.5了所以保证了背光面也能有阴影变化

 

Shader "Untiy Book/Chapter6/HalfLambert"
{
    Properties
    {
        //控制漫反射颜色
        _Diffuse("Diffuse",Color) = (1,1,1,1)
    }
    SubShader
    {
        Pass
        {
            //只有定义了正确的LigtMode,才能得到后续的一些光照变量
            Tags { "LightMode" = "ForwardBase" }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "Lighting.cginc"
            //为了使用在Properties定义的变量,要定义一个与之属性类型匹配的变量
            fixed4 _Diffuse;
            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float4 pos:SV_POSITION;
                fixed3 worldNormal : TEXCOORD0;
            };

            v2f vert (a2v v)
            {
                v2f o;
                // 顶点着色器基本任务 将模型空间转为裁剪空间
                o.pos = UnityObjectToClipPos(v.vertex);
                // 顶点着色器不再计算,只需要将世界坐标法线给片元着色器
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                // 将顶点的法线从模型空间转为世界空间   归一化处理,防止结果为负数
                fixed3 worldNormal = normalize(i.worldNormal);
                // 得到光照强度和方向
                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);

                fixed halfLambert = dot(worldNormal,worldLight)*0.5 +0.5;
                // 得到物体散射
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * halfLambert;
                
                fixed3 color = ambient+diffuse;
                return fixed4(color,1.0);
            }
            ENDCG
        }
    }
    Fallback "Diffuse"
}

效果:

 三种漫反射光照对比效果:

5.使用Unity Shader实现高光反射模型

  高光反射的基本光照模型为:

   Cspecular=(Clight*Mspecular)max(0,(v*r))gloss;

  其中,入射光线的颜色和强度Clight,材质的高光反射系数Mspecular,视角方向v,反射方向r。其中r = l-2(n*l)n

  1.逐顶点光照

Shader "Untiy Book/Chapter6/SpecularVertex"
{
    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"
            #include "UnityCG.cginc"
            
            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;
            
            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(UnityObjectToWorldNormal(v.normal));
                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                fixed3 diffuse = _LightColor0 * _Diffuse.rgb *saturate(dot(worldNormal,worldLight));
                // 高光反射部分
                fixed3 reflectDir = normalize(reflect(-worldLight,worldNormal));
                fixed3 viewDir = normalize(UnityWorldSpaceViewDir(mul(unity_ObjectToWorld,v.vertex)));
                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 "Specular"
}

  这种方式得到的高光部分明显不平滑,因为高光反射部分的计算是非线性的,但是顶点着色器中计算光照部分是线性的。

  2.逐像素光照

Shader "Untiy Book/Chapter6/SpecularPixel"
{
    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"
            #include "UnityCG.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 = UnityObjectToWorldNormal(v.vertex);
                o.worldPos = mul(unity_ObjectToWorld,v.vertex);
                return o;
            }

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

                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                // 漫反射
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal,worldLight));
                // 计算高光
                fixed3 reflectDir = normalize(reflect(-worldLight,worldNormal));
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz-i.worldPos.xyz);

                fixed3 specular = _LightColor0.rbg * _Specular.rbg* pow(saturate(dot(reflectDir,viewDir)),_Gloss);

                return fixed4(ambient+diffuse+specular,1.0);
            }
            ENDCG
        }
    }
    Fallback "Specular"
}

效果:我们看到了,光照部分更加平滑了。

   3.Blinn-Phong 光照模型

  该公式比较复杂,它是一个经验模型,并不完全符合真实世界中的光照现象。具体公式为:

 

Shader "Untiy Book/Chapter6/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"
            #include "UnityCG.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 = UnityObjectToWorldNormal(v.vertex);
                o.worldPos = mul(unity_ObjectToWorld,v.vertex);
                return o;
            }

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

                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                // 漫反射
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal,worldLight));
                // 计算高光
                //fixed3 reflectDir = normalize(reflect(-worldLight,worldNormal));
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz-i.worldPos.xyz);
                // change
                fixed3 halfDir = normalize(worldLight+viewDir);
                fixed3 specular = _LightColor0.rbg * _Specular.rbg* pow(max(0,dot(worldNormal,halfDir)),_Gloss);
                return fixed4(ambient+diffuse+specular,1.0);
            }
            ENDCG
        }
    }
    Fallback "Specular"
}

效果:我们可以看到高光反射部分看起来更大,更亮一些。实际渲染中我们更喜欢这种这种方式

 全部对比:从左到右,从上到下

 

标签:vertex,Shader,fixed3,Unity,build,worldNormal,Diffuse,光照,v2f
From: https://www.cnblogs.com/CatSevenMillion/p/17544927.html

相关文章

  • Burp Suite Professional / Community 2023.7 (macOS, Linux, Windows) - Web 应用安
    BurpSuiteProfessional/Community2023.7(macOS,Linux,Windows)-Web应用安全、测试和扫描BurpSuiteProfessional,Test,find,andexploitvulnerabilities.请访问原文链接:https://sysin.org/blog/burp-suite-pro-2023/,查看最新版。原创作品,转载请保留出处。作者......
  • Camtasia Studio 2023.0.2 Build 45178中文版功能介绍及免费下载安装教程
    TechSmithCamtasia2023Mac版软件由兔八哥爱分享的Macos系统上一款屏幕录制软件中文版,它可以帮助用户录制电脑屏幕、添加音频、视频和图片,进行剪辑和编辑,并输出高质量的视频文件。CamtasiaStudio2023.0.2Build45178软件介绍Camtasia2023是一款简便的屏幕录制程序,该软件帮助......
  • Unity3D高级编程主程手记 学习笔记五:网络通讯
    1.C#实现TCP1.1实现所需APIC#提供了TCP的Socket连接API。一般的游戏项目我们不会使用阻塞方式连接和接收。因为我们不会让游戏卡住等待传输链接,大多数情况下我们还是会使用更加平滑的异步操作作为网络连接和收发的操作。常用的API如下:BeginConnect:开始连接Be......
  • Unity UGUI的Mask(遮罩)组件的介绍及使用
    UnityUGUI的Mask(遮罩)组件的介绍及使用1.什么是Mask组件?Mask(遮罩)组件是UnityUGUI中的一个重要组件,用于限制子对象的可见区域。通过设置遮罩组件,可以实现一些特殊效果,如显示部分图片、裁剪文本等。2.Mask组件的工作原理Mask组件通过将子对象与遮罩对象进行比较,只显示与遮罩......
  • 使用CRM REST Builder的Predefined Query在js结合FetchXML语句进行查询
    一般情况下使用拓展工具RESTBuilder编辑器,可以很方便的进行操作js中增删改查均能实现,但在某些较为特殊的场景下,需要根据条件去拼接查询过滤条件的,使用编辑器生成的代码无法实现,需要结合使用fetchXML,比如某个条件多个值都查询需要使用in查询,再或者需要过滤关联表中的某个字段的值。......
  • String、StringBuffer、StringBuilder 的区别?
    一.介绍String、StringBuffer、StringBuilder:  前言: String、StringBuffer、StringBuilder均在java.lang包下;String: 在Java中,String是一个特殊的引用类型,用于表示文本字符串。它提供了许多方法来操作和处理字符串,比如连接、截取、查找、替换等。String类......
  • MySQL--Sorted Index Builds 导致备份失败故障分析
    问题概述xtrabackup备份失败,日志中有这样的信息InnoDB:Anoptimized(withoutredologging)DDLoperationhasbeenperformed.Allmodifiedpagesmaynothavebeenflushedtothediskyet.问题原因redologs会跳过一些DDL,PerconaXtraBackup监测到redolog有跳过时,它会......
  • Unity UGUI的Slider(滑动条)件组的介绍及使用
    UnityUGUI的Slider(滑动条)件组的介绍及使用1.什么是Slider组件?Slider(滑动条)是UnityUGUI中的一种常用UI组件用,于在用户界面中实现滑动选择的功能。通过拖动滑块,用户可以选择一个数值范围的内值。2.Slider组件的工作原理Slider组件由两部分组成:滑动区域和滑块。滑动区域用于显......
  • Unity UGUI的Toggle(复选框)组件的介绍及使用
    UnityUGUI的Toggle(复选框)组件的介绍及使用1.什么是Toggle组件?Toggle(复选框)是UnityUGUI中的一个常用组件,用于实现复选框的功能。它可以被选中或取消选中,并且可以代码通过其制控状态。2.Toggle组件的工作原理组Toggle件由两个部分组成:背景记标和。景背用于显示复选框的外观表......
  • WINUI 引入 CommunityToolkit.WinUI.UI进行数据验证
     先在xaml命名空间中引入  xmlns:ui="using:CommunityToolkit.WinUI.UI"textbox中进行IP验证如下:<TextBoxx:Name="textBox"Width="200"Height="30"Background="AliceBlue"ui:TextBoxExten......