Catlike Coding Custom SRP笔记 - 平行光阴影1

原文链接: Directional Shadows

 

效果图

当然,这里的阴影并不对,因为还不是最终的,后面几篇会分析为啥会这样以及解决办法

涉及的资源可以在原文链接中下载到 

 

 CustomRenderPipelineAsset.cs

复制代码
[CreateAssetMenu(menuName = "Rendering/Custom Render Pipeline")]
public class CustomRenderPipelineAsset : RenderPipelineAsset {

    [SerializeField]
    bool useDynamicBatching = true; //启用动态合批
    [SerializeField]
    bool useGPUInstancing = true; //GPU Instancing
    [SerializeField]
    bool useSRPBatcher = true; //SRP合批

    [SerializeField]
    ShadowSettings shadows = default;

    protected override RenderPipeline CreatePipeline () {
        return new CustomRenderPipeline(useDynamicBatching, useGPUInstancing, useSRPBatcher, shadows);
    }
    
}
复制代码

 

CustomRenderPipeline.cs

复制代码
public class CustomRenderPipeline : RenderPipeline {

    CameraRenderer renderer = new CameraRenderer();

    bool useDynamicBatching, useGPUInstancing;

    ShadowSettings shadowSettings;

    public CustomRenderPipeline (
        bool useDynamicBatching, bool useGPUInstancing, bool useSRPBatcher,
        ShadowSettings shadowSettings
    ) {
        this.shadowSettings = shadowSettings;
        this.useDynamicBatching = useDynamicBatching;
        this.useGPUInstancing = useGPUInstancing;
        GraphicsSettings.useScriptableRenderPipelineBatching = useSRPBatcher;
        GraphicsSettings.lightsUseLinearIntensity = true;
    }

    protected override void Render(ScriptableRenderContext context, Camera[] cameras) {
        for (int i = 0; i < cameras.Length; i++) {
            renderer.Render(context, cameras[i], useDynamicBatching, useGPUInstancing,
                shadowSettings
            );
        }
    }

    protected override void Render (ScriptableRenderContext context, List<Camera> cameras) {
        for (int i = 0; i < cameras.Count; i++) {
            renderer.Render(context, cameras[i], useDynamicBatching, useGPUInstancing,
                shadowSettings
            );
        }
    }
    
}
复制代码

 

CameraRenderer.cs

复制代码
public partial class CameraRenderer {

#if UNITY_EDITOR
#else
    const string SampleName = "Render Camera";
#endif

    static ShaderTagId unlitShaderTagId = new ShaderTagId("SRPDefaultUnlit");
    static ShaderTagId litShaderTagId = new ShaderTagId("CustomLit");

    CommandBuffer buffer = new CommandBuffer {
        name = "Render Camera"
    };

    ScriptableRenderContext context;

    Camera camera;

    CullingResults cullingResults;

    Lighting lighting = new Lighting();

    public void Render (
        ScriptableRenderContext context, Camera camera,
        bool useDynamicBatching, bool useGPUInstancing,
        ShadowSettings shadowSettings
    ) {
        this.context = context;
        this.camera = camera;

#if UNITY_EDITOR
        PrepareBuffer();
        PrepareForSceneWindow();
#endif
        if (!Cull(shadowSettings.maxDistance)) {
            return;
        }
        
        buffer.BeginSample(SampleName);
        ExecuteBuffer();
        
        lighting.Setup(camera, context, cullingResults, shadowSettings);
        buffer.EndSample(SampleName);
        
        Setup();
        DrawVisibleGeometry(useDynamicBatching, useGPUInstancing);
#if UNITY_EDITOR
        DrawUnsupportedShaders();
        DrawGizmos();
#endif
        lighting.Cleanup();
        Submit();
    }

    bool Cull (float maxShadowDistance) {
        if (camera.TryGetCullingParameters(out ScriptableCullingParameters p)) {
            p.shadowDistance = Mathf.Min(maxShadowDistance, camera.farClipPlane);
            cullingResults = context.Cull(ref p);
            return true;
        }
        return false;
    }

    void Setup () {
        context.SetupCameraProperties(camera);
        CameraClearFlags flags = camera.clearFlags;
        buffer.ClearRenderTarget(
            flags <= CameraClearFlags.Depth,
            flags <= CameraClearFlags.Color,
            (flags == CameraClearFlags.Color) ? camera.backgroundColor.linear : Color.clear
        );
        buffer.BeginSample(SampleName);
        ExecuteBuffer();
    }

    void Submit () {
        buffer.EndSample(SampleName);
        ExecuteBuffer();
        context.Submit();
    }

