unity shader学习---基础光照模型 Phong、Blinn-Phong

unity实现基础光照模型

  • unity shader的结构
  • unity shader的形式
    • 1、顶点/片元着色器
    • 2、表面着色器
    • 3、固定着色器
  • 基础光照模型
    • 顶点着色模型
    • phong模型
    • Blinn-Phong模型
    • 效果

unity shader的结构

Shader "MyShader" { // 名字
        Properties {
                //所需的各种属性
        }
        SubShader { // 显卡A用的子着色器
                [Tags] //可选的,告诉Unity的渲染引擎:希望怎样以及何时渲染这个对象
                [RenderSetup] //可选的,状态设置
                Pass { // 每个 Pass定义了一次完整的渲染流程
                        [Name]
                        [Tags]
                        [RenderSetup] 
                         // other code
                 }
         }
        SubShader { // 显卡B用的子着色器
            //和上一个SubShader类似
        }
        
        FallBack "Diffuse" // 如果上面的SubShader都不能在这块显卡上运行,就使用这个最低级的Shader 
}

【Name】
通过在字符串中添加斜杠("/") 可以控制 nity Shader 在材质面板中出现的位置
【Properties】
这些属性将会出现在材质面板中
unity shader学习---基础光照模型 Phong、Blinn-Phong_第1张图片
unity shader学习---基础光照模型 Phong、Blinn-Phong_第2张图片
【SubShader】

SubShader { // 显卡A用的子着色器
                [Tags] // /可选的,告诉Unity的渲染引擎:希望怎样以及何时渲染这个对象
                [RenderSetup] //可选的,状态设置
                Pass { // 每个 Pass定义了一次完整的渲染流程
                }
         }

1 SubShader标签Tags:

2 状态设置:设置显卡的各种状态
unity shader学习---基础光照模型 Phong、Blinn-Phong_第3张图片
3 Pass语义块

Pass { // 每个 Pass定义了一次完整的渲染流程
      [Name]
      [Tags]
      [RenderSetup] 
       // other code
}

Pass 中定义该 Pass 名称,通过这个名称,我们可以使用 ShaderLab UsePass 令来直接使用其他 Unity Shader 中的Pass,注意在使用 sePass 命令时必须使用大写形式的名字。

Pass 同样可以设置标签,是用千告诉渲染引擎我们希望怎样来渲染该物体
unity shader学习---基础光照模型 Phong、Blinn-Phong_第4张图片
4、Fallback
自动返回一个通用的最低级的Pass

unity shader的形式

1、顶点/片元着色器

unity shader学习---基础光照模型 Phong、Blinn-Phong_第5张图片
定义在Pass 语义块内的 CGPROGRAM ENDCG 之间
需要自已定义每个 Pass 需要使用的 Shader 代码,更复杂但灵活性高

2、表面着色器

unity shader学习---基础光照模型 Phong、Blinn-Phong_第6张图片
是 Unity 对顶点/片元着色器的更高一层的抽象,Unity 为我们处理了很多光照细节, 在背后仍旧把它转换成对应的顶点/片元着色器
表面着色器被定义在 SubShader 语义块内的 CGPROGRAM ENDCG 之间,不需要开发者关心使用多少个 Pass 、每个Pass 如何渲染等问题,

3、固定着色器

用在一些旧的设备上 其 GPU 仅支持 DirectX 7.0 OpenGL 1.5 OpenGL ES 1. 1), 它们不支持可编程管线着色器,这些着色器往往只可以完 一些非常简单效果

总结
【固定函数着色器】需要在非常旧的设备上运行
【表面着色器】 有各种光源打,但需要小心在移动平台上的性能表现。
【顶点/片元着色器】需要使用的光照数目非常少(例如只有个平行光);或者需要很多自定义的渲染效果

基础光照模型

顶点着色模型

在VS计算颜色,然后直接传到FS输出即可

Shader "Custom/VertexLevel"
{
    Properties
    {
        _Diffuse("Diffuse", Color) = (1,1,1,1)  // 得到并且控制材质的漫反射颜色
        // _Diffuse("Diffuse", Color) = (0, 0, 0, 0)  // 得到并且控制材质的漫反射颜色
        _Specular("Specular", Color) = (1, 1, 1, 1) // 材质的高光颜色
        _Gloss("Gloss", Range(8.0, 256)) = 20 // 高光范围
    }
        SubShader{
            Pass {
                Tags { "LightMode" = "ForwardBase" } // 该Pass的光照模式,只有定义了正确的 LightMode,才能得到一些 Unity 的内置光照变蜇

                CGPROGRAM

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

                fixed4 _Diffuse;  // 要加Properties 语义块中声明的属性
                fixed4 _Specular; // 颜色属性的范围在0~1,用fixed
                float _Gloss; // 范围很大,用float

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

                struct v2f {
                    float4  pos : SV_POSITION;
                    fixed3  color : COLOR; // 为了VS中计算得到的光照颜色传递给FS
                };

                v2f vert(a2v v) {
                    v2f o;
                    o.pos = UnityObjectToClipPos(v.vertex); // 顶点位置转到clip
                    fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject)); // unity_WorldToObject为模型到世界的逆矩阵,调换Mul中位置得到转置,法线是3维的,取前三行前三列

                    fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); // 光源方向由_WorldSpaceLightPos0得到
                    fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal)); // 注意是入射方向
                    fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz); // 世界空间中的摄像机位置 - 世界空间中的顶点位置

                    fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; // 内置变量得到环境光
                    fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir)); // saturate限制在 [0,1];点积时向量要出在同一坐标系下,这里选择世界坐标系,记得归一化
                    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"
}

phong模型

