Updated realtime occlusion culling algorithm to reduce the number of occlusion tests.

--HG--
extra : convert_revision : svn%3A7752d6cf-9f14-4ad2-affc-04f1e67b81a5/trunk%40160
This commit is contained in:
kearwood
2012-11-17 05:20:26 +00:00
parent a0549b4cfb
commit aae50ff178
6 changed files with 56 additions and 55 deletions

View File

@@ -179,8 +179,6 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
KRVector3 vecCameraDirection = m_viewport.getCameraDirection();
std::set<KRAABB> newVisibleBounds;
// GLuint shadowDepthTexture[KRENGINE_MAX_SHADOW_BUFFERS];
// KRViewport shadowViewports[KRENGINE_MAX_SHADOW_BUFFERS];
// int cShadows = 0;
@@ -209,10 +207,10 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
GLDEBUG(glDisable(GL_BLEND));
// Render the geometry
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_DEFERRED_GBUFFER, newVisibleBounds);
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_DEFERRED_GBUFFER);
// ----====---- Generate Shadowmaps for Lights ----====----
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_GENERATE_SHADOWMAPS, newVisibleBounds);
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_GENERATE_SHADOWMAPS);
GLDEBUG(glViewport(0, 0, m_viewport.getSize().x, m_viewport.getSize().y));
// ----====---- Opaque Geometry, Deferred rendering Pass 2 ----====----
@@ -239,7 +237,7 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
// Render the geometry
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_GENERATE_SHADOWMAPS, newVisibleBounds);
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_GENERATE_SHADOWMAPS);
// ----====---- Opaque Geometry, Deferred rendering Pass 3 ----====----
// Set render target
@@ -270,8 +268,8 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
GLDEBUG(glDepthMask(GL_TRUE));
// Render the geometry
std::set<KRAABB> emptyBoundsSet; // At this point, we only render octree nodes that produced fragments during the 1st pass into the GBuffer
scene.render(this, emptyBoundsSet, m_viewport, KRNode::RENDER_PASS_DEFERRED_OPAQUE, newVisibleBounds);
// TODO: At this point, we only want to render octree nodes that produced fragments during the 1st pass into the GBuffer
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_DEFERRED_OPAQUE);
// Deactivate source buffer texture units
m_pContext->getTextureManager()->selectTexture(6, NULL);
@@ -282,7 +280,7 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
GLDEBUG(glBindTexture(GL_TEXTURE_2D, 0));
} else {
// ----====---- Generate Shadowmaps for Lights ----====----
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_GENERATE_SHADOWMAPS, newVisibleBounds);
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_GENERATE_SHADOWMAPS);
GLDEBUG(glViewport(0, 0, m_viewport.getSize().x, m_viewport.getSize().y));
// ----====---- Opaque Geometry, Forward Rendering ----====----
@@ -313,7 +311,7 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
// Render the geometry
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_FORWARD_OPAQUE, newVisibleBounds);
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_FORWARD_OPAQUE);
}
// ----====---- Sky Box ----====----
@@ -372,7 +370,7 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
GLDEBUG(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
// Render all transparent geometry
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_FORWARD_TRANSPARENT, newVisibleBounds);
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_FORWARD_TRANSPARENT);
// ----====---- Flares ----====----
@@ -396,7 +394,7 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
GLDEBUG(glBlendFunc(GL_ONE, GL_ONE));
// Render all flares
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_ADDITIVE_PARTICLES, newVisibleBounds);
scene.render(this, m_viewport.getVisibleBounds(), m_viewport, KRNode::RENDER_PASS_ADDITIVE_PARTICLES);
// ----====---- Volumetric Lighting ----====----
@@ -424,7 +422,7 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
GLDEBUG(glDepthRangef(0.0, 1.0));
}
scene.render(this, m_viewport.getVisibleBounds(), volumetricLightingViewport, KRNode::RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE, newVisibleBounds);
scene.render(this, m_viewport.getVisibleBounds(), volumetricLightingViewport, KRNode::RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE);
if(volumetric_environment_downsample != 0) {
// Set render target
@@ -459,10 +457,10 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
KRMat4 projectionMatrix = getProjectionMatrix();
m_pContext->getModelManager()->bindVBO((void *)KRENGINE_VBO_3D_CUBE, KRENGINE_VBO_3D_CUBE_SIZE, true, false, false, false, false);
for(std::set<KRAABB>::iterator itr=m_viewport.getVisibleBounds().begin(); itr != m_viewport.getVisibleBounds().end(); itr++) {
for(std::map<KRAABB, int>::iterator itr=m_viewport.getVisibleBounds().begin(); itr != m_viewport.getVisibleBounds().end(); itr++) {
KRMat4 matModel = KRMat4();
matModel.scale((*itr).size() / 2.0f);
matModel.translate((*itr).center());
matModel.scale((*itr).first.size() / 2.0f);
matModel.translate((*itr).first.center());
if(getContext().getShaderManager()->selectShader(pVisShader, m_viewport, matModel, std::vector<KRLight *>(), KRNode::RENDER_PASS_FORWARD_TRANSPARENT)) {
GLDEBUG(glDrawArrays(GL_TRIANGLE_STRIP, 0, 14));
@@ -470,8 +468,6 @@ void KRCamera::renderFrame(KRScene &scene, float deltaTime) {
}
}
m_viewport.setVisibleBounds(newVisibleBounds);
// Re-enable z-buffer write
GLDEBUG(glDepthMask(GL_TRUE));

View File

@@ -300,9 +300,7 @@ void KRLight::renderShadowBuffers(KRCamera *pCamera)
getContext().getShaderManager()->selectShader(shadowShader, m_shadowViewports[iShadow], KRMat4(), std::vector<KRLight *>(), KRNode::RENDER_PASS_SHADOWMAP);
std::set<KRAABB> newVisibleBounds;
getScene().render(pCamera, m_shadowViewports[iShadow].getVisibleBounds(), m_shadowViewports[iShadow], KRNode::RENDER_PASS_SHADOWMAP, newVisibleBounds);
m_shadowViewports[iShadow].setVisibleBounds(newVisibleBounds);
getScene().render(pCamera, m_shadowViewports[iShadow].getVisibleBounds(), m_shadowViewports[iShadow], KRNode::RENDER_PASS_SHADOWMAP);
}
}

View File

@@ -45,6 +45,8 @@
#import "KRPointLight.h"
#import "KRQuaternion.h"
const long KRENGINE_OCCLUSION_TEST_EXPIRY = 60;
KRScene::KRScene(KRContext &context, std::string name) : KRResource(context, name) {
m_pFirstLight = NULL;
m_pRootNode = new KRNode(*this, "scene_root");
@@ -58,7 +60,7 @@ KRScene::~KRScene() {
#if TARGET_OS_IPHONE
void KRScene::render(KRCamera *pCamera, const std::set<KRAABB> &visibleBounds, const KRViewport &viewport, KRNode::RenderPass renderPass, std::set<KRAABB> &newVisibleBounds) {
void KRScene::render(KRCamera *pCamera, std::map<KRAABB, int> &visibleBounds, const KRViewport &viewport, KRNode::RenderPass renderPass) {
std::vector<KRLight *> lights;
@@ -96,10 +98,10 @@ void KRScene::render(KRCamera *pCamera, const std::set<KRAABB> &visibleBounds, c
newRemainingOctrees.clear();
newRemainingOctreesTestResults.clear();
for(std::vector<KROctreeNode *>::iterator octree_itr = remainingOctrees.begin(); octree_itr != remainingOctrees.end(); octree_itr++) {
render(*octree_itr, visibleBounds, pCamera, lights, viewport, renderPass, newRemainingOctrees, newRemainingOctreesTestResults, remainingOctreesTestResultsOnly, newVisibleBounds, false, false);
render(*octree_itr, visibleBounds, pCamera, lights, viewport, renderPass, newRemainingOctrees, newRemainingOctreesTestResults, remainingOctreesTestResultsOnly, false, false);
}
for(std::vector<KROctreeNode *>::iterator octree_itr = remainingOctreesTestResults.begin(); octree_itr != remainingOctreesTestResults.end(); octree_itr++) {
render(*octree_itr, visibleBounds, pCamera, lights, viewport, renderPass, newRemainingOctrees, newRemainingOctreesTestResults, remainingOctreesTestResultsOnly, newVisibleBounds, true, false);
render(*octree_itr, visibleBounds, pCamera, lights, viewport, renderPass, newRemainingOctrees, newRemainingOctreesTestResults, remainingOctreesTestResultsOnly, true, false);
}
remainingOctrees = newRemainingOctrees;
remainingOctreesTestResults = newRemainingOctreesTestResults;
@@ -108,11 +110,23 @@ void KRScene::render(KRCamera *pCamera, const std::set<KRAABB> &visibleBounds, c
newRemainingOctrees.clear();
newRemainingOctreesTestResults.clear();
for(std::vector<KROctreeNode *>::iterator octree_itr = remainingOctreesTestResultsOnly.begin(); octree_itr != remainingOctreesTestResultsOnly.end(); octree_itr++) {
render(*octree_itr, visibleBounds, pCamera, lights, viewport, renderPass, newRemainingOctrees, newRemainingOctreesTestResults, remainingOctreesTestResultsOnly, newVisibleBounds, true, true);
render(*octree_itr, visibleBounds, pCamera, lights, viewport, renderPass, newRemainingOctrees, newRemainingOctreesTestResults, remainingOctreesTestResultsOnly, true, true);
}
// Expire cached occlusion test results
std::set<KRAABB> expired_visible_bounds;
for(std::map<KRAABB, int>::iterator visible_bounds_itr = visibleBounds.begin(); visible_bounds_itr != visibleBounds.end(); visible_bounds_itr++) {
if((*visible_bounds_itr).second + KRENGINE_OCCLUSION_TEST_EXPIRY < getContext().getCurrentFrame()) {
expired_visible_bounds.insert((*visible_bounds_itr).first);
}
}
for(std::set<KRAABB>::iterator expired_visible_bounds_itr = expired_visible_bounds.begin(); expired_visible_bounds_itr != expired_visible_bounds.end(); expired_visible_bounds_itr++) {
visibleBounds.erase(*expired_visible_bounds_itr);
}
}
void KRScene::render(KROctreeNode *pOctreeNode, const std::set<KRAABB> &visibleBounds, KRCamera *pCamera, std::vector<KRLight *> lights, const KRViewport &viewport, KRNode::RenderPass renderPass, std::vector<KROctreeNode *> &remainingOctrees, std::vector<KROctreeNode *> &remainingOctreesTestResults, std::vector<KROctreeNode *> &remainingOctreesTestResultsOnly, std::set<KRAABB> &newVisibleBounds, bool bOcclusionResultsPass, bool bOcclusionTestResultsOnly)
void KRScene::render(KROctreeNode *pOctreeNode, std::map<KRAABB, int> &visibleBounds, KRCamera *pCamera, std::vector<KRLight *> lights, const KRViewport &viewport, KRNode::RenderPass renderPass, std::vector<KROctreeNode *> &remainingOctrees, std::vector<KROctreeNode *> &remainingOctreesTestResults, std::vector<KROctreeNode *> &remainingOctreesTestResultsOnly, bool bOcclusionResultsPass, bool bOcclusionTestResultsOnly)
{
if(pOctreeNode) {
@@ -124,8 +138,9 @@ void KRScene::render(KROctreeNode *pOctreeNode, const std::set<KRAABB> &visibleB
GLuint params = 0;
GLDEBUG(glGetQueryObjectuivEXT(pOctreeNode->m_occlusionQuery, GL_QUERY_RESULT_EXT, &params));
if(params) {
newVisibleBounds.insert(octreeBounds); // Record the actual tests that succeeded during this frame
// visibleBounds.insert(octreeBounds); // Update the list of tests that we won't repeat for subsequent passes during this frame
// Record the frame number that the test has passed on
visibleBounds[octreeBounds] = getContext().getCurrentFrame();
if(!bOcclusionTestResultsOnly) {
// Schedule a pass to perform the rendering
remainingOctrees.push_back(pOctreeNode);
@@ -140,8 +155,8 @@ void KRScene::render(KROctreeNode *pOctreeNode, const std::set<KRAABB> &visibleB
float min_coverage = 0.0f;
float lod_coverage = pOctreeNode->getBounds().coverage(viewport.getViewProjectionMatrix(), viewport.getSize()); // This also checks the view frustrum culling
if(lod_coverage > min_coverage) {
float lod_coverage = pOctreeNode->getBounds().coverage(viewport.getViewProjectionMatrix(), viewport.getSize()); // Cull against the view frustrum
if(lod_coverage > min_coverage) {
// ----====---- Rendering and occlusion test pass ----====----
bool bVisible = false;
@@ -153,25 +168,24 @@ void KRScene::render(KROctreeNode *pOctreeNode, const std::set<KRAABB> &visibleB
KRAABB cameraExtents = KRAABB(viewport.getCameraPosition() - KRVector3(pCamera->getPerspectiveNearZ()), viewport.getCameraPosition() + KRVector3(pCamera->getPerspectiveNearZ()));
bVisible = octreeBounds.intersects(cameraExtents);
if(bVisible) {
newVisibleBounds.insert(octreeBounds); // Record the actual tests that succeeded during this frame
// visibleBounds.insert(octreeBounds); // Update the list of tests that we won't repeat for subsequent passes during this frame
// Record the frame number in which the camera was within the bounds
visibleBounds[octreeBounds] = getContext().getCurrentFrame();
bNeedOcclusionTest = false;
}
}
if(!bVisible) {
// Check if an occlusion query from the prior pass has returned true
bVisible = newVisibleBounds.find(octreeBounds) != newVisibleBounds.end();
if(bVisible) {
bNeedOcclusionTest = false;
}
}
if(!bVisible) {
// Take advantage of temporal consistency of visible elements from frame to frame
// Check if a previous occlusion query has returned true, taking advantage of temporal consistency of visible elements from frame to frame
// If the previous frame rendered this octree, then attempt to render it in this frame without performing a pre-occlusion test
bVisible = visibleBounds.find(octreeBounds) != visibleBounds.end();
// We don't set bNeedOcclusionTest to false here, as we need to perform an occlusion test to record if this octree node was visible for the next frame
std::map<KRAABB, int>::iterator match_itr = visibleBounds.find(octreeBounds);
if(match_itr != visibleBounds.end()) {
bVisible = true;
// We set bNeedOcclusionTest to false only when the previous occlusion test is old and we need to perform an occlusion test to record if this octree node was visible for the next frame
bNeedOcclusionTest = false;
}
}
if(!bVisible) {
@@ -270,7 +284,7 @@ void KRScene::render(KROctreeNode *pOctreeNode, const std::set<KRAABB> &visibleB
const int *childOctreeOrder = renderPass == KRNode::RENDER_PASS_FORWARD_TRANSPARENT || renderPass == KRNode::RENDER_PASS_ADDITIVE_PARTICLES || renderPass == KRNode::RENDER_PASS_VOLUMETRIC_EFFECTS_ADDITIVE ? viewport.getBackToFrontOrder() : viewport.getFrontToBackOrder();
for(int i=0; i<8; i++) {
render(pOctreeNode->getChildren()[childOctreeOrder[i]], visibleBounds, pCamera, lights, viewport, renderPass, remainingOctrees, remainingOctreesTestResults, remainingOctreesTestResultsOnly, newVisibleBounds, false, false);
render(pOctreeNode->getChildren()[childOctreeOrder[i]], visibleBounds, pCamera, lights, viewport, renderPass, remainingOctrees, remainingOctreesTestResults, remainingOctreesTestResultsOnly, false, false);
}
// Remove lights added at this octree level from the stack

View File

@@ -62,9 +62,9 @@ public:
#if TARGET_OS_IPHONE
void render(KRCamera *pCamera, const std::set<KRAABB> &visibleBounds, const KRViewport &viewport, KRNode::RenderPass renderPass, std::set<KRAABB> &newVisibleBounds);
void render(KRCamera *pCamera, std::map<KRAABB, int> &visibleBounds, const KRViewport &viewport, KRNode::RenderPass renderPass);
void render(KROctreeNode *pOctreeNode, const std::set<KRAABB> &visibleBounds, KRCamera *pCamera, std::vector<KRLight *> lights, const KRViewport &viewport, KRNode::RenderPass renderPass, std::vector<KROctreeNode *> &remainingOctrees, std::vector<KROctreeNode *> &remainingOctreesTestResults, std::vector<KROctreeNode *> &remainingOctreesTestResultsOnly, std::set<KRAABB> &newVisibleBounds, bool bOcclusionResultsPass, bool bOcclusionTestResultsOnly);
void render(KROctreeNode *pOctreeNode, std::map<KRAABB, int> &visibleBounds, KRCamera *pCamera, std::vector<KRLight *> lights, const KRViewport &viewport, KRNode::RenderPass renderPass, std::vector<KROctreeNode *> &remainingOctrees, std::vector<KROctreeNode *> &remainingOctreesTestResults, std::vector<KROctreeNode *> &remainingOctreesTestResultsOnly, bool bOcclusionResultsPass, bool bOcclusionTestResultsOnly);
#endif

View File

@@ -154,14 +154,7 @@ void KRViewport::calculateDerivedValues()
}
const std::set<KRAABB> &KRViewport::getVisibleBounds()
std::map<KRAABB, int> &KRViewport::getVisibleBounds()
{
return m_visibleBounds;
}
void KRViewport::setVisibleBounds(const std::set<KRAABB> visibleBounds)
{
m_visibleBounds = visibleBounds;
}

View File

@@ -12,6 +12,7 @@
#include "KRVector2.h"
#include "KRMat4.h"
#include "KRAABB.h"
#include <map.h>
class KRLight;
@@ -38,8 +39,7 @@ public:
// Overload assignment operator
KRViewport& operator=(const KRViewport &v);
const std::set<KRAABB> &getVisibleBounds();
void setVisibleBounds(const std::set<KRAABB> visibleBounds);
std::map<KRAABB, int> &getVisibleBounds();
const std::set<KRLight *> &getVisibleLights();
void setVisibleLights(const std::set<KRLight *> visibleLights);
@@ -62,7 +62,7 @@ private:
void calculateDerivedValues();
std::set<KRAABB> m_visibleBounds; // AABB's that output fragments in the last frame
std::map<KRAABB, int> m_visibleBounds; // AABB's that output fragments in the last frame
};
#endif