Unity学习之Shader(Phong与Blinn-Phong)

三、Lesson3

1、关键名称

csharp 复制代码
向量
• nDir:法线方向,点乘操作时简称n; 
• lDir:光照方向,点乘操作时简称l;
• vDir:观察方向,点乘操作时简称v;
• rDir:光反射方向,点乘操作时简称r;
• hDir:半角方向(Halfway),lDir和vDir的中间角方向,点乘操作时简称h
空间
• OS:ObjectSpace 物体空间,本地空间;
• WS:WorldSpace世界空间;
• VS:ViewSpace 观察空间;
• CS:HomogenousClipSpace 齐次剪裁空间;
• TS:TangentSpace 切线空间;
• TXS:TextureSpace 纹理空间;

2、漫反射与镜面反射

(1)漫反射

特点:向四面八方均匀反射,反射亮度与观察者看的方向无关

主要模型:兰伯特、半兰伯特

涉及向量:nDir、lDir

(2)镜面反射

特点:反射具有明显方向性,观察者的视角决定了反射光线的有无、明暗

主要模型:Phong、Blinn-Phong

涉及向量:nDir、lDir、vDir、rDir、hDir

3、Phong

(1)效果展示

(2)计算方式

Phong=rDir(光反射方向) dot vDir(观察方向)

rDir=Reflect(-lDir,nDir)

(3)实现代码

