diff --git a/WickedEngine/Renderable3DComponent.cpp b/WickedEngine/Renderable3DComponent.cpp index 339ae82d6..cceb2313c 100644 --- a/WickedEngine/Renderable3DComponent.cpp +++ b/WickedEngine/Renderable3DComponent.cpp @@ -180,7 +180,7 @@ void Renderable3DComponent::Compose(){ RenderColorGradedComposition(); - //wiImage::Draw(wiRenderer::ComputeTiledLightCulling(GRAPHICSTHREAD_IMMEDIATE), wiImageEffects((float)wiRenderer::GetDevice()->GetScreenWidth(), (float)wiRenderer::GetDevice()->GetScreenHeight())); + //wiImage::Draw((Texture2D*)wiRenderer::textures[TEXTYPE_2D_DEBUGUAV], wiImageEffects((float)wiRenderer::GetDevice()->GetScreenWidth(), (float)wiRenderer::GetDevice()->GetScreenHeight())); Renderable2DComponent::Compose(); } diff --git a/WickedEngine/cullingShaderHF.hlsli b/WickedEngine/cullingShaderHF.hlsli index 881ac5af6..b82d5271e 100644 --- a/WickedEngine/cullingShaderHF.hlsli +++ b/WickedEngine/cullingShaderHF.hlsli @@ -108,4 +108,50 @@ bool PointInsidePlane(float3 p, Plane plane) return dot(plane.N, p) - plane.d < 0; } +struct Cone +{ + float3 T; // Cone tip. + float h; // Height of the cone. + float3 d; // Direction of the cone. + float r; // bottom radius of the cone. +}; +// Check to see if a cone if fully behind (inside the negative halfspace of) a plane. +// Source: Real-time collision detection, Christer Ericson (2005) +bool ConeInsidePlane(Cone cone, Plane plane) +{ + // Compute the farthest point on the end of the cone to the positive space of the plane. + float3 m = cross(cross(plane.N, cone.d), cone.d); + float3 Q = cone.T + cone.d * cone.h - m * cone.r; + + // The cone is in the negative halfspace of the plane if both + // the tip of the cone and the farthest point on the end of the cone to the + // positive halfspace of the plane are both inside the negative halfspace + // of the plane. + return PointInsidePlane(cone.T, plane) && PointInsidePlane(Q, plane); +} +bool ConeInsideFrustum(Cone cone, Frustum frustum, float zNear, float zFar) +{ + bool result = true; + + Plane nearPlane = { float3(0, 0, 1), zNear }; + Plane farPlane = { float3(0, 0, -1), -zFar }; + + // First check the near and far clipping planes. + if (ConeInsidePlane(cone, nearPlane) || ConeInsidePlane(cone, farPlane)) + { + result = false; + } + + // Then check frustum planes + for (int i = 0; i < 4 && result; i++) + { + if (ConeInsidePlane(cone, frustum.planes[i])) + { + result = false; + } + } + + return result; +} + #endif // _CULLING_SHADER_HF_ diff --git a/WickedEngine/lightCullingCS.hlsl b/WickedEngine/lightCullingCS.hlsl index 78ba30b63..d872c1b44 100644 --- a/WickedEngine/lightCullingCS.hlsl +++ b/WickedEngine/lightCullingCS.hlsl @@ -2,8 +2,10 @@ #include "cullingShaderHF.hlsli" #include "tiledLightingHF.hlsli" -//RWTEXTURE2D(DebugTexture, float4, UAVSLOT_DEBUGTEXTURE); -//groupshared uint _counter = 0; +#ifdef DEBUG_TILEDLIGHTCULLING +RWTEXTURE2D(DebugTexture, float4, UAVSLOT_DEBUGTEXTURE); +groupshared uint _counter = 0; +#endif STRUCTUREDBUFFER(in_Frustums, Frustum, SBSLOT_TILEFRUSTUMS); @@ -87,45 +89,6 @@ void main(ComputeShaderInput IN) // Get frustum from frustum buffer: GroupFrustum = in_Frustums[IN.groupID.x + (IN.groupID.y * xDispatchParams_numThreads.x)]; // numthreads is from the frustum computation phase, so not actual number of threads here - - //// Calculate frustum in place: - //{ - // // View space eye position is always at the origin. - // const float3 eyePos = float3(0, 0, 0); - - // // Compute 4 points on the far clipping plane to use as the - // // frustum vertices. - // float4 screenSpace[4]; - // // Top left point - // screenSpace[0] = float4(IN.dispatchThreadID.xy, 1.0f, 1.0f); - // // Top right point - // screenSpace[1] = float4(float2(IN.dispatchThreadID.x + BLOCK_SIZE, IN.dispatchThreadID.y), 1.0f, 1.0f); - // // Bottom left point - // screenSpace[2] = float4(float2(IN.dispatchThreadID.x, IN.dispatchThreadID.y + BLOCK_SIZE), 1.0f, 1.0f); - // // Bottom right point - // screenSpace[3] = float4(float2(IN.dispatchThreadID.x + BLOCK_SIZE, IN.dispatchThreadID.y + BLOCK_SIZE), 1.0f, 1.0f); - - // float3 viewSpace[4]; - // // Now convert the screen space points to view space - // for (int i = 0; i < 4; i++) - // { - // viewSpace[i] = ScreenToView(screenSpace[i]).xyz; - // } - - // // Now build the frustum planes from the view space points - // Frustum frustum; - - // // Left plane - // frustum.planes[0] = ComputePlane(eyePos, viewSpace[2], viewSpace[0]); - // // Right plane - // frustum.planes[1] = ComputePlane(eyePos, viewSpace[1], viewSpace[3]); - // // Top plane - // frustum.planes[2] = ComputePlane(eyePos, viewSpace[0], viewSpace[1]); - // // Bottom plane - // frustum.planes[3] = ComputePlane(eyePos, viewSpace[3], viewSpace[2]); - - // GroupFrustum = frustum; - //} } GroupMemoryBarrierWithGroupSync(); @@ -138,9 +101,6 @@ void main(ComputeShaderInput IN) float fMinDepth = asfloat(uMinDepth); float fMaxDepth = asfloat(uMaxDepth); - //fMinDepth = g_xCamera_ZFarP; - //fMaxDepth = g_xCamera_ZNearP; - // Convert depth values to view space. float minDepthVS = ScreenToView(float4(0, 0, fMinDepth, 1)).z; float maxDepthVS = ScreenToView(float4(0, 0, fMaxDepth, 1)).z; @@ -162,7 +122,7 @@ void main(ComputeShaderInput IN) { case 1/*POINT_LIGHT*/: { - Sphere sphere = { light.PositionVS.xyz, light.range }; + Sphere sphere = { light.positionVS.xyz, light.range }; if (SphereInsideFrustum(sphere, GroupFrustum, nearClipVS, maxDepthVS)) { //// Add light to light list for transparent geometry. @@ -175,29 +135,34 @@ void main(ComputeShaderInput IN) // Add light to light list for opaque geometry. o_AppendLight(i); - //InterlockedAdd(_counter, 1); +#ifdef DEBUG_TILEDLIGHTCULLING + InterlockedAdd(_counter, 1); +#endif } } } break; - //case SPOT_LIGHT: - //{ - // float coneRadius = tan(radians(light.SpotlightAngle)) * light.distance; - // Cone cone = { light.PositionVS.xyz, light.distance, light.DirectionVS.xyz, coneRadius }; - // if (ConeInsideFrustum(cone, GroupFrustum, nearClipVS, maxDepthVS)) - // { - // // Add light to light list for transparent geometry. - // t_AppendLight(i); + case 2/*SPOT_LIGHT*/: + { + float coneRadius = tan(/*radians*/(light.coneAngle)) * light.range; + Cone cone = { light.positionVS.xyz, light.range, -light.directionVS.xyz, coneRadius }; + if (ConeInsideFrustum(cone, GroupFrustum, nearClipVS, maxDepthVS)) + { + // Add light to light list for transparent geometry. + t_AppendLight(i); +#ifdef DEBUG_TILEDLIGHTCULLING + InterlockedAdd(_counter, 1); +#endif - // if (!ConeInsidePlane(cone, minPlane)) - // { - // // Add light to light list for opaque geometry. - // o_AppendLight(i); - // } - // } - //} - //break; + if (!ConeInsidePlane(cone, minPlane)) + { + // Add light to light list for opaque geometry. + o_AppendLight(i); + } + } + } + break; case 0/*DIRECTIONAL_LIGHT*/: { // Directional lights always get added to our light list. @@ -240,6 +205,7 @@ void main(ComputeShaderInput IN) t_LightIndexList[t_LightIndexStartOffset + i] = t_LightList[i]; } - - //DebugTexture[texCoord] = float4((float)_counter / (float)lightCount,0,0,1); +#ifdef DEBUG_TILEDLIGHTCULLING + DebugTexture[texCoord] = float4((float)_counter / (float)lightCount,0,0,0.75); +#endif } \ No newline at end of file diff --git a/WickedEngine/lightCullingCSInterop.h b/WickedEngine/lightCullingCSInterop.h index 88ceaf028..696238a55 100644 --- a/WickedEngine/lightCullingCSInterop.h +++ b/WickedEngine/lightCullingCSInterop.h @@ -4,4 +4,6 @@ #define BLOCK_SIZE 16 #define MAX_LIGHTS 1024 +//#define DEBUG_TILEDLIGHTCULLING + #endif // _LIGHTCULLING_INTEROP_H_ diff --git a/WickedEngine/objectHF.hlsli b/WickedEngine/objectHF.hlsli index af0a1963e..4677e95f6 100644 --- a/WickedEngine/objectHF.hlsli +++ b/WickedEngine/objectHF.hlsli @@ -178,7 +178,7 @@ inline void TiledLighting(in float2 pixel, in float3 N, in float3 V, in float3 P uint lightIndex = LightIndexList[startOffset + i]; LightArrayType light = LightArray[lightIndex]; - float3 L = light.PositionWS - P; + float3 L = light.positionWS - P; float lightDistance = length(L); if (light.type > 0 && lightDistance > light.range) continue; @@ -188,20 +188,21 @@ inline void TiledLighting(in float2 pixel, in float3 N, in float3 V, in float3 P float3 lightColor = light.color.rgb * light.energy; + [branch] switch (light.type) { case 0/*DIRECTIONAL*/: { - L = light.direction.xyz; + L = light.directionWS.xyz; BRDF_MAKE(N, L, V); result.specular = lightColor * BRDF_SPECULAR(roughness, f0); result.diffuse = lightColor * BRDF_DIFFUSE(roughness); float sh = max(NdotL, 0); float4 ShPos[3]; - ShPos[0] = mul(float4(P, 1), g_xDirLight_ShM[0]); - ShPos[1] = mul(float4(P, 1), g_xDirLight_ShM[1]); - ShPos[2] = mul(float4(P, 1), g_xDirLight_ShM[2]); + ShPos[0] = mul(float4(P, 1), light.shadowMat[0]); + ShPos[1] = mul(float4(P, 1), light.shadowMat[1]); + ShPos[2] = mul(float4(P, 1), light.shadowMat[2]); float3 ShTex[3]; ShTex[0] = ShPos[0].xyz*float3(1, -1, 1) / ShPos[0].w / 2.0f + 0.5f; ShTex[1] = ShPos[1].xyz*float3(1, -1, 1) / ShPos[1].w / 2.0f + 0.5f; @@ -255,19 +256,47 @@ inline void TiledLighting(in float2 pixel, in float3 N, in float3 V, in float3 P //} result.diffuse *= sh; result.specular *= sh; - - result.diffuse = max(result.diffuse, 0); - result.specular = max(result.specular, 0); } break; case 2/*SPOT*/: { + float SpotFactor = dot(L, light.directionWS); + + float spotCutOff = light.coneAngleCos; + + [branch]if (SpotFactor > spotCutOff) + { + + BRDF_MAKE(N, L, V); + result.specular = lightColor * BRDF_SPECULAR(roughness, f0); + result.diffuse = lightColor * BRDF_DIFFUSE(roughness); + + float att = (light.energy * (light.range / (light.range + 1 + lightDistance))); + float attenuation = /*saturate*/(att * (light.range - lightDistance) / light.range); + attenuation *= saturate((1.0 - (1.0 - SpotFactor) * 1.0 / (1.0 - spotCutOff))); + result.diffuse *= attenuation; + result.specular *= attenuation; + + float sh = max(NdotL, 0); + //float4 ShPos = mul(float4(P, 1), xShMat); + //float2 ShTex = ShPos.xy / ShPos.w * float2(0.5f, -0.5f) + float2(0.5f, 0.5f); + //[branch]if ((saturate(ShTex.x) == ShTex.x) && (saturate(ShTex.y) == ShTex.y)) + //{ + // //light.r+=1.0f; + // sh *= shadowCascade(ShPos, ShTex, texture_shadow0); + //} + result.diffuse *= sh; + result.specular *= sh; + + result.diffuse = max(result.diffuse, 0); + result.specular = max(result.specular, 0); + } } break; } - diffuse += result.diffuse; - specular += result.specular; + diffuse += max(0.0f, result.diffuse); + specular += max(0.0f, result.specular); } } diff --git a/WickedEngine/tiledLightingHF.hlsli b/WickedEngine/tiledLightingHF.hlsli index fc51c976a..713e78f03 100644 --- a/WickedEngine/tiledLightingHF.hlsli +++ b/WickedEngine/tiledLightingHF.hlsli @@ -3,17 +3,26 @@ struct LightArrayType { - float3 PositionVS; // View Space! + float3 positionVS; // View Space! float range; + // -- float4 color; - float3 PositionWS; + // -- + float3 positionWS; float energy; - float3 direction; - uint type; - float shadowBias; + // -- + float3 directionVS; float _pad0; - float _pad1; - float _pad2; + // -- + float3 directionWS; + uint type; + // -- + float shadowBias; + uint shadowMap_index; + float coneAngle; + float coneAngleCos; + // -- + float4x4 shadowMat[3]; }; #endif // _TILEDLIGHTING_HF_ \ No newline at end of file diff --git a/WickedEngine/wiLoader.cpp b/WickedEngine/wiLoader.cpp index cbb315a48..80fde3a46 100644 --- a/WickedEngine/wiLoader.cpp +++ b/WickedEngine/wiLoader.cpp @@ -4049,6 +4049,13 @@ Light::~Light() { for (string x : lensFlareNames) wiResourceManager::GetGlobal()->del(x); } +XMFLOAT3 Light::GetDirection() +{ + XMMATRIX rot = XMMatrixRotationQuaternion(XMLoadFloat4(&rotation)); + XMFLOAT3 retVal; + XMStoreFloat3(&retVal, XMVector3Normalize(-XMVector3Transform(XMVectorSet(0, -1, 0, 1), rot))); + return retVal; +} void Light::UpdateTransform() { Transform::UpdateTransform(); diff --git a/WickedEngine/wiLoader.h b/WickedEngine/wiLoader.h index 4cee7c266..85d5b06b8 100644 --- a/WickedEngine/wiLoader.h +++ b/WickedEngine/wiLoader.h @@ -794,6 +794,8 @@ struct Light : public Cullable , public Transform }; LightType type; + XMFLOAT3 GetDirection(); + Light(); virtual ~Light(); virtual void UpdateTransform(); diff --git a/WickedEngine/wiRenderer.cpp b/WickedEngine/wiRenderer.cpp index ac53cdc2b..fe3e08ba4 100644 --- a/WickedEngine/wiRenderer.cpp +++ b/WickedEngine/wiRenderer.cpp @@ -1425,7 +1425,7 @@ void wiRenderer::UpdatePerFrameData() { for (auto& x : frameCullings) { - Camera* cam = x.first; + Camera* camera = x.first; FrameCulling& culling = x.second; culling.culledRenderer.clear(); @@ -1435,12 +1435,12 @@ void wiRenderer::UpdatePerFrameData() if (spTree != nullptr) { - wiSPTree::getVisible(spTree->root, cam->frustum, culling.culledObjects, wiSPTree::SortType::SP_TREE_SORT_FRONT_TO_BACK); - wiSPTree::getVisible(spTree->root, cam->frustum, culling.culledObjects_transparent, wiSPTree::SortType::SP_TREE_SORT_BACK_TO_FRONT); + wiSPTree::getVisible(spTree->root, camera->frustum, culling.culledObjects, wiSPTree::SortType::SP_TREE_SORT_FRONT_TO_BACK); + wiSPTree::getVisible(spTree->root, camera->frustum, culling.culledObjects_transparent, wiSPTree::SortType::SP_TREE_SORT_BACK_TO_FRONT); for (Cullable* object : culling.culledObjects) { for (wiHairParticle* hair : ((Object*)object)->hParticleSystems) { - hair->PerformCulling(cam); + hair->PerformCulling(camera); } culling.culledRenderer[((Object*)object)->mesh].insert((Object*)object); } @@ -1448,7 +1448,7 @@ void wiRenderer::UpdatePerFrameData() if (spTree_lights != nullptr) { Frustum frustum; - frustum.ConstructFrustum(min(cam->zFarP, GetScene().worldInfo.fogSEH.y), cam->Projection, cam->View); + frustum.ConstructFrustum(min(camera->zFarP, GetScene().worldInfo.fogSEH.y), camera->Projection, camera->View); wiSPTree::getVisible(spTree_lights->root, frustum, culling.culledLights); } } @@ -2246,7 +2246,7 @@ void wiRenderer::DrawLights(Camera* camera, GRAPHICSTHREAD threadID) else if(type==2) //spot { SpotLightCB lcb; - const float coneS=(const float)(l->enerDis.z/0.7853981852531433); + const float coneS = (const float)(l->enerDis.z / XM_PIDIV4); XMMATRIX world,rot; world = XMMatrixTranspose( XMMatrixScaling(coneS*l->enerDis.y,l->enerDis.y,coneS*l->enerDis.y)* @@ -3578,23 +3578,41 @@ void wiRenderer::ComputeTiledLightCulling(GRAPHICSTHREAD threadID) static LightArrayType* lightArray = (LightArrayType*)_mm_malloc(sizeof(LightArrayType)*MAX_LIGHTS, 16); ZeroMemory(lightArray, sizeof(lightArray)); + XMMATRIX viewMatrix = cam->GetView(); + UINT lightCounter = 0; for (Cullable* c : culledLights) { Light* l = (Light*)c; lightArray[lightCounter].posWS = l->translation; - XMStoreFloat3(&lightArray[lightCounter].posVS, XMVector3TransformCoord(XMLoadFloat3(&lightArray[lightCounter].posWS), cam->GetView())); + XMStoreFloat3(&lightArray[lightCounter].posVS, XMVector3TransformCoord(XMLoadFloat3(&lightArray[lightCounter].posWS), viewMatrix)); lightArray[lightCounter].distance = l->enerDis.y; lightArray[lightCounter].col = l->color; lightArray[lightCounter].energy = l->enerDis.x; lightArray[lightCounter].type = l->type; lightArray[lightCounter].shadowBias = l->shadowBias; + lightArray[lightCounter].shadowMap_index = l->shadowMap_index; switch (l->type) { case Light::DIRECTIONAL: { - XMStoreFloat3(&lightArray[lightCounter].direction, XMVector3Normalize(-XMVector3Transform(XMVectorSet(0, -1, 0, 1), XMMatrixRotationQuaternion(XMLoadFloat4(&l->rotation))))); + lightArray[lightCounter].directionWS = l->GetDirection(); + for (unsigned int shmap = 0; shmap < min(l->shadowCam_dirLight.size(),ARRAYSIZE(lightArray[lightCounter].shadowMatrix)); ++shmap) { + lightArray[lightCounter].shadowMatrix[shmap] = l->shadowCam_dirLight[shmap].getVP(); + } + } + break; + case Light::SPOT: + { + lightArray[lightCounter].coneAngle = (l->enerDis.z * 0.5f); + lightArray[lightCounter].coneAngleCos = cosf(lightArray[lightCounter].coneAngle); + lightArray[lightCounter].directionWS = l->GetDirection(); + XMStoreFloat3(&lightArray[lightCounter].directionVS, XMVector3TransformNormal(XMLoadFloat3(&lightArray[lightCounter].directionWS), viewMatrix)); + if (l->shadow && l->shadowMap_index >= 0) + { + lightArray[lightCounter].shadowMatrix[0] = l->shadowCam_spotLight[0].getVP(); + } } break; default: @@ -3739,7 +3757,9 @@ void wiRenderer::ComputeTiledLightCulling(GRAPHICSTHREAD threadID) device->BindResourceCS(frustumBuffer, SBSLOT_TILEFRUSTUMS, threadID); device->BindCS(computeShaders[CSTYPE_TILEDLIGHTCULLING], threadID); - //device->BindUnorderedAccessResourceCS(textures[TEXTYPE_2D_DEBUGUAV], UAVSLOT_DEBUGTEXTURE, threadID); +#ifdef DEBUG_TILEDLIGHTCULLING + device->BindUnorderedAccessResourceCS(textures[TEXTYPE_2D_DEBUGUAV], UAVSLOT_DEBUGTEXTURE, threadID); +#endif device->BindUnorderedAccessResourceCS(lightCounterHelper_Opaque, UAVSLOT_LIGHTINDEXCOUNTERHELPER_OPAQUE, threadID); device->BindUnorderedAccessResourceCS(lightCounterHelper_Transparent, UAVSLOT_LIGHTINDEXCOUNTERHELPER_TRANSPARENT, threadID); device->BindUnorderedAccessResourceCS(resourceBuffers[RBTYPE_LIGHTINDEXLIST_OPAQUE], UAVSLOT_LIGHTINDEXLIST_OPAQUE, threadID); diff --git a/WickedEngine/wiRenderer.h b/WickedEngine/wiRenderer.h index 53716ccb3..ec04a5bdb 100644 --- a/WickedEngine/wiRenderer.h +++ b/WickedEngine/wiRenderer.h @@ -285,12 +285,15 @@ public: XMFLOAT4 col; XMFLOAT3 posWS; float energy; - XMFLOAT3 direction; + XMFLOAT3 directionVS; + float __pad0; + XMFLOAT3 directionWS; UINT type; float shadowBias; - float _pad0; - float _pad1; - float _pad2; + UINT shadowMap_index; + float coneAngle; + float coneAngleCos; + XMMATRIX shadowMatrix[3]; STRUCTUREDBUFFER_SETBINDSLOT(SBSLOT_LIGHTARRAY) diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index b38ededf1..7fce85910 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -7,7 +7,7 @@ namespace wiVersion // minor features, major updates const int minor = 9; // minor bug fixes, alterations, refactors, updates - const int revision = 5; + const int revision = 6; long GetVersion()