【理论】
漫反射:
在这里插入图片描述
  参数:入射光线的颜色和强度+材质的漫反射系数+表面法线+光源方向
  HalfLambert 半兰伯特漫反射:max(0, nl)改成了 nl*0.5 + 0.5, 背面也有明暗变化

高光反射:
unity shader学习---基础光照模型 Phong、Blinn-Phong_第7张图片
在这里插入图片描述
在这里插入图片描述
  参数:入射光线的颜色和强度+材质高光反射颜色+观察方向+反射方向+材质反光度(反光区域大小)

【源代码】各项同性,逐像素

Shader "Custom/Phong"
{
    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 worldNormal = normalize(i.worldNormal);
                    fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
                    fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
                    fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz); 

                    fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                    fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
                    fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);

                    return fixed4(ambient + diffuse + specular, 1.0);
                }


                ENDCG
             }
    }
        FallBack "Diffuse"
}

【具体解释】

Shader "Custom/C6_PixelLevel"
{
    Properties
    {
        // 得到并且控制材质的漫反射颜色
        _Diffuse("Diffuse", Color) = (1,1,1,1)
        // 材质的高光颜色
        _Specular("Specular", Color) = (1, 1, 1, 1) 
        // 高光范围
        _Gloss("Gloss", Range(8.0, 256)) = 20 
    }
        SubShader{
            Pass {
                // 有定义了正确的 LightMode,我们才能得到 Unity 的内置光照变量
                Tags { "LightMode" = "ForwardBase" }

                CGPROGRAM
                
                // 表明该函数包含了顶点着色器的代码,一般用vert
                #pragma vertex vert 
                // 该函数包含了片元着色器的代码, 一般用frag
                #pragma fragment frag
                // 使用 Unity 内置的一些变量
                #include "Lighting.cginc" 
                
                //在CG代码中, 定义一个与属性名称和类型都匹配的变量,也就是说Properties有啥这里也要有啥
                fixed4 _Diffuse;
                // 颜色属性的范围在0~1,用fixed
                fixed4 _Specular; 
                // 范围很大,用float
                float _Gloss; 

                //使用一个结构体来定义项点着色器的输入
                struct a2v {
                    // POSITION 语义告诉Unity,用模型空间的顶点坐标填充vertex变量
                    float4 vertex : POSITION;
                    // NORMAL语义告诉Unity,用模型空间的法线方向填充normal变量
                    float3 normal : NORMAL;
                };
                //使用一个结构体来定义项点着色器的输出
                struct v2f {
                    // SV_POSITION语义告诉Unity, pos里包含了顶点在裁剪空间中的位置信息
                    float4 pos : SV_POSITION;
                    // TEXCOORDO语义告诉Unity,用模型的第一套纹理坐标填充texcoord变量
                    float3 worldNormal : TEXCOORD0;
                    float3 worldPos : TEXCOORD1;
                };

  
                v2f vert(a2v v) {
                    // 声明输出结构
                    v2f o;

                    // 顶点位置从模型空间变换到裁剪空间
                    o.pos = UnityObjectToClipPos(v.vertex);
                    // 将表面法线从模型空间变换到世界空间
                    // 用顶点变换矩阵的逆转置矩阵:WorldToObject逆,放在mul右侧代表转置,因为法线是三维矢量,截取前三行前三列
                    o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
                    // 顶点位置从模型空间变换到世界空间,用于FS计算光照
                    o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;

                    return o;
                }

                fixed4 frag(v2f i) : SV_Target{ 
                    // SV_Target 也是 HLSL 中的一个系统语义,它等同于告诉渲染器,把用户的输出颜色存储到一个渲染目标(render target) 中,这里将输出到默认的帧缓存中
                    // 法线方向,世界坐标下
                    fixed3 worldNormal = normalize(i.worldNormal);
                    // 光源方向,世界坐标下
                    fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
                    // 反射方向,世界坐标下:reflect(i,n),注意入射方向
                    fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
                    // 观察方向:= 世界坐标下的相机位置-世界坐标下的物体位置
                    fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz); 

                    // 环境光:利用内置变量得到
                    fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                    // 漫反射:saturate(x):把x截取在[0,1]范围内
                    fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
                    // 高光
                    fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);

                    return fixed4(ambient + diffuse + specular, 1.0);
                }


                ENDCG
             }
    }
        FallBack "Diffuse"
}

Blinn-Phong模型

【理论】
unity shader学习---基础光照模型 Phong、Blinn-Phong_第8张图片
在这里插入图片描述在这里插入图片描述
这两种光照模型都是经验模型
【代码】

Shader "Custom/C6_Blinn-Phong"
{
    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; // 颜色属性的范围在0~1,用fixed
            float _Gloss; // 范围很大,用float

            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 worldNormal = normalize(i.worldNormal);
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
                // fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz); // 在世界坐标下计算
                fixed3 halfDir = normalize(worldLightDir + viewDir);

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
                // fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * (dot(worldNormal, worldLightDir)*0.5 + 0.5); // HalfLambert 半兰伯特漫反射,背面也有明暗变化
                // fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal, halfDir)), _Gloss);
                
                return fixed4(ambient + diffuse + specular, 1.0);
            }


            ENDCG
        }
    }
        FallBack "Diffuse"
}

效果

Phong和Blinn-Phong 对比:
unity shader学习---基础光照模型 Phong、Blinn-Phong_第9张图片
半兰伯特漫反射和Blinn-Phong对比:
unity shader学习---基础光照模型 Phong、Blinn-Phong_第10张图片
unity shader学习---基础光照模型 Phong、Blinn-Phong_第11张图片

你可能感兴趣的:(图形学,unity,光照模型,shader,Phong)