漫反射实现+逐像素漫反射+逐像素漫反射实现

标准光照的构成结构

自发光:材质本身发出的光,模拟环境使用的光

漫反射光:光照在粗糙材质后,光的反射方向随机,还有一些光发生了折射,造成材质

表面没有明显的光斑。

高光反射光:光照到材质表面后,无(低)损失直接反射给观察者眼睛,材质表面能观察

到光斑。

环境光:模拟场景光照(简单理解为太阳光)

裴祥凤提出的光照理论:标准光照=自发光+漫发射光+高光反射光+环境光这个理论是模拟

光照效果,并不是真实效果。

以他的名字命名:Phong光照模型

逐顶点光照和逐像素光照

顶点着色器:会在模型渲染点上运行,其他的点会线性插值

片元着色器:会在模型的所有像素点上运行。

逐顶点光照:会在顶点着色器上进行光照运算(高洛德着色)

逐像素光照:会在片元着色器上运行光照运算(phong着色)

逐像素比逐顶点效果好,逐顶点比逐像素性能好

漫反射光照(兰伯特定律)

漫反射光照=光源的颜色*材质的漫反射颜色*MAX(0,标准化后物体表面法向量·标准化

后光源方向向量)

光源颜色:场景中光GameObject取得

材质的漫反射颜色:材质球配置

Max是数学函数,标准化也有函数

表面法线向量:CPU加载模型后,传递到GPU中的

光源方向向量:场景中光GameObject取得

实现:所有数据拿到,拿公式计算

半兰伯特定律:将整体颜色降低一半,再加一半

漫反射光照=(光源的颜色·材质的漫反射颜色)·((标准化后物体表面法线向量·标准化后光源

方向向量)·0.5+0.5)

高光反射光照

高光光照=光源的颜色*材质高光反射颜色*MAX(0,标准化后的观察方向向量·标准化后的反射方向)^光晕系数

逐顶点光照测试相关代码如下所示:

cs 复制代码
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "CreateTest/DiffuseVertex"
{
    Properties
    {
        _DiffuseColor("漫反射颜色",Color) = (1,1,1,1)
    }
    SubShader
    {
        Pass
        {
            //设定光照模式为前向模式(才能正常获取光的颜色和光的方向)
            //Tags{"LightMode" = "ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            //加载Cg语言的脚本,用来处理光照参数
            //处理光照的Cg库文件(cginc扩展名)
            //目录在Unity的安装目录下Editor/Data/CGIncludes/Lighting.cginc
            #include "Lighting.cginc"
            //导入材质颜色
            fixed4 _DiffuseColor;
            
            //如果在Cg编程中,顶点或片元着色器接收多个数值的时候,一般会用结构体实现

            //从CPU接收到的数据
            struct c2v
            {
                float4 vertex:POSITION;//从CPU接收到的模型空间下的点的位置
                float3 normal:NORMAL; //从CPU接收到的当前点的模型空间下的法线向量
            };

            //因为在顶点着色器中,需要计算裁剪空间下点的位置和Phong着色计算出的兰伯特定律计算后的颜色
            struct v2f
            {
                float4 pos:SV_POSITION;//经过顶点着色器计算后的,当前点的裁剪空间下的位置
                fixed3 color : COLOR;//经过兰伯特定律计算后的当前点的颜色
            };
            //高洛德着色(逐顶点光照),光照计算应该编写在顶点着色器中
            v2f vert(c2v data)
            {
                //顶点着色器传递给片元着色器的数据结构体声明
                v2f r;
                //必须做的:将点从模型空间下,转换到裁剪空间下
                r.pos=UnityObjectToClipPos(data.vertex);
                
                //兰伯特定律计算
                //漫反射光照=光源的颜色*材质的漫反射颜色*MAX(0,标准化后物体表面法线向量*标准化后光源方向向量)
    
                //光照是在世界中发生,需要将所有的数值,变换到世界坐标系下,再运算
                // _Object2World矩阵是Unity提供的,用于转换模型空间下到世界空间下的转换矩阵
                //因为法线传递过来的是3x1的列矩阵,_Object2World是4x4的齐次矩阵,如果想做矩阵乘法,需要将齐次矩阵,变成3x3矩阵
                fixed3 worldNormal = normalize(mul((float3x3)unity_ObjectToWorld, data.normal));
                //获得直射光的光方向
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

                //公式运算
                fixed3 diffuse=_LightColor0.rgb* _DiffuseColor.rgb* max(0,dot(worldNormal,worldLightDir));
                
                //根据高洛德光照模型,将环境光追加在最终计算后的颜色上
                r.color = UNITY_LIGHTMODEL_AMBIENT.xyz + diffuse;

                //r.color = _LightColor0.rgb;

                return r;
            }
            //千万记得带"数字",若未带4可能会出现材质球颜色为红色情况
            fixed4 frag(v2f data) :SV_Target
            {
                return fixed4(data.color,1.0);
            }
            ENDCG
        }
    }
    Fallback "Diffuse"
}

