Una entrada de blog Unity Shader

Una fundación

// 这里指定 Shader 的名字,不要求跟文件名保持一致
Shader "Tint/First"{ 
    // 属性
    Properties{
        _Color("Color",Color)= (1,1,1,1)  // 颜色
        _Vertor("Vector",Vector)=(1,2,3,4) //一维向量
        _Int("Int",Int) = 1 // 整数
        _Float("Float",Float) = 2.3 //浮点数
        _Range("Range",Range(1,10))= 1 // 范围
        _MainTex("Main Tex",2D) = "white"{} // 图片纹理
        _Cube("Cube",Cube)= "white"{} // 天空盒 
        _3D("3D Tex",3D) ="white"{} // 3D 纹理
    }
    //SubShader 可以写很多个 显卡运行效果的时候,从第一个SubShader开始,如果第一个 SubShader里面的效果可以实现,
    //那么就使用第一个 SubShader ,如果显卡这个 SubShader 里面的某些效果它实现不理,它会自动去实现第二个SubShader
    //如果所有的 SubShader 都无法运行,那么将运行  Fallback "" 
    SubShader{
        // 至少有一个Pass
        Pass{
            // 使用 CG 语言编写 Shader 代码
            CGPROGRAM
            // 顶点函数,这里只是声明了顶点函数的函数名
            // 基本作用是 完成顶点坐标从模型空间到剪裁空间的转换(从游戏环境转换到视野相机屏幕上)
            #pragma vertex vert
            // 片元函数,这里只是声明了片元函数的片元名
            // 基本作用是返回模型对应的屏幕的每一个像素的颜色值
            #pragma fragment frag

            // 从应用程序传递到 顶点函数的所有语义
            struct a2v{
                // 告诉Unity把模型空间下的顶点坐标填充给 vertex
                float4 vertex : POSITION; 

                // 告诉Unity把模型空间下的法线方向填充给 normal
                float4 normal : NORMAL;

                // 告诉Unity把模型空间下的切线方向填充给 tangent  (TANGENT 0~n)
                float4 tangent : TANGENT;

                // 告诉Unity把第一套纹理坐标填充给 texcoord   (TEXCOORD 0~n)
                float4 texcoord : TEXCOORD0;

                // 告诉Unity把模型空间下的顶点颜色填充给 color (COLOR 0~n)
                fixed3 color : COLOR0;
                
            };

            // 从顶点函数 传递给 片元函数的所有语义
            struct v2f{
                // 剪裁空间中的顶点坐标(一般是系统直接使用)
                float4 position:SV_POSITION;

                // 不一定传递颜色,可以传递一组4个的值
                fixed3 color :COLOR0;

                // 不一定传递颜色,可以传递一组4个的值 (TEXCOORD 0~7)
                float4 texcoord:TEXCOORD0;
            };

            // 通过语义告诉系统,我这个参数是干嘛的,比如 POSITION 是告诉系统我需要顶点坐标
            // SV_POSITION这个语义用来解释说明返回值,意思是返回值是剪裁空间下的顶点坐标
            // v2f vert(a2v v : POSITION):SV_POSITION{}
            v2f vert(a2v v){
                v2f f;
                f.position = UnityObjectToClipPos(v.vertex);
                // 这里场景中可以建一个正方体,它的模型空间下 法线(1,0,0)为红色 法线(0,1,0)为绿色 法线(0,0,1)为蓝色 
                // 其他法线方向是负数所以为黑色 过渡系统会自动进行插值运算
                f.color = v.normal;
                return f;
            }
            // 片元函数传递给 系统的所有语义
            //SV_TARGET 这个语义用来解释说明返回值,意思是返回值是模型对应的屏幕的每一个像素的颜色值
            fixed4 frag(v2f f):SV_TARGET{
                // 模型显示法线颜色
                return fixed4(f.color,1.0);
            }
            ENDCG
        }
    }
    Fallback "VertexLit"
}

Dos luces

Difuso a la izquierda es Unity2018.4, a la derecha es Unity2020.1.5 Urp Después de agregar LightMode = ForwardBase, ¿por qué no hay efecto en Urp? ¿Alguien sabe que puede comentarlo? Gracias

