zoukankan      html  css  js  c++  java
  • 【Unity Shader】三、漫反射Diffuse Shader例子

    学习目标:

    • 漫反射Diffuse Shader,逐顶点计算和逐像素计算。
    • 半兰伯特(HalfLambert)光照模型。
    • 结合材质自身的色彩(Material Color)及环境光(Ambient)。

    漫反射计算公式:

    漫反射Diffuse颜色 = 直射光颜色 * max(0, cos(光源方向和法线方向夹角)) * 材质自身色彩

    其中max(0, cos(光源方向和法线方向夹角))部分可以改用半兰伯特光照模型以增强背光面的光照效果。


     逐顶点漫反射

    Shader "Custom/04-Diffuse Vertex" { // 逐顶点漫反射
        Properties{
            _Diffuse("Diffuse Color", Color) = (1,1,1,1) // 可在编辑器面板定义材质自身色彩
        }
        SubShader{
            Pass {
                
                // 只有定义了正确的LightMode才能得到一些Unity的内置光照变量
                Tags{"LightMode" = "ForwardBase"}
    
                CGPROGRAM
    
    // 包含unity的内置的文件,才可以使用Unity内置的一些变量
    #include "Lighting.cginc" // 取得第一个直射光的颜色_LightColor0 第一个直射光的位置_WorldSpaceLightPos0(即方向)
    #pragma vertex vert
    #pragma fragment frag
    
                fixed4 _Diffuse; // 使用属性
    
                struct a2v
                {
                    float4 vertex : POSITION;    // 告诉Unity把模型空间下的顶点坐标填充给vertex属性
                    float3 normal : NORMAL;      // 告诉Unity把模型空间下的法线方向填充给normal属性
                    float4 texcoord : TEXCOORD0; // 告诉Unity把第一套纹理坐标填充给texcoord属性
    // POSITON、NORMAL、TEXCOORD0都是Unity内置的变量 }; struct v2f { float4 position : SV_POSITION; // 声明用来存储顶点在裁剪空间下的坐标 float3 color : COLOR; // 用于传递计算出来的漫反射颜色
             // SV_POSITION、COLOR都是Unity内置的变量 }; // 计算顶点坐标从模型坐标系转换到裁剪面坐标系 v2f vert(a2v v) { v2f f; f.position = mul(UNITY_MATRIX_MVP, v.vertex); // UNITY_MATRIX_MVP是内置矩阵。该步骤用来把一个坐标从模型空间转换到剪裁空间 // 环境光 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb; // 法线方向。把法线方向从模型空间转换到世界空间 fixed normalDir = normalize(mul(v.normal, (float3x3)unity_WorldToObject)); // 反过来相乘就是从模型到世界,否则是从世界到模型 // 光照方向 fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz); // 对于每个顶点来说,光的位置就是光的方向,因为光是平行光 // 漫反射Diffuse颜色 = 直射光颜色 * max(0, cos(光源方向和法线方向夹角)) * 材质自身色彩 fixed3 diffuse = _LightColor0 * max(0, dot(normalDir, lightDir)) * _Diffuse; // 融合材质自身色彩用乘法 // 加上环境光 f.color = diffuse + ambient; // 颜色叠加用加法(通常亮度会增加) return f; } // 计算每个像素点的颜色值 fixed4 frag(v2f f) : SV_Target { return fixed4(f.color, 1); // f.color是float3已经包含了三个数值 } ENDCG } } FallBack "Diffuse" }

    如果去掉自定义的_Diffuse(材质自身色彩)属性和环境光(Ambient),漫反射颜色值将仅由光源颜色来决定。白色光源的显示效果如下:

    现在想要融合材质自身的颜色值来显示,编辑器面板选择一个材质自身的色彩,显示效果如下:

    最后再加上环境光(UNITY_LIGHTMODEL_AMBIENT),能看到变白变亮了,效果如下:


    逐像素漫反射

    Shader "Custom/05-Diffuse Fragment" { // 逐像素漫反射
        Properties{
            _Diffuse("Diffuse Color", Color) = (1,1,1,1) // 可在编辑器面板定义材质自身色彩
        }
        SubShader{
            Pass {
                
                // 只有定义了正确的LightMode才能得到一些Unity的内置光照变量
                Tags{"LightMode" = "ForwardBase"}
    
                CGPROGRAM
    
    // 包含unity的内置的文件,才可以使用Unity内置的一些变量
    #include "Lighting.cginc" // 取得第一个直射光的颜色_LightColor0 第一个直射光的位置_WorldSpaceLightPos0(即方向)
    #pragma vertex vert
    #pragma fragment frag
    
                fixed4 _Diffuse; // 使用属性
    
                struct a2v
                {
                    float4 vertex : POSITION;    // 告诉Unity把模型空间下的顶点坐标填充给vertex属性
                    float3 normal : NORMAL;        // 告诉Unity把模型空间下的法线方向填充给normal属性
                    float4 texcoord : TEXCOORD0;// 告诉Unity把第一套纹理坐标填充给texcoord属性
                };
    
                struct v2f
                {
                    float4 position : SV_POSITION; // 声明用来存储顶点在裁剪空间下的坐标
                    float3 worldNomalDir : COLOR;  // 用于存储世界空间下的法线方向
                };
    
                // 计算顶点坐标从模型坐标系转换到裁剪面坐标系
                v2f vert(a2v v)
                {
                    v2f f;
                    f.position = mul(UNITY_MATRIX_MVP, v.vertex); // UNITY_MATRIX_MVP是内置矩阵。该步骤用来把一个坐标从模型空间转换到剪裁空间
                    
                    // 法线方向。把法线方向从模型空间转换到世界空间
                    f.worldNomalDir = (mul(v.normal, (float3x3)unity_WorldToObject)); // 反过来相乘就是从模型到世界,否则是从世界到模型
                    
                    return f;
                }
    
                // 计算每个像素点的颜色值
                fixed4 frag(v2f f) : SV_Target 
                {
                    // 环境光
                    fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
                    // 法线方向。
                    fixed normalDir = normalize(f.worldNomalDir); // 单位向量
                    // 光照方向。
                    fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz); // 对于每个顶点来说,光的位置就是光的方向,因为光是平行光
                    // 漫反射Diffuse颜色 = 直射光颜色 * max(0, cos(光源方向和法线方向夹角)) * 材质自身色彩
                    fixed3 diffuse = _LightColor0 * max(0, dot(normalDir, lightDir)) * _Diffuse; // 颜色融合用乘法
                    // 加上环境光
                    fixed3 tempColor = diffuse + ambient; // 颜色叠加用加法(亮度通常会增加)
    
                    return fixed4(tempColor, 1); // tempColor是float3已经包含了三个数值
                }
    
                ENDCG
            }
            
        }
        FallBack "Diffuse"
    }

     如下图,左边逐像素计算,右边逐顶点计算,在观察背光面阴影边缘时能看出明显差别,逐顶点计算的阴影过度呈块状的。


     半兰伯特(HalfLambert)光照模型

    直接修改上面frag函数中漫反射Diffuse颜色的计算公式。

    // 计算每个像素点的颜色值
    fixed4 frag(v2f f) : SV_Target 
    {
        // 环境光
        fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
        // 法线方向。
        fixed normalDir = normalize(f.worldNomalDir); // 单位向量
        // 光照方向。
        fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz); // 对于每个顶点来说,光的位置就是光的方向,因为光是平行光
        // 半兰伯特光照
        float3 halfLambert = dot(normalDir, lightDir) * 0.5 + 0.5; // 使得背光面不至于为0全黑
        // 漫反射Diffuse颜色 = 直射光颜色 * max(0, cos(光源方向和法线方向夹角)) * 材质自身色彩
        fixed3 diffuse = _LightColor0 * halfLambert * _Diffuse; // 颜色融合用乘法
        // 加上环境光
        fixed3 tempColor = diffuse + ambient; // 颜色叠加用加法(亮度通常会增加)
    
         return fixed4(tempColor, 1); // tempColor是float3已经包含了三个数值
     }

    可以看到在背光面也有了少量光照,明显没有旁边的对照组那么黑。


     注意点:

    • 颜色融合是相乘,颜色叠加是相加(通常亮度会增加)。
    • 逐像素计算光照要比逐顶点计算光照更加消耗性能。

    学习资料:

  • 相关阅读:
    java Activiti 工作流引擎 SSM 框架模块设计方案
    自定义表单 Flowable 工作流 Springboot vue.js 前后分离 跨域 有代码生成器
    数据库设计的十个最佳实践
    activiti 汉化 stencilset.json 文件内容
    JAVA oa 办公系统模块 设计方案
    java 考试系统 在线学习 视频直播 人脸识别 springboot框架 前后分离 PC和手机端
    集成 nacos注册中心配置使用
    “感恩节 ”怼记
    仓颉编程语言的一点期望
    关于System.out.println()与System.out.print("\n")的区别
  • 原文地址:https://www.cnblogs.com/guxin/p/unity-diffuse-shader-demo.html
Copyright © 2011-2022 走看看