├── .gitignore ├── CS_AssignLightsToClusters.compute ├── CS_AssignLightsToClusters.compute.meta ├── CS_ClusterSample.compute ├── CS_ClusterSample.compute.meta ├── CS_ComputeClusterAABB.compute ├── CS_ComputeClusterAABB.compute.meta ├── CS_DebugLightCount.compute ├── CS_DebugLightCount.compute.meta ├── CS_FindUniqueCluster.compute ├── CS_FindUniqueCluster.compute.meta ├── CS_UpdateIndirectArgumentBuffers.compute ├── CS_UpdateIndirectArgumentBuffers.compute.meta ├── Cgs_ClusterCommon.cginc ├── Cgs_ClusterCommon.cginc.meta ├── LightCountHeatMap.jpg ├── LightCountHeatMap.jpg.meta ├── Mtl_DebugCluster.mat ├── Mtl_DebugCluster.mat.meta ├── Mtl_DepthPre.mat ├── Mtl_DepthPre.mat.meta ├── Prefab_PointLights.prefab ├── Prefab_PointLights.prefab.meta ├── Scene_ClusterBasedLighitngTest.unity ├── Scene_ClusterBasedLighitngTest.unity.meta ├── Script_ClusterBasedLighting.cs ├── Script_ClusterBasedLighting.cs.meta ├── Script_FreeCamera.cs ├── Script_FreeCamera.cs.meta ├── Shader_DebugCluster.shader ├── Shader_DebugCluster.shader.meta ├── Shader_DepthPre.shader ├── Shader_DepthPre.shader.meta ├── Shader_DrawSceneColor.shader └── Shader_DrawSceneColor.shader.meta /.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wsqjny/Unity_ClusterBasedLighting_git/190abaf175ad64f3e8eb5cea5a07c6359445d893/.gitignore -------------------------------------------------------------------------------- /CS_AssignLightsToClusters.compute: -------------------------------------------------------------------------------- 1 | // Each #kernel tells which function to compile; you can have many kernels 2 | #pragma kernel CSMain 3 | 4 | #include"Cgs_ClusterCommon.cginc" 5 | 6 | RWStructuredBuffer RWPointLightIndexCounter_Cluster;// : register(u2); 7 | RWStructuredBuffer RWPointLightGrid_Cluster;// : register(u4); 8 | RWStructuredBuffer RWPointLightIndexList_Cluster;// : register(u6); 9 | 10 | StructuredBuffer UniqueClusters; 11 | StructuredBuffer ClusterAABBs;// : register(t1); 12 | StructuredBuffer PointLights;// : register(t8); 13 | 14 | uint PointLightCount; 15 | float4x4 _CameraLastViewMatrix; 16 | 17 | float4 WorldToView(float4 posWorld) 18 | { 19 | float4 posView = mul(_CameraLastViewMatrix, posWorld); 20 | posView.z *= -1; 21 | return posView; 22 | } 23 | 24 | //////////////////////////////////////////////////////////////////////////////////////////////// 25 | #define NUM_THREADS 1024 26 | groupshared uint gs_ClusterIndex1D; 27 | groupshared AABB gs_ClusterAABB; 28 | 29 | groupshared uint gs_PointLightCount; 30 | groupshared uint gs_SpotLightCount; 31 | groupshared uint gs_PointLightStartOffset; 32 | groupshared uint gs_SpotLightStartOffset; 33 | groupshared uint gs_PointLightList[1024]; 34 | groupshared uint gs_SpotLightList[1024]; 35 | 36 | 37 | #define AppendLight( lightIndex, counter, lightList ) \ 38 | InterlockedAdd( counter, 1, index ); \ 39 | if ( index < 1024 ) \ 40 | { \ 41 | lightList[index] = lightIndex; \ 42 | } 43 | 44 | [numthreads(NUM_THREADS, 1, 1)] 45 | void CSMain (ComputeShaderInput IN) 46 | { 47 | uint i, index; 48 | 49 | if (IN.GroupIndex == 0) 50 | { 51 | gs_PointLightCount = 0; 52 | gs_SpotLightCount = 0; 53 | 54 | gs_ClusterIndex1D = UniqueClusters[IN.GroupID.x]; 55 | gs_ClusterAABB = ClusterAABBs[gs_ClusterIndex1D]; 56 | } 57 | 58 | GroupMemoryBarrierWithGroupSync(); 59 | 60 | // Intersect point lights against AABB. 61 | for (i = IN.GroupIndex; i < PointLightCount; i += NUM_THREADS) 62 | { 63 | //if ( PointLights[i].Enabled ) 64 | { 65 | float4 pointLight = PointLights[i]; 66 | float3 pointLightPosView = WorldToView(float4(pointLight.xyz, 1)).xyz;// mul(float4(pointLight.xyz, 1), _CameraLastViewMatrix).xyz; 67 | Sphere sphere = { pointLightPosView, pointLight.w }; 68 | 69 | if (SphereInsideAABB(sphere, gs_ClusterAABB)) 70 | { 71 | AppendLight(i, gs_PointLightCount, gs_PointLightList); 72 | } 73 | } 74 | } 75 | 76 | GroupMemoryBarrierWithGroupSync(); 77 | 78 | // Now update the global light grids with the light lists and light counts. 79 | if (IN.GroupIndex == 0) 80 | { 81 | // Update light grid for point lights. 82 | InterlockedAdd(RWPointLightIndexCounter_Cluster[0], gs_PointLightCount, gs_PointLightStartOffset); 83 | RWPointLightGrid_Cluster[gs_ClusterIndex1D] = uint2(gs_PointLightStartOffset, gs_PointLightCount); 84 | } 85 | 86 | GroupMemoryBarrierWithGroupSync(); 87 | 88 | // Now update the global light index lists with the group shared light lists. 89 | for (i = IN.GroupIndex; i < gs_PointLightCount; i += NUM_THREADS) 90 | { 91 | RWPointLightIndexList_Cluster[gs_PointLightStartOffset + i] = gs_PointLightList[i]; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /CS_AssignLightsToClusters.compute.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ec1539b0a8a73c749ae1175062491705 3 | ComputeShaderImporter: 4 | externalObjects: {} 5 | currentAPIMask: 4 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /CS_ClusterSample.compute: -------------------------------------------------------------------------------- 1 | // Each #kernel tells which function to compile; you can have many kernels 2 | #pragma kernel CSMain 3 | #include "Cgs_ClusterCommon.cginc" 4 | 5 | Texture2D DepthTexture; 6 | RWStructuredBuffer RWClusterFlags; 7 | 8 | [numthreads(32,32,1)] 9 | void CSMain (uint3 id : SV_DispatchThreadID) 10 | { 11 | int2 texCoord = id.xy; 12 | float fDepth = DepthTexture.Load(uint3(texCoord, 0)).x; 13 | 14 | if (fDepth > 0) 15 | { 16 | float4 viewPos = ScreenToView(float4(texCoord, fDepth, 1)); 17 | viewPos.z *= -1; 18 | 19 | // Compute the 3D cluster index. 20 | uint3 clusterIndex3D = ComputeClusterIndex3D(texCoord, viewPos.z); 21 | // Convert to 1D cluster index. 22 | uint clusterIndex1D = ComputeClusterIndex1D(clusterIndex3D); 23 | 24 | RWClusterFlags[clusterIndex1D] = 1.0; 25 | return; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /CS_ClusterSample.compute.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b385335561d176446b9158144ed48999 3 | ComputeShaderImporter: 4 | externalObjects: {} 5 | currentAPIMask: 4 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /CS_ComputeClusterAABB.compute: -------------------------------------------------------------------------------- 1 | // Each #kernel tells which function to compile; you can have many kernels 2 | #pragma kernel CSMain 3 | #pragma enable_d3d11_debug_symbols 4 | #include "Cgs_ClusterCommon.cginc" 5 | 6 | #ifndef BLOCK_SIZE 7 | #define BLOCK_SIZE 1024 8 | #endif 9 | 10 | RWStructuredBuffer RWClusterAABBs; 11 | 12 | [numthreads(BLOCK_SIZE, 1, 1)] 13 | void CSMain(ComputeShaderInput cs_IDs) 14 | { 15 | uint clusterIndex1D = cs_IDs.DispatchThreadID.x; 16 | 17 | // Convert the 1D cluster index into a 3D index in the cluster grid. 18 | uint3 clusterIndex3D = ComputeClusterIndex3D(clusterIndex1D); 19 | 20 | // Compute the near and far planes for cluster K. 21 | Plane nearPlane = { 0.0f, 0.0f, 1.0f, ClusterCB_ViewNear * pow(abs(ClusterCB_NearK), clusterIndex3D.z) }; 22 | Plane farPlane = { 0.0f, 0.0f, 1.0f, ClusterCB_ViewNear * pow(abs(ClusterCB_NearK), clusterIndex3D.z + 1) }; 23 | 24 | // The top-left point of cluster K in screen space. 25 | float4 pMin = float4(clusterIndex3D.xy * ClusterCB_Size.xy, 0.0f, 1.0f); 26 | // The bottom-right point of cluster K in screen space. 27 | float4 pMax = float4((clusterIndex3D.xy + 1) * ClusterCB_Size.xy, 0.0f, 1.0f); 28 | 29 | // Transform the screen space points to view space. 30 | pMin = ScreenToView(pMin); 31 | pMax = ScreenToView(pMax); 32 | 33 | pMin.z *= -1; 34 | pMax.z *= -1; 35 | 36 | // Find the min and max points on the near and far planes. 37 | float3 nearMin, nearMax, farMin, farMax; 38 | // Origin (camera eye position) 39 | float3 eye = float3(0, 0, 0); 40 | IntersectLinePlane(eye, (float3)pMin, nearPlane, nearMin); 41 | IntersectLinePlane(eye, (float3)pMax, nearPlane, nearMax); 42 | IntersectLinePlane(eye, (float3)pMin, farPlane, farMin); 43 | IntersectLinePlane(eye, (float3)pMax, farPlane, farMax); 44 | 45 | float3 aabbMin = min(nearMin, min(nearMax, min(farMin, farMax))); 46 | float3 aabbMax = max(nearMin, max(nearMax, max(farMin, farMax))); 47 | 48 | AABB aabb = { float4(aabbMin, 1.0f), float4(aabbMax, 1.0f) }; 49 | 50 | RWClusterAABBs[clusterIndex1D] = aabb; 51 | } 52 | -------------------------------------------------------------------------------- /CS_ComputeClusterAABB.compute.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4925670d60116cb4cbba21c48d2844ba 3 | ComputeShaderImporter: 4 | externalObjects: {} 5 | currentAPIMask: 4 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /CS_DebugLightCount.compute: -------------------------------------------------------------------------------- 1 | // Each #kernel tells which function to compile; you can have many kernels 2 | #pragma kernel CSMain 3 | #pragma target 5.0 4 | #include "Cgs_ClusterCommon.cginc" 5 | 6 | Texture2D SourceTex; 7 | Texture2D DepthTexture; 8 | Texture2D LightCountHeatMapTex; 9 | SamplerState sampler_linear_repeat; 10 | 11 | StructuredBuffer PointLightGrid_Cluster; 12 | RWTexture2D RWDebugTexture; 13 | 14 | [numthreads(32, 32, 1)] 15 | void CSMain(uint3 id : SV_DispatchThreadID) 16 | { 17 | int2 texCoord = id.xy; 18 | float fDepth = DepthTexture.Load(uint3(texCoord, 0)).x; 19 | float4 fColor = SourceTex.Load(uint3(texCoord, 0)); 20 | 21 | uint lightCount = 0; 22 | if (fDepth > 0) 23 | { 24 | float4 viewPos = ScreenToView(float4(texCoord, fDepth, 1)); 25 | viewPos.z *= -1; 26 | 27 | // Compute the 3D cluster index. 28 | uint3 clusterIndex3D = ComputeClusterIndex3D(texCoord, viewPos.z); 29 | // Convert to 1D cluster index. 30 | uint clusterIndex1D = ComputeClusterIndex1D(clusterIndex3D); 31 | 32 | lightCount += PointLightGrid_Cluster[clusterIndex1D].y; 33 | } 34 | 35 | RWDebugTexture[texCoord] = fColor * 0.2; 36 | if (lightCount > 0) 37 | { 38 | float normalizedLightCount = lightCount / 50.0f; 39 | 40 | float4 lightCountColor = LightCountHeatMapTex.SampleLevel(sampler_linear_repeat, float2(normalizedLightCount, 0), 0);// tex2D(LightCountHeatMapTex, float2(normalizedLightCount, 0)); 41 | float3 color = lightCountColor.rgb; 42 | //float4 color = tex2D(LightCountHeatMapTex, float2(normalizedLightCount, 0)); 43 | RWDebugTexture[texCoord] += float4(color.rgb, 0.9f); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /CS_DebugLightCount.compute.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1c2171f10f6be524f9500df50be481d4 3 | ComputeShaderImporter: 4 | externalObjects: {} 5 | currentAPIMask: 4 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /CS_FindUniqueCluster.compute: -------------------------------------------------------------------------------- 1 | // Each #kernel tells which function to compile; you can have many kernels 2 | #pragma kernel CSMain 3 | #define NUM_THREADS 1024 4 | 5 | 6 | // A buffer that contains a set flag for clusters that contain samples. 7 | StructuredBuffer ClusterFlags; 8 | // For each unique cluster, append the 1D cluster index. 9 | RWStructuredBuffer RWUniqueClusters; 10 | 11 | [numthreads(NUM_THREADS, 1, 1)] 12 | void CSMain (uint3 id : SV_DispatchThreadID) 13 | { 14 | uint clusterID = id.x; 15 | if (ClusterFlags[clusterID] > 0.5) 16 | { 17 | uint i = RWUniqueClusters.IncrementCounter(); 18 | RWUniqueClusters[i] = clusterID; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /CS_FindUniqueCluster.compute.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dba29efd0d2e88942b666533dd819694 3 | ComputeShaderImporter: 4 | externalObjects: {} 5 | currentAPIMask: 4 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /CS_UpdateIndirectArgumentBuffers.compute: -------------------------------------------------------------------------------- 1 | // Each #kernel tells which function to compile; you can have many kernels 2 | #pragma kernel CSMain 3 | 4 | ByteAddressBuffer ClusterCounter; 5 | RWByteAddressBuffer AssignLightsToClustersIndirectArgumentBuffer; 6 | RWByteAddressBuffer DebugClustersIndirectArgumentBuffer; 7 | 8 | [numthreads(1,1,1)] 9 | void CSMain (uint3 id : SV_DispatchThreadID) 10 | { 11 | // Read the cluster counter. 12 | uint clusterCount = ClusterCounter.Load(0); 13 | 14 | // Update the indirect argument buffers. 15 | AssignLightsToClustersIndirectArgumentBuffer.Store3(0, uint3(clusterCount, 1, 1)); // NumThreadGroupsX, NumThreadGroupsY, NumThreadGroupsZ 16 | 17 | //if (UpdateUniqueClusters) 18 | { 19 | DebugClustersIndirectArgumentBuffer.Store4(0, uint4(clusterCount, 1, 0, 0)); // VertexCountPerInstance, InstanceCount, StartVertexLocation, StartInstanceLocation 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /CS_UpdateIndirectArgumentBuffers.compute.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cf5afb0ed079fe947b1cd9bce1c5d41c 3 | ComputeShaderImporter: 4 | externalObjects: {} 5 | currentAPIMask: 4 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Cgs_ClusterCommon.cginc: -------------------------------------------------------------------------------- 1 | 2 | // Camera Data 3 | float4x4 _InverseProjectionMatrix; 4 | 5 | 6 | //Cluster Data 7 | uint3 ClusterCB_GridDim; // The 3D dimensions of the cluster grid. 8 | float ClusterCB_ViewNear; // The distance to the near clipping plane. (Used for computing the index in the cluster grid) 9 | uint2 ClusterCB_Size; // The size of a cluster in screen space (pixels). 10 | float ClusterCB_NearK; // ( 1 + ( 2 * tan( fov * 0.5 ) / ClusterGridDim.y ) ) // Used to compute the near plane for clusters at depth k. 11 | float ClusterCB_LogGridDimY; // 1.0f / log( 1 + ( tan( fov * 0.5 ) / ClusterGridDim.y ) 12 | float4 ClusterCB_ScreenDimensions; 13 | 14 | 15 | struct AABB 16 | { 17 | float4 Min; 18 | float4 Max; 19 | }; 20 | 21 | struct Sphere 22 | { 23 | float3 c; // Center point. 24 | float r; // Radius. 25 | }; 26 | 27 | struct Plane 28 | { 29 | float3 N; // Plane normal. 30 | float d; // Distance to origin. 31 | }; 32 | 33 | 34 | struct ComputeShaderInput 35 | { 36 | uint3 GroupID : SV_GroupID; // 3D index of the thread group in the dispatch. 37 | uint3 GroupThreadID : SV_GroupThreadID; // 3D index of local thread ID in a thread group. 38 | uint3 DispatchThreadID : SV_DispatchThreadID; // 3D index of global thread ID in the dispatch. 39 | uint GroupIndex : SV_GroupIndex; // Flattened local index of the thread within a thread group. 40 | }; 41 | 42 | 43 | 44 | 45 | /** 46 | * Convert a 1D cluster index into a 3D cluster index. 47 | */ 48 | uint3 ComputeClusterIndex3D(uint clusterIndex1D) 49 | { 50 | uint i = clusterIndex1D % ClusterCB_GridDim.x; 51 | uint j = clusterIndex1D % (ClusterCB_GridDim.x * ClusterCB_GridDim.y) / ClusterCB_GridDim.x; 52 | uint k = clusterIndex1D / (ClusterCB_GridDim.x * ClusterCB_GridDim.y); 53 | 54 | return uint3(i, j, k); 55 | } 56 | 57 | /** 58 | * Convert the 3D cluster index into a 1D cluster index. 59 | */ 60 | uint ComputeClusterIndex1D(uint3 clusterIndex3D) 61 | { 62 | return clusterIndex3D.x + (ClusterCB_GridDim.x * (clusterIndex3D.y + ClusterCB_GridDim.y * clusterIndex3D.z)); 63 | } 64 | 65 | /** 66 | * Compute the 3D cluster index from a 2D screen position and Z depth in view space. 67 | * source: Clustered deferred and forward shading (Olsson, Billeter, Assarsson, 2012) 68 | */ 69 | uint3 ComputeClusterIndex3D(float2 screenPos, float viewZ) 70 | { 71 | uint i = screenPos.x / ClusterCB_Size.x; 72 | uint j = screenPos.y / ClusterCB_Size.y; 73 | // It is assumed that view space z is negative (right-handed coordinate system) 74 | // so the view-space z coordinate needs to be negated to make it positive. 75 | uint k = log(viewZ / ClusterCB_ViewNear) * ClusterCB_LogGridDimY; 76 | 77 | return uint3(i, j, k); 78 | } 79 | 80 | /// Functions.hlsli 81 | // Convert clip space coordinates to view space 82 | float4 ClipToView(float4 clip) 83 | { 84 | // View space position. 85 | //float4 view = mul(clip, g_Com.Camera.CameraProjectInv); 86 | float4 view = mul(_InverseProjectionMatrix, clip); 87 | // Perspecitive projection. 88 | view = view / view.w; 89 | 90 | return view; 91 | } 92 | 93 | // Convert screen space coordinates to view space. 94 | float4 ScreenToView(float4 screen) 95 | { 96 | // Convert to normalized texture coordinates in the range [0 .. 1]. 97 | float2 texCoord = screen.xy * ClusterCB_ScreenDimensions.zw; 98 | 99 | // Convert to clip space 100 | float4 clip = float4(texCoord * 2.0f - 1.0f, screen.z, screen.w); 101 | 102 | return ClipToView(clip); 103 | } 104 | 105 | /** 106 | * Find the intersection of a line segment with a plane. 107 | * This function will return true if an intersection point 108 | * was found or false if no intersection could be found. 109 | * Source: Real-time collision detection, Christer Ericson (2005) 110 | */ 111 | bool IntersectLinePlane(float3 a, float3 b, Plane p, out float3 q) 112 | { 113 | float3 ab = b - a; 114 | 115 | float t = (p.d - dot(p.N, a)) / dot(p.N, ab); 116 | 117 | bool intersect = (t >= 0.0f && t <= 1.0f); 118 | 119 | q = float3(0, 0, 0); 120 | if (intersect) 121 | { 122 | q = a + t * ab; 123 | } 124 | 125 | return intersect; 126 | } 127 | 128 | 129 | // Compute the square distance between a point p and an AABB b. 130 | // Source: Real-time collision detection, Christer Ericson (2005) 131 | float SqDistancePointAABB(float3 p, AABB b) 132 | { 133 | float sqDistance = 0.0f; 134 | 135 | for (int i = 0; i < 3; ++i) 136 | { 137 | float v = p[i]; 138 | 139 | if (v < b.Min[i]) sqDistance += pow(b.Min[i] - v, 2); 140 | if (v > b.Max[i]) sqDistance += pow(v - b.Max[i], 2); 141 | } 142 | 143 | return sqDistance; 144 | } 145 | 146 | // Check to see if a sphere is interesecting an AABB 147 | // Source: Real-time collision detection, Christer Ericson (2005) 148 | bool SphereInsideAABB(Sphere sphere, AABB aabb) 149 | { 150 | float sqDistance = SqDistancePointAABB(sphere.c, aabb); 151 | 152 | return sqDistance <= sphere.r * sphere.r; 153 | } -------------------------------------------------------------------------------- /Cgs_ClusterCommon.cginc.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 15901749d6c3872489b927d51211c260 3 | ShaderImporter: 4 | externalObjects: {} 5 | defaultTextures: [] 6 | nonModifiableTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /LightCountHeatMap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wsqjny/Unity_ClusterBasedLighting_git/190abaf175ad64f3e8eb5cea5a07c6359445d893/LightCountHeatMap.jpg -------------------------------------------------------------------------------- /LightCountHeatMap.jpg.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dbcda371860f1a5429724c9bc0510b9a 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 10 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | grayScaleToAlpha: 0 27 | generateCubemap: 6 28 | cubemapConvolution: 0 29 | seamlessCubemap: 0 30 | textureFormat: 1 31 | maxTextureSize: 2048 32 | textureSettings: 33 | serializedVersion: 2 34 | filterMode: -1 35 | aniso: -1 36 | mipBias: -100 37 | wrapU: -1 38 | wrapV: -1 39 | wrapW: -1 40 | nPOTScale: 1 41 | lightmap: 0 42 | compressionQuality: 50 43 | spriteMode: 0 44 | spriteExtrude: 1 45 | spriteMeshType: 1 46 | alignment: 0 47 | spritePivot: {x: 0.5, y: 0.5} 48 | spritePixelsToUnits: 100 49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 50 | spriteGenerateFallbackPhysicsShape: 1 51 | alphaUsage: 1 52 | alphaIsTransparency: 0 53 | spriteTessellationDetail: -1 54 | textureType: 0 55 | textureShape: 1 56 | singleChannelComponent: 0 57 | maxTextureSizeSet: 0 58 | compressionQualitySet: 0 59 | textureFormatSet: 0 60 | platformSettings: 61 | - serializedVersion: 2 62 | buildTarget: DefaultTexturePlatform 63 | maxTextureSize: 2048 64 | resizeAlgorithm: 0 65 | textureFormat: -1 66 | textureCompression: 1 67 | compressionQuality: 50 68 | crunchedCompression: 0 69 | allowsAlphaSplitting: 0 70 | overridden: 0 71 | androidETC2FallbackOverride: 0 72 | spriteSheet: 73 | serializedVersion: 2 74 | sprites: [] 75 | outline: [] 76 | physicsShape: [] 77 | bones: [] 78 | spriteID: 79 | internalID: 0 80 | vertices: [] 81 | indices: 82 | edges: [] 83 | weights: [] 84 | secondaryTextures: [] 85 | spritePackingTag: 86 | pSDRemoveMatte: 0 87 | pSDShowRemoveMatteOption: 0 88 | userData: 89 | assetBundleName: 90 | assetBundleVariant: 91 | -------------------------------------------------------------------------------- /Mtl_DebugCluster.mat: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!21 &2100000 4 | Material: 5 | serializedVersion: 6 6 | m_ObjectHideFlags: 0 7 | m_CorrespondingSourceObject: {fileID: 0} 8 | m_PrefabInstance: {fileID: 0} 9 | m_PrefabAsset: {fileID: 0} 10 | m_Name: Mtl_DebugCluster 11 | m_Shader: {fileID: 4800000, guid: 71ed406ec57de7c469ea4fdcc2525959, type: 3} 12 | m_ShaderKeywords: 13 | m_LightmapFlags: 4 14 | m_EnableInstancingVariants: 0 15 | m_DoubleSidedGI: 0 16 | m_CustomRenderQueue: -1 17 | stringTagMap: {} 18 | disabledShaderPasses: [] 19 | m_SavedProperties: 20 | serializedVersion: 3 21 | m_TexEnvs: 22 | - _BumpMap: 23 | m_Texture: {fileID: 0} 24 | m_Scale: {x: 1, y: 1} 25 | m_Offset: {x: 0, y: 0} 26 | - _DetailAlbedoMap: 27 | m_Texture: {fileID: 0} 28 | m_Scale: {x: 1, y: 1} 29 | m_Offset: {x: 0, y: 0} 30 | - _DetailMask: 31 | m_Texture: {fileID: 0} 32 | m_Scale: {x: 1, y: 1} 33 | m_Offset: {x: 0, y: 0} 34 | - _DetailNormalMap: 35 | m_Texture: {fileID: 0} 36 | m_Scale: {x: 1, y: 1} 37 | m_Offset: {x: 0, y: 0} 38 | - _EmissionMap: 39 | m_Texture: {fileID: 0} 40 | m_Scale: {x: 1, y: 1} 41 | m_Offset: {x: 0, y: 0} 42 | - _MainTex: 43 | m_Texture: {fileID: 0} 44 | m_Scale: {x: 1, y: 1} 45 | m_Offset: {x: 0, y: 0} 46 | - _MetallicGlossMap: 47 | m_Texture: {fileID: 0} 48 | m_Scale: {x: 1, y: 1} 49 | m_Offset: {x: 0, y: 0} 50 | - _OcclusionMap: 51 | m_Texture: {fileID: 0} 52 | m_Scale: {x: 1, y: 1} 53 | m_Offset: {x: 0, y: 0} 54 | - _ParallaxMap: 55 | m_Texture: {fileID: 0} 56 | m_Scale: {x: 1, y: 1} 57 | m_Offset: {x: 0, y: 0} 58 | m_Floats: 59 | - _BumpScale: 1 60 | - _Cutoff: 0.5 61 | - _DetailNormalMapScale: 1 62 | - _DstBlend: 0 63 | - _GlossMapScale: 1 64 | - _Glossiness: 0.5 65 | - _GlossyReflections: 1 66 | - _Metallic: 0 67 | - _Mode: 0 68 | - _OcclusionStrength: 1 69 | - _Parallax: 0.02 70 | - _SmoothnessTextureChannel: 0 71 | - _SpecularHighlights: 1 72 | - _SrcBlend: 1 73 | - _UVSec: 0 74 | - _ZWrite: 1 75 | m_Colors: 76 | - _Color: {r: 1, g: 1, b: 1, a: 1} 77 | - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} 78 | -------------------------------------------------------------------------------- /Mtl_DebugCluster.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4acde06a77ad6b843bb1d4b5c6602af6 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 2100000 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Mtl_DepthPre.mat: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!21 &2100000 4 | Material: 5 | serializedVersion: 6 6 | m_ObjectHideFlags: 0 7 | m_CorrespondingSourceObject: {fileID: 0} 8 | m_PrefabInstance: {fileID: 0} 9 | m_PrefabAsset: {fileID: 0} 10 | m_Name: Mtl_DepthPre 11 | m_Shader: {fileID: 4800000, guid: 8bae537807914ef4a8e0cba897c83008, type: 3} 12 | m_ShaderKeywords: 13 | m_LightmapFlags: 4 14 | m_EnableInstancingVariants: 0 15 | m_DoubleSidedGI: 0 16 | m_CustomRenderQueue: -1 17 | stringTagMap: {} 18 | disabledShaderPasses: [] 19 | m_SavedProperties: 20 | serializedVersion: 3 21 | m_TexEnvs: 22 | - _BumpMap: 23 | m_Texture: {fileID: 0} 24 | m_Scale: {x: 1, y: 1} 25 | m_Offset: {x: 0, y: 0} 26 | - _DetailAlbedoMap: 27 | m_Texture: {fileID: 0} 28 | m_Scale: {x: 1, y: 1} 29 | m_Offset: {x: 0, y: 0} 30 | - _DetailMask: 31 | m_Texture: {fileID: 0} 32 | m_Scale: {x: 1, y: 1} 33 | m_Offset: {x: 0, y: 0} 34 | - _DetailNormalMap: 35 | m_Texture: {fileID: 0} 36 | m_Scale: {x: 1, y: 1} 37 | m_Offset: {x: 0, y: 0} 38 | - _EmissionMap: 39 | m_Texture: {fileID: 0} 40 | m_Scale: {x: 1, y: 1} 41 | m_Offset: {x: 0, y: 0} 42 | - _MainTex: 43 | m_Texture: {fileID: 0} 44 | m_Scale: {x: 1, y: 1} 45 | m_Offset: {x: 0, y: 0} 46 | - _MetallicGlossMap: 47 | m_Texture: {fileID: 0} 48 | m_Scale: {x: 1, y: 1} 49 | m_Offset: {x: 0, y: 0} 50 | - _OcclusionMap: 51 | m_Texture: {fileID: 0} 52 | m_Scale: {x: 1, y: 1} 53 | m_Offset: {x: 0, y: 0} 54 | - _ParallaxMap: 55 | m_Texture: {fileID: 0} 56 | m_Scale: {x: 1, y: 1} 57 | m_Offset: {x: 0, y: 0} 58 | m_Floats: 59 | - _BumpScale: 1 60 | - _Cutoff: 0.5 61 | - _DetailNormalMapScale: 1 62 | - _DstBlend: 0 63 | - _GlossMapScale: 1 64 | - _Glossiness: 0.5 65 | - _GlossyReflections: 1 66 | - _Metallic: 0 67 | - _Mode: 0 68 | - _OcclusionStrength: 1 69 | - _Parallax: 0.02 70 | - _SmoothnessTextureChannel: 0 71 | - _SpecularHighlights: 1 72 | - _SrcBlend: 1 73 | - _UVSec: 0 74 | - _ZWrite: 1 75 | m_Colors: 76 | - _Color: {r: 1, g: 1, b: 1, a: 1} 77 | - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} 78 | -------------------------------------------------------------------------------- /Mtl_DepthPre.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 335ac4a317af68b48b032d3983da4bf1 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 2100000 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Prefab_PointLights.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 21b3f2009a12e304786534ceb59c3bf6 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Scene_ClusterBasedLighitngTest.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7e7d035a3ca4e564c864a3dc6aae2cba 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Script_ClusterBasedLighting.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using UnityEngine; 5 | 6 | 7 | struct CD_DIM 8 | { 9 | public float fieldOfViewY; 10 | public float zNear; 11 | public float zFar; 12 | 13 | public float sD; 14 | public float logDimY; 15 | public float logDepth; 16 | 17 | public int clusterDimX; 18 | public int clusterDimY; 19 | public int clusterDimZ; 20 | public int clusterDimXYZ; 21 | }; 22 | 23 | struct AABB 24 | { 25 | public Vector4 Min; 26 | public Vector4 Max; 27 | }; 28 | 29 | struct ShaderIDs 30 | { 31 | internal static readonly int InverseProjectionMatrix = Shader.PropertyToID("_InverseProjectionMatrix"); 32 | internal static readonly int ClusterCB_ViewNear = Shader.PropertyToID("ClusterCB_ViewNear"); 33 | internal static readonly int ClusterCB_ScreenDimensions = Shader.PropertyToID("ClusterCB_ScreenDimensions"); 34 | internal static readonly int ClusterCB_GridDim = Shader.PropertyToID("ClusterCB_GridDim"); 35 | internal static readonly int ClusterCB_Size = Shader.PropertyToID("ClusterCB_Size"); 36 | internal static readonly int ClusterCB_NearK = Shader.PropertyToID("ClusterCB_NearK"); 37 | internal static readonly int ClusterCB_LogGridDimY = Shader.PropertyToID("ClusterCB_LogGridDimY"); 38 | internal static readonly int DepthTexture = Shader.PropertyToID("DepthTexture"); 39 | internal static readonly int RWClusterFlags = Shader.PropertyToID("RWClusterFlags"); 40 | }; 41 | 42 | //[ExecuteInEditMode] 43 | #if UNITY_5_4_OR_NEWER 44 | //[ImageEffectAllowedInSceneView] 45 | #endif 46 | public class Script_ClusterBasedLighting : MonoBehaviour 47 | { 48 | //////////////////////////////////////////////////////////////////////////////////////////////// 49 | ///Compute Shaders 50 | public ComputeShader cs_ComputeClusterAABB; 51 | public ComputeShader cs_ClusterSample; 52 | public ComputeShader cs_AssignLightsToClusts; 53 | public ComputeShader cs_FindUniqueClusters; 54 | public ComputeShader cs_UpdateIndirectArgumentBuffers; 55 | public ComputeShader cs_DebugLightCount; 56 | 57 | private ComputeBuffer cb_ClusterAABBs; 58 | private ComputeBuffer cb_ClusterPointLightIndexCounter; 59 | private ComputeBuffer cb_ClusterPointLightGrid; 60 | private ComputeBuffer cb_ClusterPointLightIndexList; 61 | private ComputeBuffer cb_PointLightPosRadius; 62 | private ComputeBuffer cb_PointLightColor; 63 | 64 | private ComputeBuffer cb_ClusterFlag; 65 | private ComputeBuffer cb_UniqueClusters; 66 | private ComputeBuffer cb_UniqueClusterCount; 67 | private ComputeBuffer cb_IAB_AssignLightsToClusters; 68 | private ComputeBuffer cb_IAB_DrawDebugClusters; 69 | 70 | public Material mtl_DebugCluster; 71 | public Material mtl_DpethPrePass; 72 | 73 | /// 74 | /// Light 75 | /// 76 | public GameObject go_PointLightGroup; 77 | private List lst_Light; 78 | 79 | /// 80 | /// SceneObject 81 | /// 82 | public GameObject go_SceneListParent; 83 | private List lst_Mtl; 84 | private List lst_Mesh; 85 | private List lst_TF; 86 | 87 | private RenderTexture _rtColor; 88 | private RenderTexture _rtDepth; 89 | private RenderTexture _rtDebugLightCount; 90 | 91 | 92 | public bool bUpdateCluster = true; 93 | public Texture2D texLightLountTex; 94 | 95 | private Camera _camera; 96 | 97 | private CD_DIM m_DimData; 98 | private int m_ClusterGridBlockSize = 64; 99 | private int m_AVERAGE_OVERLAPPING_LIGHTS_PER_CLUSTER = 20; 100 | private int MAX_NUM_LIGHTS = 2 * 1024; 101 | 102 | private Matrix4x4 mmatPreviousView; 103 | 104 | void Start() 105 | { 106 | _camera = Camera.main; 107 | 108 | //////////////////////////////////////////////////////////////////////////////////////////////// 109 | /// Init RT 110 | _rtColor = new RenderTexture(Screen.width, Screen.height, 24); 111 | _rtDepth = new RenderTexture(Screen.width, Screen.height, 24, RenderTextureFormat.Depth, RenderTextureReadWrite.Linear); 112 | 113 | _rtDebugLightCount = new RenderTexture(Screen.width, Screen.height, 24); 114 | _rtDebugLightCount.enableRandomWrite = true; 115 | _rtDebugLightCount.Create(); 116 | 117 | //////////////////////////////////////////////////////////////////////////////////////////////// 118 | /// On Reisze 119 | OnResize(); 120 | 121 | //////////////////////////////////////////////////////////////////////////////////////////////// 122 | /// Init Light 123 | InitLightBuffer(); 124 | Light[] l_Parent = go_PointLightGroup.GetComponentsInChildren(); 125 | lst_Light = new List(); 126 | foreach (Light l in l_Parent) 127 | { 128 | lst_Light.Add(l); 129 | } 130 | 131 | //////////////////////////////////////////////////////////////////////////////////////////////// 132 | ///Init SceneObject 133 | InitSceneObject(); 134 | } 135 | 136 | 137 | void OnRenderImage(RenderTexture sourceTexture, RenderTexture destTexture) 138 | { 139 | Graphics.SetRenderTarget(_rtColor.colorBuffer, _rtDepth.depthBuffer); 140 | GL.Clear(true, true, Color.gray); 141 | 142 | UpdateLightBuffer(); 143 | UpdateCamera(); 144 | Pass_DepthPre(); 145 | 146 | if(bUpdateCluster) 147 | { 148 | Pass_ClusterSample_CSVer(); 149 | } 150 | 151 | Pass_FinduniqueCluster(); 152 | 153 | ComputeBuffer.CopyCount(cb_UniqueClusters, cb_UniqueClusterCount, 0); 154 | 155 | Pass_UpdateIndirectArgumentBuffers(); 156 | 157 | Pass_AssignLightsToClusts(); 158 | 159 | Graphics.SetRenderTarget(_rtColor.colorBuffer, _rtDepth.depthBuffer); 160 | Pass_DrawSceneColor(); 161 | //Pass_DebugCluster(); 162 | //Pass_DebugLightCount(); 163 | 164 | Graphics.Blit(_rtColor, destTexture); 165 | //Graphics.Blit(_rtDebugLightCount, destTexture); 166 | } 167 | 168 | 169 | void Pass_ComputeClusterAABB() 170 | { 171 | var projectionMatrix = GL.GetGPUProjectionMatrix(_camera.projectionMatrix, false); 172 | var projectionMatrixInvers = projectionMatrix.inverse; 173 | cs_ComputeClusterAABB.SetMatrix("_InverseProjectionMatrix", projectionMatrixInvers); 174 | 175 | UpdateClusterCBuffer(cs_ComputeClusterAABB); 176 | 177 | int threadGroups = Mathf.CeilToInt(m_DimData.clusterDimXYZ / 1024.0f); 178 | 179 | int kernel = cs_ComputeClusterAABB.FindKernel("CSMain"); 180 | cs_ComputeClusterAABB.SetBuffer(kernel, "RWClusterAABBs", cb_ClusterAABBs); 181 | cs_ComputeClusterAABB.Dispatch(kernel, threadGroups, 1, 1); 182 | 183 | AABB[] output = new AABB[100]; 184 | cb_ClusterAABBs.GetData(output); 185 | 186 | // Debug.Log(output); 187 | } 188 | 189 | void Pass_AssignLightsToClusts() 190 | { 191 | ClearLightGirdIndexCounter(); 192 | 193 | int kernel = cs_AssignLightsToClusts.FindKernel("CSMain"); 194 | 195 | //Output 196 | cs_AssignLightsToClusts.SetBuffer(kernel, "RWPointLightIndexCounter_Cluster", cb_ClusterPointLightIndexCounter); 197 | cs_AssignLightsToClusts.SetBuffer(kernel, "RWPointLightGrid_Cluster", cb_ClusterPointLightGrid); 198 | cs_AssignLightsToClusts.SetBuffer(kernel, "RWPointLightIndexList_Cluster", cb_ClusterPointLightIndexList); 199 | 200 | //Input 201 | cs_AssignLightsToClusts.SetInt("PointLightCount", lst_Light.Count); 202 | cs_AssignLightsToClusts.SetMatrix("_CameraLastViewMatrix", _camera.worldToCameraMatrix); 203 | cs_AssignLightsToClusts.SetBuffer(kernel, "PointLights", cb_PointLightPosRadius); 204 | cs_AssignLightsToClusts.SetBuffer(kernel, "ClusterAABBs", cb_ClusterAABBs); 205 | cs_AssignLightsToClusts.SetBuffer(kernel, "UniqueClusters", cb_UniqueClusters); 206 | 207 | //cs_AssignLightsToClusts.Dispatch(kernel, m_DimData.clusterDimXYZ, 1, 1); 208 | cs_AssignLightsToClusts.DispatchIndirect(kernel, cb_IAB_AssignLightsToClusters); 209 | } 210 | 211 | void Pass_DepthPre() 212 | { 213 | mtl_DpethPrePass.SetPass(0); 214 | DrawMeshListNow(); 215 | } 216 | 217 | void Pass_ClusterSample_CSVer() 218 | { 219 | ClearClusterFlag(); 220 | UpdateClusterCBuffer(cs_ClusterSample); 221 | 222 | var projectionMatrix = GL.GetGPUProjectionMatrix(_camera.projectionMatrix, true); 223 | var projectionMatrixInvers = projectionMatrix.inverse; 224 | 225 | int kernel = cs_ClusterSample.FindKernel("CSMain"); 226 | Vector4 screenDim = new Vector4((float)Screen.width, (float)Screen.height, 1.0f / Screen.width, 1.0f / Screen.height); 227 | 228 | cs_ClusterSample.SetTexture(kernel, ShaderIDs.DepthTexture, _rtDepth); 229 | cs_ClusterSample.SetBuffer(kernel, ShaderIDs.RWClusterFlags, cb_ClusterFlag); 230 | cs_ClusterSample.SetMatrix(ShaderIDs.InverseProjectionMatrix, projectionMatrixInvers); 231 | cs_ClusterSample.SetVector(ShaderIDs.ClusterCB_ScreenDimensions, screenDim); 232 | 233 | cs_ClusterSample.Dispatch(kernel, Mathf.CeilToInt(Screen.width / 32.0f), Mathf.CeilToInt(Screen.height / 32.0f), 1); 234 | } 235 | 236 | void Pass_FinduniqueCluster() 237 | { 238 | ClearUniqueCluster(); 239 | 240 | cb_UniqueClusters.SetCounterValue(0); 241 | 242 | int threadGroups = Mathf.CeilToInt(m_DimData.clusterDimXYZ / 1024.0f); 243 | 244 | int kernel = cs_FindUniqueClusters.FindKernel("CSMain"); 245 | cs_FindUniqueClusters.SetBuffer(kernel, "RWUniqueClusters", cb_UniqueClusters); 246 | cs_FindUniqueClusters.SetBuffer(kernel, "ClusterFlags", cb_ClusterFlag); 247 | cs_FindUniqueClusters.Dispatch(kernel, threadGroups, 1, 1); 248 | } 249 | 250 | void Pass_UpdateIndirectArgumentBuffers() 251 | { 252 | int kernel = cs_UpdateIndirectArgumentBuffers.FindKernel("CSMain"); 253 | cs_UpdateIndirectArgumentBuffers.SetBuffer(kernel, "ClusterCounter", cb_UniqueClusterCount); 254 | cs_UpdateIndirectArgumentBuffers.SetBuffer(kernel, "AssignLightsToClustersIndirectArgumentBuffer", cb_IAB_AssignLightsToClusters); 255 | cs_UpdateIndirectArgumentBuffers.SetBuffer(kernel, "DebugClustersIndirectArgumentBuffer", cb_IAB_DrawDebugClusters); 256 | cs_UpdateIndirectArgumentBuffers.Dispatch(kernel, 1, 1, 1); 257 | } 258 | 259 | void Pass_DrawSceneColor() 260 | { 261 | //GL.wireframe = true; 262 | for (int i = 0; i < lst_Mesh.Count; i++) 263 | { 264 | UpdateClusterCBufferForMtl(lst_Mtl[i]); 265 | 266 | lst_Mtl[i].SetBuffer("PointLightGrid_Cluster", cb_ClusterPointLightGrid); 267 | lst_Mtl[i].SetBuffer("PointLightIndexList_Cluster", cb_ClusterPointLightIndexList); 268 | lst_Mtl[i].SetBuffer("PointLights", cb_PointLightPosRadius); 269 | lst_Mtl[i].SetBuffer("PointLightsColors", cb_PointLightColor); 270 | 271 | lst_Mtl[i].SetPass(0); 272 | Graphics.DrawMeshNow(lst_Mesh[i], lst_TF[i].localToWorldMatrix); 273 | } 274 | //GL.wireframe = false; 275 | } 276 | 277 | void Pass_DebugCluster() 278 | { 279 | GL.wireframe = true; 280 | 281 | mtl_DebugCluster.SetBuffer("ClusterAABBs", cb_ClusterAABBs); 282 | mtl_DebugCluster.SetBuffer("PointLightGrid_Cluster", cb_ClusterPointLightGrid); 283 | mtl_DebugCluster.SetBuffer("UniqueClusters", cb_UniqueClusters); 284 | mtl_DebugCluster.SetMatrix("_ViewInvMatrix", mmatPreviousView.inverse); 285 | 286 | mtl_DebugCluster.SetPass(0); 287 | //Graphics.DrawProceduralNow(MeshTopology.Points, m_DimData.clusterDimXYZ); 288 | Graphics.DrawProceduralIndirectNow(MeshTopology.Points, cb_IAB_DrawDebugClusters); 289 | 290 | GL.wireframe = false; 291 | } 292 | 293 | void Pass_DebugLightCount() 294 | { 295 | UpdateClusterCBuffer(cs_DebugLightCount); 296 | 297 | var projectionMatrix = GL.GetGPUProjectionMatrix(_camera.projectionMatrix, true); 298 | var projectionMatrixInvers = projectionMatrix.inverse; 299 | cs_DebugLightCount.SetMatrix(ShaderIDs.InverseProjectionMatrix, projectionMatrixInvers); 300 | 301 | int kernel = cs_ComputeClusterAABB.FindKernel("CSMain"); 302 | cs_DebugLightCount.SetTexture(kernel, "RWDebugTexture", _rtDebugLightCount); 303 | cs_DebugLightCount.SetTexture(kernel, "DepthTexture", _rtDepth); 304 | cs_DebugLightCount.SetTexture(kernel, "LightCountHeatMapTex", texLightLountTex); 305 | cs_DebugLightCount.SetTexture(kernel, "SourceTex", _rtColor); 306 | cs_DebugLightCount.SetBuffer(kernel, "PointLightGrid_Cluster", cb_ClusterPointLightGrid); 307 | cs_DebugLightCount.Dispatch(kernel, Mathf.CeilToInt(Screen.width / 32.0f), Mathf.CeilToInt(Screen.height / 32.0f), 1); 308 | } 309 | 310 | void DrawMeshListNow() 311 | { 312 | for (int i = 0; i < lst_Mesh.Count; i++) 313 | { 314 | Graphics.DrawMeshNow(lst_Mesh[i], lst_TF[i].localToWorldMatrix); 315 | } 316 | } 317 | 318 | void UpdateClusterCBuffer(ComputeShader cs) 319 | { 320 | int[] gridDims = { m_DimData.clusterDimX, m_DimData.clusterDimY, m_DimData.clusterDimZ }; 321 | int[] sizes = { m_ClusterGridBlockSize, m_ClusterGridBlockSize }; 322 | Vector4 screenDim = new Vector4((float)Screen.width, (float)Screen.height, 1.0f / Screen.width, 1.0f / Screen.height); 323 | float viewNear = m_DimData.zNear; 324 | 325 | cs.SetInts(ShaderIDs.ClusterCB_GridDim, gridDims); 326 | cs.SetInts(ShaderIDs.ClusterCB_Size, sizes); 327 | cs.SetFloat(ShaderIDs.ClusterCB_ViewNear, viewNear); 328 | cs.SetFloat(ShaderIDs.ClusterCB_NearK, 1.0f + m_DimData.sD); 329 | cs.SetFloat(ShaderIDs.ClusterCB_LogGridDimY, m_DimData.logDimY); 330 | cs.SetVector(ShaderIDs.ClusterCB_ScreenDimensions, screenDim); 331 | } 332 | 333 | void UpdateClusterCBufferForMtl(Material mtl) 334 | { 335 | int[] gridDims = { m_DimData.clusterDimX, m_DimData.clusterDimY, m_DimData.clusterDimZ }; 336 | int[] sizes = { m_ClusterGridBlockSize, m_ClusterGridBlockSize }; 337 | Vector4 screenDim = new Vector4((float)Screen.width, (float)Screen.height, 1.0f / Screen.width, 1.0f / Screen.height); 338 | float viewNear = m_DimData.zNear; 339 | 340 | mtl.SetInt("ClusterCB_GridDimX", gridDims[0]); 341 | mtl.SetInt("ClusterCB_GridDimY", gridDims[1]); 342 | mtl.SetInt("ClusterCB_GridDimZ", gridDims[2]); 343 | mtl.SetFloat("ClusterCB_ViewNear", viewNear); 344 | mtl.SetInt("ClusterCB_SizeX", sizes[0]); 345 | mtl.SetInt("ClusterCB_SizeY", sizes[1]); 346 | mtl.SetFloat("ClusterCB_NearK", 1.0f + m_DimData.sD); 347 | mtl.SetFloat("ClusterCB_LogGridDimY", m_DimData.logDimY); 348 | mtl.SetVector("ClusterCB_ScreenDimensions", screenDim); 349 | } 350 | 351 | void UpdateLightBuffer() 352 | { 353 | List lightPosRatioList = new List(); 354 | foreach (var lit in lst_Light) 355 | { 356 | lightPosRatioList.Add(new Vector4(lit.transform.position.x, lit.transform.position.y, lit.transform.position.z, lit.range)); 357 | } 358 | 359 | cb_PointLightPosRadius.SetData(lightPosRatioList); 360 | } 361 | 362 | void UpdateCamera() 363 | { 364 | Matrix4x4 matCameraView = _camera.worldToCameraMatrix; 365 | if (bUpdateCluster) 366 | { 367 | mmatPreviousView = matCameraView; 368 | } 369 | } 370 | 371 | void CalculateMDim(Camera cam) 372 | { 373 | // The half-angle of the field of view in the Y-direction. 374 | float fieldOfViewY = cam.fieldOfView * Mathf.Deg2Rad * 0.5f;//Degree 2 Radiance: Param.CameraInfo.Property.Perspective.fFovAngleY * 0.5f; 375 | float zNear = cam.nearClipPlane;// Param.CameraInfo.Property.Perspective.fMinVisibleDistance; 376 | float zFar = cam.farClipPlane;// Param.CameraInfo.Property.Perspective.fMaxVisibleDistance; 377 | 378 | // Number of clusters in the screen X direction. 379 | int clusterDimX = Mathf.CeilToInt(Screen.width / (float)m_ClusterGridBlockSize); 380 | // Number of clusters in the screen Y direction. 381 | int clusterDimY = Mathf.CeilToInt(Screen.height / (float)m_ClusterGridBlockSize); 382 | 383 | // The depth of the cluster grid during clustered rendering is dependent on the 384 | // number of clusters subdivisions in the screen Y direction. 385 | // Source: Clustered Deferred and Forward Shading (2012) (Ola Olsson, Markus Billeter, Ulf Assarsson). 386 | float sD = 2.0f * Mathf.Tan(fieldOfViewY) / (float)clusterDimY; 387 | float logDimY = 1.0f / Mathf.Log(1.0f + sD); 388 | 389 | float logDepth = Mathf.Log(zFar / zNear); 390 | int clusterDimZ = Mathf.FloorToInt(logDepth * logDimY); 391 | 392 | m_DimData.zNear = zNear; 393 | m_DimData.zFar = zFar; 394 | m_DimData.sD = sD; 395 | m_DimData.fieldOfViewY = fieldOfViewY; 396 | m_DimData.logDepth = logDepth; 397 | m_DimData.logDimY = logDimY; 398 | m_DimData.clusterDimX = clusterDimX; 399 | m_DimData.clusterDimY = clusterDimY; 400 | m_DimData.clusterDimZ = clusterDimZ; 401 | m_DimData.clusterDimXYZ = clusterDimX * clusterDimY * clusterDimZ; 402 | } 403 | 404 | void ClearClusterFlag() 405 | { 406 | float[] flags = new float[m_DimData.clusterDimXYZ]; 407 | for (int i = 0; i < m_DimData.clusterDimXYZ; i++) 408 | { 409 | flags[i] = 0.0f; 410 | } 411 | cb_ClusterFlag.SetData(flags); 412 | } 413 | 414 | void ClearLightGirdIndexCounter() 415 | { 416 | uint[] uCounter = { 0 }; 417 | cb_ClusterPointLightIndexCounter.SetData(uCounter); 418 | 419 | Vector2Int[] vec2Girds = new Vector2Int[m_DimData.clusterDimXYZ]; 420 | for (int i = 0; i < m_DimData.clusterDimXYZ; i++) 421 | { 422 | vec2Girds[i] = new Vector2Int(0, 0); 423 | } 424 | cb_ClusterPointLightGrid.SetData(vec2Girds); 425 | } 426 | 427 | void ClearUniqueCluster() 428 | { 429 | uint[] uniqueClusters = new uint[m_DimData.clusterDimXYZ]; 430 | for (int i = 0; i < m_DimData.clusterDimXYZ; i++) 431 | { 432 | uniqueClusters[i] = 0; 433 | } 434 | cb_UniqueClusters.SetData(uniqueClusters); 435 | } 436 | 437 | void OnResize() 438 | { 439 | //////////////////////////////////////////////////////////////////////////////////////////////// 440 | /// Init Dim 441 | CalculateMDim(_camera); 442 | 443 | int stride = Marshal.SizeOf(typeof(AABB)); 444 | cb_ClusterAABBs = new ComputeBuffer(m_DimData.clusterDimXYZ, stride); 445 | Pass_ComputeClusterAABB(); 446 | 447 | //////////////////////////////////////////////////////////////////////////////////////////////// 448 | ///Init Cluster Buffers 449 | InitClusterBuffers(); 450 | } 451 | 452 | void InitClusterBuffers() 453 | { 454 | cb_ClusterPointLightIndexCounter = new ComputeBuffer(1, sizeof(uint)); 455 | cb_UniqueClusterCount = new ComputeBuffer(1, sizeof(uint), ComputeBufferType.Raw); 456 | cb_IAB_AssignLightsToClusters = new ComputeBuffer(1, sizeof(uint) * 3, ComputeBufferType.IndirectArguments); 457 | cb_IAB_DrawDebugClusters = new ComputeBuffer(1, sizeof(uint) * 4, ComputeBufferType.IndirectArguments); 458 | 459 | 460 | cb_ClusterPointLightGrid = new ComputeBuffer(m_DimData.clusterDimXYZ, sizeof(uint) * 2); 461 | cb_ClusterPointLightIndexList = new ComputeBuffer(m_DimData.clusterDimXYZ * m_AVERAGE_OVERLAPPING_LIGHTS_PER_CLUSTER, sizeof(uint)); 462 | 463 | cb_ClusterFlag = new ComputeBuffer(m_DimData.clusterDimXYZ, sizeof(float)); 464 | cb_UniqueClusters = new ComputeBuffer(m_DimData.clusterDimXYZ, sizeof(uint), ComputeBufferType.Counter); 465 | } 466 | 467 | void InitLightBuffer() 468 | { 469 | cb_PointLightPosRadius = new ComputeBuffer(MAX_NUM_LIGHTS, sizeof(float) * 4); 470 | 471 | cb_PointLightColor = new ComputeBuffer(MAX_NUM_LIGHTS, sizeof(float) * 4); 472 | Vector4[] colors = new Vector4[MAX_NUM_LIGHTS]; 473 | for (int i = 0; i < MAX_NUM_LIGHTS; i++) 474 | { 475 | colors[i] = GenerateRadomColor(); 476 | } 477 | 478 | cb_PointLightColor.SetData(colors); 479 | } 480 | Vector4 GenerateRadomColor() 481 | { 482 | float r = Random.Range(0.0f, 1.0f); 483 | float g = Random.Range(0.0f, 1.0f); 484 | float b = Random.Range(0.0f, 1.0f); 485 | float a = 1.0f; 486 | 487 | float fIntensity = Random.Range(0.1f, 10.0f); 488 | return new Vector4(r * fIntensity, g * fIntensity, b * fIntensity, a); 489 | } 490 | 491 | void InitSceneObject() 492 | { 493 | lst_Mesh = new List(); 494 | lst_TF = new List(); 495 | lst_Mtl = new List(); 496 | 497 | MeshFilter[] mf_Parent = go_SceneListParent.GetComponentsInChildren(); 498 | foreach (MeshFilter mf in mf_Parent) 499 | { 500 | lst_Mesh.Add(mf.mesh); 501 | } 502 | 503 | Transform[] tf_Parent = go_SceneListParent.GetComponentsInChildren(); 504 | foreach (Transform tf in tf_Parent) 505 | { 506 | lst_TF.Add(tf); 507 | } 508 | 509 | MeshRenderer[] mr_Parent = go_SceneListParent.GetComponentsInChildren(); 510 | foreach (MeshRenderer mr in mr_Parent) 511 | { 512 | Material mtl = new Material(Shader.Find("ClusterBasedLightingGit/Shader_Color")); 513 | //Material mtl = new Material(Shader.Find("Unlit/Texture")); 514 | mtl.SetTexture("_MainTex", mr.material.GetTexture("_MainTex")); 515 | lst_Mtl.Add(mtl); 516 | } 517 | } 518 | } 519 | -------------------------------------------------------------------------------- /Script_ClusterBasedLighting.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 39e99c948a2c1c942b3be0cfee4e401c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Script_FreeCamera.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEngine; 6 | 7 | /// 8 | /// A simple free camera to be added to a Unity game object. 9 | /// 10 | /// Keys: 11 | /// wasd / arrows - movement 12 | /// q/e - up/down (local space) 13 | /// r/f - up/down (world space) 14 | /// pageup/pagedown - up/down (world space) 15 | /// hold shift - enable fast movement mode 16 | /// right mouse - enable free look 17 | /// mouse - free look / rotation 18 | /// 19 | /// 20 | public class Script_FreeCamera : MonoBehaviour 21 | { 22 | /// 23 | /// Normal speed of camera movement. 24 | /// 25 | public float movementSpeed = 10f; 26 | 27 | /// 28 | /// Speed of camera movement when shift is held down, 29 | /// 30 | public float fastMovementSpeed = 100f; 31 | 32 | /// 33 | /// Sensitivity for free look. 34 | /// 35 | public float freeLookSensitivity = 3f; 36 | 37 | /// 38 | /// Amount to zoom the camera when using the mouse wheel. 39 | /// 40 | public float zoomSensitivity = 10f; 41 | 42 | /// 43 | /// Amount to zoom the camera when using the mouse wheel (fast mode). 44 | /// 45 | public float fastZoomSensitivity = 50f; 46 | 47 | /// 48 | /// Set to true when free looking (on right mouse button). 49 | /// 50 | private bool looking = false; 51 | 52 | void Update() 53 | { 54 | var fastMode = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift); 55 | var movementSpeed = fastMode ? this.fastMovementSpeed : this.movementSpeed; 56 | 57 | if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow)) 58 | { 59 | transform.position = transform.position + (-transform.right * movementSpeed * Time.deltaTime); 60 | } 61 | 62 | if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow)) 63 | { 64 | transform.position = transform.position + (transform.right * movementSpeed * Time.deltaTime); 65 | } 66 | 67 | if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow)) 68 | { 69 | transform.position = transform.position + (transform.forward * movementSpeed * Time.deltaTime); 70 | } 71 | 72 | if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow)) 73 | { 74 | transform.position = transform.position + (-transform.forward * movementSpeed * Time.deltaTime); 75 | } 76 | 77 | if (Input.GetKey(KeyCode.Q)) 78 | { 79 | transform.position = transform.position + (transform.up * movementSpeed * Time.deltaTime); 80 | } 81 | 82 | if (Input.GetKey(KeyCode.E)) 83 | { 84 | transform.position = transform.position + (-transform.up * movementSpeed * Time.deltaTime); 85 | } 86 | 87 | if (Input.GetKey(KeyCode.R) || Input.GetKey(KeyCode.PageUp)) 88 | { 89 | transform.position = transform.position + (Vector3.up * movementSpeed * Time.deltaTime); 90 | } 91 | 92 | if (Input.GetKey(KeyCode.F) || Input.GetKey(KeyCode.PageDown)) 93 | { 94 | transform.position = transform.position + (-Vector3.up * movementSpeed * Time.deltaTime); 95 | } 96 | 97 | if (looking) 98 | { 99 | float newRotationX = transform.localEulerAngles.y + Input.GetAxis("Mouse X") * freeLookSensitivity; 100 | float newRotationY = transform.localEulerAngles.x - Input.GetAxis("Mouse Y") * freeLookSensitivity; 101 | transform.localEulerAngles = new Vector3(newRotationY, newRotationX, 0f); 102 | } 103 | 104 | float axis = Input.GetAxis("Mouse ScrollWheel"); 105 | if (axis != 0) 106 | { 107 | var zoomSensitivity = fastMode ? this.fastZoomSensitivity : this.zoomSensitivity; 108 | transform.position = transform.position + transform.forward * axis * zoomSensitivity; 109 | } 110 | 111 | if (Input.GetKeyDown(KeyCode.Mouse1)) 112 | { 113 | StartLooking(); 114 | } 115 | else if (Input.GetKeyUp(KeyCode.Mouse1)) 116 | { 117 | StopLooking(); 118 | } 119 | } 120 | 121 | void OnDisable() 122 | { 123 | StopLooking(); 124 | } 125 | 126 | /// 127 | /// Enable free looking. 128 | /// 129 | public void StartLooking() 130 | { 131 | looking = true; 132 | Cursor.visible = false; 133 | Cursor.lockState = CursorLockMode.Locked; 134 | } 135 | 136 | /// 137 | /// Disable free looking. 138 | /// 139 | public void StopLooking() 140 | { 141 | looking = false; 142 | Cursor.visible = true; 143 | Cursor.lockState = CursorLockMode.None; 144 | } 145 | } -------------------------------------------------------------------------------- /Script_FreeCamera.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a08a1d6c1bfd29d4f853cf351b4dc2b2 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Shader_DebugCluster.shader: -------------------------------------------------------------------------------- 1 | // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' 2 | 3 | Shader "ClusterBasedLightingGit/Shader_DebugCluster" 4 | { 5 | Properties 6 | { 7 | _MainTex ("Texture", 2D) = "white" {} 8 | } 9 | SubShader 10 | { 11 | Tags { "RenderType"="Opaque" } 12 | LOD 100 13 | 14 | Pass 15 | { 16 | CGPROGRAM 17 | 18 | #pragma vertex main_VS 19 | #pragma fragment main_PS 20 | #pragma geometry main_GS 21 | #pragma target 5.0 22 | #pragma enable_d3d11_debug_symbols 23 | 24 | #include "UnityCG.cginc" 25 | 26 | struct VertexShaderOutput 27 | { 28 | float4 Min : AABB_MIN; // Min vertex position in view space. 29 | float4 Max : AABB_MAX; // Max vertex position in view space. 30 | float4 Color : COLOR; // Cluster color. 31 | }; 32 | 33 | struct GeometryShaderOutput 34 | { 35 | float4 Color : COLOR; 36 | float4 Position : SV_POSITION; // Clip space position. 37 | }; 38 | 39 | struct AABB 40 | { 41 | float4 Min; 42 | float4 Max; 43 | }; 44 | 45 | StructuredBuffer ClusterAABBs;// : register(t1); 46 | StructuredBuffer PointLightGrid_Cluster; 47 | StructuredBuffer UniqueClusters; 48 | 49 | float4x4 _ViewInvMatrix; 50 | 51 | bool CMin(float3 a, float3 b) 52 | { 53 | if (a.x < b.x && a.y < b.y && a.z < b.z) 54 | return true; 55 | else 56 | return false; 57 | } 58 | 59 | bool CMax(float3 a, float3 b) 60 | { 61 | if (a.x > b.x && a.y > b.y && a.z > b.z) 62 | { 63 | return true; 64 | } 65 | else 66 | { 67 | return false; 68 | } 69 | } 70 | 71 | float4 WorldToProject(float4 posWorld) 72 | { 73 | float4 l_posWorld = posWorld; 74 | l_posWorld.z *= -1; 75 | float4 l_posViewInv = mul(_ViewInvMatrix, l_posWorld); 76 | //float4 l_posView = mul(_ViewMatrix, l_posViewInv); 77 | //float4 posVP = mul(_ProjectMatrix, l_posView); 78 | float4 posVP = UnityObjectToClipPos(l_posViewInv); 79 | //float4 posVP0 = UnityObjectToClipPos(posWorld); 80 | return posVP; 81 | } 82 | 83 | VertexShaderOutput main_VS(uint VertexID : SV_VertexID) 84 | { 85 | uint clusterID = UniqueClusters[VertexID];// VertexID; 86 | 87 | VertexShaderOutput vsOutput = (VertexShaderOutput)0; 88 | 89 | AABB aabb = ClusterAABBs[clusterID];// ClusterAABBs[VertexID]; 90 | 91 | vsOutput.Min = aabb.Min; 92 | vsOutput.Max = aabb.Max; 93 | 94 | float4 factor = aabb.Max - aabb.Min; 95 | factor *= 0.2; 96 | vsOutput.Max = aabb.Min + factor; 97 | vsOutput.Color = float4(1,1,1,1); 98 | 99 | float fClusterLightCount = PointLightGrid_Cluster[clusterID].y; 100 | if (fClusterLightCount > 0) 101 | { 102 | vsOutput.Color = float4(1, 0, 0, 1); 103 | } 104 | 105 | 106 | return vsOutput; 107 | } 108 | 109 | 110 | // Geometry shader to convert AABB to cube. 111 | [maxvertexcount(16)] 112 | void main_GS(point VertexShaderOutput IN[1], inout TriangleStream OutputStream) 113 | { 114 | float4 min = IN[0].Min; 115 | float4 max = IN[0].Max; 116 | 117 | // Clip space position 118 | GeometryShaderOutput OUT = (GeometryShaderOutput)0; 119 | 120 | // AABB vertices 121 | const float4 Pos[8] = { 122 | float4(min.x, min.y, min.z, 1.0f), // 0 123 | float4(min.x, min.y, max.z, 1.0f), // 1 124 | float4(min.x, max.y, min.z, 1.0f), // 2 125 | 126 | float4(min.x, max.y, max.z, 1.0f), // 3 127 | float4(max.x, min.y, min.z, 1.0f), // 4 128 | float4(max.x, min.y, max.z, 1.0f), // 5 129 | float4(max.x, max.y, min.z, 1.0f), // 6 130 | float4(max.x, max.y, max.z, 1.0f) // 7 131 | }; 132 | 133 | // Colors (to test correctness of AABB vertices) 134 | const float4 Col[8] = { 135 | float4(0.0f, 0.0f, 0.0f, 1.0f), // Black 136 | float4(0.0f, 0.0f, 1.0f, 1.0f), // Blue 137 | float4(0.0f, 1.0f, 0.0f, 1.0f), // Green 138 | float4(0.0f, 1.0f, 1.0f, 1.0f), // Cyan 139 | float4(1.0f, 0.0f, 0.0f, 1.0f), // Red 140 | float4(1.0f, 0.0f, 1.0f, 1.0f), // Magenta 141 | float4(1.0f, 1.0f, 0.0f, 1.0f), // Yellow 142 | float4(1.01, 1.0f, 1.0f, 1.0f) // White 143 | }; 144 | 145 | const uint Index[18] = { 146 | 0, 1, 2, 147 | 3, 6, 7, 148 | 4, 5, -1, 149 | 2, 6, 0, 150 | 4, 1, 5, 151 | 3, 7, -1 152 | }; 153 | 154 | [unroll] 155 | for (uint i = 0; i < 18; ++i) 156 | { 157 | if (Index[i] == (uint) - 1) 158 | { 159 | OutputStream.RestartStrip(); 160 | } 161 | else 162 | { 163 | OUT.Position = WorldToProject(Pos[Index[i]]); 164 | OUT.Color = IN[0].Color; 165 | OutputStream.Append(OUT); 166 | } 167 | } 168 | } 169 | 170 | float4 main_PS(GeometryShaderOutput IN) : SV_Target 171 | { 172 | return IN.Color; 173 | } 174 | 175 | ENDCG 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /Shader_DebugCluster.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 71ed406ec57de7c469ea4fdcc2525959 3 | ShaderImporter: 4 | externalObjects: {} 5 | defaultTextures: [] 6 | nonModifiableTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Shader_DepthPre.shader: -------------------------------------------------------------------------------- 1 | Shader "ClusterBasedLightingGit/Shader_DepthPrePass" 2 | { 3 | Properties 4 | { 5 | } 6 | SubShader 7 | { 8 | Tags { "RenderType" = "Opaque" } 9 | LOD 100 10 | 11 | Pass 12 | { 13 | CGPROGRAM 14 | #pragma vertex vert 15 | 16 | #include "UnityCG.cginc" 17 | 18 | struct appdata 19 | { 20 | float4 vertex : POSITION; 21 | }; 22 | 23 | struct v2f 24 | { 25 | float4 vertex : SV_POSITION; 26 | }; 27 | 28 | v2f vert(appdata v) 29 | { 30 | v2f o; 31 | o.vertex = UnityObjectToClipPos(v.vertex); 32 | return o; 33 | } 34 | 35 | ENDCG 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Shader_DepthPre.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8bae537807914ef4a8e0cba897c83008 3 | ShaderImporter: 4 | externalObjects: {} 5 | defaultTextures: [] 6 | nonModifiableTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Shader_DrawSceneColor.shader: -------------------------------------------------------------------------------- 1 | Shader "ClusterBasedLightingGit/Shader_Color" 2 | { 3 | Properties 4 | { 5 | _MainTex("Texture", 2D) = "white" {} 6 | } 7 | SubShader 8 | { 9 | Tags { "RenderType" = "Opaque" } 10 | LOD 100 11 | 12 | Pass 13 | { 14 | CGPROGRAM 15 | #pragma vertex vert 16 | #pragma fragment frag 17 | 18 | #include "UnityCG.cginc" 19 | 20 | struct appdata 21 | { 22 | float4 vertex : POSITION; 23 | float2 uv : TEXCOORD0; 24 | float3 normal : NORMAL; 25 | }; 26 | 27 | struct v2f 28 | { 29 | float4 vertex : SV_POSITION; 30 | float4 posWorld : WORLDPOS; 31 | float2 uv : TEXCOORD0; 32 | float3 normal : TEXCOORD1; 33 | }; 34 | 35 | sampler2D _MainTex; 36 | float4 _MainTex_ST; 37 | 38 | v2f vert(appdata v) 39 | { 40 | v2f o; 41 | o.vertex = UnityObjectToClipPos(v.vertex); 42 | o.posWorld = mul(unity_ObjectToWorld, v.vertex); 43 | o.uv = TRANSFORM_TEX(v.uv, _MainTex); 44 | o.normal = UnityObjectToWorldNormal(v.normal); 45 | return o; 46 | } 47 | 48 | StructuredBuffer PointLightGrid_Cluster; 49 | StructuredBuffer PointLightIndexList_Cluster; 50 | StructuredBuffer PointLights; 51 | StructuredBuffer PointLightsColors; 52 | 53 | //Cluster Data 54 | uint ClusterCB_GridDimX; // The 3D dimensions of the cluster grid. 55 | uint ClusterCB_GridDimY; // The 3D dimensions of the cluster grid. 56 | uint ClusterCB_GridDimZ; // The 3D dimensions of the cluster grid. 57 | float ClusterCB_ViewNear; // The distance to the near clipping plane. (Used for computing the index in the cluster grid) 58 | uint ClusterCB_SizeX; // The size of a cluster in screen space (pixels). 59 | uint ClusterCB_SizeY; // The size of a cluster in screen space (pixels). 60 | float ClusterCB_NearK; // ( 1 + ( 2 * tan( fov * 0.5 ) / ClusterGridDim.y ) ) // Used to compute the near plane for clusters at depth k. 61 | float ClusterCB_LogGridDimY; // 1.0f / log( 1 + ( tan( fov * 0.5 ) / ClusterGridDim.y ) 62 | float4 ClusterCB_ScreenDimensions; 63 | 64 | /** 65 | * Convert a 1D cluster index into a 3D cluster index. 66 | */ 67 | uint3 ComputeClusterIndex3D(uint clusterIndex1D) 68 | { 69 | uint i = clusterIndex1D % ClusterCB_GridDimX; 70 | uint j = clusterIndex1D % (ClusterCB_GridDimX * ClusterCB_GridDimY) / ClusterCB_GridDimX; 71 | uint k = clusterIndex1D / (ClusterCB_GridDimX * ClusterCB_GridDimY); 72 | 73 | return uint3(i, j, k); 74 | } 75 | 76 | /** 77 | * Convert the 3D cluster index into a 1D cluster index. 78 | */ 79 | uint ComputeClusterIndex1D(uint3 clusterIndex3D) 80 | { 81 | return clusterIndex3D.x + (ClusterCB_GridDimX * (clusterIndex3D.y + ClusterCB_GridDimY * clusterIndex3D.z)); 82 | } 83 | 84 | /** 85 | * Compute the 3D cluster index from a 2D screen position and Z depth in view space. 86 | * source: Clustered deferred and forward shading (Olsson, Billeter, Assarsson, 2012) 87 | */ 88 | uint3 ComputeClusterIndex3D(float2 screenPos, float viewZ) 89 | { 90 | uint i = screenPos.x / ClusterCB_SizeX; 91 | uint j = screenPos.y / ClusterCB_SizeY; 92 | // It is assumed that view space z is negative (right-handed coordinate system) 93 | // so the view-space z coordinate needs to be negated to make it positive. 94 | uint k = log(viewZ / ClusterCB_ViewNear) * ClusterCB_LogGridDimY; 95 | 96 | return uint3(i, j, k); 97 | } 98 | 99 | uint ComputeClusterIndex1D(float2 screenPos, float viewZ) 100 | { 101 | uint3 clusterIndex3D = ComputeClusterIndex3D(screenPos, viewZ); 102 | uint clusterIndex1D = ComputeClusterIndex1D(clusterIndex3D); 103 | return clusterIndex1D; 104 | } 105 | 106 | float3 ACESFilm(float3 x) 107 | { 108 | float a = 2.51f; 109 | float b = 0.03f; 110 | float c = 2.43f; 111 | float d = 0.59f; 112 | float e = 0.14f; 113 | return saturate((x*(a*x + b)) / (x*(c*x + d) + e)); 114 | } 115 | 116 | fixed4 frag(v2f psInput) : SV_Target 117 | { 118 | // sample the texture 119 | fixed4 col = tex2D(_MainTex, psInput.uv); 120 | 121 | float3 normal = normalize(psInput.normal); 122 | 123 | uint clusterIndex1D = ComputeClusterIndex1D(psInput.vertex.xy, psInput.vertex.w); 124 | 125 | // Get the start position and offset of the light in the light index list. 126 | uint startOffset = PointLightGrid_Cluster[clusterIndex1D].x; 127 | uint lightCount = PointLightGrid_Cluster[clusterIndex1D].y; 128 | 129 | float4 pointLightFinal = 0; 130 | // Iterate point lights. 131 | for (uint i = 0; i < lightCount; ++i) 132 | { 133 | uint lightIndex = PointLightIndexList_Cluster[startOffset + i]; 134 | 135 | float3 pointLightPos = PointLights[lightIndex].xyz; 136 | float pointLightRadius = PointLights[lightIndex].w; 137 | float4 pointLightColor = PointLightsColors[lightIndex]; 138 | 139 | float3 ToLight = psInput.posWorld.xyz - pointLightPos; 140 | float disToLight = distance(psInput.posWorld.xyz, pointLightPos); 141 | 142 | float3 lightDirection = normalize(ToLight); 143 | float NdotL = saturate(dot(normal, lightDirection)); 144 | 145 | float disFactor = (saturate(pointLightRadius - disToLight)) / pointLightRadius; 146 | pointLightFinal += NdotL * col * disFactor * pointLightColor; 147 | } 148 | 149 | pointLightFinal += col * 0.1; 150 | pointLightFinal.rgb = ACESFilm(pointLightFinal.rgb); 151 | 152 | return pointLightFinal; 153 | } 154 | ENDCG 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /Shader_DrawSceneColor.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 06207cd955a565e48b76044357fa8b1a 3 | ShaderImporter: 4 | externalObjects: {} 5 | defaultTextures: [] 6 | nonModifiableTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | --------------------------------------------------------------------------------