Funciones comunes de UnityCG.cginc:

 /* 
 UnityCG.cginc 中一些常用的函数
 //摄像机方向(视角方向)
 float3 WorldSpaceViewDir(float4 v) 根据模型空间中的顶点坐标 得到(世界空间)从这个点到摄像机的观察方向
 float3 UnityWorldSpaceViewDir(float4 v) 世界空间中的顶点坐标 ==> 世界空间从这个点到摄像机的观察方向
 float3 ObjSpaceViewDir(float4 v) 模型空间中的顶点坐标 ==> 模型空间从这个点到摄像机的观察
 //光源方向
 float3 WorldSpaceLightDir(float4 v) 模型空间中的顶点坐标 ==> 世界空间中从这个点到光源的方向
 float3 UnityWorldSpaceLightDir(float4 v) 世界空间中的顶点坐标 ==> 世界空间中从这个点到光源的方向
 float3 ObjSpaceLightDir(float4 v) 模型空间中的顶点坐标 ==> 模型空间中从这个点到光源的
 // 方向转换
 float3 UnityObjectToWorldNormal(float3 norm) 把法线方向从模型空间==> 世界空间
 float3 UnityObjectToWorldDir(float3 dir) 把方向从模型空间 ==> 世界空间
 float3 UnityWorldToObjectDir(float3 dir) 把方向从世界空间 ==> 模型空间
 */

 

1. Reflexión difusa

Fórmula:   Difusa = color de luz directa * max (0, cos ángulo incluido (ángulo entre luz y normal)) cos (θ) = dirección de la luz · dirección normal

 

// 普通漫反射
fixed3 diffuse = _LightColor0.rgb * dot(worldNormalDir,worldLightDir) * _Color.rgb;

 

Si el ángulo es superior a 90 grados, se considera negro.

2. Modelo de iluminación difusa semi-Lambert 

Fórmula:  Difusa = color de luz directa * máx. (0,0.5 * cos ángulo incluido (ángulo entre luz y normal) + 0.5) cos (θ) = dirección de luz · dirección normal

     // 半兰伯特光照模型 漫反射
     fixed3  halfLambert = 0.5* dot(worldNormalDir,worldLightDir) + 0.5;
     fixed3 diffuse = _LightColor0.rgb * halfLambert * _Color.rgb;

 

Gradiente de 0-180 grados, solo el ángulo de 180 grados con la dirección de la luz será negro y también se desvanecerá gradualmente.

 

 float halfLambert = dot(worldNormalDir,worldLightDir)*0.5 + 0.5;

¿Cómo surgió esta fórmula? Por favor, vea las siguientes 3 imágenes

 

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

/*   什么是光照模型 
光照模型就是一个公式,使用这个公式来计算某个点的光照效果
标准光照模型
在标准光照模型里面,我们把进入摄像机的光分为下面四个部分:
1.自发光
2.高光反射 Specular
3.环境光

4.漫反射 Diffuse = 直射光颜色 * max(0,cos夹角(光和法线的夹角))
当光和法线方向为单位向量的时候,可得公式: cos(θ) = 光方向·法线方向

一些公式 a · b = |a| × |b| × cos(θ)
其中:
|a| 是 矢量 a 的量值
|b| 是 矢量 b 的量值
θ 是 a 和 b 之间的 角度

点积的值:
u的大小、v的大小、u,v夹角的余弦。在u,v非零的前提下,点积如果为负,则u,v形成的角大于90度;如果为零,那么u,v垂直;如果为正,那么u,v形成的角为锐角。
两个单位向量的点积得到两个向量的夹角的cos值,通过它可以知道两个向量的相似性,利用点积可判断一个多边形是面向摄像机还是背向摄像机。
向量的点积与它们夹角的余弦成正比,因此在聚光灯的效果计算中,可以根据点积来得到光照效果,如果点积越大,说明夹角越小,则物体离光照的轴线越近,光照越强。

*/

