"Introducción a Unity Shader Essentials" Capítulo 6 Iluminación básica en Unity

Capítulo 6 Iluminación básica en Unity

6.1 Cómo vemos el mundo

En términos generales, necesitamos simular el entorno de iluminación real para generar una imagen y debemos considerar tres fenómenos físicos:

  • En primer lugar, los rayos de luz se emiten 光源(light source)desde
  • Luego, el rayo se cruza con algunos objetos en la escena, algunos rayos se absorben y otros se dispersan en otras direcciones.
  • Finalmente, la cámara absorbe algo de luz, produciendo una imagen

fuente de luz

En óptica, utilizamos 辐照度(irradiance)para cuantificar la luz.Para la luz paralela, su radiancia se puede obtener calculando la energía que pasa por unidad de tiempo en una unidad de área perpendicular a la dirección de la luz.
inserte la descripción de la imagen aquí
Dado que la irradiancia es inversamente proporcional a la distancia d/cos entre los rayos que inciden en la superficie, es directamente proporcional a cos, que se puede obtener usando el producto escalar de la dirección de la fuente de luz y la superficie normal n.

absorción y dispersión

Después de que la fuente de luz emita la luz, se cruzará con algunos objetos, y hay dos resultados de la intersección: 散射(scattering)y 吸收(absorption). La dispersión solo cambia la dirección de la luz, no la densidad y el color de la luz, mientras que la absorción solo cambia la densidad y el color de la luz, pero no la dirección de la luz.
Después de que la luz se dispersa en la superficie del objeto, hay dos direcciones: una se dispersará hacia el interior del objeto, este fenómeno se llama o ; 折射(refraction)la 透射(transmission)otra se dispersará hacia el exterior, este fenómeno se llama 反射(reflection). Para los objetos opacos, los rayos de luz refractados en el interior del objeto continúan intersecándose con las partículas del interior, algunas de las cuales finalmente se vuelven a emitir fuera de la superficie del objeto, mientras que otras son absorbidas por el objeto. Esos rayos reemitidos desde la superficie del objeto tendrán una distribución de dirección y color diferente a los rayos incidentes.
inserte la descripción de la imagen aquí
Para distinguir entre estas dos direcciones de dispersión diferentes, usamos diferentes partes en el modelo de iluminación para calcularlas: la 高光反射(specular)parte representa cómo la superficie del objeto refleja la luz y 漫反射(diffuse)la parte representa cuánta luz será refractada, absorbida y dispersada. de la superficie Según el número y la dirección de los rayos incidentes, solemos utilizarlo 出射度(exitance)para describirlo. Existe una relación lineal entre la irradiación y la emisión, y la relación entre ellos es la reflexión difusa y las propiedades de reflexión especular del material.

colorante

着色(shading)Se refiere al proceso de usar una ecuación para calcular el grado de salida a lo largo de una determinada dirección de visualización de acuerdo con las propiedades del material y la información de la fuente de luz. Generalmente llamamos a esta ecuación 光照模型(Lighting Model).

BRDF

BRDF (Función de distribución de reflectancia bidireccional) se refiere a una función de cuatro variables reales que define cómo se refleja la luz en superficies opacas. Cuando se dan la dirección y la irradiación de la luz incidente, BRDF puede dar la distribución de la energía de la luz en una determinada dirección de salida. Todos los BRDF involucrados en este capítulo son modelos idealizados y simplificados de escenas reales, es decir, no pueden reflejar verdaderamente la interacción entre los objetos y la luz.Estos modelos de iluminación se denominan modelos empíricos. Sin embargo, estos modelos empíricos se han utilizado en la representación en tiempo real durante muchos años. A veces esperamos simular la interacción entre la luz y los objetos de manera más realista, y esto sucede 基于物理的 BRDF 模型, y veremos estos modelos de iluminación más complejos más adelante.

6.2 Modelo de iluminación estándar

