26.单张纹理结合BlinnPhong光照模型

26.纹理-单张纹理-纹理结合光照模型


26.1 知识点

利用单张纹理结合 Blinn-Phong 光照模型

注意事项

  • 在计算时,有以下的3点注意点
    1. 纹理颜色需要和漫反射颜色 进行乘法叠加, 它们两共同影响最终的颜色
    2. 兰伯特光照模型计算时,漫反射材质颜色使用 1 中的叠加颜色计算
    3. 最终使用的环境光叠加时,环境光变量UNITY_LIGHTMODEL_AMBIENT需要和 1 中颜色进行乘法叠加,为了避免最终的渲染效果偏灰
  • 其他的计算步骤同BlinnPhong的逐片元光照实现

创建 Shader 骨架

首先,创建一个 Shader 文件,声明顶点和片元函数的骨架:

Shader "Unlit/Lesson26_Single_Texture_BlinnPhong_Pixel"
{
    Properties
    {

    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"


            v2f vert(appdata_base appdata_base)
            {
    
            }

            fixed4 frag(v2f v2f) : SV_Target
            {

            }
            ENDCG
        }
    }
}

声明属性

接着声明所需的属性,包括纹理、漫反射颜色、高光反射颜色和光泽度:

Properties
{
    //主要就是将单张纹理Shader和布林方光照模型逐片元Shader进行一个结合
    _MainTex("MainTex", 2D) = ""{}
    //漫反射颜色
    _MainColor("MainColor", Color) = (1,1,1,1)
    //高光反射颜色
    _SpecularColor("SpecularColor", Color) = (1,1,1,1)
    //光泽度
    _SpecularNum("SpecularNum", Range(0,20)) = 15
}

应用向前渲染

为光照渲染设置 Tags

Tags
{
    "LightMode" = "ForwardBase"
}

引用内置文件

引用内置文件以使用内置结构体和函数:

#include "UnityCG.cginc"
#include "Lighting.cginc"

映射纹理贴图及相关数据

映射纹理贴图的成员:

//纹理贴图对应的映射成员

//映射对应纹理属性的图片颜色相关数据
sampler2D _MainTex;

//映射对应纹理属性的 缩放 平(偏)移数据
float4 _MainTex_ST; //xy代表缩放 zw代表平移

//漫反射颜色、高光反射颜色、光泽度
fixed4 _MainColor;
fixed4 _SpecularColor;
float _SpecularNum;

声明结构体

声明 v2f 结构体,用于传递顶点坐标、UV 坐标、法线和顶点世界空间位置:

struct v2f
{
    // 裁剪空间下的顶点坐标
    float4 pos:SV_POSITION;
    // UV 坐标
    float2 uv:TEXCOORD0;
    // 世界空间下的法线
    float3 wNormal:NORMAL;
    // 世界空间下的顶点坐标
    float3 wPos:TEXCOORD1;
};

顶点函数

在顶点函数中,将裁剪坐标、UV 坐标、世界法线和世界坐标传递到片元函数:

v2f vert(appdata_base appdata_base)
{
    v2f v2f;
    
    //把模型空间下的顶点转换到裁剪空间下
    v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
    
    //uv坐标计算
    //appdata_base.texcoord.xy //代表uv坐标
    //appdata_base.texcoord.zw //代表一些额外信息 例如深度值
    //先缩放 后平移 这个是一个固定的算法 规则如此
    //如果没有进行缩放和平移 那么 这个计算后 值是不会产生变化的
    //因为缩放默认值是1和1 ,平移默认值是0和0
    v2f.uv = appdata_base.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
    
    //世界空间下的法线
    v2f.wNormal = UnityObjectToWorldNormal(appdata_base.normal);
    
    //世界空间下的顶点坐标
    v2f.wPos = mul(unity_ObjectToWorld, appdata_base.vertex);
    
    return v2f;
}

片元函数

