【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"
}

三、综合实践

待完善

相关推荐
Allen747411 小时前
ComfyUI 自动化生产 3D资产 工作流笔记
图像处理·opencv·unity·自然语言处理·3d模型生成·confyui
nnsix13 小时前
Unity Windows11 打字中文显示不出来输入法的候选框
unity
adogai1 天前
unity mcp接入 实现一句话生成游戏!
游戏·unity·游戏引擎
mxwin1 天前
Unity Shader 逐像素光照 vs 逐顶点光照性能与画质的权衡策略
unity·游戏引擎·shader·着色器
CDN3601 天前
游戏盾导致 Unity/UE 引擎崩溃的主要原因排查?
游戏·unity·游戏引擎
mxwin1 天前
Unity URP 全局光照 (GI) 完全指南 Lightmap 采样与实时 GI(光照探针、反射探针)的 Shader 集成
unity·游戏引擎·shader·着色器
mxwin1 天前
Unity URP 溶解效果基于噪声纹理与 clip 函数实现物体渐隐渐显
unity·游戏引擎·shader
CheerWWW1 天前
GameFramework——Download篇
笔记·学习·unity·c#
mxwin1 天前
Unity URP 下的 Early-Z / Depth Prepass 解决复杂片元着色器造成的 Overdraw 问题
unity·游戏引擎·着色器
mxwin1 天前
Unity Shader 顶点色:利用模型顶点颜色传递渲染数据
unity·游戏引擎·shader