    void ExecuteBuffer () {
        context.ExecuteCommandBuffer(buffer);
        buffer.Clear();
    }

    void DrawVisibleGeometry (bool useDynamicBatching, bool useGPUInstancing) {
        //先渲染不透明物体
        var sortingSettings = new SortingSettings(camera) {
            criteria = SortingCriteria.CommonOpaque
        };
        var drawingSettings = new DrawingSettings(
            unlitShaderTagId, sortingSettings
        ) {
            enableDynamicBatching = useDynamicBatching,
            enableInstancing = useGPUInstancing
        };
        drawingSettings.SetShaderPassName(1, litShaderTagId);

        var filteringSettings = new FilteringSettings(RenderQueueRange.opaque);
        context.DrawRenderers(cullingResults, ref drawingSettings, ref filteringSettings);

        //再渲染天空盒
        context.DrawSkybox(camera);

        //再渲染半透明物体
        sortingSettings.criteria = SortingCriteria.CommonTransparent;
        drawingSettings.sortingSettings = sortingSettings;
        filteringSettings.renderQueueRange = RenderQueueRange.transparent;

        context.DrawRenderers(cullingResults, ref drawingSettings, ref filteringSettings);
    }
    
}
复制代码

 

CameraRenderer.Editor.cs

复制代码
partial class CameraRenderer {

#if UNITY_EDITOR

    //内置渲染管线的tag标识
    static ShaderTagId[] legacyShaderTagIds = {
        new ShaderTagId("Always"),
        new ShaderTagId("ForwardBase"),
        new ShaderTagId("PrepassBase"),
        new ShaderTagId("Vertex"),
        new ShaderTagId("VertexLMRGBM"),
        new ShaderTagId("VertexLM")
    };

    static Material errorMaterial;

    string SampleName { get; set; }

    void DrawGizmos () {
        if (Handles.ShouldRenderGizmos()) {
            context.DrawGizmos(camera, GizmoSubset.PreImageEffects);
            context.DrawGizmos(camera, GizmoSubset.PostImageEffects);
        }
    }

    //shader错误时, 显示为粉红
    void DrawUnsupportedShaders () {
        if (errorMaterial == null) {
            errorMaterial = new Material(Shader.Find("Hidden/InternalErrorShader"));
        }
        var drawingSettings = new DrawingSettings(legacyShaderTagIds[0], new SortingSettings(camera)) {
            overrideMaterial = errorMaterial
        };
        for (int i = 1; i < legacyShaderTagIds.Length; i++) {
            drawingSettings.SetShaderPassName(i, legacyShaderTagIds[i]);
        }
        var filteringSettings = FilteringSettings.defaultValue;
        context.DrawRenderers(cullingResults, ref drawingSettings, ref filteringSettings);
    }

    void PrepareForSceneWindow () {
        if (camera.cameraType == CameraType.SceneView) {
            ScriptableRenderContext.EmitWorldGeometryForSceneView(camera); //渲染ugui的几何图形
        }
    }

    void PrepareBuffer () {
        Profiler.BeginSample("Editor Only"); //Window -> Analysis -> Profile -> CPU Usage:Hierarchy中会显示
        string camName = camera.name;
        SampleName = camName; //编辑器下BeginSample使用相机名字
        buffer.name = camName; 
        Profiler.EndSample();
    }
    
#endif

}
复制代码

 

 Lighting.cs

复制代码
public class Lighting {

    const string Sample_Name = "Lighting";

    const int maxDirLightCount = 4;

    static int dirLightCountId = Shader.PropertyToID("_DirectionalLightCount");
    
    static int dirLightColorsId = Shader.PropertyToID("_DirectionalLightColors"); //用于向shader传入光源颜色
    static int dirLightDirectionsId = Shader.PropertyToID("_DirectionalLightDirections"); //用于向shader传入光源方向
    static int dirLightShadowDataId = Shader.PropertyToID("_DirectionalLightShadowData"); //用于向shader传入阴影投射数据

    static Vector4[] dirLightColors = new Vector4[maxDirLightCount]; //存放光源颜色
    static Vector4[] dirLightDirections = new Vector4[maxDirLightCount]; //存放光源方向
    static Vector4[] dirLightShadowData = new Vector4[maxDirLightCount]; //存放阴影投射数据

    CommandBuffer buffer = new CommandBuffer {
        name = "Lighting"
    };

    CullingResults cullingResults;

    Shadows shadows = new Shadows();

