【Shader学习】完整光照效果

一、相关知识介绍

1、光照分析

2、最终实现效果

二、完整光照效果实现

1、环境部分

(1)漫反射部分

通过分离模型上、下、侧三个面,并选择合适的颜色进行模拟

(i)上、下、侧三面环境颜色模拟效果

(2)遮挡阴影

上、下、侧三面环境颜色模拟+AO遮罩模拟环境阴影效果

(3)完整实现代码

csharp 复制代码
Shader "AP01/L07/3ColAmbient" {
    Properties {
        _Occlusion  ("环境遮挡图", 2d)      = "white" {}
        _EnvUpCol   ("朝上环境色", color)   = (1.0, 1.0, 1.0, 1.0)
        _EnvSideCol ("侧面环境色", color)   = (0.5, 0.5, 0.5, 1.0)
        _EnvDownCol ("朝下环境色", color)   = (0.0, 0.0, 0.0, 1.0)
    }
    SubShader {
        Tags {
            "RenderType"="Opaque"
        }
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode"="ForwardBase"
            }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #pragma multi_compile_fwdbase_fullshadows
            #pragma target 3.0
            // 输入参数
            uniform float3 _EnvUpCol;
            uniform float3 _EnvSideCol;
            uniform float3 _EnvDownCol;
            uniform sampler2D _Occlusion;
            // 输入结构
            struct VertexInput {
                float4 vertex : POSITION;   // 将模型顶点信息输入进来
                float4 normal : NORMAL;     // 将模型法线信息输入进来
                float2 uv0 : TEXCOORD0;     // 将模型UV信息输入进来 0通道 共4通道
            };
            // 输出结构
            struct VertexOutput {
                float4 pos : SV_POSITION;   // 由模型顶点信息换算而来的顶点屏幕位置
                float3 nDirWS : TEXCOORD0;  // 由模型法线信息换算来的世界空间法线信息
                float2 uv : TEXCOORD1;      // 追加UV信息用语像素Shader采样贴图
            };
            // 输入结构>>>顶点Shader>>>输出结构
            VertexOutput vert (VertexInput v) {
                VertexOutput o = (VertexOutput)0;               // 新建一个输出结构
                o.pos = UnityObjectToClipPos( v.vertex );       // 变换顶点信息 并将其塞给输出结构
                o.nDirWS = UnityObjectToWorldNormal(v.normal);  // 变换法线信息 并将其塞给输出结构
                o.uv = v.uv0;                                   // 图森破
                return o;                                       // 将输出结构 输出
            }
            // 输出结构>>>像素
            float4 frag(VertexOutput i) : COLOR {
                // 准备向量
                float3 nDir = i.nDirWS;                         // 获取nDir
                // 计算各部位遮罩
                float upMask = max(0.0, nDir.g);                // 获取朝上部分遮罩
                float downMask = max(0.0, -nDir.g);             // 获取朝下部分遮罩
                float sideMask = 1.0 - upMask - downMask;       // 获取侧面部分遮罩
                // 混合环境色
                float3 envCol = _EnvUpCol * upMask + _EnvSideCol * sideMask + _EnvDownCol * downMask;
                // 采样Occlusion贴图
                float occlusion = tex2D(_Occlusion, i.uv);
                // 计算环境光照
                float3 envLighting = envCol * occlusion;
                // 返回最终颜色
                return float4(envLighting, 1.0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

2、光源部分

(1)漫反射

使用Lambert模型

(2)镜面反射

使用Phong模型

(3)遮挡阴影

使用Unity中内置的投影方法

(i)遮挡投影效果
(ii)实现代码
csharp 复制代码
Shader "AP01/L07/Shadow" {
    Properties {
    }
    SubShader {
        Tags {
            "RenderType"="Opaque"
        }
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode"="ForwardBase"
            }


            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "AutoLight.cginc"      // 使用Unity投影必须包含这两个库文件
            #include "Lighting.cginc"       // 同上
            #pragma multi_compile_fwdbase_fullshadows
            #pragma target 3.0
            // 输入结构
            struct VertexInput {
                float4 vertex : POSITION;   // 将模型的顶点信息输入进来
            };
            // 输出结构
            struct VertexOutput {
                float4 pos : SV_POSITION;   // 由模型顶点信息换算而来的顶点屏幕位置
                LIGHTING_COORDS(0,1)        // 投影用坐标信息 Unity已封装 不用管细节
            };
            // 输入结构>>>顶点Shader>>>输出结构
            VertexOutput vert (VertexInput v) {
                VertexOutput o = (VertexOutput)0;           // 新建一个输出结构
                o.pos = UnityObjectToClipPos( v.vertex );   // 变换顶点信息 并将其塞给输出结构
                TRANSFER_VERTEX_TO_FRAGMENT(o)              // Unity封装 不用管细节
                return o;                                   // 将输出结构 输出
            }
            // 输出结构>>>像素
            float4 frag(VertexOutput i) : COLOR {
                float shadow = LIGHT_ATTENUATION(i);        // 同样Unity封装好的函数 可取出投影
                return float4(shadow, shadow, shadow, 1.0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

3、完整光照

(1)效果

(2)实现代码

csharp 复制代码
Shader "AP01/L08/OldSchoolPlus" {
    Properties {
        _BaseCol    ("基本色",      Color)          = (0.5, 0.5, 0.5, 1.0)
        _LightCol   ("光颜色",      Color)          = (1.0, 1.0, 1.0, 1.0)
        _SpecPow    ("高光次幂",    Range(1, 90))   = 30
        _Occlusion  ("AO图",        2D)             = "white" {}
        _EnvInt     ("环境光强度",  Range(0, 1))    = 0.2
        _EnvUpCol   ("环境天顶颜色", Color)          = (1.0, 1.0, 1.0, 1.0)
        _EnvSideCol ("环境水平颜色", Color)          = (0.5, 0.5, 0.5, 1.0)
        _EnvDownCol ("环境地表颜色", Color)          = (0.0, 0.0, 0.0, 0.0)
    }
    SubShader {
        Tags {
            "RenderType"="Opaque"
        }
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode"="ForwardBase"
            }


            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            // 追加投影相关包含文件
            #include "AutoLight.cginc"
            #include "Lighting.cginc"
            #pragma multi_compile_fwdbase_fullshadows
            #pragma target 3.0
            // 输入参数
            uniform float3 _BaseCol;
            uniform float3 _LightCol;
            uniform float _SpecPow;
            uniform sampler2D _Occlusion;
            uniform float _EnvInt;
            uniform float3 _EnvUpCol;
            uniform float3 _EnvSideCol;
            uniform float3 _EnvDownCol;
            // 输入结构
            struct VertexInput {
                float4 vertex   : POSITION;   // 顶点信息 Get✔
                float4 normal   : NORMAL;     // 法线信息 Get✔
                float2 uv0      : TEXCOORD0;  // UV信息 Get✔
            };
            // 输出结构
            struct VertexOutput {
                float4 pos    : SV_POSITION;  // 裁剪空间(暂理解为屏幕空间吧)顶点位置
                float2 uv0      : TEXCOORD0;    // UV0
                float4 posWS    : TEXCOORD1;    // 世界空间顶点位置
                float3 nDirWS   : TEXCOORD2;    // 世界空间法线方向
                LIGHTING_COORDS(3,4)            // 投影相关
            };
            // 输入结构>>>顶点Shader>>>输出结构
            VertexOutput vert (VertexInput v) {
                VertexOutput o = (VertexOutput)0;                   // 新建输出结构
                    o.pos = UnityObjectToClipPos( v.vertex );       // 变换顶点位置 OS>CS
                    o.uv0 = v.uv0;                                  // 传递UV
                    o.posWS = mul(unity_ObjectToWorld, v.vertex);   // 变换顶点位置 OS>WS
                    o.nDirWS = UnityObjectToWorldNormal(v.normal);  // 变换法线方向 OS>WS
                    TRANSFER_VERTEX_TO_FRAGMENT(o)                  // 投影相关
                return o;                                           // 返回输出结构
            }
            // 输出结构>>>像素
            float4 frag(VertexOutput i) : COLOR {
                // 准备向量
                float3 nDir = normalize(i.nDirWS);
                float3 lDir = _WorldSpaceLightPos0.xyz;
                float3 vDir = normalize(_WorldSpaceCameraPos.xyz - i.posWS.xyz);
                float3 rDir = reflect(-lDir, nDir);

                // 准备点积结果
                float ndotl = dot(nDir, lDir);
                float vdotr = dot(vDir, rDir);

                // 光照模型(直接光照部分)
                float shadow = LIGHT_ATTENUATION(i);        // 获取投影
                float lambert = max(0.0, ndotl);
                float phong = pow(max(0.0, vdotr), _SpecPow);
                float3 dirLighting = (_BaseCol * lambert + phong) * _LightCol * shadow;

                // 光照模型(环境光照部分)
                float upMask = max(0.0, nDir.g);                // 获取朝上部分遮罩
                float downMask = max(0.0, -nDir.g);             // 获取朝下部分遮罩
                float sideMask = 1.0 - upMask - downMask;       // 获取侧面部分遮罩
                // 混合环境色
                float3 envCol = _EnvUpCol * upMask + _EnvSideCol * sideMask + _EnvDownCol * downMask;
                float occlusion = tex2D(_Occlusion, i.uv0);         // 采样Occlusion贴图
                float3 envLighting = envCol * _EnvInt * occlusion;  // 计算环境光照

                // 返回结果
                float3 finalRGB = dirLighting + envLighting;
                return float4(finalRGB, 1.0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

三、综合实践

待完善

相关推荐
weixin_5316389413 小时前
Rokid AR交互开发工具对比
unity·游戏引擎·xr
留待舞人归1 天前
【Unity3D优化】优化多语言字体包大小
游戏·unity·游戏引擎·unity3d·优化
9765033351 天前
iOS 审核 cocos 4.3a【苹果机审的“分层阈值”设计】
flutter·游戏·unity·ios
EQ-雪梨蛋花汤2 天前
【Unity笔记】Unity Animation组件使用详解:Play方法重载与动画播放控制
笔记·unity·游戏引擎
AgilityBaby2 天前
Untiy打包安卓踩坑
android·笔记·学习·unity·游戏引擎
菌菌巧乐兹2 天前
Unity | AmplifyShaderEditor插件基础(第九集:旗子进阶版)
unity·游戏引擎
心前阳光2 天前
Unity编辑器-获取Projectwindow中拖拽内容的路径
unity·编辑器·游戏引擎
湖北二师的咸鱼3 天前
unity学习摘要
学习·unity·游戏引擎
小张不爱写代码3 天前
Unity Android 启动应用的时候黑屏问题
unity·游戏引擎