From d98dfff0a840d4c4392185b66c067b45edbb4618 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 17 Nov 2017 19:25:21 +0100 Subject: [PATCH] Still working on solving that underestimated shadow far plane on the first cascade. I have finally understood the problem (see TODO in LightStage) --- libraries/render-utils/src/LightStage.cpp | 5 +- .../render-utils/src/RenderDeferredTask.cpp | 4 +- .../render-utils/src/RenderShadowTask.cpp | 9 +- libraries/render-utils/src/ShadowCore.slh | 4 +- libraries/render/src/render/DrawTask.cpp | 185 +++++++++++++----- libraries/render/src/render/DrawTask.h | 54 +++-- libraries/shared/src/GeometryUtil.cpp | 2 +- scripts/developer/utilities/render/shadow.qml | 29 ++- 8 files changed, 212 insertions(+), 80 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 38bbc2ceb9..e9cc7d93b1 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -116,9 +116,12 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie fitFrustum(farCorners.bottomRight); fitFrustum(farCorners.topLeft); fitFrustum(farCorners.topRight); + + // TODO: Far distance should be extended to the intersection of the exteruded shadow frustum far plane + // with the view frustum. // Re-adjust near shadow distance - auto near = glm::max(max.z, -nearDepth); + auto near = glm::min(-max.z, nearDepth); auto far = -min.z; glm::mat4 ortho = glm::ortho(min.x, max.x, min.y, max.y, near, far); cascade._frustum->setProjection(ortho); diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index d2606bf42f..8bd5ae8447 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -204,7 +204,9 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren for (auto i = 0; i < ExtractFrustums::SHADOW_CASCADE_FRUSTUM_COUNT; i++) { const auto shadowFrustum = frustums.getN(ExtractFrustums::SHADOW_CASCADE0_FRUSTUM+i); float tint = 1.0f - i / float(ExtractFrustums::SHADOW_CASCADE_FRUSTUM_COUNT - 1); - task.addJob("DrawShadowFrustum", shadowFrustum, glm::vec3(0.0f, tint, 1.0f)); + char jobName[64]; + sprintf(jobName, "DrawShadowFrustum%d", i); + task.addJob(jobName, shadowFrustum, glm::vec3(0.0f, tint, 1.0f)); } // Render.getConfig("RenderMainView.DrawSelectionBounds").enabled = true diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index fef5cdddbc..6b00a14d23 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -112,8 +112,7 @@ static void adjustNearFar(const AABox& inShapeBounds, ViewFrustum& shadowFrustum float far = 0.0f; computeNearFar(sceneBoundVertices, shadowClipPlanes, near, far); - // Limit the far range to the one used originally. There's no point in rendering objects - // that are not in the view frustum. + // Limit the far range to the one used originally. far = glm::min(far, shadowFrustum.getFarClip()); const auto depthEpsilon = 0.1f; @@ -233,7 +232,6 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende // GPU jobs: Render to shadow map task.addJob("RenderShadowMap", sortedShapesAndBounds, shapePlumber, i); - task.addJob("ShadowTeardown", setupOutput); } } @@ -272,12 +270,13 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O minCascadeDistance = maxCascadeDistance / cascadeLevelScale; } - shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 3.0f; + shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 4.0f; maxCascadeDistance += shadowOverlapDistance; if (_cascadeIndex == 0) { minCascadeDistance = nearClip; + } else { + minCascadeDistance = std::max(minCascadeDistance, nearClip); } - minCascadeDistance = std::max(minCascadeDistance, nearClip); maxCascadeDistance = std::min(maxCascadeDistance, farClip); globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), minCascadeDistance, maxCascadeDistance, shadowOverlapDistance, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); diff --git a/libraries/render-utils/src/ShadowCore.slh b/libraries/render-utils/src/ShadowCore.slh index 8ad972316a..0a34cafe6f 100644 --- a/libraries/render-utils/src/ShadowCore.slh +++ b/libraries/render-utils/src/ShadowCore.slh @@ -49,8 +49,8 @@ vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) { } bool isShadowCascadeProjectedOnPixel(vec4 cascadeTexCoords) { - bvec3 greaterThanZero = greaterThanEqual(cascadeTexCoords.xyz, vec3(0)); - bvec3 lessThanOne = lessThanEqual(cascadeTexCoords.xyz, vec3(1)); + bvec2 greaterThanZero = greaterThanEqual(cascadeTexCoords.xy, vec2(0)); + bvec2 lessThanOne = lessThanEqual(cascadeTexCoords.xy, vec2(1)); return all(greaterThanZero) && all(lessThanOne); } diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 0f4137e38d..629cc55ccb 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -215,79 +215,158 @@ void DrawBounds::run(const RenderContextPointer& renderContext, }); } -gpu::PipelinePointer DrawFrustum::_pipeline; +DrawQuadVolume::DrawQuadVolume(const glm::vec3& color) : + _color{ color } { + _meshVertices = gpu::BufferView(std::make_shared(sizeof(glm::vec3) * 8, nullptr), gpu::Element::VEC3F_XYZ); + _meshStream.addBuffer(_meshVertices._buffer, _meshVertices._offset, _meshVertices._stride); +} + +void DrawQuadVolume::configure(const Config& configuration) { + _isUpdateEnabled = !configuration.isFrozen; +} + +void DrawQuadVolume::run(const render::RenderContextPointer& renderContext, const glm::vec3 vertices[8], + const gpu::BufferView& indices, int indexCount) { + assert(renderContext->args); + assert(renderContext->args->_context); + if (_isUpdateEnabled) { + auto& streamVertices = _meshVertices.edit >(); + std::copy(vertices, vertices + 8, streamVertices.begin()); + } + + RenderArgs* args = renderContext->args; + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + args->_batch = &batch; + batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(args->_viewport); + + glm::mat4 projMat; + Transform viewMat; + args->getViewFrustum().evalProjectionMatrix(projMat); + args->getViewFrustum().evalViewTransform(viewMat); + + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + batch.setPipeline(getPipeline()); + batch.setIndexBuffer(indices); + + batch._glUniform4f(0, _color.x, _color.y, _color.z, 1.0f); + batch.setInputStream(0, _meshStream); + batch.drawIndexed(gpu::LINES, indexCount, 0U); + + args->_batch = nullptr; + }); +} + +gpu::PipelinePointer DrawQuadVolume::getPipeline() { + static gpu::PipelinePointer pipeline; + + if (!pipeline) { + auto vs = gpu::StandardShaderLib::getDrawTransformVertexPositionVS(); + auto ps = gpu::StandardShaderLib::getDrawColorPS(); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding("color", 0)); + gpu::Shader::makeProgram(*program, slotBindings); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(gpu::State::DepthTest(true, false)); + pipeline = gpu::Pipeline::create(program, state); + } + return pipeline; +} + +gpu::BufferView DrawAABox::_cubeMeshIndices; + +DrawAABox::DrawAABox(const glm::vec3& color) : + DrawQuadVolume{ color } { +} + +void DrawAABox::run(const render::RenderContextPointer& renderContext, const Inputs& box) { + if (!box.isNull()) { + static const uint8_t indexData[] = { + 0, 1, + 1, 2, + 2, 3, + 3, 0, + 4, 5, + 5, 6, + 6, 7, + 7, 4, + 0, 4, + 1, 5, + 3, 7, + 2, 6 + }; + + if (!_cubeMeshIndices._buffer) { + auto indices = std::make_shared(sizeof(indexData), indexData); + _cubeMeshIndices = gpu::BufferView(indices, gpu::Element(gpu::SCALAR, gpu::UINT8, gpu::INDEX)); + } + + glm::vec3 vertices[8]; + + getVertices(box, vertices); + + DrawQuadVolume::run(renderContext, vertices, _cubeMeshIndices, sizeof(indexData) / sizeof(indexData[0])); + } +} + +void DrawAABox::getVertices(const AABox& box, glm::vec3 vertices[8]) { + vertices[0] = box.getVertex(TOP_LEFT_NEAR); + vertices[1] = box.getVertex(TOP_RIGHT_NEAR); + vertices[2] = box.getVertex(BOTTOM_RIGHT_NEAR); + vertices[3] = box.getVertex(BOTTOM_LEFT_NEAR); + vertices[4] = box.getVertex(TOP_LEFT_FAR); + vertices[5] = box.getVertex(TOP_RIGHT_FAR); + vertices[6] = box.getVertex(BOTTOM_RIGHT_FAR); + vertices[7] = box.getVertex(BOTTOM_LEFT_FAR); +} + gpu::BufferView DrawFrustum::_frustumMeshIndices; DrawFrustum::DrawFrustum(const glm::vec3& color) : - _color{ color } { - _frustumMeshVertices = gpu::BufferView(std::make_shared(sizeof(glm::vec3) * 8, nullptr), gpu::Element::VEC3F_XYZ); - _frustumMeshStream.addBuffer(_frustumMeshVertices._buffer, _frustumMeshVertices._offset, _frustumMeshVertices._stride); -} - -void DrawFrustum::configure(const Config& configuration) { - _updateFrustum = !configuration.isFrozen; + DrawQuadVolume{ color } { } void DrawFrustum::run(const render::RenderContextPointer& renderContext, const Input& input) { - assert(renderContext->args); - assert(renderContext->args->_context); - - RenderArgs* args = renderContext->args; if (input) { const auto& frustum = *input; - static uint8_t indexData[] = { 0, 1, 2, 3, 0, 4, 5, 6, 7, 4, 5, 1, 2, 6, 7, 3 }; + static const uint8_t indexData[] = { + 0, 1, + 1, 2, + 2, 3, + 3, 0, + 0, 2, + 3, 1, + 4, 5, + 5, 6, + 6, 7, + 7, 4, + 4, 6, + 7, 5, + 0, 4, + 1, 5, + 3, 7, + 2, 6 + }; if (!_frustumMeshIndices._buffer) { auto indices = std::make_shared(sizeof(indexData), indexData); _frustumMeshIndices = gpu::BufferView(indices, gpu::Element(gpu::SCALAR, gpu::UINT8, gpu::INDEX)); } - if (!_pipeline) { - auto vs = gpu::StandardShaderLib::getDrawTransformVertexPositionVS(); - auto ps = gpu::StandardShaderLib::getDrawColorPS(); - gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + glm::vec3 vertices[8]; - gpu::Shader::BindingSet slotBindings; - slotBindings.insert(gpu::Shader::Binding("color", 0)); - gpu::Shader::makeProgram(*program, slotBindings); + getVertices(frustum, vertices); - gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - state->setDepthTest(gpu::State::DepthTest(true, false)); - _pipeline = gpu::Pipeline::create(program, state); - } - - if (_updateFrustum) { - updateFrustum(frustum); - } - - // Render the frustums in wireframe - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { - args->_batch = &batch; - batch.setViewportTransform(args->_viewport); - batch.setStateScissorRect(args->_viewport); - - glm::mat4 projMat; - Transform viewMat; - args->getViewFrustum().evalProjectionMatrix(projMat); - args->getViewFrustum().evalViewTransform(viewMat); - - batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat); - batch.setPipeline(_pipeline); - batch.setIndexBuffer(_frustumMeshIndices); - - batch._glUniform4f(0, _color.x, _color.y, _color.z, 1.0f); - batch.setInputStream(0, _frustumMeshStream); - batch.drawIndexed(gpu::LINE_STRIP, sizeof(indexData) / sizeof(indexData[0]), 0U); - - args->_batch = nullptr; - }); + DrawQuadVolume::run(renderContext, vertices, _frustumMeshIndices, sizeof(indexData) / sizeof(indexData[0])); } } -void DrawFrustum::updateFrustum(const ViewFrustum& frustum) { - auto& vertices = _frustumMeshVertices.edit >(); +void DrawFrustum::getVertices(const ViewFrustum& frustum, glm::vec3 vertices[8]) { vertices[0] = frustum.getNearTopLeft(); vertices[1] = frustum.getNearTopRight(); vertices[2] = frustum.getNearBottomRight(); diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index 5d98c37c21..6f98e3bef1 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -70,12 +70,12 @@ private: int _colorLocation { -1 }; }; -class DrawFrustumConfig : public render::JobConfig { +class DrawQuadVolumeConfig : public render::JobConfig { Q_OBJECT Q_PROPERTY(bool isFrozen MEMBER isFrozen NOTIFY dirty) public: - DrawFrustumConfig(bool enabled = false) : JobConfig(enabled) {} + DrawQuadVolumeConfig(bool enabled = false) : JobConfig(enabled) {} bool isFrozen{ false }; signals: @@ -83,30 +83,58 @@ signals: }; -class DrawFrustum { +class DrawQuadVolume { +public: + + using Config = DrawQuadVolumeConfig; + + void configure(const Config& configuration); + +protected: + DrawQuadVolume(const glm::vec3& color); + + void run(const render::RenderContextPointer& renderContext, const glm::vec3 vertices[8], + const gpu::BufferView& indices, int indexCount); + + gpu::BufferView _meshVertices; + gpu::BufferStream _meshStream; + glm::vec3 _color; + bool _isUpdateEnabled{ true }; + + static gpu::PipelinePointer getPipeline(); +}; + +class DrawAABox : public DrawQuadVolume { +public: + using Inputs = AABox; + using JobModel = render::Job::ModelI; + + DrawAABox(const glm::vec3& color = glm::vec3(1.0f, 1.0f, 1.0f)); + + void run(const render::RenderContextPointer& renderContext, const Inputs& box); + +protected: + + static gpu::BufferView _cubeMeshIndices; + + static void getVertices(const AABox& box, glm::vec3 vertices[8]); +}; + +class DrawFrustum : public DrawQuadVolume { public: - using Config = DrawFrustumConfig; using Input = ViewFrustumPointer; using JobModel = render::Job::ModelI; DrawFrustum(const glm::vec3& color = glm::vec3(1.0f, 1.0f, 1.0f)); - void configure(const Config& configuration); void run(const render::RenderContextPointer& renderContext, const Input& input); private: - static gpu::PipelinePointer _pipeline; static gpu::BufferView _frustumMeshIndices; - bool _updateFrustum{ true }; - gpu::BufferView _frustumMeshVertices; - gpu::BufferStream _frustumMeshStream; - glm::vec3 _color; - - void updateFrustum(const ViewFrustum& frustum); + static void getVertices(const ViewFrustum& frustum, glm::vec3 vertices[8]); }; - } #endif // hifi_render_DrawTask_h diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index 2ee761a1f7..956c61deaf 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -422,7 +422,7 @@ int clipTriangleWithPlanes(const Triangle& triangle, const Plane* planes, int pl *clippedTriangles = triangle; - while (planes < planesEnd) { + while (planes < planesEnd && triangleCount) { int clippedSubTriangleCount; trianglesToTest.clear(); diff --git a/scripts/developer/utilities/render/shadow.qml b/scripts/developer/utilities/render/shadow.qml index 8548ba4119..a8fcf1aec7 100644 --- a/scripts/developer/utilities/render/shadow.qml +++ b/scripts/developer/utilities/render/shadow.qml @@ -15,15 +15,24 @@ Column { id: root spacing: 8 property var viewConfig: Render.getConfig("RenderMainView.DrawViewFrustum"); - property var shadowConfig: Render.getConfig("RenderMainView.DrawShadowFrustum"); + property var shadow0Config: Render.getConfig("RenderMainView.DrawShadowFrustum0"); + property var shadow1Config: Render.getConfig("RenderMainView.DrawShadowFrustum1"); + property var shadow2Config: Render.getConfig("RenderMainView.DrawShadowFrustum2"); + property var shadow3Config: Render.getConfig("RenderMainView.DrawShadowFrustum3"); Component.onCompleted: { viewConfig.enabled = true; - shadowConfig.enabled = true; + shadow0Config.enabled = true; + shadow1Config.enabled = true; + shadow2Config.enabled = true; + shadow3Config.enabled = true; } Component.onDestruction: { viewConfig.enabled = false; - shadowConfig.enabled = false; + shadow0Config.enabled = false; + shadow1Config.enabled = false; + shadow2Config.enabled = false; + shadow3Config.enabled = false; } CheckBox { @@ -31,7 +40,14 @@ Column { checked: false onCheckedChanged: { viewConfig.isFrozen = checked; - shadowConfig.isFrozen = checked; + shadow0Config.isFrozen = checked; + shadow1Config.isFrozen = checked; + shadow2Config.isFrozen = checked; + shadow3Config.isFrozen = checked; + shadow0BoundConfig.isFrozen = checked; + shadow1BoundConfig.isFrozen = checked; + shadow2BoundConfig.isFrozen = checked; + shadow3BoundConfig.isFrozen = checked; } } Row { @@ -46,5 +62,10 @@ Column { color: "blue" font.italic: true } + Label { + text: "Items" + color: "magenta" + font.italic: true + } } }