    public void Setup (
        Camera cam, ScriptableRenderContext context, CullingResults cullingResults,
        ShadowSettings shadowSettings
    ) {
        this.cullingResults = cullingResults;
        buffer.BeginSample(Sample_Name);
        shadows.Setup(cam, context, cullingResults, shadowSettings);
        SetupLights();
        shadows.Render();
        buffer.EndSample(Sample_Name);
        
        context.ExecuteCommandBuffer(buffer);
        buffer.Clear();
    }

    public void Cleanup () {
        shadows.Cleanup();
    }

    void SetupLights () {
        NativeArray<VisibleLight> visibleLights = cullingResults.visibleLights;
        int dirLightCount = 0;
        for (int i = 0; i < visibleLights.Length; i++) {
            VisibleLight visibleLight = visibleLights[i];
            if (visibleLight.lightType == LightType.Directional) {
                SetupDirectionalLight(dirLightCount++, ref visibleLight);
                if (dirLightCount >= maxDirLightCount) {
                    break;
                }
            }
        }

        buffer.SetGlobalInt(dirLightCountId, dirLightCount);
        buffer.SetGlobalVectorArray(dirLightColorsId, dirLightColors);
        buffer.SetGlobalVectorArray(dirLightDirectionsId, dirLightDirections);
        buffer.SetGlobalVectorArray(dirLightShadowDataId, dirLightShadowData);
    }

    void SetupDirectionalLight (int index, ref VisibleLight visibleLight) {
        dirLightColors[index] = visibleLight.finalColor;
        dirLightDirections[index] = -visibleLight.localToWorldMatrix.GetColumn(2);
        dirLightShadowData[index] = shadows.ReserveDirectionalShadows(visibleLight.light, index);
    }
    
}
复制代码

 

ShadowSettings.cs

复制代码
[System.Serializable]
public class ShadowSettings {

    public enum MapSize {
        _256 = 256, _512 = 512, _1024 = 1024,
        _2048 = 2048, _4096 = 4096, _8192 = 8192
    }

    [Min(0.001f)]
    public float maxDistance = 100f;

    [System.Serializable]
    public struct Directional {
        public MapSize atlasSize; //shadowMap贴图大小
    }

    public Directional directional = new Directional {
        atlasSize = MapSize._1024,
    };
    
}
复制代码

 

Shadows.cs

复制代码
public class Shadows {

    const string Sample_Name = "Shadows";

    const int maxShadowedDirLightCount = 4;

    static int dirShadowAtlasId = Shader.PropertyToID("_DirectionalShadowAtlas");
    static int dirShadowMatricesId = Shader.PropertyToID("_DirectionalShadowMatrices"); //用于将坐标从世界空间转为ShadowMap贴图uv(世界空间坐标 -> 裁剪空间坐标 -> shadowMap uv坐标)

    static Matrix4x4[] dirShadowMatrices = new Matrix4x4[maxShadowedDirLightCount];

    struct ShadowedDirectionalLight {
        public int visibleLightIndex; //是哪一个光的阴影
    }
    //会产生阴影的光源
    ShadowedDirectionalLight[] shadowedDirectionalLights = new ShadowedDirectionalLight[maxShadowedDirLightCount];

    int shadowedDirLightCount;

    CommandBuffer buffer = new CommandBuffer {
        name = "Shadows"
    };
    
    Camera camera;
     
    ScriptableRenderContext context;

    CullingResults cullingResults;

    ShadowSettings settings;

    public void Setup (
        Camera cam, ScriptableRenderContext context, CullingResults cullingResults,
        ShadowSettings settings
    ) {
        this.camera = cam;
        this.context = context;
        this.cullingResults = cullingResults;
        this.settings = settings;
        shadowedDirLightCount = 0;
    }

    public void Cleanup () {
        buffer.ReleaseTemporaryRT(dirShadowAtlasId); //每帧都会释放rt
        ExecuteBuffer();
    }

    public Vector3 ReserveDirectionalShadows (Light light, int visibleLightIndex) {
        if (shadowedDirLightCount < maxShadowedDirLightCount
            && light.shadows != LightShadows.None && light.shadowStrength > 0f
            && cullingResults.GetShadowCasterBounds(visibleLightIndex, out Bounds b)
        ) {
            shadowedDirectionalLights[shadowedDirLightCount] =new ShadowedDirectionalLight {
                visibleLightIndex = visibleLightIndex
            };
            return new Vector3(light.shadowStrength, shadowedDirLightCount++, 0); //阴影强度, 阴影图在Atlas中的索引
        }
        return Vector3.zero;
    }

    public void Render () {
        if (shadowedDirLightCount > 0) {
            RenderDirectionalShadows();
        } else {
            buffer.GetTemporaryRT(dirShadowAtlasId, 1, 1, 32, FilterMode.Bilinear, RenderTextureFormat.Shadowmap);
        }
    }