Shader "Unlit/sLight"
{
    Properties
    {
        _Diffuse("Diffuse Color",Color)=(1,1,1,1)
    }
    SubShader
    {
        Pass
        {
            // 只有定义了正确的 LightMode, 才能得到一些 Untiy的内置光照变量
            // 添加这个 LightMode 会在 Unity2020.1.5 Urp项目中无法显现效果
            // Tags{"LightMode"="ForwardBase"}

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // 包含 Unity的内置文件,才可以使用 unity内置的一些变量
            #include "Lighting.cginc"
            #include "UnityCG.cginc"
            
            fixed4 _Diffuse;


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

            struct v2f
            {
                float4 vertex : SV_POSITION;
                fixed3 color : COLOR0;
                fixed3 worldNormalDirFrag:TEXCOORD0;
            };

            v2f vert (a2v v)
            {
                v2f o;
                // 顶点转到 剪裁空间
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.worldNormalDirFrag = normalize(UnityObjectToWorldNormal(v.normal));
                return o;
                /*   顶点运算漫反射     

                // 世界空间法线方向计算第一种方式 需引入  #include "UnityCG.cginc"
                float3 worldNormalDir = normalize(UnityObjectToWorldNormal(v.normal));
                //世界空间法线方向计算第二种方式
                //float3 worldNormalDir = normalize(mul(v.normal,(float3x3)unity_WorldToObject));
                
                // 世界空间光的方向 对于每个顶点来说,光的位置就是光的方向,因为光是平行光
                float3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
                
                // 环境光
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;

                // 漫反射 Diffuse = 直射光颜色 * max(0,cos夹角(光和法线的夹角))  cos(θ) = 光方向·法线方向
                fixed3 diffuse= _LightColor0.rgb * max(0,dot(worldNormalDir,worldLightDir))*_Diffuse.rgb;

                // 为啥 _Diffuse.rgb用乘, ambient用加法呢?
                // 因为 环境光是 4种光之一,漫反射光+环境光会增强,而 _Diffuse.rgb 只是漫反射的颜色,它属于漫反射的一部分
                o.color = diffuse + ambient; 
                return o;*/
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // 世界空间法线方向计算第一种方式 需引入  #include "UnityCG.cginc"
                float3 worldNormalDir = normalize(i.worldNormalDirFrag);

                // 世界空间光的方向 对于每个顶点来说,光的位置就是光的方向,因为光是平行光
                float3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
                
                // 环境光
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;

                // // 普通漫反射 Diffuse = 直射光颜色 * max(0,cos夹角(光和法线的夹角))  cos(θ) = 光方向·法线方向
                // fixed3 diffuse= _LightColor0.rgb * max(0,dot(worldNormalDir,worldLightDir))*_Diffuse.rgb;

                // 半兰伯特光照模型 max() 函数不需要了 因为这个时候它的值已经不会小于0了
                float halfLambert = dot(worldNormalDir,worldLightDir)*0.5 + 0.5;
                fixed3 diffuse= _LightColor0.rgb * halfLambert *_Diffuse.rgb;

                fixed3 col = diffuse + ambient;
                return fixed4(col,1);
            }
            ENDCG
        }
    }
    Fallback "Diffuse"
}

3. Alto reflejo de luz

Fórmula:  Especular = luz directa * pow (max (cosx, 0), 10) * color de luz reflejada cosx = punto (dirección de la luz reflejada, dirección de la vista)

 

  // 反射光方向 = reflect(入射光方向 , 法线方向) 
  //那么问题来了 ,下面这个worldLightDir为啥要加负号呢?
  // 因为worldLightDir 是点到顶光坐标的方向,而我们需要的事 光到顶点的入射光方向
  fixed3 reflectDir = normalize(reflect(-worldLightDir,worldNormalDir));
  // 高光
  fixed3 specular =  _LightColor0.rgb * pow(max(0,dot(reflectDir,worldViewDir)),_Gloss )* _SpeularColor.rgb;

 

No se puede dibujar la fórmula completa de este sitio web. Es vergonzoso. Déjame dictarlo. 

1 imagen, max (cos (x), 0) no tendrá un valor menor que 0,

2, 3 gráfico pow (cos (x), 10) Cuando el valor de 10 es mayor, la línea es más pronunciada y el área resaltada también es más pequeña. Solo mire x <(3.14 / 2), es decir, Cos (x) es menor o igual que 90 En el caso de los grados, la inclinación detrás es la razón de la necesidad de agregar la función max (), pow (max (cosx, 0), 10), cuando es mayor que 90 grados, max (cos (x), 0) son todos 0, entonces pow (max (cosx, 0), 10) un valor superior a 90 grados también debe ser 0

,

 

/* 
高光 Specular =直射光* pow(max(cosx,0),10)*反射光颜色   x=dot(反射光方向,视野方向)

 */