csharp 复制代码
Shader "AP01/Phong" {
    Properties {
        _MainCol ("颜色", color) = (1.0, 1.0, 1.0, 1.0)
        _SpecularPow ("高光次幂", range(1, 90)) = 30
    }
    SubShader {
        Tags {
            "RenderType"="Opaque"
        }
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode"="ForwardBase"
            }


            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #pragma multi_compile_fwdbase_fullshadows
            #pragma target 3.0
            // 输入参数
            // 修饰字(满足小朋友太多的问好, 想保发量的大家看热闹)
                // uniform  共享于vert,frag
                // attibute 仅用于vert
                // varying  用于vert,frag传数据
            uniform float3 _MainCol;     // RGB够了 float3
            uniform float _SpecularPow;  // 标量 float
            // 输入结构
            struct VertexInput {
                float4 vertex : POSITION;   // 顶点信息 Get✔
                float4 normal : NORMAL;     // 法线信息 Get✔
            };
            // 输出结构
            struct VertexOutput {
                float4 posCS : SV_POSITION;     // 裁剪空间(暂理解为屏幕空间吧)顶点位置
                float4 posWS : TEXCOORD0;       // 世界空间顶点位置
                float3 nDirWS : TEXCOORD1;      // 世界空间法线方向
            };
            // 输入结构>>>顶点Shader>>>输出结构
            VertexOutput vert (VertexInput v) {
                VertexOutput o = (VertexOutput)0;                   // 新建输出结构
                    o.posCS = UnityObjectToClipPos( v.vertex );     // 变换顶点位置 OS>CS
                    o.posWS = mul(unity_ObjectToWorld, v.vertex);   // 变换顶点位置 OS>WS
                    o.nDirWS = UnityObjectToWorldNormal(v.normal);  // 变换法线方向 OS>WS
                return o;                                           // 返回输出结构
            }
            // 输出结构>>>像素
            float4 frag(VertexOutput i) : COLOR {
                // 准备向量
                float3 nDir = normalize(i.nDirWS);
                float3 lDir = _WorldSpaceLightPos0.xyz;
                float3 vDir = normalize(_WorldSpaceCameraPos.xyz - i.posWS.xyz);
                float3 hDir = normalize(vDir + lDir);
                // 准备点积结果
                float ndotl = dot(nDir, lDir);
                float ndoth = dot(nDir, hDir);
                // 光照模型
                float lambert = max(0.0, ndotl);
                float phong = dot(reflect(-lDir,nDir),vDir);
                // 返回结果
                return phong ;
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

4、Blinn-Phong

(1)效果展示

(2)计算方式

Blinn-Phong=nDir dot hDir

hDir:lDir和vDir的中间角方向

(3)实现代码

csharp 复制代码
Shader "AP01/BlinnPhong" {
    Properties {
        _MainCol ("颜色", color) = (1.0, 1.0, 1.0, 1.0)
        _SpecularPow ("高光次幂", range(1, 90)) = 30
    }
    SubShader {
        Tags {
            "RenderType"="Opaque"
        }
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode"="ForwardBase"
            }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #pragma multi_compile_fwdbase_fullshadows
            #pragma target 3.0
            // 输入参数
            // 修饰字(满足小朋友太多的问好, 想保发量的大家看热闹)
                // uniform  共享于vert,frag
                // attibute 仅用于vert
                // varying  用于vert,frag传数据
            uniform float3 _MainCol;     // RGB够了 float3
            uniform float _SpecularPow;  // 标量 float
            // 输入结构
            struct VertexInput {
                float4 vertex : POSITION;   // 顶点信息 Get✔
                float4 normal : NORMAL;     // 法线信息 Get✔
            };
            // 输出结构
            struct VertexOutput {
                float4 posCS : SV_POSITION;     // 裁剪空间(暂理解为屏幕空间吧)顶点位置
                float4 posWS : TEXCOORD0;       // 世界空间顶点位置
                float3 nDirWS : TEXCOORD1;      // 世界空间法线方向
            };
            // 输入结构>>>顶点Shader>>>输出结构
            VertexOutput vert (VertexInput v) {
                VertexOutput o = (VertexOutput)0;                   // 新建输出结构
                    o.posCS = UnityObjectToClipPos( v.vertex );     // 变换顶点位置 OS>CS
                    o.posWS = mul(unity_ObjectToWorld, v.vertex);   // 变换顶点位置 OS>WS
                    o.nDirWS = UnityObjectToWorldNormal(v.normal);  // 变换法线方向 OS>WS
                return o;                                           // 返回输出结构
            }
            // 输出结构>>>像素
            float4 frag(VertexOutput i) : COLOR {
                // 准备向量
                float3 nDir = normalize(i.nDirWS);
                float3 lDir = _WorldSpaceLightPos0.xyz;
                float3 vDir = normalize(_WorldSpaceCameraPos.xyz - i.posWS.xyz);
                float3 hDir = normalize(vDir + lDir);
                // 准备点积结果
                float ndotl = dot(nDir, lDir);
                float ndoth = dot(nDir, hDir);
                // 光照模型
                float lambert = max(0.0, ndotl);
                float phong = dot(reflect(-lDir,nDir),vDir);
                float blinnPhong = pow(max(0.0, ndoth), _SpecularPow);
                // 返回结果
                return blinnPhong;
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

5、实践

(1)漫反射+镜面反射+固有色

(i)效果
(ii)实现代码
csharp 复制代码
Shader "AP01/OldSchool" {
    Properties {
        _MainCol ("颜色", color) = (1.0, 1.0, 1.0, 1.0)
        _SpecularPow ("高光次幂", range(1, 90)) = 30
    }
    SubShader {
        Tags {
            "RenderType"="Opaque"
        }
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode"="ForwardBase"
            }


            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #pragma multi_compile_fwdbase_fullshadows
            #pragma target 3.0
            // 输入参数
            // 修饰字(满足小朋友太多的问好, 想保发量的大家看热闹)
                // uniform  共享于vert,frag
                // attibute 仅用于vert
                // varying  用于vert,frag传数据
            uniform float3 _MainCol;     // RGB够了 float3
            uniform float _SpecularPow;  // 标量 float
            // 输入结构
            struct VertexInput {
                float4 vertex : POSITION;   // 顶点信息 Get✔
                float4 normal : NORMAL;     // 法线信息 Get✔
            };
            // 输出结构
            struct VertexOutput {
                float4 posCS : SV_POSITION;     // 裁剪空间(暂理解为屏幕空间吧)顶点位置
                float4 posWS : TEXCOORD0;       // 世界空间顶点位置
                float3 nDirWS : TEXCOORD1;      // 世界空间法线方向
            };
            // 输入结构>>>顶点Shader>>>输出结构
            VertexOutput vert (VertexInput v) {
                VertexOutput o = (VertexOutput)0;                   // 新建输出结构
                    o.posCS = UnityObjectToClipPos( v.vertex );     // 变换顶点位置 OS>CS
                    o.posWS = mul(unity_ObjectToWorld, v.vertex);   // 变换顶点位置 OS>WS
                    o.nDirWS = UnityObjectToWorldNormal(v.normal);  // 变换法线方向 OS>WS
                return o;                                           // 返回输出结构
            }
            // 输出结构>>>像素
            float4 frag(VertexOutput i) : COLOR {
                // 准备向量
                float3 nDir = normalize(i.nDirWS);
                float3 lDir = _WorldSpaceLightPos0.xyz;
                float3 vDir = normalize(_WorldSpaceCameraPos.xyz - i.posWS.xyz);
                float3 hDir = normalize(vDir + lDir);
                // 准备点积结果
                float ndotl = dot(nDir, lDir);
                float ndoth = dot(nDir, hDir);
                // 光照模型
                float lambert = max(0.0, ndotl);
                float phong = dot(reflect(-lDir,nDir),vDir);
                float blinnPhong = pow(max(0.0, ndoth), _SpecularPow);
                float3 finalRGB = _MainCol * lambert + blinnPhong;
                // 返回结果
                return float4(finalRGB, 1.0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

(2)结合噪声图,实现类似不锈钢效果

(i)效果
(ii)实现方式
相关推荐
悠哉悠哉愿意1 小时前
【电赛学习笔记】MaxiCAM 项目实践——与单片机的串口通信
笔记·python·单片机·嵌入式硬件·学习·视觉检测
快乐肚皮2 小时前
ZooKeeper学习专栏(五):Java客户端开发(原生API)详解
学习·zookeeper·java-zookeeper
慕y2742 小时前
Java学习第七十二部分——Zookeeper
java·学习·java-zookeeper
★YUI★3 小时前
学习游戏制作记录(剑投掷技能)7.26
学习·游戏·unity·c#
蓝桉8023 小时前
opencv学习(图像金字塔)
人工智能·opencv·学习
rannn_1114 小时前
Java学习|黑马笔记|Day23】网络编程、反射、动态代理
java·笔记·后端·学习
go54631584654 小时前
中文语音识别与偏误检测系统开发
开发语言·人工智能·学习·生成对抗网络·数学建模·语音识别
好奇龙猫5 小时前
日语学习-日语知识点小记-构建基础-JLPT-N3阶段(9):ようなN
学习
牵牛老人5 小时前
OpenCV学习探秘之二 :数字图像的矩阵原理,OpenCV图像类与常用函数接口说明,及其常见操作核心技术详解
opencv·学习·矩阵
flashier6 小时前
ESP32学习笔记_Components(1)——使用LED Strip组件点亮LED灯带
学习·esp32·led·led灯带·esp32组件