    //所有光源的阴影信息都生成在一张阴影图上
    void RenderDirectionalShadows () {
        int atlasSize = (int)settings.directional.atlasSize;
        buffer.GetTemporaryRT(dirShadowAtlasId, atlasSize, atlasSize, 32, FilterMode.Bilinear, RenderTextureFormat.Shadowmap);
        buffer.SetRenderTarget(dirShadowAtlasId, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store);
        buffer.ClearRenderTarget(true, false, Color.clear);
        buffer.BeginSample(Sample_Name);
        ExecuteBuffer();

        int split = shadowedDirLightCount <= 1 ? 1 : 2; //因为所有光源都生成在一张阴影图上, 所有得把阴影图分成nxn块
        int tileSize = atlasSize / split;

        for (int i = 0; i < shadowedDirLightCount; i++) {
            RenderDirectionalShadows(i, split, tileSize);
        }

        buffer.SetGlobalMatrixArray(dirShadowMatricesId, dirShadowMatrices);
        buffer.EndSample(Sample_Name);
        ExecuteBuffer();
    }

    //单个光源的阴影生成在阴影图的某一块上
    void RenderDirectionalShadows (int index, int split, int tileSize) {
        ShadowedDirectionalLight light = shadowedDirectionalLights[index];
        var shadowSettings = new ShadowDrawingSettings(cullingResults, light.visibleLightIndex);

        cullingResults.ComputeDirectionalShadowMatricesAndCullingPrimitives(
            light.visibleLightIndex, 0, 1, Vector3.zero, tileSize, 0, 
            out var viewMatrix, out var projMatrix, 
            out ShadowSplitData splitData
        );
        shadowSettings.splitData = splitData;
        
        Vector2 offset = SetTileViewport(index, split, tileSize);
        dirShadowMatrices[index] = ConvertToAtlasMatrix(projMatrix * viewMatrix, offset, split);
        buffer.SetViewProjectionMatrices(viewMatrix, projMatrix);
        ExecuteBuffer();
        context.DrawShadows(ref shadowSettings); //使用物体的ShadowCaster Pass渲染阴影
    }

    Matrix4x4 ConvertToAtlasMatrix (Matrix4x4 m, Vector2 offset, int split) {
        if (SystemInfo.usesReversedZBuffer) {
            m.m20 = -m.m20;
            m.m21 = -m.m21;
            m.m22 = -m.m22;
            m.m23 = -m.m23;
        }
        
        float scale = 1f / split;

        //下面的计算主要包括:
        //1) 裁剪空间坐标的[-1, 1]转为uv的[0, 1](转换公式:x*0.5+0.5,换成矩阵的形式就是:先左乘0.5缩放矩阵, 再左乘0.5的平移矩阵)
        //2) 因为ShadowAtals分割成了多个tile, 再转到对应的tile上(比如:split=2, offset:1,1,就是先缩放0.5,再x,y平移1,1)
        m.m00 = (0.5f * (m.m00 + m.m30) + offset.x * m.m30) * scale;
        m.m01 = (0.5f * (m.m01 + m.m31) + offset.x * m.m31) * scale;
        m.m02 = (0.5f * (m.m02 + m.m32) + offset.x * m.m32) * scale;
        m.m03 = (0.5f * (m.m03 + m.m33) + offset.x * m.m33) * scale;
        
        m.m10 = (0.5f * (m.m10 + m.m30) + offset.y * m.m30) * scale;
        m.m11 = (0.5f * (m.m11 + m.m31) + offset.y * m.m31) * scale;
        m.m12 = (0.5f * (m.m12 + m.m32) + offset.y * m.m32) * scale;
        m.m13 = (0.5f * (m.m13 + m.m33) + offset.y * m.m33) * scale;
        
        m.m20 = 0.5f * (m.m20 + m.m30);
        m.m21 = 0.5f * (m.m21 + m.m31);
        m.m22 = 0.5f * (m.m22 + m.m32);
        m.m23 = 0.5f * (m.m23 + m.m33);
        
        return m;
    }

    Vector2 SetTileViewport (int lightIndex, int split, float tileSize) {
        int col = lightIndex % split;
        int row = lightIndex / split;
        buffer.SetViewport(new Rect(col * tileSize, row * tileSize, tileSize, tileSize));
        Vector2 offset = new Vector2(col, row);
        return offset;
    }

    void ExecuteBuffer () {
        context.ExecuteCommandBuffer(buffer);
        buffer.Clear();
    }
    
}
复制代码

 

ShaderLibrary/UnityInput.hlsl

