Skip to content

Commit

Permalink
CamRenderer::addLight() performs AABB-based culling of lights
Browse files Browse the repository at this point in the history
As each light is submitted to addLight(), a simple intersection test is
performed between the view frustum and the light's AABB, with any light which
falls outside of the frustum (and therefore cannot possibly illuminate any
visible pixels) being discarded rather than added to the internal list of
lights.

In order to allow this test, RendererLight needed a new interface method to
return the illuminated AABB, which was easily satisfied by the existing
lightAABB() method on the Light class.

The submitted lights are still not used for rendering but the number of visible
and total lights is now shown in the render stats display, so it is possible to
confirm that the count increases or decreases as lights enter or leave the
camera view.
  • Loading branch information
Matthew Mott committed Sep 8, 2020
1 parent 470a99d commit d6cfb85
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 41 deletions.
15 changes: 15 additions & 0 deletions include/irender.h
Expand Up @@ -186,6 +186,21 @@ class RendererLight :
/// Return true if this light intersects the given AABB
virtual bool intersectsAABB(const AABB& aabb) const = 0;

/**
* \brief
* Return the AABB of the illuminated volume.
*
* This AABB represents the boundaries of the volume which are illuminated
* by this light. Anything outside of this volume does not need to be
* considered for shading by this light.
*
* Note that for omni lights, dragging the light center point outside of
* the light volume does not expand the lightAABB() value, because the
* light center only affects the direction of the light rays, not the size
* of the illuminated volume.
*/
virtual AABB lightAABB() const = 0;