Shader "Tint/Specular"{
    Properties {
        _Color("Color",Color)= (1,1,1,1)
        _SpeularColor("Specular Color",Color) = (1,0,0,1)
        _Gloss("Gloss",Range(1,200)) = 10

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

            fixed4 _Color;
            fixed4 _SpeularColor;
            half _Gloss;

            struct a2v{
                float4 vertex : POSITION;
                float3 normal: NORMAL;
            };
            struct v2f{
                float4 vertex : SV_POSITION;
                float3 worldNormal: TEXCOORD0;
                float3 worldView: TEXCOORD1;
            };

            v2f vert(a2v v){
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                // Mul(x,_World2Object) 对象空间转世界空间
                // Mul(_World2Object,x) 世界空间转对象空间
                // 摄像机世界坐标  _WordSpaceCameraPos.xyz
                //  为什么_WordSpaceCameraPos.xyz要加.xyz呢? 因为 v.vertex 是4阶的 而我们只需要xyz即可
                o.worldView = _WorldSpaceCameraPos.xyz-mul(v.vertex,unity_WorldToObject).xyz;
                return o;
            }

            fixed4 frag(v2f f):SV_TARGET{
                // 世界法线单位向量
                fixed3 worldNormalDir = normalize(f.worldNormal);

                // 世界光方向单位向量
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

                // 半兰伯特
                fixed3  halfLambert = 0.5* dot(worldNormalDir,worldLightDir) + 0.5;
                fixed3 diffuse = _LightColor0.rgb * halfLambert * _Color.rgb;

                // 环境光
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;

                // 世界空间视野方向
                fixed3 worldViewDir = normalize(f.worldView );

                // 反射光方向 = reflect(入射光方向 , 法线方向) 
                //那么问题来了 ,下面这个worldLightDir为啥要加负号呢?
                // 因为worldLightDir 是点到顶光坐标的方向,而我们需要的事 光到顶点的入射光方向
                fixed3 reflectDir = normalize(reflect(-worldLightDir,worldNormalDir));

                // 高光
                fixed3 specular =  _LightColor0.rgb * pow(max(0,dot(reflectDir,worldViewDir)),_Gloss )* _SpeularColor.rgb;

                // 最后渲染颜色
                fixed3 finalCol = ambient + diffuse + specular;


                return fixed4(finalCol,1);
            }


            ENDCG
        }
    }
    Fallback "Diffuse"
}

4. Modelo de iluminación de alta luminosidad Blinn-Phone

Fórmula: Blinn-Phone Specular = luz directa * pow (max (cosx, 0), 10) * color de luz reflejada x = cosx = dot (dirección de la bisectriz entre la dirección de la luz paralela y la dirección de la vista, dirección normal)

 // 平行光方向 = 世界光方向 + 世界视野方向  两个向量相加就是他们之间的平方线

 fixed3 halfDir = normalize(worldLightDir+worldViewDir);

fixed3 specular =  _LightColor0.rgb * pow(max(0,dot(worldNormalDir,halfDir)),_Gloss )* _SpeularColor.rgb;
/* 
Blinn-Phone高光 Specular =直射光* pow(max(cosx,0),10)*反射光颜色   x=平行光方向和视野方向的平分线
 */
Shader "Tint/sBlinnPhoneSpecular"{
    Properties {
        _Color("Color",Color)= (1,1,1,1)
        _SpeularColor("Specular Color",Color) = (1,0,0,1)
        _Gloss("Gloss",Range(1,200)) = 10

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

            fixed4 _Color;
            fixed4 _SpeularColor;
            half _Gloss;

            struct a2v{
                float4 vertex : POSITION;
                float3 normal: NORMAL;
            };
            struct v2f{
                float4 vertex : SV_POSITION;
                float3 worldNormal: TEXCOORD0;
                float3 worldView: TEXCOORD1;
            };

            v2f vert(a2v v){
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                // Mul(x,_World2Object) 对象空间转世界空间
                // Mul(_World2Object,x) 世界空间转对象空间
                // 摄像机世界坐标  _WordSpaceCameraPos.xyz
                //  为什么_WordSpaceCameraPos.xyz要加.xyz呢? 因为 v.vertex 是4阶的 而我们只需要xyz即可
                o.worldView = _WorldSpaceCameraPos.xyz-mul(v.vertex,unity_WorldToObject).xyz;
                return o;
            }

            fixed4 frag(v2f f):SV_TARGET{
                // 世界法线单位向量
                fixed3 worldNormalDir = normalize(f.worldNormal);

                // 世界光方向单位向量
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

                // 半兰伯特
                fixed3  halfLambert = 0.5* dot(worldNormalDir,worldLightDir) + 0.5;
                fixed3 diffuse = _LightColor0.rgb * halfLambert * _Color.rgb;

                // 环境光
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;

                // 世界空间视野方向
                fixed3 worldViewDir = normalize(f.worldView );

                // 平行光方向 = 世界光方向 + 世界视野方向  两个向量相加就是他们之间的平方线
                fixed3 halfDir = normalize(worldLightDir+worldViewDir);
                // Blinn-Phone高光
                fixed3 specular =  _LightColor0.rgb * pow(max(0,dot(worldNormalDir,halfDir)),_Gloss )* _SpeularColor.rgb;

                // 最后渲染颜色
                fixed3 finalCol = ambient + diffuse + specular;


                return fixed4(finalCol,1);
            }


            ENDCG
        }
    }
    Fallback "Diffuse"
}

 

Supongo que te gusta

Origin blog.csdn.net/qq_39097425/article/details/109358028
Recomendado
Clasificación