复制代码
#ifndef CUSTOM_UNITY_INPUT_INCLUDED
#define CUSTOM_UNITY_INPUT_INCLUDED

CBUFFER_START(UnityPerDraw)
    float4x4 unity_ObjectToWorld;
    float4x4 unity_WorldToObject;
    float4 unity_LODFade;
    real4 unity_WorldTransformParams;
CBUFFER_END

float4x4 unity_MatrixVP; //世界空间坐标转裁剪空间
float4x4 unity_MatrixV; //世界空间坐标转相机空间
float4x4 unity_MatrixInvV;
float4x4 unity_prev_MatrixM;
float4x4 unity_prev_MatrixIM;
float4x4 glstate_matrix_projection; //投影矩阵

float3 _WorldSpaceCameraPos; //相机位置

#endif
复制代码

 

ShaderLibrary/Common.hlsl

复制代码
#ifndef CUSTOM_COMMON_INCLUDED
#define CUSTOM_COMMON_INCLUDED

#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl"
#include "UnityInput.hlsl"

//下面的宏是SpaceTransforms会用到, 不定义无法通过编译
#define UNITY_MATRIX_M unity_ObjectToWorld
#define UNITY_MATRIX_I_M unity_WorldToObject
#define UNITY_MATRIX_V unity_MatrixV
#define UNITY_MATRIX_I_V unity_MatrixInvV
#define UNITY_MATRIX_VP unity_MatrixVP
#define UNITY_PREV_MATRIX_M unity_prev_MatrixM
#define UNITY_PREV_MATRIX_I_M unity_prev_MatrixIM
#define UNITY_MATRIX_P glstate_matrix_projection

#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/SpaceTransforms.hlsl"

float Square(float x) {
    return x * x;
}

#endif
复制代码

 

ShaderLibrary/Surface.hlsl

复制代码
#ifndef CUSTOM_SURFACE_INCLUDED
#define CUSTOM_SURFACE_INCLUDED

//光照射到的物体表面的相关信息
struct Surface {
    float3 position; //顶点坐标(世界空间)
    float3 normal; //顶点法线
    float3 viewDirection; //视角方向
    float3 color; //光照计算前的颜色
    float alpha;
    float metallic; //金属度
    float smoothness; //光滑度
};

#endif
复制代码

 

ShaderLibrary/Light.hlsl

复制代码
#ifndef CUSTOM_LIGHT_INCLUDED
#define CUSTOM_LIGHT_INCLUDED

// 光源信息

#define MAX_DIRECTIONAL_LIGHT_COUNT 4

//要兼容SRP Batcher的话, 变量要缓存到CBuffer中
CBUFFER_START(_CustomLight)
    int _DirectionalLightCount;
    float4 _DirectionalLightColors[MAX_DIRECTIONAL_LIGHT_COUNT];
    float4 _DirectionalLightDirections[MAX_DIRECTIONAL_LIGHT_COUNT];
    float4 _DirectionalLightShadowData[MAX_DIRECTIONAL_LIGHT_COUNT];
CBUFFER_END

struct Light {
    float3 color; //光源颜色
    float3 direction; //光源方向
    float attenuation; //衰减因子
};

//平行光总数量
int GetDirectionalLightCount () {
    return _DirectionalLightCount;
}

DirectionalShadowData GetDirectionalShadowData(int lightIndex) {
    DirectionalShadowData data;
    data.strength = _DirectionalLightShadowData[lightIndex].x; //Light组件shadow的强度比例, 范围[0, 1]
    data.tileIndex = _DirectionalLightShadowData[lightIndex].y;
    return data;
}

//平行光信息
Light GetDirectionalLight (int lightIndex, Surface surfaceWS) {
    Light light;
    light.color = _DirectionalLightColors[lightIndex].rgb;
    light.direction = _DirectionalLightDirections[lightIndex].xyz;

    DirectionalShadowData shadowData = GetDirectionalShadowData(lightIndex);
    light.attenuation = GetDirectionalShadowAttenuation(shadowData, surfaceWS);
    
    return light;
}

#endif
复制代码

 

ShaderLibrary/BRDF.hlsl

复制代码
#ifndef CUSTOM_BRDF_INCLUDED
#define CUSTOM_BRDF_INCLUDED

//光照到物体表面后, 一部分被漫反射(向各个方向均匀反射), 其余部分被镜面反射(某个方向完全反射)。 注意: 折射和进入物体等情况不考虑时
//金属度决定镜面反射比例, 光滑度决定镜面反射比例中有多少比例反射出来

//双向反射分布计算要用到的数据
struct BRDF {
    float3 diffuse;  //漫反射
    float3 specular; //高光
    float roughness; //粗糙度
};