逐像素光照测试相关代码如下:

cs 复制代码
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "CreateTest/DiffusePixel"
{
    Properties
    {
        _DiffuseColor("漫反射颜色",Color) = (1,1,1,1)
    }
    SubShader
    {
        Pass
        {
            //设定光照模式为前向模式(才能正常获取光的颜色和光的方向)
            //Tags{"LightMode" = "ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            //加载Cg语言的脚本,用来处理光照参数
            //处理光照的Cg库文件(cginc扩展名)
            //目录在Unity的安装目录下Editor/Data/CGIncludes/Lighting.cginc
            #include "Lighting.cginc"
            //导入材质颜色
            fixed4 _DiffuseColor;
            
            //如果在Cg编程中,顶点或片元着色器接收多个数值的时候,一般会用结构体实现

            //从CPU接收到的数据
            struct c2v
            {
                float4 vertex:POSITION;//从CPU接收到的模型空间下的点的位置
                float3 normal:NORMAL; //从CPU接收到的当前点的模型空间下的法线向量
            };

            //因为在顶点着色器中,需要计算裁剪空间下点的位置和Phong着色计算出的兰伯特定律计算后的颜色
            struct v2f
            {
                float4 pos:SV_POSITION;//经过顶点着色器计算后的,当前点的裁剪空间下的位置
                //fixed3 color : COLOR;//经过兰伯特定律计算后的当前点的颜色
                float3 worldNormal:NORMAL;//经过矩阵转换后世界空间下的,法线向量
            };
            //将渲染点,从模型空间下,转换到裁剪空间下
            //将渲染点对应的法线,从模型空间下,转换到世界空间下
            v2f vert(c2v data)
            {
                //顶点着色器传递给片元着色器的数据结构体声明
                v2f r;
                
                
                //必须做的:将点从模型空间下,转换到裁剪空间下
                
                r.pos=UnityObjectToClipPos(data.vertex);
                
                //几何运算,在顶点着色器中完成,再将运算好的数值,传递给片元着色器
                // _Object2World矩阵是Unity提供的,用于转换模型空间下到世界空间下的转换矩阵
                //因为法线传递过来的是3x1的列矩阵,_Object2World是4x4的齐次矩阵,如果想做矩阵乘法,需要将齐次矩阵,变成3x3矩阵
                r.worldNormal = mul((float3x3)unity_ObjectToWorld, data.normal);
           
                //r.color = _LightColor0.rgb;

                return r;
            }
            //phong着色(逐像素光照),光照计算应该编写在片元着色器中
            fixed4 frag(v2f data) :SV_Target
            {
                //兰伯特定律计算
                //漫反射光照=光源的颜色*材质的漫反射颜色*MAX(0,标准化后物体表面法线向量*标准化后光源方向向量)
                //光照是在世界中发生,需要将所有的数值,变换到世界坐标系下,再运算
                //世界空间下的,表面法线向量标准化
                fixed3 worldNormal = normalize(data.worldNormal);
                
                //获得直射光的光方向,标准化向量
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
                
                //公式运算
                fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.rgb * max(0, dot(worldNormal, worldLightDir));

                //根据Phong光照模型,将环境光追加在最终计算后的颜色上
                fixed3 color = UNITY_LIGHTMODEL_AMBIENT.xyz + diffuse;
                return fixed4(color,1.0);
            }
            ENDCG
        }
    }
    Fallback "Diffuse"
}

两者之间的区别如下图所示:

该系列专栏为网课课程笔记,仅用于学习参考。

相关推荐
mxwin4 小时前
Unity Shader 半透明物体为什么不能写入深度缓冲?
unity·游戏引擎·shader
晚枫歌F5 小时前
三层时间轮的实现
网络·unity·游戏引擎
咸鱼永不翻身6 小时前
Lua脚本事件检查工具
unity·lua·工具
leo__5208 小时前
单载波中继系统资源分配算法MATLAB仿真程序
算法·matlab·unity
努力长头发的程序猿9 小时前
Unity使用ScriptableObject序列化资源
unity·游戏引擎
mxwin10 小时前
Unity Shader 手写基于 PBR 的 URP Lit Shader 核心光照计算
unity·游戏引擎·shader
小贺儿开发10 小时前
Unity3D 智能云端数字标牌系统
unity·阿里云·人机交互·视频·oss·广告·互动
魔士于安10 小时前
Unity windows 同步 异步 打开文件文件夹工具
游戏·unity·游戏引擎·贴图·模型
魔士于安11 小时前
unity lowpoly 风格 城市 建筑 道路 交通标志
游戏·unity·游戏引擎·贴图·模型
mxwin11 小时前
Unity GPU Shader 性能优化指南
unity·游戏引擎·shader