Aunque hay muchos tipos de modelos de iluminación, solo se usaba un modelo de iluminación en los primeros motores de juegos, es decir, el modelo de iluminación estándar. Su método básico es dividir la luz que ingresa a la cámara en 4 partes, y cada parte utiliza un método para calcular su contribución:

  • 自发光(emissive), que describe la cantidad de radiación que emite una superficie en esa dirección cuando se le da una dirección. Sin iluminación global, estas superficies autoiluminadas en realidad no iluminan los objetos circundantes, simplemente se hacen parecer más brillantes.
  • 高光反射(specular), que se utiliza para describir la cantidad de radiación dispersada por la superficie en la dirección de la reflexión especular perfecta cuando la luz incide en la superficie del modelo desde la fuente de luz.
  • 漫反射(diffuse), que se utiliza para describir cuánta radiación se dispersa en cada dirección por la superficie del modelo cuando la luz de la fuente de luz incide sobre él.
  • 环境光(ambient), esta sección se utiliza para describir el resto de la iluminación indirecta.

luz ambiental

Mientras que el modelo de iluminación estándar se centra en describir la iluminación directa, en el mundo real los objetos también pueden iluminarse 间接光照con (luz indirecta). La iluminación indirecta se refiere a la luz que rebota entre múltiples objetos y finalmente ingresa a la cámara.
En el modelo de iluminación estándar, usamos algo llamado luz ambiental para aproximarnos a la iluminación indirecta. El cálculo de la luz ambiental es muy sencillo, suele ser una variable global, es decir, todos los objetos de la escena utilizan esta luz ambiental:
inserte la descripción de la imagen aquí

Auto-luminoso

La luz también se puede emitir directamente desde la fuente de luz hacia la cámara. El modelo de iluminación estándar utiliza la autoiluminación para calcular la contribución de esta parte, y su cálculo también es muy simple, es decir, se utiliza directamente el color de autoiluminación del material: en el renderizado normal en tiempo real, la
inserte la descripción de la imagen aquí
autoiluminación superficie no ilumina las superficies circundantes, es decir, el objeto no será tratado como una fuente de luz. Presentado por Unity 5 全局光照系统, es posible simular el impacto de dichos objetos que se iluminan solos en los objetos circundantes.

reflexión difusa