#define MIN_REFLECTIVITY 0.04 //非金属的平均镜面反射比例

//获取漫反射比例
float OneMinusReflectivity(float metallic) {
    float range = 1.0 - MIN_REFLECTIVITY;
    return range - metallic * range; //确保metallic=0时也有镜面反射
}

//双向反射分布计算后的漫反射(会比物体原有颜色弱)和镜面反射
BRDF GetBRDF(Surface surface, bool applyAlphaToDiffuse = false) {
    BRDF brdf;
    float oneMinusReflectivity = OneMinusReflectivity(surface.metallic);

    brdf.diffuse = surface.color * oneMinusReflectivity;
    if (applyAlphaToDiffuse) {
        brdf.diffuse *= surface.alpha;
    }
    brdf.specular = lerp(MIN_REFLECTIVITY, surface.color, surface.metallic);

    //迪士尼光照模型中的方法计算出粗糙度
    float perceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(surface.smoothness);
    brdf.roughness = PerceptualRoughnessToRoughness(perceptualRoughness); //直观方式表示的粗糙度(即: 0~1)转换为公式使用的粗糙度
    return brdf;
}

//高光反射强度取决于视角方向和完全发射方向的正对程度, 这边使用简化版CookTorrance BRDF公式计算
float SpecularStrength(Surface surface, BRDF brdf, Light light) {
    // r^2 / (d^2*max(0.1, (L·H)^2)*n), r为粗糙度
    // d = (N·H)^2*(r^2 - 1) + 1.0001, N为表面法线方向
    // H = L+V, L为光源方向, V为视角方向
    // n = 4*r+2
    float3 h = SafeNormalize(light.direction + surface.viewDirection);
    float nh2 = Square(saturate(dot(surface.normal, h)));
    float lh2 = Square(saturate(dot(light.direction, h)));
    float r2 = Square(brdf.roughness);
    float d2 = Square(nh2 * (r2 - 1.0) + 1.00001);
    float normalization = brdf.roughness * 4.0 + 2.0;
    return r2 / (d2 * max(0.1, lh2) * normalization);
}

//平行光的BRDF
float3 DirectBRDF(Surface surface, BRDF brdf, Light light) {
    //高光反射叠加漫反射(叠加用+,混合用乘)
    return SpecularStrength(surface, brdf, light) * brdf.specular + brdf.diffuse;
}

#endif
复制代码

 

ShaderLibrary/Lighting.hlsl

复制代码
#ifndef CUSTOM_LIGHTING_INCLUDED
#define CUSTOM_LIGHTING_INCLUDED

//入射光强度计算
float3 IncomingLight (Surface surface, Light light) {
    //入射光与表面法线的夹角越大, 强度越小, cos函数可以达到这样的效果
    float lambert = dot(surface.normal, light.direction);
    return saturate(lambert * light.attenuation) * light.color;
}

//单个入射光照到物体表面后的光照的计算
float3 GetLighting (Surface surface, BRDF brdf, Light light) {
    return IncomingLight(surface, light) * DirectBRDF(surface, brdf, light);
}

//所有光照的计算
float3 GetLighting (Surface surfaceWS, BRDF brdf) {
    float3 color = 0.0;
    for (int i = 0; i < GetDirectionalLightCount(); i++) {
        Light light = GetDirectionalLight(i, surfaceWS);
        color += GetLighting(surfaceWS, brdf, light);
    }
    return color;
}

#endif
复制代码

 

ShaderLibrary/Shadows.hlsl

复制代码
#ifndef CUSTOM_SHADOWS_INCLUDED
#define CUSTOM_SHADOWS_INCLUDED

#define MAX_SHADOWED_DIRECTIONAL_LIGHT_COUNT 4

TEXTURE2D_SHADOW(_DirectionalShadowAtlas);
#define SHADOW_SAMPLER sampler_linear_clamp_compare
SAMPLER_CMP(SHADOW_SAMPLER);

CBUFFER_START(_CustomShadows)
    float4x4 _DirectionalShadowMatrices[MAX_SHADOWED_DIRECTIONAL_LIGHT_COUNT];
CBUFFER_END

struct DirectionalShadowData {
    float strength; //光的阴影强度比例, 范围[0, 1]
    int tileIndex; //在阴影图的哪个tile上
};

//positionSTS: 阴影图空间坐标
float SampleDirectionalShadowAtlas(float3 positionSTS) {
    return SAMPLE_TEXTURE2D_SHADOW(_DirectionalShadowAtlas, SHADOW_SAMPLER, positionSTS);
}
 
