diff --git a/Editor/ObjectWindow.cpp b/Editor/ObjectWindow.cpp index 6724d52fe..2afd77c4e 100644 --- a/Editor/ObjectWindow.cpp +++ b/Editor/ObjectWindow.cpp @@ -246,10 +246,19 @@ ObjectWindow::ObjectWindow(wiGUI* gui) : GUI(gui) y += 30; - generateAtlasButton = new wiButton("Generate Atlas"); - generateAtlasButton->SetTooltip("Toggle kinematic behaviour."); - generateAtlasButton->SetPos(XMFLOAT2(x, y += 30)); - generateAtlasButton->OnClick([&](wiEventArgs args) { + + lightmapResolutionSlider = new wiSlider(32, 1024, 128, 1024 - 32, "Lightmap resolution: "); + lightmapResolutionSlider->SetTooltip("Set the approximate resolution for this object's lightmap. This will be packed into the larger global lightmap later."); + lightmapResolutionSlider->SetSize(XMFLOAT2(100, 30)); + lightmapResolutionSlider->SetPos(XMFLOAT2(x, y += 30)); + objectWindow->AddWidget(lightmapResolutionSlider); + + + generateLightmapButton = new wiButton("Generate Lightmap"); + generateLightmapButton->SetTooltip("Toggle kinematic behaviour."); + generateLightmapButton->SetPos(XMFLOAT2(x, y += 30)); + generateLightmapButton->SetSize(XMFLOAT2(140,30)); + generateLightmapButton->OnClick([&](wiEventArgs args) { Scene& scene = wiRenderer::GetScene(); ObjectComponent* objectcomponent = scene.objects.GetComponent(entity); @@ -288,7 +297,7 @@ ObjectWindow::ObjectWindow(wiGUI* gui) : GUI(gui) // Generate atlas: { xatlas::PackerOptions packerOptions; - packerOptions.resolution = 1024; + packerOptions.resolution = (uint32_t)lightmapResolutionSlider->GetValue(); packerOptions.conservative = true; packerOptions.padding = 1; xatlas::GenerateCharts(atlas, xatlas::CharterOptions(), nullptr, nullptr); @@ -418,7 +427,7 @@ ObjectWindow::ObjectWindow(wiGUI* gui) : GUI(gui) }); - objectWindow->AddWidget(generateAtlasButton); + objectWindow->AddWidget(generateLightmapButton); diff --git a/Editor/ObjectWindow.h b/Editor/ObjectWindow.h index 9e8249415..3dabd809c 100644 --- a/Editor/ObjectWindow.h +++ b/Editor/ObjectWindow.h @@ -33,6 +33,7 @@ public: wiCheckBox* kinematicCheckBox; wiComboBox* collisionShapeComboBox; - wiButton* generateAtlasButton; + wiSlider* lightmapResolutionSlider; + wiButton* generateLightmapButton; }; diff --git a/WickedEngine/WickedEngine_SHADERS.vcxproj b/WickedEngine/WickedEngine_SHADERS.vcxproj index a0140ea48..8e4f24631 100644 --- a/WickedEngine/WickedEngine_SHADERS.vcxproj +++ b/WickedEngine/WickedEngine_SHADERS.vcxproj @@ -35,6 +35,7 @@ + diff --git a/WickedEngine/WickedEngine_SHADERS.vcxproj.filters b/WickedEngine/WickedEngine_SHADERS.vcxproj.filters index 4e8736ef6..81e7fccc8 100644 --- a/WickedEngine/WickedEngine_SHADERS.vcxproj.filters +++ b/WickedEngine/WickedEngine_SHADERS.vcxproj.filters @@ -115,6 +115,9 @@ HF + + HF + diff --git a/WickedEngine/globals.hlsli b/WickedEngine/globals.hlsli index 92bd90dad..d72f5bfde 100644 --- a/WickedEngine/globals.hlsli +++ b/WickedEngine/globals.hlsli @@ -106,7 +106,7 @@ struct ComputeShaderInput // Helpers: -// returns a random float in range (0, 1). seed must not be >0! +// returns a random float in range (0, 1). seed must be >0! inline float rand(inout float seed, in float2 uv) { float result = frac(sin(seed * dot(uv, float2(12.9898f, 78.233f))) * 43758.5453f); diff --git a/WickedEngine/objectInputLayoutHF.hlsli b/WickedEngine/objectInputLayoutHF.hlsli index 64e57c3a7..69f8b8469 100644 --- a/WickedEngine/objectInputLayoutHF.hlsli +++ b/WickedEngine/objectInputLayoutHF.hlsli @@ -14,6 +14,10 @@ struct Input_InstancePrev float4 wiPrev1 : MATIPREV1; float4 wiPrev2 : MATIPREV2; }; +struct Input_InstanceAtlas +{ + float4 atlasMulAdd : INSTANCEATLAS; +}; struct Input_Object_POS { @@ -34,6 +38,7 @@ struct Input_Object_ALL float4 pre : PREVPOS; Input_Instance instance; Input_InstancePrev instancePrev; + Input_InstanceAtlas instanceAtlas; }; inline float4x4 MakeWorldMatrixFromInstance(in Input_Instance input) @@ -108,7 +113,7 @@ inline VertexSurface MakeVertexSurfaceFromInput(Input_Object_ALL input) surface.uv = input.tex; - surface.atlas = input.atl; + surface.atlas = input.atl * input.instanceAtlas.atlasMulAdd.xy + input.instanceAtlas.atlasMulAdd.zw; surface.prevPos = float4(input.pre.xyz, 1); diff --git a/WickedEngine/raySceneIntersectHF.hlsli b/WickedEngine/raySceneIntersectHF.hlsli new file mode 100644 index 000000000..0616fe6eb --- /dev/null +++ b/WickedEngine/raySceneIntersectHF.hlsli @@ -0,0 +1,278 @@ +#ifndef _RAY_SCENE_INTERSECT_HF_ +#define _RAY_SCENE_INTERSECT_HF_ +#include "globals.hlsli" +#include "tracedRenderingHF.hlsli" + +STRUCTUREDBUFFER(materialBuffer, TracedRenderingMaterial, TEXSLOT_ONDEMAND0); +STRUCTUREDBUFFER(triangleBuffer, BVHMeshTriangle, TEXSLOT_ONDEMAND1); +RAWBUFFER(clusterCounterBuffer, TEXSLOT_ONDEMAND2); +STRUCTUREDBUFFER(clusterIndexBuffer, uint, TEXSLOT_ONDEMAND3); +STRUCTUREDBUFFER(clusterOffsetBuffer, uint2, TEXSLOT_ONDEMAND4); +STRUCTUREDBUFFER(clusterConeBuffer, ClusterCone, TEXSLOT_ONDEMAND5); +STRUCTUREDBUFFER(bvhNodeBuffer, BVHNode, TEXSLOT_ONDEMAND6); +STRUCTUREDBUFFER(bvhAABBBuffer, BVHAABB, TEXSLOT_ONDEMAND7); + +TEXTURE2D(materialTextureAtlas, float4, TEXSLOT_ONDEMAND8); + +inline RayHit TraceScene(Ray ray) +{ + RayHit bestHit = CreateRayHit(); + + // Using BVH acceleration structure: + + // Emulated stack for tree traversal: + const uint stacksize = 32; + uint stack[stacksize]; + uint stackpos = 0; + + const uint clusterCount = clusterCounterBuffer.Load(0); + const uint leafNodeOffset = clusterCount - 1; + + // push root node + stack[stackpos] = 0; + stackpos++; + + uint k = 0; + do { + k++; + if (k > 256) + return bestHit; + + // pop untraversed node + stackpos--; + const uint nodeIndex = stack[stackpos]; + + BVHNode node = bvhNodeBuffer[nodeIndex]; + BVHAABB box = bvhAABBBuffer[nodeIndex]; + + if (IntersectBox(ray, box)) + { + //if (node.LeftChildIndex == 0 && node.RightChildIndex == 0) + if (nodeIndex >= clusterCount - 1) + { + // Leaf node + const uint nodeToClusterID = nodeIndex - leafNodeOffset; + const uint clusterIndex = clusterIndexBuffer[nodeToClusterID]; + bool cullCluster = false; + + //// Compute per cluster visibility: + //const ClusterCone cone = clusterConeBuffer[clusterIndex]; + //if (cone.valid) + //{ + // const float3 testVec = normalize(ray.origin - cone.position); + // if (dot(testVec, cone.direction) > cone.angleCos) + // { + // cullCluster = true; + // } + //} + + if (!cullCluster) + { + const uint2 cluster = clusterOffsetBuffer[clusterIndex]; + const uint triangleOffset = cluster.x; + const uint triangleCount = cluster.y; + + for (uint tri = 0; tri < triangleCount; ++tri) + { + const uint primitiveID = triangleOffset + tri; + IntersectTriangle(ray, bestHit, triangleBuffer[primitiveID], primitiveID); + } + } + } + else + { + // Internal node + if (stackpos < stacksize - 1) + { + // push left child + stack[stackpos] = node.LeftChildIndex; + stackpos++; + // push right child + stack[stackpos] = node.RightChildIndex; + stackpos++; + } + else + { + // stack overflow, terminate + break; + } + } + + } + + } while (stackpos > 0); + + + return bestHit; +} + +inline bool TraceSceneANY(Ray ray, float maxDistance) +{ + bool shadow = false; + + // Using BVH acceleration structure: + + // Emulated stack for tree traversal: + const uint stacksize = 32; + uint stack[stacksize]; + uint stackpos = 0; + + const uint clusterCount = clusterCounterBuffer.Load(0); + const uint leafNodeOffset = clusterCount - 1; + + // push root node + stack[stackpos] = 0; + stackpos++; + + uint k = 0; + do { + k++; + if (k > 256) + return false; + + // pop untraversed node + stackpos--; + const uint nodeIndex = stack[stackpos]; + + BVHNode node = bvhNodeBuffer[nodeIndex]; + BVHAABB box = bvhAABBBuffer[nodeIndex]; + + if (IntersectBox(ray, box)) + { + //if (node.LeftChildIndex == 0 && node.RightChildIndex == 0) + if (nodeIndex >= clusterCount - 1) + { + // Leaf node + const uint nodeToClusterID = nodeIndex - leafNodeOffset; + const uint clusterIndex = clusterIndexBuffer[nodeToClusterID]; + bool cullCluster = false; + + //// Compute per cluster visibility: + //const ClusterCone cone = clusterConeBuffer[clusterIndex]; + //if (cone.valid) + //{ + // const float3 testVec = normalize(ray.origin - cone.position); + // if (dot(testVec, cone.direction) > cone.angleCos) + // { + // cullCluster = true; + // } + //} + + if (!cullCluster) + { + const uint2 cluster = clusterOffsetBuffer[clusterIndex]; + const uint triangleOffset = cluster.x; + const uint triangleCount = cluster.y; + + for (uint tri = 0; tri < triangleCount; ++tri) + { + const uint primitiveID = triangleOffset + tri; + if (IntersectTriangleANY(ray, maxDistance, triangleBuffer[primitiveID])) + { + shadow = true; + break; + } + } + } + } + else + { + // Internal node + if (stackpos < stacksize - 1) + { + // push left child + stack[stackpos] = node.LeftChildIndex; + stackpos++; + // push right child + stack[stackpos] = node.RightChildIndex; + stackpos++; + } + else + { + // stack overflow, terminate + break; + } + } + + } + + } while (!shadow && stackpos > 0); + + return shadow; +} + +inline float3 Shade(inout Ray ray, RayHit hit, inout float seed, in float2 pixel) +{ + if (hit.distance < INFINITE_RAYHIT) + { + BVHMeshTriangle tri = triangleBuffer[hit.primitiveID]; + + float u = hit.bary.x; + float v = hit.bary.y; + float w = 1 - u - v; + + float3 N = normalize(tri.n0 * w + tri.n1 * u + tri.n2 * v); + float2 UV = tri.t0 * w + tri.t1 * u + tri.t2 * v; + + uint materialIndex = tri.materialIndex; + + TracedRenderingMaterial mat = materialBuffer[materialIndex]; + + UV = frac(UV); // emulate wrap + float4 baseColorMap = materialTextureAtlas.SampleLevel(sampler_linear_clamp, UV * mat.baseColorAtlasMulAdd.xy + mat.baseColorAtlasMulAdd.zw, 0); + float4 surfaceMap = materialTextureAtlas.SampleLevel(sampler_linear_clamp, UV * mat.surfaceMapAtlasMulAdd.xy + mat.surfaceMapAtlasMulAdd.zw, 0); + float4 normalMap = materialTextureAtlas.SampleLevel(sampler_linear_clamp, UV * mat.normalMapAtlasMulAdd.xy + mat.normalMapAtlasMulAdd.zw, 0); + + float4 baseColor = DEGAMMA(mat.baseColor * baseColorMap); + float reflectance = mat.reflectance * surfaceMap.r; + float metalness = mat.metalness * surfaceMap.g; + float3 emissive = baseColor.rgb * mat.emissive * surfaceMap.b; + float roughness = mat.roughness /** normalMap.a*/; + float sss = mat.subsurfaceScattering; + + float3 albedo = ComputeAlbedo(baseColor, reflectance, metalness); + float3 specular = ComputeF0(baseColor, reflectance, metalness); + + + // Calculate chances of diffuse and specular reflection + albedo = min(1.0f - specular, albedo); + float specChance = dot(specular, 0.33); + float diffChance = dot(albedo, 0.33); + float inv = 1.0f / (specChance + diffChance); + specChance *= inv; + diffChance *= inv; + + // Roulette-select the ray's path + float roulette = rand(seed, pixel); + if (roulette < specChance) + { + // Specular reflection + //float alpha = 150.0f; + float alpha = sqr(1 - roughness) * 1000; + ray.direction = SampleHemisphere(reflect(ray.direction, N), alpha, seed, pixel); + float f = (alpha + 2) / (alpha + 1); + ray.energy *= (1.0f / specChance) * specular * saturate(dot(N, ray.direction) * f); + } + else + { + // Diffuse reflection + ray.direction = SampleHemisphere(N, 1.0f, seed, pixel); + ray.energy *= (1.0f / diffChance) * albedo; + } + + ray.origin = hit.position + N * EPSILON; + ray.primitiveID = hit.primitiveID; + ray.bary = hit.bary; + + return emissive; + } + else + { + // Erase the ray's energy - the sky doesn't reflect anything + ray.energy = 0.0f; + + return GetDynamicSkyColor(ray.direction); + } +} + +#endif // _RAY_SCENE_INTERSECT_HF_ diff --git a/WickedEngine/raytrace_lightsamplingCS.hlsl b/WickedEngine/raytrace_lightsamplingCS.hlsl index 6c5b6da24..ef24de051 100644 --- a/WickedEngine/raytrace_lightsamplingCS.hlsl +++ b/WickedEngine/raytrace_lightsamplingCS.hlsl @@ -2,112 +2,12 @@ #include "ShaderInterop_TracedRendering.h" #include "ShaderInterop_BVH.h" #include "tracedRenderingHF.hlsli" - -RWTEXTURE2D(resultTexture, float4, 0); - -STRUCTUREDBUFFER(materialBuffer, TracedRenderingMaterial, TEXSLOT_ONDEMAND0); -STRUCTUREDBUFFER(triangleBuffer, BVHMeshTriangle, TEXSLOT_ONDEMAND1); -RAWBUFFER(clusterCounterBuffer, TEXSLOT_ONDEMAND2); -STRUCTUREDBUFFER(clusterIndexBuffer, uint, TEXSLOT_ONDEMAND3); -STRUCTUREDBUFFER(clusterOffsetBuffer, uint2, TEXSLOT_ONDEMAND4); -STRUCTUREDBUFFER(clusterConeBuffer, ClusterCone, TEXSLOT_ONDEMAND5); -STRUCTUREDBUFFER(bvhNodeBuffer, BVHNode, TEXSLOT_ONDEMAND6); -STRUCTUREDBUFFER(bvhAABBBuffer, BVHAABB, TEXSLOT_ONDEMAND7); - -TEXTURE2D(materialTextureAtlas, float4, TEXSLOT_ONDEMAND8); +#include "raySceneIntersectHF.hlsli" RAWBUFFER(counterBuffer_READ, TEXSLOT_UNIQUE0); STRUCTUREDBUFFER(rayBuffer_READ, TracedRenderingStoredRay, TEXSLOT_UNIQUE1); -inline bool TraceSceneANY(Ray ray, float maxDistance) -{ - bool shadow = false; - - // Using BVH acceleration structure: - - // Emulated stack for tree traversal: - const uint stacksize = 32; - uint stack[stacksize]; - uint stackpos = 0; - - const uint clusterCount = clusterCounterBuffer.Load(0); - const uint leafNodeOffset = clusterCount - 1; - - // push root node - stack[stackpos] = 0; - stackpos++; - - do { - // pop untraversed node - stackpos--; - const uint nodeIndex = stack[stackpos]; - - BVHNode node = bvhNodeBuffer[nodeIndex]; - BVHAABB box = bvhAABBBuffer[nodeIndex]; - - if (IntersectBox(ray, box)) - { - //if (node.LeftChildIndex == 0 && node.RightChildIndex == 0) - if (nodeIndex >= clusterCount - 1) - { - // Leaf node - const uint nodeToClusterID = nodeIndex - leafNodeOffset; - const uint clusterIndex = clusterIndexBuffer[nodeToClusterID]; - bool cullCluster = false; - - //// Compute per cluster visibility: - //const ClusterCone cone = clusterConeBuffer[clusterIndex]; - //if (cone.valid) - //{ - // const float3 testVec = normalize(ray.origin - cone.position); - // if (dot(testVec, cone.direction) > cone.angleCos) - // { - // cullCluster = true; - // } - //} - - if (!cullCluster) - { - const uint2 cluster = clusterOffsetBuffer[clusterIndex]; - const uint triangleOffset = cluster.x; - const uint triangleCount = cluster.y; - - for (uint tri = 0; tri < triangleCount; ++tri) - { - const uint primitiveID = triangleOffset + tri; - if (IntersectTriangleANY(ray, maxDistance, triangleBuffer[primitiveID])) - { - shadow = true; - break; - } - } - } - } - else - { - // Internal node - if (stackpos < stacksize - 1) - { - // push left child - stack[stackpos] = node.LeftChildIndex; - stackpos++; - // push right child - stack[stackpos] = node.RightChildIndex; - stackpos++; - } - else - { - // stack overflow, terminate - break; - } - } - - } - - } while (!shadow && stackpos > 0); - - return shadow; -} +RWTEXTURE2D(resultTexture, float4, 0); [numthreads(TRACEDRENDERING_TRACE_GROUPSIZE, 1, 1)] void main( uint3 DTid : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex) diff --git a/WickedEngine/raytrace_primaryCS.hlsl b/WickedEngine/raytrace_primaryCS.hlsl index 0aa25fadd..5614461f1 100644 --- a/WickedEngine/raytrace_primaryCS.hlsl +++ b/WickedEngine/raytrace_primaryCS.hlsl @@ -2,7 +2,10 @@ #include "ShaderInterop_TracedRendering.h" #include "ShaderInterop_BVH.h" #include "tracedRenderingHF.hlsli" +#include "raySceneIntersectHF.hlsli" +RAWBUFFER(counterBuffer_READ, TEXSLOT_UNIQUE0); +STRUCTUREDBUFFER(rayBuffer_READ, TracedRenderingStoredRay, TEXSLOT_UNIQUE1); RWRAWBUFFER(counterBuffer_WRITE, 0); RWSTRUCTUREDBUFFER(rayBuffer_WRITE, TracedRenderingStoredRay, 1); @@ -18,180 +21,6 @@ groupshared uint GroupRayCount; groupshared uint GroupRayWriteOffset; #endif // ADVANCED_ALLOCATION -STRUCTUREDBUFFER(materialBuffer, TracedRenderingMaterial, TEXSLOT_ONDEMAND0); -STRUCTUREDBUFFER(triangleBuffer, BVHMeshTriangle, TEXSLOT_ONDEMAND1); -RAWBUFFER(clusterCounterBuffer, TEXSLOT_ONDEMAND2); -STRUCTUREDBUFFER(clusterIndexBuffer, uint, TEXSLOT_ONDEMAND3); -STRUCTUREDBUFFER(clusterOffsetBuffer, uint2, TEXSLOT_ONDEMAND4); -STRUCTUREDBUFFER(clusterConeBuffer, ClusterCone, TEXSLOT_ONDEMAND5); -STRUCTUREDBUFFER(bvhNodeBuffer, BVHNode, TEXSLOT_ONDEMAND6); -STRUCTUREDBUFFER(bvhAABBBuffer, BVHAABB, TEXSLOT_ONDEMAND7); - -TEXTURE2D(materialTextureAtlas, float4, TEXSLOT_ONDEMAND8); - -RAWBUFFER(counterBuffer_READ, TEXSLOT_UNIQUE0); -STRUCTUREDBUFFER(rayBuffer_READ, TracedRenderingStoredRay, TEXSLOT_UNIQUE1); - -inline RayHit TraceScene(Ray ray) -{ - RayHit bestHit = CreateRayHit(); - - // Using BVH acceleration structure: - - // Emulated stack for tree traversal: - const uint stacksize = 32; - uint stack[stacksize]; - uint stackpos = 0; - - const uint clusterCount = clusterCounterBuffer.Load(0); - const uint leafNodeOffset = clusterCount - 1; - - // push root node - stack[stackpos] = 0; - stackpos++; - - do { - // pop untraversed node - stackpos--; - const uint nodeIndex = stack[stackpos]; - - BVHNode node = bvhNodeBuffer[nodeIndex]; - BVHAABB box = bvhAABBBuffer[nodeIndex]; - - if (IntersectBox(ray, box)) - { - //if (node.LeftChildIndex == 0 && node.RightChildIndex == 0) - if (nodeIndex >= clusterCount - 1) - { - // Leaf node - const uint nodeToClusterID = nodeIndex - leafNodeOffset; - const uint clusterIndex = clusterIndexBuffer[nodeToClusterID]; - bool cullCluster = false; - - //// Compute per cluster visibility: - //const ClusterCone cone = clusterConeBuffer[clusterIndex]; - //if (cone.valid) - //{ - // const float3 testVec = normalize(ray.origin - cone.position); - // if (dot(testVec, cone.direction) > cone.angleCos) - // { - // cullCluster = true; - // } - //} - - if (!cullCluster) - { - const uint2 cluster = clusterOffsetBuffer[clusterIndex]; - const uint triangleOffset = cluster.x; - const uint triangleCount = cluster.y; - - for (uint tri = 0; tri < triangleCount; ++tri) - { - const uint primitiveID = triangleOffset + tri; - IntersectTriangle(ray, bestHit, triangleBuffer[primitiveID], primitiveID); - } - } - } - else - { - // Internal node - if (stackpos < stacksize - 1) - { - // push left child - stack[stackpos] = node.LeftChildIndex; - stackpos++; - // push right child - stack[stackpos] = node.RightChildIndex; - stackpos++; - } - else - { - // stack overflow, terminate - break; - } - } - - } - - } while (stackpos > 0); - - - return bestHit; -} - -inline float3 Shade(inout Ray ray, RayHit hit, inout float seed, in float2 pixel) -{ - if (hit.distance < INFINITE_RAYHIT) - { - BVHMeshTriangle tri = triangleBuffer[hit.primitiveID]; - - float u = hit.bary.x; - float v = hit.bary.y; - float w = 1 - u - v; - - float3 N = normalize(tri.n0 * w + tri.n1 * u + tri.n2 * v); - float2 UV = tri.t0 * w + tri.t1 * u + tri.t2 * v; - - uint materialIndex = tri.materialIndex; - - TracedRenderingMaterial mat = materialBuffer[materialIndex]; - - UV = frac(UV); // emulate wrap - float4 baseColorMap = materialTextureAtlas.SampleLevel(sampler_linear_clamp, UV * mat.baseColorAtlasMulAdd.xy + mat.baseColorAtlasMulAdd.zw, 0); - float4 surfaceMap = materialTextureAtlas.SampleLevel(sampler_linear_clamp, UV * mat.surfaceMapAtlasMulAdd.xy + mat.surfaceMapAtlasMulAdd.zw, 0); - float4 normalMap = materialTextureAtlas.SampleLevel(sampler_linear_clamp, UV * mat.normalMapAtlasMulAdd.xy + mat.normalMapAtlasMulAdd.zw, 0); - - float4 baseColor = DEGAMMA(mat.baseColor * baseColorMap); - float reflectance = mat.reflectance * surfaceMap.r; - float metalness = mat.metalness * surfaceMap.g; - float3 emissive = baseColor.rgb * mat.emissive * surfaceMap.b; - float roughness = mat.roughness /** normalMap.a*/; - float sss = mat.subsurfaceScattering; - - float3 albedo = ComputeAlbedo(baseColor, reflectance, metalness); - float3 specular = ComputeF0(baseColor, reflectance, metalness); - - - // Calculate chances of diffuse and specular reflection - albedo = min(1.0f - specular, albedo); - float specChance = dot(specular, 0.33); - float diffChance = dot(albedo, 0.33); - float inv = 1.0f / (specChance + diffChance); - specChance *= inv; - diffChance *= inv; - - // Roulette-select the ray's path - float roulette = rand(seed, pixel); - if (roulette < specChance) - { - // Specular reflection - //float alpha = 150.0f; - float alpha = sqr(1 - roughness) * 1000; - ray.direction = SampleHemisphere(reflect(ray.direction, N), alpha, seed, pixel); - float f = (alpha + 2) / (alpha + 1); - ray.energy *= (1.0f / specChance) * specular * saturate(dot(N, ray.direction) * f); - } - else - { - // Diffuse reflection - ray.direction = SampleHemisphere(N, 1.0f, seed, pixel); - ray.energy *= (1.0f / diffChance) * albedo; - } - - ray.origin = hit.position + N * EPSILON; - ray.primitiveID = hit.primitiveID; - ray.bary = hit.bary; - - return emissive; - } - else - { - // Erase the ray's energy - the sky doesn't reflect anything - ray.energy = 0.0f; - - return GetDynamicSkyColor(ray.direction); - } -} [numthreads(TRACEDRENDERING_TRACE_GROUPSIZE, 1, 1)] void main( uint3 DTid : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex ) diff --git a/WickedEngine/renderlightmapPS.hlsl b/WickedEngine/renderlightmapPS.hlsl index 39b4f6dab..f772c8017 100644 --- a/WickedEngine/renderlightmapPS.hlsl +++ b/WickedEngine/renderlightmapPS.hlsl @@ -1,122 +1,31 @@ #include "globals.hlsli" #include "tracedRenderingHF.hlsli" - -STRUCTUREDBUFFER(materialBuffer, TracedRenderingMaterial, TEXSLOT_ONDEMAND0); -STRUCTUREDBUFFER(triangleBuffer, BVHMeshTriangle, TEXSLOT_ONDEMAND1); -RAWBUFFER(clusterCounterBuffer, TEXSLOT_ONDEMAND2); -STRUCTUREDBUFFER(clusterIndexBuffer, uint, TEXSLOT_ONDEMAND3); -STRUCTUREDBUFFER(clusterOffsetBuffer, uint2, TEXSLOT_ONDEMAND4); -STRUCTUREDBUFFER(clusterConeBuffer, ClusterCone, TEXSLOT_ONDEMAND5); -STRUCTUREDBUFFER(bvhNodeBuffer, BVHNode, TEXSLOT_ONDEMAND6); -STRUCTUREDBUFFER(bvhAABBBuffer, BVHAABB, TEXSLOT_ONDEMAND7); - -TEXTURE2D(materialTextureAtlas, float4, TEXSLOT_ONDEMAND8); - -inline bool TraceSceneANY(Ray ray, float maxDistance) -{ - bool shadow = false; - - // Using BVH acceleration structure: - - // Emulated stack for tree traversal: - const uint stacksize = 32; - uint stack[stacksize]; - uint stackpos = 0; - - const uint clusterCount = clusterCounterBuffer.Load(0); - const uint leafNodeOffset = clusterCount - 1; - - // push root node - stack[stackpos] = 0; - stackpos++; - - do { - // pop untraversed node - stackpos--; - const uint nodeIndex = stack[stackpos]; - - BVHNode node = bvhNodeBuffer[nodeIndex]; - BVHAABB box = bvhAABBBuffer[nodeIndex]; - - if (IntersectBox(ray, box)) - { - //if (node.LeftChildIndex == 0 && node.RightChildIndex == 0) - if (nodeIndex >= clusterCount - 1) - { - // Leaf node - const uint nodeToClusterID = nodeIndex - leafNodeOffset; - const uint clusterIndex = clusterIndexBuffer[nodeToClusterID]; - bool cullCluster = false; - - //// Compute per cluster visibility: - //const ClusterCone cone = clusterConeBuffer[clusterIndex]; - //if (cone.valid) - //{ - // const float3 testVec = normalize(ray.origin - cone.position); - // if (dot(testVec, cone.direction) > cone.angleCos) - // { - // cullCluster = true; - // } - //} - - if (!cullCluster) - { - const uint2 cluster = clusterOffsetBuffer[clusterIndex]; - const uint triangleOffset = cluster.x; - const uint triangleCount = cluster.y; - - for (uint tri = 0; tri < triangleCount; ++tri) - { - const uint primitiveID = triangleOffset + tri; - if (IntersectTriangleANY(ray, maxDistance, triangleBuffer[primitiveID])) - { - shadow = true; - break; - } - } - } - } - else - { - // Internal node - if (stackpos < stacksize - 1) - { - // push left child - stack[stackpos] = node.LeftChildIndex; - stackpos++; - // push right child - stack[stackpos] = node.RightChildIndex; - stackpos++; - } - else - { - // stack overflow, terminate - break; - } - } - - } - - } while (!shadow && stackpos > 0); - - return shadow; -} - - +#include "raySceneIntersectHF.hlsli" struct Input { float4 pos : SV_POSITION; + float2 uv : TEXCOORD; float3 pos3D : WORLDPOSITION; float3 normal : NORMAL; }; float4 main(Input input) : SV_TARGET { - Ray ray = CreateRay(input.pos3D, input.normal); + float3 N = normalize(input.normal); + float2 uv = input.uv; + float seed = 12345; + float3 result = 0; - bool hit = TraceSceneANY(ray, 100); + const uint iterations = 1024; + for (uint i = 0; i < iterations; ++i) + { + float3 direction = SampleHemisphere(N, 1.0f, seed, uv); + Ray ray = CreateRay(input.pos3D + direction * EPSILON, direction); + RayHit hit = TraceScene(ray); + result += ray.energy * Shade(ray, hit, seed, uv); + } + result /= (float)iterations; - //return hit ? float4(1,1,0,1) : float4(1,0,0,1); - return float4(input.pos3D * 0.1f, 1); + return float4(result, 1); } diff --git a/WickedEngine/renderlightmapVS.hlsl b/WickedEngine/renderlightmapVS.hlsl index e8d5301ff..f5a9f50ed 100644 --- a/WickedEngine/renderlightmapVS.hlsl +++ b/WickedEngine/renderlightmapVS.hlsl @@ -11,6 +11,7 @@ struct Input struct Output { float4 pos : SV_POSITION; + float2 uv : TEXCOORD; float3 pos3D : WORLDPOSITION; float3 normal : NORMAL; }; @@ -21,10 +22,12 @@ Output main(Input input) float4x4 WORLD = MakeWorldMatrixFromInstance(input.instance); - output.pos = float4(input.atl.xy, 0, 1); + output.pos = float4(input.atl, 0, 1); output.pos.xy = output.pos.xy * 2 - 1; output.pos.y *= -1; + output.uv = input.atl; + output.pos3D = mul(float4(input.pos.xyz, 1), WORLD).xyz; uint normal_wind_matID = asuint(input.pos.w); diff --git a/WickedEngine/wiGPUBVH.cpp b/WickedEngine/wiGPUBVH.cpp index be60acd3f..e27dd514d 100644 --- a/WickedEngine/wiGPUBVH.cpp +++ b/WickedEngine/wiGPUBVH.cpp @@ -398,7 +398,7 @@ void wiGPUBVH::Build(const Scene& scene, GRAPHICSTHREAD threadID) wiProfiler::EndRange(threadID); // BVH rebuild } -void wiGPUBVH::Bind(GRAPHICSTHREAD threadID) +void wiGPUBVH::Bind(SHADERSTAGE stage, GRAPHICSTHREAD threadID) { GraphicsDevice* device = wiRenderer::GetDevice(); @@ -411,7 +411,7 @@ void wiGPUBVH::Bind(GRAPHICSTHREAD threadID) bvhNodeBuffer.get(), bvhAABBBuffer.get(), }; - device->BindResources(CS, res, TEXSLOT_ONDEMAND1, ARRAYSIZE(res), threadID); + device->BindResources(stage, res, TEXSLOT_ONDEMAND1, ARRAYSIZE(res), threadID); } diff --git a/WickedEngine/wiGPUBVH.h b/WickedEngine/wiGPUBVH.h index c45b63f3c..e3f3ee852 100644 --- a/WickedEngine/wiGPUBVH.h +++ b/WickedEngine/wiGPUBVH.h @@ -25,7 +25,7 @@ public: ~wiGPUBVH(); void Build(const wiSceneSystem::Scene& scene, GRAPHICSTHREAD threadID); - void Bind(GRAPHICSTHREAD threadID); + void Bind(wiGraphicsTypes::SHADERSTAGE stage, GRAPHICSTHREAD threadID); static void LoadShaders(); diff --git a/WickedEngine/wiRenderer.cpp b/WickedEngine/wiRenderer.cpp index cac142d91..c246fd2cc 100644 --- a/WickedEngine/wiRenderer.cpp +++ b/WickedEngine/wiRenderer.cpp @@ -327,6 +327,25 @@ GFX_STRUCT InstancePrev ALIGN_16 }; +GFX_STRUCT InstanceAtlas +{ + XMFLOAT4A atlasMulAdd; + + InstanceAtlas(){} + InstanceAtlas(const XMFLOAT4& atlasRemap) + { + Create(atlasRemap); + } + inline void Create(const XMFLOAT4& atlasRemap) volatile + { + atlasMulAdd.x = atlasRemap.x; + atlasMulAdd.y = atlasRemap.y; + atlasMulAdd.z = atlasRemap.z; + atlasMulAdd.w = atlasRemap.w; + } + + ALIGN_16 +}; Sampler* GetSampler(int slot) @@ -1303,6 +1322,7 @@ void RenderMeshes(const RenderQueue& renderQueue, SHADERTYPE shaderType, UINT re { Instance instance; InstancePrev instancePrev; + InstanceAtlas instanceAtlas; }; const bool advancedVBRequest = @@ -1387,6 +1407,7 @@ void RenderMeshes(const RenderQueue& renderQueue, SHADERTYPE shaderType, UINT re const XMFLOAT4X4& prev_worldMatrix = instance.prev_transform_index >= 0 ? scene.prev_transforms[instance.prev_transform_index].world_prev : IDENTITYMATRIX; ((volatile InstBuf*)instances)[batchID].instancePrev.Create(prev_worldMatrix); + ((volatile InstBuf*)instances)[batchID].instanceAtlas.Create(instance.globalLightMapMulAdd); } else { @@ -1721,6 +1742,7 @@ void LoadShaders() { "MATIPREV", 0, FORMAT_R32G32B32A32_FLOAT, 4, APPEND_ALIGNED_ELEMENT, INPUT_PER_INSTANCE_DATA, 1 }, { "MATIPREV", 1, FORMAT_R32G32B32A32_FLOAT, 4, APPEND_ALIGNED_ELEMENT, INPUT_PER_INSTANCE_DATA, 1 }, { "MATIPREV", 2, FORMAT_R32G32B32A32_FLOAT, 4, APPEND_ALIGNED_ELEMENT, INPUT_PER_INSTANCE_DATA, 1 }, + { "INSTANCEATLAS", 0, FORMAT_R32G32B32A32_FLOAT, 4, APPEND_ALIGNED_ELEMENT, INPUT_PER_INSTANCE_DATA, 1 }, }; vertexShaders[VSTYPE_OBJECT_COMMON] = static_cast(wiResourceManager::GetShaderManager().add(SHADERPATH + "objectVS_common.cso", wiResourceManager::VERTEXSHADER)); device->CreateInputLayout(layout, ARRAYSIZE(layout), &vertexShaders[VSTYPE_OBJECT_COMMON]->code, vertexLayouts[VLTYPE_OBJECT_ALL]); @@ -5991,6 +6013,7 @@ void RefreshImpostors(GRAPHICSTHREAD threadID) { Instance instance; InstancePrev instancePrev; + InstanceAtlas instanceAtlas; }; for (size_t impostorID = 0; impostorID < min(maxImpostorCount, scene.impostors.GetCount()); ++impostorID) @@ -6007,6 +6030,7 @@ void RefreshImpostors(GRAPHICSTHREAD threadID) volatile InstBuf* buff = (volatile InstBuf*)device->AllocateFromRingBuffer(&dynamicVertexBufferPools[threadID], sizeof(InstBuf), instancesOffset, threadID); buff->instance.Create(IDENTITYMATRIX); buff->instancePrev.Create(IDENTITYMATRIX); + buff->instanceAtlas.Create(XMFLOAT4(1, 1, 0, 0)); device->InvalidateBufferAccess(&dynamicVertexBufferPools[threadID], threadID); state_set = true; @@ -7188,7 +7212,7 @@ void DrawTracedScene(const CameraComponent& camera, Texture2D* result, GRAPHICST // Set up tracing resources: - sceneBVH.Bind(threadID); + sceneBVH.Bind(CS, threadID); GPUResource* res[] = { tracedSceneParams.materialBuffer, @@ -7483,20 +7507,117 @@ void ManageGlobalLightmapAtlas(GRAPHICSTHREAD threadID) GraphicsDevice* device = GetDevice(); static Texture2D* atlasTexture = nullptr; + bool repackAtlas = false; + const int atlasClampBorder = 1; + + using namespace wiRectPacker; + static unordered_map storedTextures; Scene& scene = GetScene(); - if(!atlasTexture) + // Gather all object lightmap textures: for (size_t i = 0; i < scene.objects.GetCount(); ++i) { - const ObjectComponent& object = scene.objects[i]; + ObjectComponent& object = scene.objects[i]; if (!object.lightmapTextureData.empty()) { - // TODO: proper packing, etc. - HRESULT hr = wiTextureHelper::CreateTexture(atlasTexture, object.lightmapTextureData.data(), object.lightmapWidth, object.lightmapHeight, 4, FORMAT_R8G8B8A8_UNORM); - assert(SUCCEEDED(hr)); - return; + if (object.lightmap == nullptr) + { + // Also create a GPU-side per object lighmap, so that copying into atlas can be done efficiently: + wiTextureHelper::CreateTexture(object.lightmap, object.lightmapTextureData.data(), object.lightmapWidth, object.lightmapHeight, 4, FORMAT_R8G8B8A8_UNORM); + } + + if (storedTextures.find(object.lightmap) == storedTextures.end()) + { + // we need to pack this lightmap texture into the atlas + rect_xywh newRect = rect_xywh(0, 0, object.lightmap->GetDesc().Width + atlasClampBorder * 2, object.lightmap->GetDesc().Height + atlasClampBorder * 2); + storedTextures[object.lightmap] = newRect; + + repackAtlas = true; + } + } + + } + + // Update atlas texture if it is invalidated: + if (repackAtlas) + { + rect_xywh** out_rects = new rect_xywh*[storedTextures.size()]; + int i = 0; + for (auto& it : storedTextures) + { + out_rects[i] = &it.second; + i++; + } + + std::vector bins; + if (pack(out_rects, (int)storedTextures.size(), 16384, bins)) + { + assert(bins.size() == 1 && "The regions won't fit into the texture!"); + + SAFE_DELETE(atlasTexture); + + TextureDesc desc; + ZeroMemory(&desc, sizeof(desc)); + desc.Width = (UINT)bins[0].size.w; + desc.Height = (UINT)bins[0].size.h; + desc.MipLevels = 0; + desc.ArraySize = 1; + desc.Format = FORMAT_R8G8B8A8_UNORM; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Usage = USAGE_DEFAULT; + desc.BindFlags = BIND_SHADER_RESOURCE | BIND_UNORDERED_ACCESS; + desc.CPUAccessFlags = 0; + desc.MiscFlags = 0; + + atlasTexture = new Texture2D; + atlasTexture->RequestIndependentUnorderedAccessResourcesForMIPs(true); + + device->CreateTexture2D(&desc, nullptr, &atlasTexture); + + for (UINT mip = 0; mip < atlasTexture->GetDesc().MipLevels; ++mip) + { + for (auto& it : storedTextures) + { + if (mip < it.first->GetDesc().MipLevels) + { + CopyTexture2D(atlasTexture, mip, (it.second.x >> mip) + atlasClampBorder, (it.second.y >> mip) + atlasClampBorder, it.first, mip, threadID, BORDEREXPAND_CLAMP); + } + } + } + } + else + { + wiBackLog::post("Global Lightmap atlas packing failed!"); + } + + SAFE_DELETE_ARRAY(out_rects); + } + + // Assign atlas buckets to objects: + for (size_t i = 0; i < scene.objects.GetCount(); ++i) + { + ObjectComponent& object = scene.objects[i]; + + if (object.lightmap != nullptr) + { + const TextureDesc& desc = atlasTexture->GetDesc(); + + rect_xywh rect = storedTextures[object.lightmap]; + + // eliminate border expansion: + rect.x += atlasClampBorder; + rect.y += atlasClampBorder; + rect.w -= atlasClampBorder * 2; + rect.h -= atlasClampBorder * 2; + + object.globalLightMapMulAdd = XMFLOAT4((float)rect.w / (float)desc.Width, (float)rect.h / (float)desc.Height, (float)rect.x / (float)desc.Width, (float)rect.y / (float)desc.Height); + } + else + { + object.globalLightMapMulAdd = XMFLOAT4(0, 0, 0, 0); } } @@ -7523,36 +7644,38 @@ void RenderObjectLightMap(ObjectComponent& object, GRAPHICSTHREAD threadID) assert(!mesh.vertex_atlas.empty()); assert(mesh.vertexBuffer_ATL != nullptr); + UpdatePerFrameData(0); + BindPersistentState(threadID); + BuildSceneBVH(threadID); - sceneBVH.Bind(threadID); TracedSceneParams tracedSceneParams = PrepareTracedSceneResources(threadID); GPUResource* res[] = { tracedSceneParams.materialBuffer, }; - device->BindResources(CS, res, TEXSLOT_ONDEMAND0, ARRAYSIZE(res), threadID); + device->BindResources(PS, res, TEXSLOT_ONDEMAND0, ARRAYSIZE(res), threadID); if (tracedSceneParams.materialAtlas != nullptr) { - device->BindResource(CS, tracedSceneParams.materialAtlas, TEXSLOT_ONDEMAND8, threadID); + device->BindResource(PS, tracedSceneParams.materialAtlas, TEXSLOT_ONDEMAND8, threadID); } else { - device->BindResource(CS, wiTextureHelper::getWhite(), TEXSLOT_ONDEMAND8, threadID); + device->BindResource(PS, wiTextureHelper::getWhite(), TEXSLOT_ONDEMAND8, threadID); } + sceneBVH.Bind(PS, threadID); - // Create a temporary render target: - Texture2D* rt = nullptr; TextureDesc desc; desc.Width = object.lightmapWidth; desc.Height = object.lightmapHeight; - desc.BindFlags = BIND_RENDER_TARGET; + desc.BindFlags = BIND_RENDER_TARGET | BIND_SHADER_RESOURCE; desc.Format = FORMAT_R8G8B8A8_UNORM; - device->CreateTexture2D(&desc, nullptr, &rt); - device->BindRenderTargets(1, &rt, nullptr, threadID); + SAFE_DELETE(object.lightmap); + device->CreateTexture2D(&desc, nullptr, &object.lightmap); + device->BindRenderTargets(1, &object.lightmap, nullptr, threadID); float clearColor[4] = { 0,0,0,1 }; - device->ClearRenderTarget(rt, clearColor, threadID); + device->ClearRenderTarget(object.lightmap, clearColor, threadID); ViewPort vp; vp.Width = (float)desc.Width; @@ -7589,6 +7712,8 @@ void RenderObjectLightMap(ObjectComponent& object, GRAPHICSTHREAD threadID) device->DrawIndexedInstanced((int)mesh.indices.size(), 1, 0, 0, 0, threadID); + device->BindRenderTargets(0, nullptr, nullptr, threadID); + // Now download the rendered lightmap from GPU and store it inside the object: @@ -7610,10 +7735,9 @@ void RenderObjectLightMap(ObjectComponent& object, GRAPHICSTHREAD threadID) HRESULT hr = device->CreateTexture2D(&staging_desc, nullptr, &stagingTex); assert(SUCCEEDED(hr)); - bool download_success = device->DownloadResource(rt, stagingTex, object.lightmapTextureData.data(), GRAPHICSTHREAD_IMMEDIATE); + bool download_success = device->DownloadResource(object.lightmap, stagingTex, object.lightmapTextureData.data(), GRAPHICSTHREAD_IMMEDIATE); assert(download_success); - delete rt; delete stagingTex; } diff --git a/WickedEngine/wiSceneSystem.h b/WickedEngine/wiSceneSystem.h index 2bcb01d13..f9ec49e85 100644 --- a/WickedEngine/wiSceneSystem.h +++ b/WickedEngine/wiSceneSystem.h @@ -409,6 +409,7 @@ namespace wiSceneSystem // Non-serialized attributes: XMFLOAT4 globalLightMapMulAdd = XMFLOAT4(0, 0, 0, 0); + wiGraphicsTypes::Texture2D* lightmap = nullptr; XMFLOAT3 center = XMFLOAT3(0, 0, 0); float impostorFadeThresholdRadius; diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index acd20db99..981376f99 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wiVersion // minor features, major updates const int minor = 24; // minor bug fixes, alterations, refactors, updates - const int revision = 0; + const int revision = 1; long GetVersion()