La iluminación difusa se utiliza para modelar la radiación que se dispersa aleatoriamente en todas las direcciones por la superficie del objeto.En la reflexión difusa, la posición del ángulo de visión no es importante, porque la reflexión es completamente aleatoria, por lo que se puede considerar que en cualquier reflexión La distribución en todas las direcciones es la misma.
Sin embargo, el ángulo del rayo entrante es importante, y la iluminación difusa sigue 兰伯特定律(Lambert's law): la intensidad del rayo reflejado es proporcional al coseno del ángulo entre la superficie normal y la dirección de la fuente de luz.
inserte la descripción de la imagen aquí
donde n^ es la superficie normal, l^ es el vector unitario que apunta a la fuente de luz, Mdiffuse es el color difuso del material y Clight es el color de la fuente de luz. Cabe señalar que necesitamos evitar que el resultado del producto puntual de la normal y la dirección de la fuente de luz sea un valor negativo, para ello utilizamos la función de tomar el valor máximo para interceptarlo a 0, lo cual puede evitar que el objeto sea iluminado por la fuente de luz desde atrás.

reflexión especular

La reflexión especular aquí es un modelo empírico, es decir, no corresponde exactamente al fenómeno especular en el mundo real. Para calcular la reflexión especular, necesita conocer mucha información: superficie normal n, dirección del ángulo de visión v, dirección de la fuente de luz l y dirección de reflexión r.
inserte la descripción de la imagen aquí
Entre estos cuatro vectores, en realidad solo necesitamos conocer 3 de ellos, y el cuarto vector, la dirección del reflejo, se puede calcular a partir de otra información: De esta manera, podemos usarlo
inserte la descripción de la imagen aquí
para Phong 模型calcular la parte del reflejo resaltado:
inserte la descripción de la imagen aquí
donde , Mgloss es un material 光泽度(gloss), también conocido como brillo. Controla qué tan ancho es el "punto brillante" en el área resaltada, cuanto más grande es Mgloss, más pequeño es el resaltado. Mespecular es el color especular del material, se utiliza para controlar la fuerza y ​​el color del material para la reflexión especular. Clight es el color y la intensidad de la fuente de luz. Del mismo modo, también es necesario evitar que el resultado de v * r sea negativo.
En comparación con el modelo de Phong anterior, Blinn propuso un método de modificación simple para obtener efectos similares. Su idea básica es evitar calcular la dirección de reflexión. Para ello, Blinn 模型se introduce un nuevo vector h, que se obtiene promediando vyl y luego normalizando.
inserte la descripción de la imagen aquí
Luego, use el ángulo entre n y h en lugar del ángulo entre v y r:
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
en la implementación de hardware, el modelo de Blinn será más rápido que el modelo de Phong si la cámara y la fuente de luz están lo suficientemente lejos del modelo, lo cual es porque, en este momento, tanto v como l se pueden considerar como valores fijos, por lo que h será una constante. Sin embargo, cuando v o l no son constantes, el modelo de Phong puede ser más rápido.
Se requiere que estos dos modelos de iluminación sean modelos empíricos y no sean completamente consistentes con el fenómeno de reflexión especular en el mundo real.

por vértice o por píxel

Por lo general, tenemos dos opciones al implementar el modelo de iluminación anterior:

  • Calculado en el sombreador de fragmentos, también conocido como 逐像素光照(per-pixel lighting). En la iluminación píxel por píxel, obtendremos su normal en función de cada píxel y luego calcularemos el modelo de iluminación. Esta técnica de interpolación de vértices normales entre parches se denomina Phong 着色(Phong shading), también conocida como interpolación de Phong o técnica de sombreado de interpolación normal.
  • Calculado en el sombreador de vértices, llamado 逐顶点光照(per-vertex lighting), también llamado 高洛德着色(Gouraud shading). En la iluminación por vértice, calculamos la iluminación en cada vértice, luego realizamos una interpolación lineal dentro de la representación primitiva y, finalmente, generamos el color del píxel.
  • Dado que el número de vértices suele ser mucho menor que el número de píxeles, la cantidad de cálculo de la iluminación por vértice suele ser menor que la de la iluminación por píxel. Sin embargo, dado que la iluminación por vértice se basa en la interpolación lineal para obtener la iluminación de píxeles, la iluminación por vértice puede causar problemas cuando hay cálculos no lineales en el modelo de iluminación (como cuando se calculan reflejos especulares).

6.3 Luz ambiental y autoiluminación en Unity

La luz ambiental en la escena de Unity se puede configurar window -> Rending -> Ligthingen
inserte la descripción de la imagen aquí
la configuración de luz específica que se puede consultar directamente en el manual oficial de Unity.
Además, es muy simple lograr la autoiluminación en Unity. Solo necesitamos configurar el color de autoiluminación de el material antes de que el sombreador de fragmentos emita el color final. Agréguelo al color de salida.

6.4 Realice el modelo de iluminación de reflexión difusa en Unity Shader

iluminación por vértice

Shader "Chapter 6/Diffuse Vertex Level"
{
    
    
    Properties
    {
    
    
        // 定义漫反射颜色,默认为白色
        _diffuse ("Diffuse", Color) = (1, 1, 1, 1)
    }

    SubShader
    {
    
    
        Pass
        {
    
    
            // 只有设置正确的光照模式,才能得到 Unity 的内置光照变量,比如 _LightColor0
            Tags {
    
     "LightMode"="ForwardBase" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            // 为了使用 Unity 内置的一些变量(比如 _LightColor0),必须包含相应 Unity 内置文件
            #include "UnityLightingCommon.cginc"

            fixed4 _diffuse;

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

            struct v2f
            {
    
    
                float4 vertex : SV_POSITION;
                fixed3 color : COLOR;
            };

            v2f vert (a2v v)
            {
    
    
                v2f o;
                // 坐标转换
                o.vertex = UnityObjectToClipPos(v.vertex);
                
                // 获取环境光
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                // 转换法线向量至世界坐标系
                fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
                // 场景中只有一个光源且是平行光时,光源方向可以由 _WorldSpaceLightPos0 得到,当有多个光源时无法这样使用
                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                // saturate 函数用于将结果截取至 [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"
}

Iluminación por píxel

Shader "Chapter 6/Diffuse Frag Level"
{
    
    
    Properties
    {
    
    
        // 定义漫反射颜色,默认为白色
        _diffuse ("Diffuse", Color) = (1, 1, 1, 1)
    }

    SubShader
    {
    
    
        Pass
        {
    
    
            // 只有设置正确的光照模式,才能得到 Unity 的内置光照变量,比如 _LightColor0
            Tags {
    
     "LightMode"="ForwardBase" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            // 为了使用 Unity 内置的一些变量(比如 _LightColor0),必须包含相应 Unity 内置文件
            #include "UnityLightingCommon.cginc"

            fixed4 _diffuse;

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

            struct v2f
            {
    
    
                float4 vertex : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
            };

            v2f vert (a2v v)
            {
    
    
                // 顶点着色器只负责进行坐标转换
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
    
    
                // 颜色计算移动至片元着色器中进行
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                fixed3 diffuse = _LightColor0.rgb * _diffuse.rgb * saturate(dot(i.worldNormal, worldLight));
                fixed3 color = ambient + diffuse;
                return fixed4(color, 1.0);
            }
            ENDCG
        }
    }
    Fallback "Diffuse"
}

Modelo Medio Lambert

Ya sea que use vértice por vértice o píxel por píxel, habrá un problema: en el área donde la luz no puede llegar, la apariencia del modelo suele ser completamente negra sin cambios claros y oscuros, lo que hará que el El área retroiluminada del modelo parece un avión, los detalles del modelo se pierden. Por ello se ha propuesto una técnica de mejora que es 半兰伯特(Half Lambert)el modelo de iluminación.
inserte la descripción de la imagen aquí
Se puede ver que, en comparación con el modelo Lambert original, el modelo de iluminación semi-Lambert no utiliza la operación máxima para evitar que el producto escalar de la suma sea negativo, sino que escala el resultado por un factor de uno y agrega un tamaño de compensar. En la mayoría de los casos, el valor de la suma es 0.5, es decir, la fórmula es:
inserte la descripción de la imagen aquí
De esta manera, podemos mapear el rango de resultados de n*l de [−1, 1] a [0, 1]. Es decir, para la superficie retroiluminada del modelo, el resultado del producto escalar en el modelo de iluminación Lambert original se mapeará al mismo valor, es decir, el valor de 0; mientras que en el modelo semi-Lambert, la superficie retroiluminada también puede tener cambios claros y oscuros, diferentes Los resultados del producto escalar se asignarán a diferentes valores. Cabe señalar que semi-Lambert no tiene base física, es solo una técnica de mejora visual.
Aquí hay una implementación vértice por vértice del modelo semi-Lambert:

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'

Shader "Chapter 6/Diffuse Half Lambert"
{
    
    
    Properties
    {
    
    
        _diffuse ("Diffuse", Color) = (1, 1, 1, 1)
    }

    SubShader
    {
    
    
        Pass
        {
    
    
            Tags {
    
     "LightMode"="ForwardBase" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "UnityLightingCommon.cginc"

            fixed4 _diffuse;

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

            struct v2f
            {
    
    
                float4 vertex : SV_POSITION;
                fixed3 color : COLOR;
            };

            v2f vert (a2v v)
            {
    
    
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                // 半兰伯特模型算法
                fixed3 halfLambert = dot(worldNormal, worldLight) * 0.5 + 0.5;
                fixed3 diffuse = _LightColor0.rgb * _diffuse.rgb * halfLambert;
                o.color = ambient + diffuse;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
    
    
                return fixed4(i.color, 1.0);
            }
            ENDCG
        }
    }
    Fallback "Diffuse"
}

Aquí hay una comparación de la iluminación difusa por vértice, la iluminación difusa por píxel y la iluminación Half-Lambert:
inserte la descripción de la imagen aquí

6.5 Unity Shader implementa un modelo de iluminación de reflexión especular

iluminación por vértice

Shader "Chapter 6/Specular Vertex Level"
{
    
    
    Properties
    {
    
    
        // 定义漫反射颜色,默认为白色
        _Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
        // 高光反射颜色
        _Specular ("Specular", Color) = (1, 1, 1, 1)
        // 用于控制高光区域大小
        _Gloss ("Gloss", Range(8.0, 256)) = 20
    }

    SubShader
    {
    
    
        Pass
        {
    
    
            // 只有设置正确的光照模式,才能得到 Unity 的内置光照变量,比如 _LightColor0
            Tags {
    
     "LightMode"="ForwardBase" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            // 为了使用 Unity 内置的一些变量(比如 _LightColor0),必须包含相应 Unity 内置文件
            #include "UnityLightingCommon.cginc"

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

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

            struct v2f
            {
    
    
                float4 vertex : SV_POSITION;
                fixed3 color : COLOR;
            };

            v2f vert (a2v v)
            {
    
    
                v2f o;
                // 坐标转换
                o.vertex = UnityObjectToClipPos(v.vertex);
                
                // 环境光
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                /*漫反射*/
                fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight));

                /*高光反射*/
                // 函数 reflect 可直接根据入射光角度与法线向量计算出反射方向
                fixed3 reflectDir = normalize(reflect(-worldLight, worldNormal));
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz);
                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"
}

Iluminación por píxel

Shader "Chapter 6/Specular Frag Level"
{
    
    
    Properties
    {
    
    
        // 定义漫反射颜色,默认为白色
        _Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
        // 高光反射颜色
        _Specular ("Specular", Color) = (1, 1, 1, 1)
        // 用于控制高光区域大小
        _Gloss ("Gloss", Range(8.0, 256)) = 20
    }

    SubShader
    {
    
    
        Pass
        {
    
    
            // 只有设置正确的光照模式,才能得到 Unity 的内置光照变量,比如 _LightColor0
            Tags {
    
     "LightMode"="ForwardBase" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            // 为了使用 Unity 内置的一些变量(比如 _LightColor0),必须包含相应 Unity 内置文件
            #include "UnityLightingCommon.cginc"

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

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

            struct v2f
            {
    
    
                float4 vertex : SV_POSITION;
                fixed3 worldNormal : TEXCOORD0;
                fixed3 worldPos : TEXCOORD1;
            };

            v2f vert (a2v v)
            {
    
    
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                return o;
            }

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

                /*漫反射*/
                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(i.worldNormal, worldLight));

                /*高光反射*/
                // 函数 reflect 可直接根据入射光角度与法线向量计算出反射方向
                fixed3 reflectDir = normalize(reflect(-worldLight, i.worldNormal));
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);

                // 叠加环境光、漫反射、高光反射,作为返回结果
                fixed3 color = ambient + diffuse + specular;
                return fixed4(color, 1.0);
            }
            ENDCG
        }
    }
    Fallback "Specular"
}

Realización píxel por píxel del modelo de iluminación Blinn-Phong

Shader "Chapter 6/Specular 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
        {
    
    
            // 只有设置正确的光照模式,才能得到 Unity 的内置光照变量,比如 _LightColor0
            Tags {
    
     "LightMode"="ForwardBase" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            // 为了使用 Unity 内置的一些变量(比如 _LightColor0),必须包含相应 Unity 内置文件
            #include "UnityLightingCommon.cginc"

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

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

            struct v2f
            {
    
    
                float4 vertex : SV_POSITION;
                fixed3 worldNormal : TEXCOORD0;
                fixed3 worldPos : TEXCOORD1;
            };

            v2f vert (a2v v)
            {
    
    
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                return o;
            }

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

                /*漫反射*/
                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(i.worldNormal, worldLight));

                /*高光反射*/
                // 函数 reflect 可直接根据入射光角度与法线向量计算出反射方向
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
                // Blinn-Phong 模型算法
                fixed3 halfDir = normalize(worldLight + viewDir);
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(halfDir, i.worldNormal)), _Gloss);

                // 叠加环境光、漫反射、高光反射,作为返回结果
                fixed3 color = ambient + diffuse + specular;
                return fixed4(color, 1.0);
            }
            ENDCG
        }
    }
    Fallback "Specular"
}

La siguiente es una comparación de las tres implementaciones anteriores (de izquierda a derecha):
inserte la descripción de la imagen aquí

  • El procesamiento de la iluminación por píxel da como resultado reflejos más suaves que por vértice
  • La parte especular del modelo de iluminación Blinn-Phong parece más grande y brillante. En el renderizado real, elegiremos el modelo de iluminación Blinn-Phong en la mayoría de los casos.

6.6 Uso de las funciones integradas de Unity

Consulte el código fuente de la función UnityCG.cgincpara obtener más información, que se omite aquí.

Supongo que te gusta

Origin blog.csdn.net/m0_37979033/article/details/128739661
Recomendado
Clasificación