//根据所用的阴影图和照射的物体表面信息, 获取阴影采样后的光衰减比例, 1表示不在阴影下, 0表示阴影最深
float GetDirectionalShadowAttenuation(DirectionalShadowData data, Surface surfaceWS) {
    if (data.strength <= 0.0) {
        return 1.0;
    }
    float3 positionSTS = mul(_DirectionalShadowMatrices[data.tileIndex], float4(surfaceWS.position, 1.0)).xyz;
    float shadow = SampleDirectionalShadowAtlas(positionSTS); //比shadowMap的z值大, 就是在阴影中
    if (positionSTS.x > 1 || positionSTS.y > 1 || positionSTS.x < 0 || positionSTS.y < 0)
    {
        //return shadow; //超出ShadowMap uv范围采样时, 得到的是未定义的值?
        return 1;
    }
    else
    {
        //return 0;
        return shadow;
    }
    return lerp(1.0, shadow, data.strength); //1+(shadow-1)*data.strength, 阴影值大得到的衰减小, 阴影就明显
}

#endif
复制代码

 

Shader/LitPass.hlsl

复制代码
#ifndef CUSTOM_LIT_PASS_INCLUDED
#define CUSTOM_LIT_PASS_INCLUDED

#include "../ShaderLibrary/Common.hlsl"
#include "../ShaderLibrary/Surface.hlsl"
#include "../ShaderLibrary/Shadows.hlsl"
#include "../ShaderLibrary/Light.hlsl"
#include "../ShaderLibrary/BRDF.hlsl"
#include "../ShaderLibrary/Lighting.hlsl"

//变量
TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);

UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)
    UNITY_DEFINE_INSTANCED_PROP(float4, _BaseMap_ST)
    UNITY_DEFINE_INSTANCED_PROP(float4, _BaseColor)
    UNITY_DEFINE_INSTANCED_PROP(float, _Cutoff)
    UNITY_DEFINE_INSTANCED_PROP(float, _Metallic)
    UNITY_DEFINE_INSTANCED_PROP(float, _Smoothness)
UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)

struct Attributes { //程序传入顶点着色器的数据
    float3 positionOS : POSITION; //用模型空间顶点坐标填充该变量
    float3 normalOS : NORMAL; //用模型空间法线方向填充该变量
    float2 baseUV : TEXCOORD0; //用模型的第一套纹理坐标(uv)填充该变量
    UNITY_VERTEX_INPUT_INSTANCE_ID //GPU实例化: 每个物体都会分配一个索引
};

struct Varyings { //顶点着色器传入片元着色器的数据
    float4 positionCS : SV_POSITION; //该变量存放了裁剪空间的顶点坐标
    float3 positionWS : VAR_POSITION;
    float3 normalWS : VAR_NORMAL;
    float2 baseUV : VAR_BASE_UV;
    UNITY_VERTEX_INPUT_INSTANCE_ID //GPU实例化会用到的索引
};

Varyings LitPassVertex (Attributes input) { //顶点着色器
    Varyings output;
    UNITY_SETUP_INSTANCE_ID(input); //GPU实例化分配索引
    UNITY_TRANSFER_INSTANCE_ID(input, output);

    output.positionWS = TransformObjectToWorld(input.positionOS); //模型空间转世界空间
    output.positionCS = TransformWorldToHClip(output.positionWS); //世界空间转裁剪空间
    output.normalWS = TransformObjectToWorldNormal(input.normalOS);

    float4 baseST = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseMap_ST);
    output.baseUV = input.baseUV * baseST.xy + baseST.zw;  //应用贴图的tiling和offset
    return output;
}

float4 LitPassFragment (Varyings input) : SV_TARGET { //片段着色器
    UNITY_SETUP_INSTANCE_ID(input);
    float4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.baseUV); //根据纹理坐标获取像素颜色
    float4 baseColor = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseColor);
    float4 base = baseMap * baseColor; //颜色混合用乘
    #if defined(_CLIPPING)
        clip(base.a - UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Cutoff));
    #endif

    //pbs光照计算
    Surface surface;
    surface.position = input.positionWS;
    surface.normal = normalize(input.normalWS);
    surface.viewDirection = normalize(_WorldSpaceCameraPos - input.positionWS);  //视角方向
    surface.color = base.rgb;
    surface.alpha = base.a;
    surface.metallic = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Metallic);
    surface.smoothness = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Smoothness);
    
    #if defined(_PREMULTIPLY_ALPHA)
        BRDF brdf = GetBRDF(surface, true);
    #else
        BRDF brdf = GetBRDF(surface);
    #endif
    
    float3 color = GetLighting(surface, brdf);
    return float4(color, surface.alpha);
}

