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 } } }
分类:
shader / SRP
, shader
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
2024-01-25 获取两个时间的相隔天数