/**
* \brief
* Return the light origin in world space.
Expand Down
88 changes: 53 additions & 35 deletions radiant/camera/CamWnd.cpp
Expand Up @@ -191,7 +191,7 @@ CamWnd::CamWnd(wxWindow* parent) :
}

wxWindow* CamWnd::getMainWidget() const
{
{
return _mainWxWidget;
}

Expand Down Expand Up @@ -219,13 +219,13 @@ void CamWnd::constructToolbar()
updateActiveRenderModeButton();

// Connect button signals
_mainWxWidget->GetParent()->Connect(wireframeBtn->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
_mainWxWidget->GetParent()->Connect(wireframeBtn->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
wxCommandEventHandler(CamWnd::onRenderModeButtonsChanged), NULL, this);
_mainWxWidget->GetParent()->Connect(flatShadeBtn->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
_mainWxWidget->GetParent()->Connect(flatShadeBtn->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
wxCommandEventHandler(CamWnd::onRenderModeButtonsChanged), NULL, this);
_mainWxWidget->GetParent()->Connect(texturedBtn->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
_mainWxWidget->GetParent()->Connect(texturedBtn->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
wxCommandEventHandler(CamWnd::onRenderModeButtonsChanged), NULL, this);
_mainWxWidget->GetParent()->Connect(lightingBtn->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
_mainWxWidget->GetParent()->Connect(lightingBtn->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
wxCommandEventHandler(CamWnd::onRenderModeButtonsChanged), NULL, this);

// Far clip buttons.
Expand All @@ -234,9 +234,9 @@ void CamWnd::constructToolbar()
const wxToolBarToolBase* clipPlaneInButton = getToolBarToolByLabel(miscToolbar, "clipPlaneInButton");
const wxToolBarToolBase* clipPlaneOutButton = getToolBarToolByLabel(miscToolbar, "clipPlaneOutButton");

_mainWxWidget->GetParent()->Connect(clipPlaneInButton->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
_mainWxWidget->GetParent()->Connect(clipPlaneInButton->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
wxCommandEventHandler(CamWnd::onFarClipPlaneInClick), NULL, this);
_mainWxWidget->GetParent()->Connect(clipPlaneOutButton->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
_mainWxWidget->GetParent()->Connect(clipPlaneOutButton->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
wxCommandEventHandler(CamWnd::onFarClipPlaneOutClick), NULL, this);

setFarClipButtonSensitivity();
Expand All @@ -248,9 +248,9 @@ void CamWnd::constructToolbar()
const wxToolBarToolBase* startTimeButton = getToolBarToolByLabel(miscToolbar, "startTimeButton");
const wxToolBarToolBase* stopTimeButton = getToolBarToolByLabel(miscToolbar, "stopTimeButton");

_mainWxWidget->GetParent()->Connect(startTimeButton->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
_mainWxWidget->GetParent()->Connect(startTimeButton->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
wxCommandEventHandler(CamWnd::onStartTimeButtonClick), NULL, this);
_mainWxWidget->GetParent()->Connect(stopTimeButton->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
_mainWxWidget->GetParent()->Connect(stopTimeButton->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
wxCommandEventHandler(CamWnd::onStopTimeButtonClick), NULL, this);

// Stop time, initially
Expand Down Expand Up @@ -291,9 +291,9 @@ void CamWnd::setFarClipButtonSensitivity()

wxToolBar* miscToolbar = static_cast<wxToolBar*>(_mainWxWidget->FindWindow("MiscToolbar"));

wxToolBarToolBase* clipPlaneInButton =
wxToolBarToolBase* clipPlaneInButton =
const_cast<wxToolBarToolBase*>(getToolBarToolByLabel(miscToolbar, "clipPlaneInButton"));
wxToolBarToolBase* clipPlaneOutButton =
wxToolBarToolBase* clipPlaneOutButton =
const_cast<wxToolBarToolBase*>(getToolBarToolByLabel(miscToolbar, "clipPlaneOutButton"));

miscToolbar->EnableTool(clipPlaneInButton->GetId(), enabled);
Expand All @@ -314,7 +314,7 @@ void CamWnd::constructGUIComponents()
_wxGLWidget->Connect(wxEVT_SIZE, wxSizeEventHandler(CamWnd::onGLResize), NULL, this);
_wxGLWidget->Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler(CamWnd::onMouseScroll), NULL, this);

_mainWxWidget->GetSizer()->Add(_wxGLWidget, 1, wxEXPAND);
_mainWxWidget->GetSizer()->Add(_wxGLWidget, 1, wxEXPAND);
}

CamWnd::~CamWnd()
Expand Down Expand Up @@ -396,8 +396,8 @@ void CamWnd::onStopTimeButtonClick(wxCommandEvent& ev)

void CamWnd::onFrame(wxTimerEvent& ev)
{
// Calling wxTheApp->Yield() might cause another timer callback if enough
// time has passed during rendering. Calling Yield() within Yield()
// Calling wxTheApp->Yield() might cause another timer callback if enough
// time has passed during rendering. Calling Yield() within Yield()
// might in the end cause stack overflows and is caught by wxWidgets.
if (!_timerLock)
{
Expand Down Expand Up @@ -556,6 +556,9 @@ namespace
// Implementation of RenderableCollector for the 3D camera view.
class CamRenderer: public RenderableCollector
{
// The View object for object culling
const render::View& _view;

// Render statistics
render::RenderStatistics& _renderStats;

Expand Down Expand Up @@ -591,16 +594,18 @@ class CamRenderer: public RenderableCollector
public:

// Initialise CamRenderer with the highlight shaders
CamRenderer(Shader& primHighlightShader, Shader& faceHighlightShader,
render::RenderStatistics& stats)
: _renderStats(stats),
CamRenderer(const render::View& view, Shader& primHighlightShader,
Shader& faceHighlightShader, render::RenderStatistics& stats)
: _view(view),
_renderStats(stats),
_highlightedPrimitiveShader(primHighlightShader),
_highlightedFaceShader(faceHighlightShader)
{}

// Instruct the CamRenderer to push its sorted renderables to their
// respective shaders and perform the actual render
void sendToShaders()
void backendRender(RenderStateFlags allowedFlags, const Matrix4& modelView,
const Matrix4& projection)
{
// For each shader in the map
for (auto i = _renderablesByShader.begin();
Expand All @@ -618,6 +623,10 @@ class CamRenderer: public RenderableCollector
lr.lights, lr.entity);
}
}

// Tell the render system to render its shaders and renderables
GlobalRenderSystem().render(allowedFlags, modelView, projection,
_view.getViewer());
}

// RenderableCollector implementation
Expand All @@ -639,11 +648,21 @@ class CamRenderer: public RenderableCollector

void addLight(const RendererLight& light) override
{
// Store the light in our list of scene lights
_sceneLights.push_back(&light);
// Determine if this light is visible within the view frustum
VolumeIntersectionValue viv = _view.TestAABB(light.lightAABB());
if (viv == VOLUME_OUTSIDE)
{
// Not interested
_renderStats.addLight(false);
}
else
{
// Store the light in our list of scene lights
_sceneLights.push_back(&light);

// Count the light for the stats display
_renderStats.addLight();
// Count the light for the stats display
_renderStats.addLight(true);
}
}

void addRenderable(Shader& shader, const OpenGLRenderable& renderable,
Expand Down Expand Up @@ -709,7 +728,7 @@ void CamWnd::Cam_Draw()

Vector3 clearColour(0, 0, 0);

if (getCameraSettings()->getRenderMode() != RENDER_MODE_LIGHTING)
if (getCameraSettings()->getRenderMode() != RENDER_MODE_LIGHTING)
{
clearColour = ColourSchemes().getColour("camera_background");
}
Expand Down Expand Up @@ -818,8 +837,8 @@ void CamWnd::Cam_Draw()
// Main scene render
{
// Front end (renderable collection from scene)
CamRenderer renderer(*_primitiveHighlightShader, *_faceHighlightShader,
_renderStats);
CamRenderer renderer(_view, *_primitiveHighlightShader,
*_faceHighlightShader, _renderStats);
render::RenderableCollectionWalker::CollectRenderablesInScene(renderer, _view);

// Render any active mousetools
Expand All @@ -829,9 +848,8 @@ void CamWnd::Cam_Draw()
}

// Backend (shader rendering)
renderer.sendToShaders();
GlobalRenderSystem().render(allowedRenderFlags, _camera.modelview,
_camera.projection, _view.getViewer());
renderer.backendRender(allowedRenderFlags, _camera.modelview,
_camera.projection);
}

// greebo: Draw the clipper's points (skipping the depth-test)
Expand Down Expand Up @@ -1120,25 +1138,25 @@ const Frustum& CamWnd::getViewFrustum() const
return _view.getFrustum();
}

void CamWnd::onFarClipPlaneOutClick(wxCommandEvent& ev)
void CamWnd::onFarClipPlaneOutClick(wxCommandEvent& ev)
{
farClipPlaneOut();
}

void CamWnd::onFarClipPlaneInClick(wxCommandEvent& ev)
void CamWnd::onFarClipPlaneInClick(wxCommandEvent& ev)
{
farClipPlaneIn();
}

void CamWnd::farClipPlaneOut()
void CamWnd::farClipPlaneOut()
{
getCameraSettings()->setCubicScale( getCameraSettings()->cubicScale() + 1 );

_camera.updateProjection();
update();
}

void CamWnd::farClipPlaneIn()
void CamWnd::farClipPlaneIn()
{
getCameraSettings()->setCubicScale( getCameraSettings()->cubicScale() - 1 );

Expand Down Expand Up @@ -1229,14 +1247,14 @@ void CamWnd::startCapture(const ui::MouseToolPtr& tool)

_freezePointer.startCapture(_wxGLWidget,
[&](int x, int y, int mouseState) // Motion Functor
{
MouseToolHandler::onGLCapturedMouseMove(x, y, mouseState);
{
MouseToolHandler::onGLCapturedMouseMove(x, y, mouseState);

if (freeMoveEnabled())
{
handleGLMouseMoveFreeMoveDelta(x, y, mouseState);
}
},
},
[&, tool]() { MouseToolHandler::handleCaptureLost(tool); }, // called when the capture is lost.
(pointerMode & MouseTool::PointerMode::Freeze) != 0,
(pointerMode & MouseTool::PointerMode::Hidden) != 0,
Expand Down
1 change: 1 addition & 0 deletions radiant/entity/light/LightNode.h
Expand Up @@ -121,6 +121,7 @@ class LightNode :
Matrix4 getLightTextureTransformation() const override;
const ShaderPtr& getShader() const override;
bool intersectsAABB(const AABB& other) const override;
AABB lightAABB() const override { return _light.lightAABB(); }

Vector3 getLightOrigin() const override;
const Matrix4& rotation() const;
Expand Down
17 changes: 11 additions & 6 deletions radiant/render/RenderStatistics.h
Expand Up @@ -12,30 +12,35 @@ class RenderStatistics
// Timer for measuring render time
wxStopWatch _timer;

// Count of lights
int _lights = 0;
// Count of lights (visible and culled)
int _visibleLights = 0;
int _culledLights = 0;

public:

/// Return the constructed string for display
std::string getStatString()
{
long msec = _timer.Time();
return "lights: " + std::to_string(_lights)
return "lights: " + std::to_string(_visibleLights)
+ " / " + std::to_string(_visibleLights + _culledLights)
+ " | msec: " + std::to_string(msec)
+ " | fps: " + (msec > 0 ? std::to_string(1000 / msec) : "-");
}

/// Increment the light count
void addLight()
void addLight(bool visible)
{
++_lights;
if (visible)
++_visibleLights;
else
++_culledLights;
}

/// Reset statistics at the beginning of a frame render
void resetStats()
{
_lights = 0;
_visibleLights = _culledLights = 0;

_timer.Start();
}
Expand Down

0 comments on commit d6cfb85

Please sign in to comment.