片元函数需要计算最终颜色。纹理颜色和漫反射材质颜色叠加,并结合 Blinn-Phong 光照模型公式:

    fixed4 frag(v2f v2f) : SV_Target
    {
        //布林方光照颜色 = 环境光颜色 + 兰伯特漫反射颜色 + 高光反射的颜色
        //兰伯特漫反射颜色 = 光的颜色 * 漫反射材质的颜色 * max(0, dot(世界坐标系下的法线, 光的方向))
        //高光反射的颜色 = 光的颜色 * 高光反射材质的颜色 * pow(max(0, dot(世界坐标系下的法线, 半角向量)), 光泽度)
        //半角向量 = 视角方向 + 光的方向
        
        //新知识点:纹理颜色需要和漫反射材质颜色叠加(乘法) 共同决定最终的颜色
        //最终的颜色
        fixed3 albedo = tex2D(_MainTex, v2f.uv).rgb * _MainColor.rgb;
        
        //光的方向(指向光源方向)
        float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
        
        //兰伯特漫反射颜色 = 光的颜色 * 漫反射材质的颜色 * max(0, dot(世界坐标系下的法线, 光的方向))
        //新知识点:兰伯特光照模型计算时,漫反射材质颜色使用叠加颜色albedo计算
        fixed3 lambertColor = _LightColor0.rgb * albedo.rgb * max(0, dot(v2f.wNormal, lightDir));

        // 视角方向
        //float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - v2f.wPos);
        float3 viewDir = normalize(UnityWorldSpaceViewDir(v2f.wPos));
        
        //半角向量 = 视角方向 + 光的方向
        float3 halfA = normalize(viewDir + lightDir);
        
        //高光反射的颜色 = 光的颜色 * 高光反射材质的颜色 * pow(max(0, dot(世界坐标系下的法线, 半角向量)), 光泽度)
        fixed3 specularColor = _LightColor0.rgb * _SpecularColor * pow(
            max(0, dot(v2f.wNormal, halfA)), _SpecularNum);

        //布林方光照颜色 = 环境光颜色 + 兰伯特漫反射颜色 + 高光反射的颜色
        //新知识点:最终使用的环境光叠加时,环境光变量UNITY_LIGHTMODEL_AMBIENT需要和 最终的颜色 进行乘法叠加
        // * albedo为了避免最终的渲染效果偏灰 否则会偏灰
        fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + lambertColor + specularColor;

        return fixed4(color.rgb, 1);
}

对比效果

应用该 Shader 到材质上,观察纹理结合 Blinn-Phong 光照后物体受到光照影响的效果。



26.2 知识点代码

Lesson26_纹理_单张纹理_纹理结合光照模型

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Lesson26_纹理_单张纹理_纹理结合光照模型 : MonoBehaviour
{
    void Start()
    {
        #region 知识点 单张纹理结合BlinnPhong光照模型
        //在计算时,有以下的3点注意点
        //1.纹理颜色需要和漫反射颜色 进行乘法叠加, 它们两共同影响最终的颜色
        //2.兰伯特光照模型计算时,漫反射材质颜色使用 1 中的叠加颜色计算
        //3.最终使用的环境光叠加时,环境光变量UNITY_LIGHTMODEL_AMBIENT需要和 1 中颜色进行乘法叠加
        //  为了避免最终的渲染效果偏灰

        //其他的计算步骤同BlinnPhong的逐片元光照实现
        #endregion
    }
}

Lesson26_Single_Texture_BlinnPhong_Pixel.shader