#endif
复制代码

 

Shader/ShadowCasterPass.hlsl

复制代码
#ifndef CUSTOM_SHADOW_CASTER_PASS_INCLUDED
#define CUSTOM_SHADOW_CASTER_PASS_INCLUDED

#include "../ShaderLibrary/Common.hlsl"

//变量
TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);

UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)
    UNITY_DEFINE_INSTANCED_PROP(float4, _BaseMap_ST)
    UNITY_DEFINE_INSTANCED_PROP(float4, _BaseColor)
    UNITY_DEFINE_INSTANCED_PROP(float, _Cutoff)
UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)

struct Attributes { //程序传入顶点着色器的数据
    float3 positionOS : POSITION; //用模型空间顶点坐标填充该变量
    float2 baseUV : TEXCOORD0; //用模型的第一套纹理坐标(uv)填充该变量
    UNITY_VERTEX_INPUT_INSTANCE_ID //GPU实例化: 每个物体都会分配一个索引
};

struct Varyings { //顶点着色器传入片元着色器的数据
    float4 positionCS : SV_POSITION; //该变量存放了裁剪空间的顶点坐标
    float2 baseUV : VAR_BASE_UV;
    UNITY_VERTEX_INPUT_INSTANCE_ID //GPU实例化会用到的索引
};

Varyings ShadowCasterPassVertex (Attributes input) { //顶点着色器
    Varyings output;
    UNITY_SETUP_INSTANCE_ID(input); //GPU实例化分配索引
    UNITY_TRANSFER_INSTANCE_ID(input, output);

    float3 positionWS = TransformObjectToWorld(input.positionOS); //模型空间转世界空间
    output.positionCS = TransformWorldToHClip(positionWS); //世界空间转裁剪空间

    float4 baseST = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseMap_ST);
    output.baseUV = input.baseUV * baseST.xy + baseST.zw;  //应用贴图的tiling和offset
    return output;
}

void ShadowCasterPassFragment (Varyings input) { //片段着色器
    UNITY_SETUP_INSTANCE_ID(input);
    float4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.baseUV); //根据纹理坐标获取像素颜色
    float4 baseColor = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseColor);
    float4 base = baseMap * baseColor; //颜色混合用乘
    #if defined(_CLIPPING)
        clip(base.a - UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Cutoff));
    #endif
}

#endif
复制代码

 

Shader/Lit.shader

复制代码
Shader "Custom RP/Lit" {
    
    Properties {
        _BaseMap("Texture", 2D) = "white" {} //主贴图
        _BaseColor("Color", Color) = (0.5, 0.5, 0.5, 1.0) //混合颜色
        _Cutoff ("Alpha Cutoff", Range(0.0, 1.0)) = 0.5
        [Toggle(_CLIPPING)] _Clipping ("Alpha Clipping", Float) = 0

        _Metallic ("Metallic", Range(0, 1)) = 0  //金属度
        _Smoothness ("Smoothness", Range(0, 1)) = 0.5  //光滑度

        [Toggle(_PREMULTIPLY_ALPHA)] _PremulAlpha ("Premultiply Alpha", Float) = 0

        [Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend ("Src Blend", Float) = 1
        [Enum(UnityEngine.Rendering.BlendMode)] _DstBlend ("Dst Blend", Float) = 0
        [Enum(Off, 0, On, 1)] _ZWrite ("Z Write", Float) = 1
    }
    
    SubShader {
        Pass {
            Tags {
                "LightMode" = "CustomLit"
            }

            Blend [_SrcBlend] [_DstBlend]
            ZWrite [_ZWrite]

            HLSLPROGRAM //hlsl代码需要用这个包着
            #pragma target 3.5 //Shader Model版本低于3.5, 核心库无法通过编译
            #pragma shader_feature _CLIPPING
            #pragma shader_feature _PREMULTIPLY_ALPHA
            #pragma multi_compile_instancing
            #pragma vertex LitPassVertex
            #pragma fragment LitPassFragment
            #include "LitPass.hlsl"
            ENDHLSL
        }

        Pass {
            Tags {
                "LightMode" = "ShadowCaster"
            }

            ColorMask 0

            HLSLPROGRAM
            #pragma target 3.5
            #pragma shader_feature _CLIPPING
            #pragma multi_compile_instancing
            #pragma vertex ShadowCasterPassVertex
            #pragma fragment ShadowCasterPassFragment
            #include "ShadowCasterPass.hlsl"
            ENDHLSL
        }
    }

}
复制代码

 

posted @   yanghui01  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
历史上的今天:
2024-01-25 获取两个时间的相隔天数
点击右上角即可分享
微信分享提示