/** * ---|> [State] */ State::stateResult_t StrangeExampleRenderer::doEnableState(FrameContext & context,Node * node, const RenderParam & rp){ if (rp.getFlag(NO_GEOMETRY) || rp.getFlag(SKIP_RENDERER)) return State::STATE_SKIPPED; /// Collect all nodes in the frustum const auto nodes = collectNodesInFrustum<GeometryNode>(node, context.getCamera()->getFrustum()); if(nodes.size() == 1 && nodes.front() == node) { WARN("This renderer must not be attached to GeometryNodes."); return State::STATE_SKIPPED; } /// Render all nodes with a little extra strangeness Geometry::Matrix4x4 m; float f=0; for(const auto & geoNode : nodes) { float f2=f+Util::Timer::now(); m.setIdentity(); m.scale(1.0+sin( f2)/40.0); m.rotate_deg( sin( f2/0.7)/2.0,0,1,0); context.getRenderingContext().pushMatrix_modelToCamera(); context.getRenderingContext().multMatrix_modelToCamera(m); context.displayNode(geoNode,rp); context.getRenderingContext().popMatrix_modelToCamera(); f+=137.0; } return State::STATE_SKIP_RENDERING; }
//! ---|> [State] void ShaderUniformState::doDisableState(FrameContext & context, Node *, const RenderParam & /*rp*/) { Rendering::Shader * shader = context.getRenderingContext().getActiveShader(); if(shader!=nullptr){ for(const auto & uniform : priorValueStack.top()) { shader->setUniform(context.getRenderingContext(), uniform, true); // warn if unused--> should not happen } } priorValueStack.pop(); }
void ColorCubeGenerator::generateColorCubes(FrameContext& context, Node * node, uint32_t nodeCount, uint32_t triangleCount){ if(fbo.isNull()){ // init the data needed for processing the colors and process the color cubes depthTexture = Rendering::TextureUtils::createDepthTexture(maxTexSize * 6, maxTexSize); colorTexture = Rendering::TextureUtils::createStdTexture(maxTexSize * 6, maxTexSize, true); fbo = new Rendering::FBO(); context.getRenderingContext().pushAndSetFBO(fbo.get()); fbo->attachColorTexture(context.getRenderingContext(),colorTexture.get()); fbo->attachDepthTexture(context.getRenderingContext(),depthTexture.get()); context.getRenderingContext().popFBO(); camera = new CameraNodeOrtho(); } processColorCubes(context, node, nodeCount, triangleCount); }
NodeRendererResult SurfelRenderer::displayNode(FrameContext & context, Node * node, const RenderParam & /*rp*/){ static const Util::StringIdentifier SURFEL_ATTRIBUTE("surfels"); auto surfelAttribute = dynamic_cast<Util::ReferenceAttribute<Rendering::Mesh>*>(node->findAttribute( SURFEL_ATTRIBUTE )); if( !surfelAttribute || !surfelAttribute->get()) return NodeRendererResult::PASS_ON; Rendering::Mesh& surfelMesh = *surfelAttribute->get(); const Geometry::Rect projection = context.getProjectedRect(node); const float approxProjectedSideLength = std::sqrt(projection.getHeight() * projection.getWidth()); // const auto& worldBB = node->getWorldBB(); // const float approxProjectedSideLength = projectionScale * worldBB.getDiameter() / (worldBB.getCenter()-cameraOrigin).length(); if(approxProjectedSideLength > maxSideLength) return NodeRendererResult::PASS_ON; static const Util::StringIdentifier REL_COVERING_ATTRIBUTE("surfelRelCovering"); auto surfelCoverageAttr = node->findAttribute(REL_COVERING_ATTRIBUTE); const float relCovering = surfelCoverageAttr ? surfelCoverageAttr->toFloat() : 0.5; const float approxProjectedArea = approxProjectedSideLength * approxProjectedSideLength * relCovering; uint32_t surfelCount = std::min( surfelMesh.isUsingIndexData() ? surfelMesh.getIndexCount() : surfelMesh.getVertexCount(), static_cast<uint32_t>(approxProjectedArea * countFactor) + 1); float surfelSize = std::min(sizeFactor * approxProjectedArea / surfelCount,maxSurfelSize); bool handled = true; if(approxProjectedSideLength > minSideLength && minSideLength<maxSideLength){ const float f = 1.0f -(approxProjectedSideLength-minSideLength) / (maxSideLength-minSideLength); surfelCount = std::min( surfelMesh.isUsingIndexData() ? surfelMesh.getIndexCount() : surfelMesh.getVertexCount(), static_cast<uint32_t>(f * surfelCount) + 1); surfelSize *= f; handled = false; // std::cout << approxProjectedSideLength<<"\t"<<f<<"\n"; } // std::cout << surfelSize<<"\t"<<"\n"; // if( node->getRenderingLayers()&0x02 ) // std::cout << "pSize"<<approxProjectedSideLength << "\t#:"<<surfelCount<<"\ts:"<<surfelSize<<"\n"; auto& renderingContext = context.getRenderingContext(); static Rendering::Uniform enableSurfels("renderSurfels", true); static Rendering::Uniform disableSurfels("renderSurfels", false); renderingContext.setGlobalUniform(enableSurfels); renderingContext.pushAndSetPointParameters( Rendering::PointParameters(std::min(surfelSize,32.0f) )); renderingContext.pushAndSetMatrix_modelToCamera( renderingContext.getMatrix_worldToCamera() ); renderingContext.multMatrix_modelToCamera(node->getWorldTransformationMatrix()); context.displayMesh(&surfelMesh, 0, surfelCount ); renderingContext.popMatrix_modelToCamera(); renderingContext.popPointParameters(); renderingContext.setGlobalUniform(disableSurfels); return handled ? NodeRendererResult::NODE_HANDLED : NodeRendererResult::PASS_ON; }
State::stateResult_t OccludeeRenderer::doEnableState(FrameContext & context, Node * rootNode, const RenderParam & rp) { if (rp.getFlag(SKIP_RENDERER)) { return State::STATE_SKIPPED; } static PolygonModeState polygonMode; polygonMode.changeParameters().setMode(Rendering::PolygonModeParameters::LINE); occlusionCullingRenderer->setMode(OccRenderer::MODE_CULLING); const State::stateResult_t resultCulling = occlusionCullingRenderer->enableState(context, rootNode, rp); if(resultCulling != State::STATE_SKIP_RENDERING) { WARN("Using the OccRenderer for culling failed."); return resultCulling; } // Clear the depth buffer to be able to render the occluded geometry in front of the previously rendered geometry. context.getRenderingContext().clearDepth(1.0f); // Activate wireframe mode. polygonMode.enableState(context, rootNode, rp); occlusionCullingRenderer->setMode(OccRenderer::MODE_SHOW_CULLED); RenderParam rpWorldMatrix = rp; rpWorldMatrix.setFlag(USE_WORLD_MATRIX); const State::stateResult_t resultShowCulled = occlusionCullingRenderer->enableState(context, rootNode, rpWorldMatrix); if(resultShowCulled != State::STATE_SKIP_RENDERING) { WARN("Using the OccRenderer for showing the culled geometry failed."); return resultShowCulled; } // Deactivate wireframe mode. polygonMode.disableState(context, rootNode, rp); return State::STATE_SKIP_RENDERING; }
//! ---|> [State] State::stateResult_t ShaderUniformState::doEnableState(FrameContext & context, Node *, const RenderParam & /*rp*/) { priorValueStack.push( std::vector<Rendering::Uniform>() ); Rendering::Shader * shader = context.getRenderingContext().getActiveShader(); if(shader!=nullptr){ // assure that possible changes in the renderingContext are applied to the sg-uniforms before querying the uniforms context.getRenderingContext().applyChanges(); for(const auto & uniformEntry : uMap) { const Rendering::Uniform & uniform(uniformEntry.second); const Rendering::Uniform & priorUniform(shader->getUniform(uniform.getNameId())); if(priorUniform.isNull()) continue; priorValueStack.top().push_back(priorUniform); shader->setUniform(context.getRenderingContext(),uniform,true); // warn if unused--> should not happen } } return State::STATE_OK; }
void VisibilitySubdivisionRenderer::debugDisplay(uint32_t & renderedTriangles, object_ptr object, FrameContext & context, const RenderParam & rp) { const Util::Color4f specular(0.1f, 0.1f, 0.1f, 1.0f); const float ratio = static_cast<float> (renderedTriangles) / static_cast<float> (maxRuntime); // Gradient from light blue to red. Util::Color4f color(ratio, 0.5f - 0.5f * ratio, 1.0f - ratio, 1.0f); Rendering::MaterialParameters material; material.setAmbient(color); material.setDiffuse(color); material.setSpecular(specular); material.setShininess(64.0f); context.getRenderingContext().pushAndSetMaterial(material); context.displayNode(object, rp); context.getRenderingContext().popMaterial(); renderedTriangles += object->getTriangleCount(); }
//! ---|> [State] State::stateResult_t EnvironmentState::doEnableState(FrameContext & context, Node * /*node*/, const RenderParam & rp) { if (rp.getFlag(NO_GEOMETRY)) return State::STATE_SKIPPED; if (!environment.isNull()) { // extract rotation of the camera Geometry::SRT camRotationSrt = context.getRenderingContext().getMatrix_worldToCamera()._toSRT(); camRotationSrt.setTranslation(Geometry::Vec3(0,0,0)); camRotationSrt.setScale(1.0); // render the environment node as seen standing at the origin (0,0,0) but looking in the camera's direction. context.getRenderingContext().pushMatrix_modelToCamera(); context.getRenderingContext().setMatrix_modelToCamera(Geometry::Matrix4x4(camRotationSrt)); context.getRenderingContext().multMatrix_modelToCamera(Geometry::Matrix4x4f(Geometry::SRT(Geometry::Vec3f(0,0,0), context.getWorldFrontVector(), context.getWorldUpVector()))); context.displayNode(environment.get(), rp); context.getRenderingContext().popMatrix_modelToCamera(); } return State::STATE_OK; }
//! ---|> [State] State::stateResult_t ShaderState::doEnableState(FrameContext & context, Node *, const RenderParam & /*rp*/) { if( !shader ) return State::STATE_SKIPPED; auto & rCtxt = context.getRenderingContext(); // push current shader to stack rCtxt.pushAndSetShader(shader.get()); if( !rCtxt.isShaderEnabled(shader.get()) ){ rCtxt.popShader(); deactivate(); WARN("Enabling ShaderState failed. State has been deactivated!"); return State::STATE_SKIPPED; } for(const auto & uniformEntry : uMap) shader->setUniform(rCtxt, uniformEntry.second); return State::STATE_OK; }
//! Distribute the budget based on the projected size of the child nodes. static void distributeProjectedSize(double value, const Util::StringIdentifier & attributeId, Node * node, FrameContext & context) { const auto children = getChildNodes(node); // A pair stores a node and its projected size. std::vector<std::pair<Node *, float>> nodeProjSizePairs; nodeProjSizePairs.reserve(children.size()); double projSizeSum = 0.0; const Geometry::Rect_f screenRect(context.getRenderingContext().getWindowClientArea()); for(const auto & child : children) { // Clip the projected rect to the screen. auto projRect = context.getProjectedRect(child); projRect.clipBy(screenRect); const auto projSize = projRect.getArea(); projSizeSum += projSize; nodeProjSizePairs.emplace_back(child, projSize); } for(const auto & nodeProjSizePair : nodeProjSizePairs) { const double factor = nodeProjSizePair.second / projSizeSum; setOrUpdateAttribute(nodeProjSizePair.first, attributeId, factor * value); } }
//! ---|> [State] void ShaderState::doDisableState(FrameContext & context, Node *, const RenderParam & /*rp*/) { // restore old shader context.getRenderingContext().popShader(); }
State::stateResult_t VisibilitySubdivisionRenderer::doEnableState( FrameContext & context, Node *, const RenderParam & rp) { if (rp.getFlag(SKIP_RENDERER)) { return State::STATE_SKIPPED; } if (viSu == nullptr) { // Invalid information. => Fall back to standard rendering. return State::STATE_SKIPPED; } // [AccumRendering] if(accumRenderingEnabled){ // camera moved? -> start new frame const Geometry::Matrix4x4 & newCamMat=context.getCamera()->getWorldTransformationMatrix(); if(newCamMat!=lastCamMatrix){ lastCamMatrix = newCamMat; startRuntime=0; } }else{ startRuntime=0; } // ---- uint32_t renderedTriangles = 0; if (hold) { for (const auto & object : holdObjects) { if (debugOutput) { debugDisplay(renderedTriangles, object, context, rp); } else { context.displayNode(object, rp); } } return State::STATE_SKIP_RENDERING; } Geometry::Vec3 pos = context.getCamera()->getWorldOrigin(); bool refreshCache = false; // Check if cached cell can be used. if (currentCell == nullptr || !currentCell->getBB().contains(pos)) { currentCell = viSu->getNodeAtPosition(pos); refreshCache = true; } if (currentCell == nullptr || !currentCell->isLeaf()) { // Invalid information. => Fall back to standard rendering. return State::STATE_SKIPPED; } if (refreshCache) { try { const auto & vv = getVV(currentCell); const uint32_t maxIndex = vv.getIndexCount(); objects.clear(); objects.reserve(maxIndex); for(uint_fast32_t index = 0; index < maxIndex; ++index) { if(vv.getBenefits(index) == 0) { continue; } const VisibilityVector::costs_t costs = vv.getCosts(index); const VisibilityVector::benefits_t benefits = vv.getBenefits(index); const float score = static_cast<float>(costs) / static_cast<float>(benefits); objects.emplace_back(score, vv.getNode(index)); } } catch(...) { // Invalid information. => Fall back to standard rendering. return State::STATE_SKIPPED; } if (displayTexturedDepthMeshes) { #ifdef MINSG_EXT_OUTOFCORE for (const auto & depthMesh : depthMeshes) { OutOfCore::getCacheManager().setUserPriority(depthMesh.get(), 0); } #endif /* MINSG_EXT_OUTOFCORE */ depthMeshes.clear(); depthMeshes.reserve(6); textures.clear(); textures.reserve(6); const std::string dmDirectionStrings[6] = { "-dir_x1_y0_z0", "-dir_x-1_y0_z0", "-dir_x0_y1_z0", "-dir_x0_y-1_z0", "-dir_x0_y0_z1", "-dir_x0_y0_z-1" }; for (auto & dmDirectionString : dmDirectionStrings) { Util::GenericAttribute * attrib = currentCell->getAttribute("DepthMesh" + dmDirectionString); if (attrib == nullptr) { continue; } Util::FileName dmMeshPath(attrib->toString()); Util::Reference<Rendering::Mesh> dmMesh; #ifdef MINSG_EXT_OUTOFCORE Util::GenericAttribute * bbAttrib = currentCell->getAttribute("DepthMesh" + dmDirectionString + "-bounds"); if (bbAttrib == nullptr) { WARN("Found depth mesh with no bounding box."); continue; } std::vector<float> bbValues = Util::StringUtils::toFloats(bbAttrib->toString()); FAIL_IF(bbValues.size() != 6); const Geometry::Box meshBB(Geometry::Vec3(bbValues[0], bbValues[1], bbValues[2]), bbValues[3], bbValues[4], bbValues[5]); dmMesh = OutOfCore::addMesh(dmMeshPath, meshBB); #else /* MINSG_EXT_OUTOFCORE */ dmMesh = Rendering::Serialization::loadMesh(dmMeshPath); #endif /* MINSG_EXT_OUTOFCORE */ depthMeshes.emplace_back(dmMesh); // Count the depth mesh here already. renderedTriangles += dmMesh->getPrimitiveCount(); Util::GenericAttribute * texAttrib = currentCell->getAttribute("Texture" + dmDirectionString); if (texAttrib == nullptr) { continue; } Util::FileName texturePath(texAttrib->toString()); Util::Reference<Rendering::Texture> texture = Rendering::Serialization::loadTexture(texturePath); if (texture.isNull()) { WARN("Loading texture for depth mesh failed."); continue; } textures.emplace_back(texture); } } } const Geometry::Frustum & frustum = context.getCamera()->getFrustum(); holdObjects.clear(); holdObjects.reserve(objects.size()); std::sort(objects.begin(), objects.end()); for(const auto & ratioObjectPair : objects) { object_ptr o = ratioObjectPair.second; #ifdef MINSG_EXT_OUTOFCORE OutOfCore::getCacheManager().setUserPriority(o->getMesh(), 5); #endif /* MINSG_EXT_OUTOFCORE */ if (conditionalFrustumTest(frustum, o->getWorldBB(), rp)) { // [AccumRendering] // skip geometry rendered in the last frame if(renderedTriangles<startRuntime){ renderedTriangles += o->getTriangleCount(); continue; } // ---- if (debugOutput) { debugDisplay(renderedTriangles, o, context, rp); } else { context.displayNode(o, rp); renderedTriangles += o->getTriangleCount(); } holdObjects.push_back(o); } if (maxRuntime != 0 && renderedTriangles >= startRuntime+maxRuntime) { break; } } // Draw the textured depth meshes at the end. if (displayTexturedDepthMeshes) { context.getRenderingContext().pushAndSetPolygonOffset(Rendering::PolygonOffsetParameters(polygonOffsetFactor, polygonOffsetUnits)); auto texIt = textures.cbegin(); for (const auto & depthMesh : depthMeshes) { context.getRenderingContext().pushAndSetShader(getTDMShader()); context.getRenderingContext().pushAndSetTexture(0,texIt->get()); if (conditionalFrustumTest(frustum, depthMesh->getBoundingBox(), rp)) { context.displayMesh(depthMesh.get()); } context.getRenderingContext().popTexture(0); context.getRenderingContext().popShader(); #ifdef MINSG_EXT_OUTOFCORE OutOfCore::getCacheManager().setUserPriority(depthMesh.get(), 2); #endif /* MINSG_EXT_OUTOFCORE */ ++texIt; } context.getRenderingContext().popPolygonOffset(); } // [AccumRendering] startRuntime=renderedTriangles; // ---- return State::STATE_SKIP_RENDERING; }
State::stateResult_t PhotonRenderer::doEnableState(FrameContext & context, Node * node, const RenderParam & rp){ #ifdef MINSG_THESISSTANISLAW_GATHER_STATISTICS Rendering::RenderingContext::finish(); _timer.reset(); #endif // MINSG_THESISSTANISLAW_GATHER_STATISTICS if(!_photonSampler){ WARN("No PhotonSampler present in PhotonRenderer!"); return State::stateResult_t::STATE_SKIPPED; } if(!_approxScene){ WARN("No approximated Scene present in PhotonRenderer!"); return State::stateResult_t::STATE_SKIPPED; } auto& rc = context.getRenderingContext(); rc.setImmediateMode(true); rc.applyChanges(); if(_fboChanged){ if(!initializeFBO(rc)){ WARN("Could not initialize FBO for PhotonRenderer!"); return State::stateResult_t::STATE_SKIPPED; } _fboChanged = false; } RenderParam newRp; rc.pushAndSetFBO(_fbo.get()); rc.clearDepth(1.0f); rc.applyChanges(); for(int32_t i = 0; i < _photonSampler->getPhotonNumber(); i++){ _fbo->setDrawBuffers(2); rc.pushAndSetShader(_indirectLightShader.get()); _lightPatchRenderer->bindTBO(rc, true, false); _photonSampler->bindPhotonBuffer(1); context.pushAndSetCamera(_photonCamera.get()); _indirectLightShader->setUniform(rc, Rendering::Uniform("photonID", i)); rc.clearDepth(1.0f); rc.clearColor(Util::Color4f(0.f, 0.f, 0.f, 0.f)); _approxScene->display(context, newRp); context.popCamera(); _photonSampler->unbindPhotonBuffer(1); _lightPatchRenderer->unbindTBO(rc); rc.popShader(); // Accumulate all pixel values in one photon _fbo->setDrawBuffers(0); rc.pushAndSetShader(_accumulationShader.get()); _accumulationShader->setUniform(rc, Rendering::Uniform("photonID", i)); _accumulationShader->setUniform(rc, Rendering::Uniform("samplingWidth", static_cast<int>(_samplingWidth))); _accumulationShader->setUniform(rc, Rendering::Uniform("samplingHeight", static_cast<int>(_samplingHeight))); _photonSampler->bindPhotonBuffer(1); rc.clearDepth(1.0f); Rendering::TextureUtils::drawTextureToScreen(rc, Geometry::Rect_i(0, 0, _samplingWidth, _samplingHeight), *_indirectLightTexture.get(), Geometry::Rect_f(0.0f, 0.0f, 1.0f, 1.0f)); _photonSampler->unbindPhotonBuffer(1); rc.popShader(); } rc.popFBO(); rc.setImmediateMode(false); // rc.pushAndSetShader(nullptr); // Rendering::TextureUtils::drawTextureToScreen(rc, Geometry::Rect_i(0, 0, _samplingWidth, _samplingHeight), *(_indirectLightTexture.get()), Geometry::Rect_f(0.0f, 0.0f, 1.0f, 1.0f)); // rc.popShader(); // return State::stateResult_t::STATE_SKIP_RENDERING; #ifdef MINSG_THESISSTANISLAW_GATHER_STATISTICS Rendering::RenderingContext::finish(); _timer.stop(); auto& stats = context.getStatistics(); Statistics::instance(stats).addPhotonRendererTime(stats, _timer.getMilliseconds()); #endif // MINSG_THESISSTANISLAW_GATHER_STATISTICS return State::stateResult_t::STATE_OK; }
void OverdrawFactorEvaluator::measure(FrameContext & frameContext, Node & node, const Geometry::Rect & rect) { Rendering::RenderingContext & renderingContext = frameContext.getRenderingContext(); // Set up FBO and texture Util::Reference<Rendering::FBO> fbo = new Rendering::FBO; renderingContext.pushAndSetFBO(fbo.get()); Util::Reference<Rendering::Texture> depthStencilTexture = Rendering::TextureUtils::createDepthStencilTexture(rect.getWidth(), rect.getHeight()); fbo->attachDepthStencilTexture(renderingContext, depthStencilTexture.get()); // Disable color and depth writes renderingContext.pushAndSetColorBuffer(Rendering::ColorBufferParameters(false, false, false, false)); renderingContext.pushAndSetDepthBuffer(Rendering::DepthBufferParameters(false, false, Rendering::Comparison::LESS)); // Increase the stencil value for every rendered pixel Rendering::StencilParameters stencilParams; stencilParams.enable(); stencilParams.setFunction(Rendering::Comparison::ALWAYS); stencilParams.setReferenceValue(0); stencilParams.setBitMask(0); stencilParams.setFailAction(Rendering::StencilParameters::INCR); stencilParams.setDepthTestFailAction(Rendering::StencilParameters::INCR); stencilParams.setDepthTestPassAction(Rendering::StencilParameters::INCR); renderingContext.pushAndSetStencil(stencilParams); // Render the node renderingContext.clearStencil(0); frameContext.displayNode(&node, 0); // Reset GL state renderingContext.popStencil(); renderingContext.popDepthBuffer(); renderingContext.popColorBuffer(); // Fetch the texture. depthStencilTexture->downloadGLTexture(renderingContext); renderingContext.popFBO(); Util::Reference<Util::PixelAccessor> stencilAccessor = Rendering::TextureUtils::createStencilPixelAccessor(renderingContext, *depthStencilTexture.get()); std::vector<uint8_t> stencilValues; stencilValues.reserve(rect.getWidth() * rect.getHeight()); for(uint_fast32_t y = 0; y < rect.getHeight(); ++y) { for(uint_fast32_t x = 0; x < rect.getWidth(); ++x) { const uint8_t stencilValue = stencilAccessor->readSingleValueByte(x, y); stencilValues.push_back(stencilValue); } } // // Create and write debug image // { // Util::Reference<Rendering::Texture> colorTexture = Rendering::TextureUtils::createStdTexture(rect.getWidth(), rect.getHeight(), true); // Util::Reference<Util::PixelAccessor> colorAccessor = Rendering::TextureUtils::createColorPixelAccessor(renderingContext, colorTexture.get()); // const auto stencilMinMax = std::minmax_element(stencilValues.cbegin(), stencilValues.cend()); // const auto stencilMin = *stencilMinMax.first; // const auto stencilMax = *stencilMinMax.second; // const double stencilRange = stencilMax - stencilMin; // const double factor = 1.0 / stencilRange; // // Color scheme RdYlBu with 5 of 8 colors from www.colorbrewer2.org // const std::array<Util::Color4f, 5> gradient = { // Util::Color4ub(44, 123, 182, 255), // Util::Color4ub(171, 217, 233, 255), // Util::Color4ub(255, 255, 191, 255), // Util::Color4ub(253, 174, 97, 255), // Util::Color4ub(215, 25, 28, 255) // }; // for(uint_fast32_t y = 0; y < rect.getHeight(); ++y) { // for(uint_fast32_t x = 0; x < rect.getWidth(); ++x) { // const uint8_t stencilValue = stencilValues[y * rect.getWidth() + x]; // if(stencilValue == 0) { // colorAccessor->writeColor(x, y, Util::Color4f(1.0, 1.0, 1.0, 0.0)); // } else { // const double normalizedValue = static_cast<double>(stencilValue - stencilMin) * factor; // const double gradientPos = normalizedValue * (gradient.size() - 1); // const size_t gradientIndex = std::floor(gradientPos); // colorAccessor->writeColor(x, y, Util::Color4f(gradient[gradientIndex], gradient[gradientIndex + 1], gradientPos - gradientIndex)); // } // } // } // Rendering::Serialization::saveTexture(renderingContext, colorTexture.get(), Util::FileName("stencil.png")); // } if(resultRemoveZeroValues) { stencilValues.erase(std::remove(stencilValues.begin(), stencilValues.end(), 0), stencilValues.end()); } uint8_t result = 0; if(!stencilValues.empty()) { if(resultQuantile >= 1.0) { result = *std::max_element(stencilValues.cbegin(), stencilValues.cend()); } else if(resultQuantile <= 0.0) { result = *std::min_element(stencilValues.cbegin(), stencilValues.cend()); } else { const std::size_t quantilePos = resultQuantile * stencilValues.size(); std::nth_element(stencilValues.begin(), std::next(stencilValues.begin(), static_cast<std::ptrdiff_t>(quantilePos)), stencilValues.end()); result = stencilValues[quantilePos]; } } values->push_back(Util::GenericAttribute::createNumber(result)); setMaxValue_i(result); }
//! ---|> Evaluator void VisibilityEvaluator::measure(FrameContext & context,Node & node,const Geometry::Rect & /*r*/) { // first pass - fill depth buffer. context.getRenderingContext().clearScreen(Util::Color4f(0.9f, 0.9f, 0.9f, 1.0f)); node.display(context,USE_WORLD_MATRIX|FRUSTUM_CULLING); if (getMaxValue()->toInt() == 0.0) { setMaxValue_i(collectNodes<GeometryNode>(&node).size()); } if (mode==SINGLE_VALUE) { // collect geometry nodes in frustum auto objectsInVFList = collectNodesInFrustum<GeometryNode>(&node, context.getCamera()->getFrustum()); for(const auto & geoNode : objectsInVFList) { objectsInVF[reinterpret_cast<uintptr_t>(geoNode)] = geoNode; } // setup occlusion queries int numQueries=objectsInVFList.size(); if (numQueries==0) return; auto queries = new Rendering::OcclusionQuery[numQueries]; { // optional second pass: test bounding boxes context.getRenderingContext().pushAndSetDepthBuffer(Rendering::DepthBufferParameters(true, true, Rendering::Comparison::LEQUAL)); context.getRenderingContext().pushAndSetPolygonOffset(Rendering::PolygonOffsetParameters(0.0f, -1.0f)); int i=0; // start queries Rendering::OcclusionQuery::enableTestMode(context.getRenderingContext()); for(const auto & geoNode : objectsInVFList) { queries[i].begin(); Rendering::drawAbsBox(context.getRenderingContext(), geoNode->getWorldBB() ); queries[i].end(); ++i; } Rendering::OcclusionQuery::disableTestMode(context.getRenderingContext()); i=0; // get results for(auto & geoNode : objectsInVFList) { if (queries[i].getResult() == 0 && !geoNode->getWorldBB().contains(context.getCamera()->getWorldOrigin()) ) { geoNode=nullptr; } ++i; } context.getRenderingContext().popPolygonOffset(); context.getRenderingContext().popDepthBuffer(); } { // third pass: test if nodes are exactly visible context.getRenderingContext().pushAndSetDepthBuffer(Rendering::DepthBufferParameters(true, true, Rendering::Comparison::EQUAL)); int i=0; // start queries for(const auto & geoNode : objectsInVFList) { if (!geoNode) continue; queries[i].begin(); // render object i context.displayNode(geoNode, USE_WORLD_MATRIX); queries[i].end(); ++i; } i=0; // get results for(const auto & geoNode : objectsInVFList) { if (!geoNode) continue; if (queries[i].getResult() > 0) { visibleObjects[reinterpret_cast<uintptr_t>(geoNode)] = geoNode; } ++i; } context.getRenderingContext().popDepthBuffer(); } delete [] queries; } else {//mode == DIRECTION_VALUES const auto nodes = collectVisibleNodes(&node, context); if(doesCountPolygons()){ uint32_t numPolys=0; for(const auto & n : nodes) { numPolys += n->getTriangleCount(); } values->push_back(new Util::_NumberAttribute<float>(numPolys)); }else{ values->push_back(new Util::_NumberAttribute<float>(nodes.size())); } } }
State::stateResult_t SkyboxState::doEnableState(FrameContext & context, Node *, const RenderParam & rp) { if (rp.getFlag(NO_GEOMETRY)) { return State::STATE_SKIPPED; } Vec3 pos = context.getCamera()->getWorldOrigin(); Geometry::Matrix4x4 matrix; matrix.translate(pos); context.getRenderingContext().pushMatrix_modelToCamera(); context.getRenderingContext().multMatrix_modelToCamera(matrix); context.getRenderingContext().pushTexture(0); context.getRenderingContext().pushAndSetDepthBuffer(Rendering::DepthBufferParameters(true, false, Rendering::Comparison::LESS)); context.getRenderingContext().pushAndSetLighting(Rendering::LightingParameters(false)); Geometry::Box box(Geometry::Vec3(0.0f, 0.0f, 0.0f), sideLength); for (uint_fast8_t side = 0; side < 6; ++side) { if (!textures[side].isNull()) { context.getRenderingContext().setTexture(0,textures[side].get()); const Geometry::corner_t * corners = Geometry::Helper::getCornerIndices(static_cast<Geometry::side_t> (side)); // Reverse the corner order because we need the inner surface. Rendering::drawQuad(context.getRenderingContext(), box.getCorner(corners[1]), box.getCorner(corners[0]), box.getCorner(corners[3]), box.getCorner(corners[2]),Util::ColorLibrary::WHITE); } } context.getRenderingContext().popLighting(); context.getRenderingContext().popDepthBuffer(); context.getRenderingContext().popTexture(0); context.getRenderingContext().popMatrix_modelToCamera(); return State::STATE_OK; }
//! ---|> Evaluator void OccOverheadEvaluator::measure(FrameContext & context,Node & node,const Geometry::Rect & /*r*/) { //float currentRenderingTime=0; //int numTests=0; //int numVBO=0; int numPoly=0; Util::Color4f clearColor(0.5f, 0.5f, 0.9f, 1.0f); { // 1. normal render pass // rc.clearScreen(0.5,0.5,0.9); // context.getRenderingContext().finish(); // node.display(rc,renderingFlags); context.getRenderingContext().clearScreen(clearColor); node.display(context,renderingFlags|USE_WORLD_MATRIX); context.getRenderingContext().clearScreen(clearColor); context.getRenderingContext().finish(); context.getStatistics().beginFrame(); node.display(context,renderingFlags|USE_WORLD_MATRIX); context.getRenderingContext().finish(); context.getStatistics().endFrame(); numPoly=static_cast<int>(context.getStatistics().getValueAsDouble(context.getStatistics().getTrianglesCounter())); } // 2. test all group nodes if their bb is visible and collect them std::deque<GroupNode *> visibleGroupNodes; { // collect geometry nodes in frustum const auto groupNodesInVFList = collectNodesInFrustum<GroupNode>(&node,context.getCamera()->getFrustum()); // setup occlusion queries int numQueries=groupNodesInVFList.size(); if (numQueries==0) { values->push_back(nullptr); // TODO!!! return; // TODO?? } auto queries = new Rendering::OcclusionQuery[numQueries]; // test bounding boxes context.getRenderingContext().pushAndSetDepthBuffer(Rendering::DepthBufferParameters(true, true, Rendering::Comparison::LEQUAL)); int i=0; // start queries Rendering::OcclusionQuery::enableTestMode(context.getRenderingContext()); for(const auto & groupNode : groupNodesInVFList) { queries[i].begin(); // queries[i].fastBoxTest((*it)->getWorldBB()); Rendering::drawAbsBox(context.getRenderingContext(), groupNode->getWorldBB() ); queries[i].end(); ++i; } Rendering::OcclusionQuery::disableTestMode(context.getRenderingContext()); i=0; // get results for(const auto & groupNode : groupNodesInVFList) { Geometry::Box extendedBox=groupNode->getWorldBB(); extendedBox.resizeAbs(context.getCamera()->getNearPlane()); if (queries[i].getResult() > 0 || extendedBox.contains(context.getCamera()->getWorldOrigin()) ) { visibleGroupNodes.push_back(groupNode); } ++i; } context.getRenderingContext().popDepthBuffer(); delete [] queries; } std::deque<GeometryNode *> geoNodesInVisibleGroupNodes; // 3. collect all direct geometry-node children { struct GeoChildrenCollector:public NodeVisitor { GeoChildrenCollector(std::deque<GeometryNode*> & _geoNodes,FrameContext & _context): geoNodes(_geoNodes),firstNode(true),frameContext(_context) {} virtual ~GeoChildrenCollector() {} std::deque<GeometryNode*> & geoNodes; bool firstNode; FrameContext & frameContext; // ---|> NodeVisitor NodeVisitor::status enter(Node * _node) override { // if this is the first call, continue... if(firstNode) { firstNode=false; return CONTINUE_TRAVERSAL; } // else collect the geometry children and break the traversal. GeometryNode * gn=dynamic_cast<GeometryNode *>(_node); if(gn){ if(frameContext.getCamera()->testBoxFrustumIntersection( _node->getWorldBB()) != Geometry::Frustum::intersection_t::OUTSIDE) geoNodes.push_back(gn); } return BREAK_TRAVERSAL; } }; for (auto & visibleGroupNode : visibleGroupNodes) { GeoChildrenCollector vis(geoNodesInVisibleGroupNodes,context); (visibleGroupNode)->traverse(vis); } } // 4. clear screen and measure time drawing children float perfectRenderingTime=0; { context.getRenderingContext().clearScreen(clearColor); context.getRenderingContext().finish(); Util::Timer timer; timer.reset(); for (auto & geoNodesInVisibleGroupNode : geoNodesInVisibleGroupNodes) { context.displayNode((geoNodesInVisibleGroupNode),renderingFlags|USE_WORLD_MATRIX); } context.getRenderingContext().finish(); timer.stop(); perfectRenderingTime=timer.getMilliseconds(); } // // Test-costs: // float result=numTests!=0?(currentRenderingTime-perfectRenderingTime)/numTests:0; // currentRenderingTime-perfRendering: // float result=currentRenderingTime-perfectRenderingTime; float result=numPoly>0?perfectRenderingTime*1000/numPoly:0; // std::cout <<currentRenderingTime<<"-"<<perfectRenderingTime<<"="<<result<<"\t"<<numTests<<"\n"; // std::cout <<numVBO<<":"<<geoNodesInVisibleGroupNodes.size()<<"\n"; // float result=perfectRenderingTime; // values.push_back(currentRenderingTime); // values.push_back(perfectRenderingTime); values->push_back(new Util::_NumberAttribute<float>(result)); // if(mode==SINGLE_VALUE){ // if(result>renderingTime || renderingTime==0) // renderingTime=result; // }else{ // values.push_back(result); // } // renderingTime=0; }
void ShaderState::setUniform(FrameContext & context, const Rendering::Uniform & value) { setUniform(value); shader->setUniform(context.getRenderingContext(),value,true,true); // warn if uniform is not defined in shader and force at least one try on applying the uniform }
//! ---|> [State] State::stateResult_t CHCRenderer::doEnableState(FrameContext & context,Node * rootNode, const RenderParam & rp){ if(rp.getFlag(SKIP_RENDERER)) return State::STATE_SKIPPED; if(debugShowVisible){ std::deque<Node *> nodes; { const auto children = getChildNodes(rootNode); nodes.insert(nodes.end(), children.begin(), children.end()); } while(!nodes.empty()){ Node& node = *nodes.front(); nodes.pop_front(); auto& nodeInfo = getNodeInfo(node); if(nodeInfo.frameNr == frameNr && nodeInfo.visible){ if(node.isClosed()) context.displayNode(&node,rp); else { const auto children = getChildNodes(&node); nodes.insert(nodes.end(), children.begin(), children.end()); } } } }else{ ++frameNr; // used for statistics unsigned int stat_numTests = 0; unsigned int stat_numTestsInvisible = 0; unsigned int stat_numTestsVisible = 0; Statistics & statistics = context.getStatistics(); RenderParam childParam = rp + USE_WORLD_MATRIX; const auto& camera = *context.getCamera(); const Geometry::Vec3 camPos = camera.getWorldOrigin(); const float bbEnlargement = camera.getNearPlane(); NodeDistancePriorityQueue_F2B distanceQueue(camPos); std::queue<std::pair<Rendering::OcclusionQuery, Node*>> queryQueue; const auto traverseNode = [&](Node& node){ if( node.isClosed() ){ // leaf node context.displayNode(&node, childParam ); }else{ for(auto & child : getChildNodes(&node)) distanceQueue.push(child); } }; const auto pullUpVisibility = [&](Node& node){ for(Node* n=&node; n&&n!=rootNode; n = n->getParent()){ auto& nodeInfo = getNodeInfo(*n); if(nodeInfo.frameNr == frameNr && nodeInfo.visible) break; nodeInfo.visible = true; nodeInfo.frameNr = frameNr; }; }; const auto startQuery = [&](Node& node,const Geometry::Box& worldBoundingBox){ Rendering::OcclusionQuery query; Rendering::OcclusionQuery::enableTestMode(context.getRenderingContext()); query.begin(); Rendering::drawAbsBox(context.getRenderingContext(), worldBoundingBox ); query.end(); Rendering::OcclusionQuery::disableTestMode(context.getRenderingContext()); return std::make_pair(std::move(query),&node); }; traverseNode(*rootNode); while(!distanceQueue.empty() || !queryQueue.empty()){ // ---- PART 1: process finished occlusion queries while( !queryQueue.empty() && (distanceQueue.empty() || queryQueue.front().first.isResultAvailable()) ){ if( queryQueue.front().first.getResult()>0 ){ ++stat_numTestsVisible; statistics.pushEvent( Statistics::EVENT_TYPE_END_TEST_VISIBLE, 1); Node& node = *queryQueue.front().second; pullUpVisibility(node); auto& nodeInfo = getNodeInfo( node ); if( !nodeInfo.wasVisible ) traverseNode(node); }else{ ++stat_numTestsInvisible; statistics.pushEvent( Statistics::EVENT_TYPE_END_TEST_INVISIBLE, 1); } queryQueue.pop(); } // ---- PART 2: tree traversal if( !distanceQueue.empty() ){ Node& node = *distanceQueue.top(); distanceQueue.pop(); const Geometry::Box worldBoundingBox = node.getWorldBB(); if (camera.testBoxFrustumIntersection( node.getWorldBB()) == Geometry::Frustum::intersection_t::OUTSIDE) { continue; } Geometry::Box enlargedBox = worldBoundingBox; enlargedBox.resizeAbs( bbEnlargement ); if( enlargedBox.contains(camPos) ){ pullUpVisibility(node); traverseNode(node); continue; } auto& nodeInfo = getNodeInfo( node ); // identify previously visible nodes const bool wasVisible = nodeInfo.frameNr == frameNr-1 && nodeInfo.visible; nodeInfo.wasVisible = wasVisible; // reset node's visibility flag nodeInfo.visible = false; // update node's visited flag nodeInfo.frameNr = frameNr; // test leafs and previously invisible nodes if( node.isClosed() || !wasVisible){ // on testing front // start test ++stat_numTests; statistics.pushEvent(Statistics::EVENT_TYPE_START_TEST, 1); queryQueue.push( std::move(startQuery(node,worldBoundingBox)) ); } // traverse a node unless it was invisible if( wasVisible ) traverseNode( node ); } } statistics.addValue(OcclusionCullingStatistics::instance(statistics).getOccTestVisibleCounter(), stat_numTestsVisible); statistics.addValue(OcclusionCullingStatistics::instance(statistics).getOccTestInvisibleCounter(), stat_numTestsInvisible); statistics.addValue(OcclusionCullingStatistics::instance(statistics).getOccTestCounter(), stat_numTests); } // std::cout << std::endl; return State::STATE_SKIP_RENDERING; }
State::stateResult_t TwinPartitionsRenderer::doEnableState(FrameContext & context, Node *, const RenderParam & rp) { if (rp.getFlag(SKIP_RENDERER)) { return State::STATE_SKIPPED; } if (data == nullptr) { // Invalid information. => Fall back to standard rendering. return State::STATE_SKIPPED; } const Geometry::Vec3f pos = context.getCamera()->getWorldOrigin(); // Check if cached cell can be used. if (currentCell == INVALID_CELL || !data->cells[currentCell].bounds.contains(pos)) { // Search the cell that contains the camera position. bool cellFound = false; const uint32_t cellCount = data->cells.size(); for (uint_fast32_t cell = 0; cell < cellCount; ++cell) { if (data->cells[cell].bounds.contains(pos)) { // #ifdef MINSG_EXT_OUTOFCORE // // Set new priorities if the current cell changes. // if (currentCell != cell) { // // Low priority for old depth mesh. // if (currentCell != INVALID_CELL) { // for(std::vector<uint32_t>::const_iterator it = data->cells[currentCell].surroundingIds.begin(); it != data->cells[currentCell].surroundingIds.end(); ++it) { // Rendering::Mesh * depthMesh = data->depthMeshes[*it].get(); // OutOfCore::getCacheManager().setUserPriority(depthMesh, 0); // } // } // // High priority for new depth mesh. // { // for(std::vector<uint32_t>::const_iterator it = data->cells[cell].surroundingIds.begin(); it != data->cells[cell].surroundingIds.end(); ++it) { // Rendering::Mesh * depthMesh = data->depthMeshes[*it].get(); // OutOfCore::getCacheManager().setUserPriority(depthMesh, 20); // } // } // // Low priority for meshes visible from the old cell. // if (currentCell != INVALID_CELL) { // const uint32_t oldVisibleSetIndex = data->cells[currentCell].visibleSetId; // const PartitionsData::visible_set_t & oldVisibleSet = data->visibleSets[oldVisibleSetIndex]; // for (PartitionsData::visible_set_t::const_iterator it = oldVisibleSet.begin(); it != oldVisibleSet.end(); ++it) { // Rendering::Mesh * mesh = data->objects[it->second].get(); // OutOfCore::getCacheManager().setUserPriority(mesh, 0); // } // } // // High priority for meshes visible from the new cell. // { // const uint32_t visibleSetIndex = data->cells[cell].visibleSetId; // const PartitionsData::visible_set_t & visibleSet = data->visibleSets[visibleSetIndex]; // for (PartitionsData::visible_set_t::const_iterator it = visibleSet.begin(); it != visibleSet.end(); ++it) { // Rendering::Mesh * mesh = data->objects[it->second].get(); // OutOfCore::getCacheManager().setUserPriority(mesh, 10); // } // } // } // #endif /* MINSG_EXT_OUTOFCORE */ // Load new textures. textures.clear(); if(drawTexturedDepthMeshes) { textures.reserve(6); for(auto & elem : data->cells[cell].surroundingIds) { const std::string & textureFile = data->textureFiles[elem]; Util::Reference<Rendering::Texture> texture = Rendering::Serialization::loadTexture(Util::FileName(textureFile)); if(texture == nullptr) { WARN("Failed to load texture for depth mesh."); } else { textures.push_back(texture); } } } currentCell = cell; cellFound = true; break; } } if (!cellFound) { // Camera outside view space. currentCell = INVALID_CELL; return State::STATE_SKIPPED; } } uint32_t renderedTriangles = 0; const uint32_t visibleSetIndex = data->cells[currentCell].visibleSetId; const PartitionsData::visible_set_t & visibleSet = data->visibleSets[visibleSetIndex]; if(drawTexturedDepthMeshes) { for(auto & elem : data->cells[currentCell].surroundingIds) { Rendering::Mesh * depthMesh = data->depthMeshes[elem].get(); // Count the depth meshes here already. renderedTriangles += depthMesh->getPrimitiveCount(); } } const Geometry::Frustum & frustum = context.getCamera()->getFrustum(); /*float minDist = 9999999.0f; float maxDist = 0.0f; float prioSum = 0.0f; uint_fast32_t objectCounter = 0; for (PartitionsData::visible_set_t::const_iterator it = visibleSet.begin(); it != visibleSet.end(); ++it) { Rendering::Mesh * mesh = data->objects[it->second].get(); float dist = mesh->getBoundingBox().getDistance(pos); minDist = std::min(dist, minDist); maxDist = std::max(dist, maxDist); if(objectCounter < 10) { prioSum += it->first; ++objectCounter; } } std::ofstream output("twinOutput.tsv", ios_base::out | ios_base::app); output << currentCell << '\t' << data->cells[currentCell].bounds.getDiameter() << '\t' << (data->cells[currentCell].bounds.getCenter() - pos).length() << '\t' << visibleSet.begin()->first << '\t' << visibleSet.rbegin()->first << '\t' << minDist << '\t' << maxDist << '\t' << prioSum << '\t'; for(std::vector<uint32_t>::const_iterator it = data->cells[currentCell].surroundingIds.begin(); it != data->cells[currentCell].surroundingIds.end(); ++it) { Rendering::Mesh * depthMesh = data->depthMeshes[*it].get(); const Geometry::Box & depthMeshBB = depthMesh->getBoundingBox(); const Geometry::Vec3f originalDirection = (depthMeshBB.getCenter() - data->cells[currentCell].bounds.getCenter()).normalize(); const Geometry::Vec3f currentDirection = (depthMeshBB.getCenter() - pos).normalize(); const float angle = currentDirection.dot(originalDirection); const bool visible = (frustum.isBoxInFrustum(depthMeshBB) != Frustum::OUTSIDE); output << '\t'; if(visible) { output << angle ; } else { output << "NA"; } } for(uint_fast32_t i = data->cells[currentCell].surroundingIds.size(); i < 6; ++i) { output << "\tNA"; } output << std::endl; output.close();*/ for (auto & elem : visibleSet) { Rendering::Mesh * mesh = data->objects[elem.second].get(); if (conditionalFrustumTest(frustum, mesh->getBoundingBox(), rp)) { context.displayMesh(mesh); renderedTriangles += mesh->getPrimitiveCount(); } if(rp.getFlag(BOUNDING_BOXES)) { Rendering::drawWireframeBox(context.getRenderingContext(), mesh->getBoundingBox(), Util::ColorLibrary::RED); } if (maxRuntime != 0 && renderedTriangles >= maxRuntime) { break; } } // Draw the textured depth meshes at the end. if(drawTexturedDepthMeshes) { context.getRenderingContext().pushAndSetPolygonOffset(Rendering::PolygonOffsetParameters(polygonOffsetFactor, polygonOffsetUnits)); context.getRenderingContext().pushAndSetShader(getTDMShader()); context.getRenderingContext().pushTexture(0); auto texIt = textures.begin(); for(auto & elem : data->cells[currentCell].surroundingIds) { Rendering::Mesh * depthMesh = data->depthMeshes[elem].get(); Rendering::Texture * texture = texIt->get(); context.getRenderingContext().setTexture(0, texture); if (conditionalFrustumTest(frustum, depthMesh->getBoundingBox(), rp)) { context.displayMesh(depthMesh); } ++texIt; } context.getRenderingContext().popTexture(0); context.getRenderingContext().popShader(); context.getRenderingContext().popPolygonOffset(); } return State::STATE_SKIP_RENDERING; }
//! Distribute the budget based on the projected size and the primitive count of the child nodes in an iterative manner for correct distribution. static void distributeProjectedSizeAndPrimitiveCountIterative(double value, const Util::StringIdentifier & attributeId, Node * node, FrameContext & context) { static const Util::StringIdentifier primitiveCountId("PrimitiveCount"); struct PrimitiveCountAnnotationVisitor : public NodeVisitor { const Util::StringIdentifier & m_primitiveCountId; PrimitiveCountAnnotationVisitor(const Util::StringIdentifier & p_primitiveCountId) : m_primitiveCountId(p_primitiveCountId) { } virtual ~PrimitiveCountAnnotationVisitor() { } NodeVisitor::status leave(Node * _node) override { auto geoNode = dynamic_cast<GeometryNode *>(_node); uint32_t primitiveCount = 0; if(geoNode != nullptr) { const auto mesh = geoNode->getMesh(); primitiveCount = mesh == nullptr ? 0 : mesh->getPrimitiveCount(); } else { const auto children = getChildNodes(_node); for(const auto & child : children) { primitiveCount += child->getAttribute(m_primitiveCountId)->toUnsignedInt(); } } _node->setAttribute(m_primitiveCountId, Util::GenericAttribute::createNumber(primitiveCount)); return CONTINUE_TRAVERSAL; } }; const auto children = getChildNodes(node); // A pair stores the primitive count, the node and its projected size. std::deque<std::tuple<uint32_t, Node *, float>> primitiveCountNodeProjSizeTuples; double projSizeSum = 0.0; const Geometry::Rect_f screenRect(context.getRenderingContext().getWindowClientArea()); for(const auto & child : children) { if(!child->isAttributeSet(primitiveCountId)) { PrimitiveCountAnnotationVisitor visitor(primitiveCountId); child->traverse(visitor); } const auto primitiveCount = child->getAttribute(primitiveCountId)->toUnsignedInt(); // Clip the projected rect to the screen. auto projRect = context.getProjectedRect(child); projRect.clipBy(screenRect); const auto projSize = projRect.getArea(); projSizeSum += projSize; primitiveCountNodeProjSizeTuples.emplace_back(primitiveCount, child, projSize); } // Begin with the node with the lowest primitive count std::sort(primitiveCountNodeProjSizeTuples.begin(), primitiveCountNodeProjSizeTuples.end()); /* Distribute budget until one node gets all budget it asked for. * This means the previous distributions would receive more budget, * thus remove that node from distribution (update value and projSizeSum) * and start again. */ std::deque<std::tuple<uint32_t, Node *, float>> tmpDeque(primitiveCountNodeProjSizeTuples); // TODO: fails to copy values correctly!!! bool nodeRemoved; double tmpValue; double tmpProjSizeSum; double remainingProjSizeSum = projSizeSum; do{ nodeRemoved = false; tmpValue = value; tmpProjSizeSum = remainingProjSizeSum; for(auto it=tmpDeque.begin(); it!=tmpDeque.end(); ++it) { std::tuple<uint32_t, Node *, float> element = *it; const auto primitiveCount = std::get<0>(element); const auto projSize = std::get<2>(element); const auto projSizeFactor = projSize / tmpProjSizeSum; const auto primitiveAssignment = std::min(static_cast<uint32_t>(projSizeFactor * tmpValue), primitiveCount); setOrUpdateAttribute(std::get<1>(element), attributeId, primitiveAssignment); if(primitiveAssignment == primitiveCount){ nodeRemoved = true; tmpDeque.erase(it); value -= primitiveCount; remainingProjSizeSum -= projSize; break; } tmpValue -= primitiveAssignment; tmpProjSizeSum -= projSize; } }while(nodeRemoved); // distribute remaining part of value based on projected size only if(tmpValue >= 1){ for(const auto & primitiveCountNodeProjSizeTuple : primitiveCountNodeProjSizeTuples) { const auto projSize = std::get<2>(primitiveCountNodeProjSizeTuple); const auto projSizeFactor = projSize / projSizeSum; const auto attribute = dynamic_cast<Util::_NumberAttribute<double> *>(std::get<1>(primitiveCountNodeProjSizeTuple)->getAttribute(attributeId)); attribute->set(attribute->get() + projSizeFactor * tmpValue); } } }
//! (internal) Main culling method State::stateResult_t OccRenderer::performCulling(FrameContext & context,Node * rootNode, const RenderParam & rp){ if(mode==MODE_UNCONDITIONED){ // destroy temporal coherence frameNumber++; } frameNumber++; updateNodeInformation(context,rootNode); // ??? // used for statistics unsigned int tests=0; int testsVisible=0; int testsInvisible=0; int occludedGeometryNodes=0; Statistics & statistics = context.getStatistics(); RenderParam childParam = rp + USE_WORLD_MATRIX; const Geometry::Vec3 camPos=context.getCamera()->getWorldOrigin(); // NodeDistancePriorityQueue_F2B distanceQueue(camPos); NodeDistancePriorityQueue_F2B distanceQueue(camPos); { // add root node's children const auto children = getChildNodes(rootNode); for(auto & child : children) { distanceQueue.push(child); } } std::queue<std::pair<Rendering::OcclusionQuery, Node *>> queryQueue; // bool wasIdle=true; int idleCount=0; while ( (!distanceQueue.empty()) || (!queryQueue.empty() ) ) { bool idle=true; while ( !queryQueue.empty() && queryQueue.front().first.isResultAvailable() ) { idle=false; const OcclusionQuery & currentQuery = queryQueue.front().first; unsigned int result = currentQuery.getResult(); Node * node = queryQueue.front().second; queryQueue.pop(); NodeInfo * nodeInfo = getNodeInfo(node); // Node is visible? if (result>0 ) { statistics.pushEvent(Statistics::EVENT_TYPE_END_TEST_VISIBLE,nodeInfo->getActualSubtreeComplexity()); testsVisible++; if (node->isClosed()) { // mark parents as visible Node * n=node; NodeInfo * ni=nodeInfo; while( ni->getVisibleFrameNumber()!=frameNumber){ ni->setVisibleFrameNumber(frameNumber); n=n->getParent(); if(n==nullptr || n==rootNode) break; ni=getNodeInfo(n); } } nodeInfo->setVisibleFrameNumber(frameNumber); // haben wir schon bearbeitet? -> Weiter... if (nodeInfo->getProcessedFrameNumber()==frameNumber) continue; //// if(node->visibleFrameNumber != (frameNumber-1)){ ???? // Process node processNode(context,node,nodeInfo,distanceQueue,childParam); //// } }// Node is fully occluded? else { testsInvisible++; statistics.pushEvent(Statistics::EVENT_TYPE_END_TEST_INVISIBLE, nodeInfo->getActualSubtreeComplexity()); // not processed yet? if (nodeInfo->getProcessedFrameNumber()!=frameNumber) { // TODO: Store and propagate in tree. occludedGeometryNodes+=collectNodesInFrustum<GeometryNode>(node,context.getCamera()->getFrustum()).size(); // mark the node as processed nodeInfo->setProcessedFrameNumber(frameNumber); } // In diesem Frame verdeckt worden? (dann haben wir nicht mit der Bearbeitung gewartet und // die Kinder liegen bereits in der distanceQueue; die sollten jetzt �bersprungen werden) if ( nodeInfo->getVisibleFrameNumber() == (frameNumber-1) ) { if (!node->isClosed()) { const auto children = getChildNodes(node); for(auto & child : children) { getNodeInfo(child)->setProcessedFrameNumber(frameNumber); // TODO: Check if ancestors have to be removed too. } } } } } // ... if ( ! distanceQueue.empty()) { idle=false; Node * node=distanceQueue.top(); NodeInfo * nodeInfo=getNodeInfo(node); distanceQueue.pop(); // If the current node has already been processed, then continue. if (nodeInfo->getProcessedFrameNumber()==frameNumber ) continue; // ||node->isEmpty() // // skip empty subtrees // does never happen (checked in processNode) // if( nodeInfo->getActualSubtreeComplexity() == 0){ // nullBoxes++; // continue; // } // Kamera innerhalb der (um die nearplane vergroesserte) Box? -> immer sichtbar // [2008-01-04_CJ] Bugfix: added nearplane addition; could result in dissapearing scene-parts. Geometry::Box extendedBox=node->getWorldBB(); //node->getGeometryBB(); extendedBox.resizeAbs(context.getCamera()->getNearPlane()); if (extendedBox.contains(camPos)) { nodeInfo->setVisibleFrameNumber(frameNumber); // Process node processNode(context,node,nodeInfo,distanceQueue,childParam); continue; } // !IS_INSIDE_FRUSTUM(node) if ( nodeInfo->getActualFrustumStatus()== Geometry::Frustum::OUTSIDE) continue; // [MODE_OPT_CULLING] For optimal culling, skip all unnecessary tests if(mode==MODE_OPT_CULLING && nodeInfo->getVisibleFrameNumber() == (frameNumber-1)){ nodeInfo->setVisibleFrameNumber(frameNumber); // Process node processNode(context,node,nodeInfo,distanceQueue,childParam); continue; } // if query reasonable // TODO: Something missing here? { // start occlusion test for the current node Rendering::OcclusionQuery query; Rendering::OcclusionQuery::enableTestMode(context.getRenderingContext()); query.begin(); Rendering::drawFastAbsBox(context.getRenderingContext(), node->getWorldBB()); query.end(); Rendering::OcclusionQuery::disableTestMode(context.getRenderingContext()); tests++; queryQueue.emplace(std::move(query), node); statistics.pushEvent(Statistics::EVENT_TYPE_START_TEST, nodeInfo->getActualSubtreeComplexity()); } // if the current node was visible in the last frame, // then assume that it is also visible in this frame and process it. if (nodeInfo->getVisibleFrameNumber() == (frameNumber-1) ) { // Process node processNode(context,node,nodeInfo,distanceQueue,childParam); } } // else{ // waiting // } if (idle) { statistics.pushEvent(Statistics::EVENT_TYPE_IDLE, idleCount++); } else { idleCount = 0; } } statistics.addValue(OcclusionCullingStatistics::instance(statistics).getOccTestCounter(), tests); statistics.addValue(OcclusionCullingStatistics::instance(statistics).getOccTestVisibleCounter(), testsVisible); statistics.addValue(OcclusionCullingStatistics::instance(statistics).getOccTestInvisibleCounter(), testsInvisible); statistics.addValue(OcclusionCullingStatistics::instance(statistics).getCulledGeometryNodeCounter(), occludedGeometryNodes); statistics.pushEvent(Statistics::EVENT_TYPE_FRAME_END,0.3); return State::STATE_SKIP_RENDERING; }
//! Distribute the budget based on the projected size and the primitive count of the child nodes. static void distributeProjectedSizeAndPrimitiveCount(double value, const Util::StringIdentifier & attributeId, Node * node, FrameContext & context) { static const Util::StringIdentifier primitiveCountId("PrimitiveCount"); struct PrimitiveCountAnnotationVisitor : public NodeVisitor { const Util::StringIdentifier & m_primitiveCountId; PrimitiveCountAnnotationVisitor(const Util::StringIdentifier & p_primitiveCountId) : m_primitiveCountId(p_primitiveCountId) { } virtual ~PrimitiveCountAnnotationVisitor() { } NodeVisitor::status leave(Node * _node) override { auto geoNode = dynamic_cast<GeometryNode *>(_node); uint32_t primitiveCount = 0; if(geoNode != nullptr) { const auto mesh = geoNode->getMesh(); primitiveCount = mesh == nullptr ? 0 : mesh->getPrimitiveCount(); } else { const auto children = getChildNodes(_node); for(const auto & child : children) { primitiveCount += child->getAttribute(m_primitiveCountId)->toUnsignedInt(); } } _node->setAttribute(m_primitiveCountId, Util::GenericAttribute::createNumber(primitiveCount)); return CONTINUE_TRAVERSAL; } }; const auto children = getChildNodes(node); // A tuple stores the primitive count, the node and its projected size. std::vector<std::tuple<uint32_t, Node *, float>> primitiveCountNodeProjSizeTuples; primitiveCountNodeProjSizeTuples.reserve(children.size()); double projSizeSum = 0.0; const Geometry::Rect_f screenRect(context.getRenderingContext().getWindowClientArea()); for(const auto & child : children) { if(!child->isAttributeSet(primitiveCountId)) { PrimitiveCountAnnotationVisitor visitor(primitiveCountId); child->traverse(visitor); } const auto primitiveCount = child->getAttribute(primitiveCountId)->toUnsignedInt(); // Clip the projected rect to the screen. auto projRect = context.getProjectedRect(child); projRect.clipBy(screenRect); const auto projSize = projRect.getArea(); projSizeSum += projSize; primitiveCountNodeProjSizeTuples.emplace_back(primitiveCount, child, projSize); } // Begin with the node with the lowest primitive count std::sort(primitiveCountNodeProjSizeTuples.begin(), primitiveCountNodeProjSizeTuples.end()); for(const auto & primitiveCountNodeProjSizeTuple : primitiveCountNodeProjSizeTuples) { const auto primitiveCount = std::get<0>(primitiveCountNodeProjSizeTuple); const auto projSize = std::get<2>(primitiveCountNodeProjSizeTuple); const auto projSizeFactor = projSize / projSizeSum; const auto primitiveAssignment = std::min(static_cast<uint32_t>(projSizeFactor * value), primitiveCount); setOrUpdateAttribute(std::get<1>(primitiveCountNodeProjSizeTuple), attributeId, primitiveAssignment); value -= primitiveAssignment; projSizeSum -= projSize; } }
/*! @note very important: color cubes of the children must already exist, otherwise there will be a segmentation fault */ void ColorCubeGenerator::processColorCube(FrameContext& context, Node * node, deque<Node*>& children) { const Geometry::Box box = node->getWorldBB(); ColorCube cc; // 1. case: current node's color cube should be processed by drawing Geometry < see processColorCubes(...) > if (children.empty()) { context.getRenderingContext().pushAndSetLighting(Rendering::LightingParameters(true)); context.getRenderingContext().applyChanges(); // render the six sides context.getRenderingContext().pushAndSetFBO(fbo.get()); for (uint8_t _side = 0; _side < 6; ++_side) { // determine the color for each of the 6 faces const Geometry::side_t side = static_cast<Geometry::side_t> (_side); if (!prepareCamera(context, box, side)) continue; context.getRenderingContext().clearScreen(Util::Color4f(0.0f, 0.0f, 0.0f, 0.0f)); context.displayNode(node, USE_WORLD_MATRIX); } context.getRenderingContext().popFBO(); colorTexture->downloadGLTexture(context.getRenderingContext()); //////////// debug // static uint32_t counter=0; // stringstream ss; // ss << "screens/colorcube_" << counter++ << ".png"; // Util::FileName filename(ss.str()); // Rendering::Serialization::saveTexture(context.getRenderingContext(), colorTexture.get(), filename); //////////// end debug // determine the color for each of the 6 faces for (uint8_t _side = 0; _side < 6; ++_side) { const Geometry::side_t side = static_cast<Geometry::side_t> (_side); if (prepareCamera(context, box, side)){ const Color4ub c = calculateColor(colorTexture.get(), camera->getViewport()); //calculate color from texture cc.colors[static_cast<uint8_t> (side)] = c; } else { cc.colors[static_cast<uint8_t> (side)] = Color4ub(0, 0, 0, 0); } } context.getRenderingContext().popLighting(); } else{ context.getRenderingContext().pushAndSetFBO(fbo.get()); // enable the fbo // 2. case: the node is not a closed GroupNode (inner node) for (uint8_t _side = 0; _side < 6; ++_side) { // for each of the six faces Geometry::side_t side = static_cast<Geometry::side_t> (_side); if (!prepareCamera(context, box, side)) continue; context.getRenderingContext().clearScreen(Util::Color4f(0.0f, 0.0f, 0.0f, 0.0f)); context.getRenderingContext().pushAndSetMatrix_modelToCamera( context.getRenderingContext().getMatrix_worldToCamera() ); // draw faces using blending context.getRenderingContext().pushAndSetBlending(Rendering::BlendingParameters(Rendering::BlendingParameters::ONE, Rendering::BlendingParameters::ONE_MINUS_SRC_ALPHA)); context.getRenderingContext().pushAndSetCullFace(Rendering::CullFaceParameters(Rendering::CullFaceParameters::CULL_BACK)); context.getRenderingContext().pushAndSetDepthBuffer(Rendering::DepthBufferParameters(true, false, Rendering::Comparison::LESS)); context.getRenderingContext().pushAndSetLighting(Rendering::LightingParameters(false)); context.getRenderingContext().applyChanges(); distanceQueue_t nodes(side); // distance queue used for sorting the children with color cubes // sort (back-to-front order) the children according to distance of current side to the camera for(const auto & child : children) { nodes.push(child); } // draw the faces in back-to-front order while (!nodes.empty()) { Node* child = nodes.top(); nodes.pop(); const ColorCube & childsColorCube = ColorCube::getColorCube(child); //cerr << "before drawing colored box " << endl; childsColorCube.drawColoredBox(context, child->getWorldBB()); //cerr << "after drawing colored box" << endl; } context.getRenderingContext().popLighting(); context.getRenderingContext().popDepthBuffer(); context.getRenderingContext().popCullFace(); context.getRenderingContext().popBlending(); context.getRenderingContext().popMatrix_modelToCamera(); } context.getRenderingContext().popFBO(); colorTexture->downloadGLTexture(context.getRenderingContext()); // determine the color for each of the 6 faces for (uint8_t _side = 0; _side < 6; ++_side) { Geometry::side_t side = static_cast<Geometry::side_t> (_side); if (!prepareCamera(context, box, side)) continue; //calculate color from texture cc.colors[static_cast<uint8_t> (side)] = calculateColor(colorTexture.get(), camera->getViewport()); } } ColorCube::attachColorCube(node, cc); }
void ColorVisibilityEvaluator::measure(FrameContext & context, Node & node, const Geometry::Rect & rect) { Rendering::RenderingContext & rCtxt = context.getRenderingContext(); // ### Set up textures for the framebuffer object. ### const uint32_t width = static_cast<uint32_t> (rect.getWidth()); const uint32_t height = static_cast<uint32_t> (rect.getHeight()); if (colorTexture == nullptr || width != static_cast<uint32_t> (colorTexture->getWidth()) || height != static_cast<uint32_t> (colorTexture->getHeight())) { // (Re-)Create textures with correct dimension. rCtxt.pushAndSetFBO(fbo.get()); fbo->detachColorTexture(context.getRenderingContext()); fbo->detachDepthTexture(context.getRenderingContext()); rCtxt.popFBO(); colorTexture = Rendering::TextureUtils::createStdTexture(width, height, true); depthTexture = Rendering::TextureUtils::createDepthTexture(width, height); // Bind textures to FBO. rCtxt.pushAndSetFBO(fbo.get()); fbo->attachColorTexture(context.getRenderingContext(),colorTexture.get()); fbo->attachDepthTexture(context.getRenderingContext(),depthTexture.get()); rCtxt.popFBO(); } // ### Render the scene into a framebuffer object. ### rCtxt.pushAndSetFBO(fbo.get()); rCtxt.pushAndSetShader(getShader()); rCtxt.clearScreen(Util::Color4f(0.0f, 0.0f, 0.0f, 0.0f)); // Color counter for assignment of unique colors to triangles. // Background is black (color 0). int32_t currentColor = 1; // Collect potentially visible geometry nodes. const auto geoNodes = collectNodesInFrustum<GeometryNode>(&node, context.getCamera()->getFrustum()); for(const auto & geoNode : geoNodes) { getShader()->setUniform(rCtxt, Rendering::Uniform("colorOffset", currentColor)); context.displayNode(geoNode, USE_WORLD_MATRIX | NO_STATES); currentColor += geoNode->getTriangleCount(); } rCtxt.popShader(); rCtxt.popFBO(); // ### Read texture and analyze colors. ### // Map from colors to number of occurrences. std::map<uint32_t, uint32_t> colorUsage; // Current color is compared with the previous color. If they match, the local count variable is used as cache before insertion into map is performed. uint32_t lastColor = 0; uint32_t lastCount = 0; colorTexture->downloadGLTexture(rCtxt); const uint32_t * texData = reinterpret_cast<const uint32_t *> (colorTexture->openLocalData(rCtxt)); const uint32_t numPixels = width * height; for (uint_fast32_t p = 0; p < numPixels; ++p) { const uint32_t & color = texData[p]; if (color == 0) { // Ignore background. continue; } if (color == lastColor) { ++lastCount; continue; } else { // Insert previous color. if (lastColor != 0) { increaseColorUsage(lastColor, lastCount, colorUsage); } lastColor = color; lastCount = 1; } } if (lastColor != 0) { increaseColorUsage(lastColor, lastCount, colorUsage); } if (mode == SINGLE_VALUE) { numTrianglesVisible += colorUsage.size(); } else { values->push_back(Util::GenericAttribute::createNumber(colorUsage.size())); } }
void ParticleBillboardRenderer::operator()(ParticleSystemNode * psys, FrameContext & context, const RenderParam & rp) { if(rp.getFlag(NO_GEOMETRY)) { return; } const auto & worldToCamera = context.getRenderingContext().getMatrix_worldToCamera(); const auto cameraToWorld = worldToCamera.inverse(); const auto halfRight = cameraToWorld.transformDirection(context.getWorldRightVector() * 0.5f); const auto halfUp = cameraToWorld.transformDirection(context.getWorldUpVector() * 0.5f); // 2. just update position for each particle and render // render particles const uint32_t count = psys->getParticleCount(); Rendering::VertexDescription vertexDesc; const Rendering::VertexAttribute & posAttrib = vertexDesc.appendPosition3D(); const Rendering::VertexAttribute & colorAttrib = vertexDesc.appendColorRGBAByte(); const Rendering::VertexAttribute & texCoordAttrib = vertexDesc.appendTexCoord(); // The usage of a cache for the mesh has been tested. Reusing a preallocated mesh is not faster. Util::Reference<Rendering::Mesh> mesh = new Rendering::Mesh(vertexDesc, 4 * count, 6 * count); mesh->setDataStrategy(Rendering::SimpleMeshDataStrategy::getPureLocalStrategy()); Rendering::MeshIndexData & indexData = mesh->openIndexData(); Rendering::MeshVertexData & vertexData = mesh->openVertexData(); Util::Reference<Rendering::PositionAttributeAccessor> positionAccessor = Rendering::PositionAttributeAccessor::create(vertexData, posAttrib.getNameId()); Util::Reference<Rendering::ColorAttributeAccessor> colorAccessor = Rendering::ColorAttributeAccessor::create(vertexData, colorAttrib.getNameId()); Util::Reference<Rendering::TexCoordAttributeAccessor> texCoordAccessor = Rendering::TexCoordAttributeAccessor::create(vertexData, texCoordAttrib.getNameId()); uint32_t * indices = indexData.data(); uint_fast32_t index = 0; for(const auto & p : psys->getParticles()) { const Geometry::Vec3f upOffset = halfUp * p.size.getHeight(); const Geometry::Vec3f rightOffset = halfRight * p.size.getWidth(); colorAccessor->setColor(index + 0, p.color); texCoordAccessor->setCoordinate(index + 0, Geometry::Vec2f(0.0f, 0.0f)); positionAccessor->setPosition(index + 0, p.position + upOffset - rightOffset); colorAccessor->setColor(index + 1, p.color); texCoordAccessor->setCoordinate(index + 1, Geometry::Vec2f(0.0f, 1.0f)); positionAccessor->setPosition(index + 1, p.position - upOffset - rightOffset); colorAccessor->setColor(index + 2, p.color); texCoordAccessor->setCoordinate(index + 2, Geometry::Vec2f(1.0f, 1.0f)); positionAccessor->setPosition(index + 2, p.position - upOffset + rightOffset); colorAccessor->setColor(index + 3, p.color); texCoordAccessor->setCoordinate(index + 3, Geometry::Vec2f(1.0f, 0.0f)); positionAccessor->setPosition(index + 3, p.position + upOffset + rightOffset); *indices++ = index + 0; *indices++ = index + 1; *indices++ = index + 3; *indices++ = index + 1; *indices++ = index + 2; *indices++ = index + 3; index += 4; } indexData.markAsChanged(); indexData.updateIndexRange(); vertexData.markAsChanged(); vertexData.updateBoundingBox(); context.displayMesh(mesh.get()); }