Shader "Unlit/Lesson26_Single_Texture_BlinnPhong_Pixel"
{
    Properties
    {
        //主要就是将单张纹理Shader和布林方光照模型逐片元Shader进行一个结合
        _MainTex("MainTex", 2D) = ""{}
        //漫反射颜色
        _MainColor("MainColor", Color) = (1,1,1,1)
        //高光反射颜色
        _SpecularColor("SpecularColor", Color) = (1,1,1,1)
        //光泽度
        _SpecularNum("SpecularNum", Range(0,20)) = 15
    }
    SubShader
    {
        Pass
        {
            Tags
            {
                "LightMode" = "ForwardBase"
            }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            //纹理贴图对应的映射成员
            
            //映射对应纹理属性的图片颜色相关数据
            sampler2D _MainTex;
            
            //映射对应纹理属性的 缩放 平(偏)移数据
            float4 _MainTex_ST; //xy代表缩放 zw代表平移
            
            //漫反射颜色、高光反射颜色、光泽度
            fixed4 _MainColor;
            fixed4 _SpecularColor;
            float _SpecularNum;

            struct v2f
            {
                //裁剪空间下的顶点坐标
                float4 pos:SV_POSITION;
                //UV坐标
                float2 uv:TEXCOORD0;
                //世界空间下的法线
                float3 wNormal:NORMAL;
                //世界空间下的顶点坐标
                float3 wPos:TEXCOORD1;
            };

            v2f vert(appdata_base appdata_base)
            {
                v2f v2f;
                
                //把模型空间下的顶点转换到裁剪空间下
                v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
                
                //uv坐标计算
                //appdata_base.texcoord.xy //代表uv坐标
                //appdata_base.texcoord.zw //代表一些额外信息 例如深度值
                //先缩放 后平移 这个是一个固定的算法 规则如此
                //如果没有进行缩放和平移 那么 这个计算后 值是不会产生变化的
                //因为缩放默认值是1和1 ,平移默认值是0和0
                v2f.uv = appdata_base.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
                
                //世界空间下的法线
                v2f.wNormal = UnityObjectToWorldNormal(appdata_base.normal);
                
                //世界空间下的顶点坐标
                v2f.wPos = mul(unity_ObjectToWorld, appdata_base.vertex);
                
                return v2f;
            }

            fixed4 frag(v2f v2f) : SV_Target
            {
                //布林方光照颜色 = 环境光颜色 + 兰伯特漫反射颜色 + 高光反射的颜色
                //兰伯特漫反射颜色 = 光的颜色 * 漫反射材质的颜色 * max(0, dot(世界坐标系下的法线, 光的方向))
                //高光反射的颜色 = 光的颜色 * 高光反射材质的颜色 * pow(max(0, dot(世界坐标系下的法线, 半角向量)), 光泽度)
                //半角向量 = 视角方向 + 光的方向
                
                //新知识点:纹理颜色需要和漫反射材质颜色叠加(乘法) 共同决定最终的颜色
                //最终的颜色
                fixed3 albedo = tex2D(_MainTex, v2f.uv).rgb * _MainColor.rgb;
                
                //光的方向(指向光源方向)
                float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                
                //兰伯特漫反射颜色 = 光的颜色 * 漫反射材质的颜色 * max(0, dot(世界坐标系下的法线, 光的方向))
                //新知识点:兰伯特光照模型计算时,漫反射材质颜色使用叠加颜色albedo计算
                fixed3 lambertColor = _LightColor0.rgb * albedo.rgb * max(0, dot(v2f.wNormal, lightDir));

                // 视角方向
                //float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - v2f.wPos);
                float3 viewDir = normalize(UnityWorldSpaceViewDir(v2f.wPos));
                
                //半角向量 = 视角方向 + 光的方向
                float3 halfA = normalize(viewDir + lightDir);
                
                //高光反射的颜色 = 光的颜色 * 高光反射材质的颜色 * pow(max(0, dot(世界坐标系下的法线, 半角向量)), 光泽度)
                fixed3 specularColor = _LightColor0.rgb * _SpecularColor * pow(
                    max(0, dot(v2f.wNormal, halfA)), _SpecularNum);

                //布林方光照颜色 = 环境光颜色 + 兰伯特漫反射颜色 + 高光反射的颜色
                //新知识点:最终使用的环境光叠加时,环境光变量UNITY_LIGHTMODEL_AMBIENT需要和 最终的颜色 进行乘法叠加
                // * albedo为了避免最终的渲染效果偏灰 否则会偏灰
                fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + lambertColor + specularColor;

                return fixed4(color.rgb, 1);
            }
            ENDCG
        }
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