Skip to content

Commit 18622d4

Browse files
committed
implement stencil test in Level1 and integrate it in render pass
1 parent 2622849 commit 18622d4

File tree

8 files changed

+132
-53
lines changed

8 files changed

+132
-53
lines changed

libopenage/presenter/presenter.cpp

Lines changed: 17 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020
#include "renderer/camera/camera.h"
2121
#include "renderer/gui/gui.h"
2222
#include "renderer/gui/integration/public/gui_application_with_logger.h"
23-
#include "renderer/opengl/context.h"
24-
#include "renderer/opengl/lookup.h"
2523
#include "renderer/render_factory.h"
2624
#include "renderer/render_pass.h"
2725
#include "renderer/render_target.h"
@@ -167,10 +165,19 @@ void Presenter::init_graphics(const renderer::window_settings &window_settings)
167165
this->asset_manager,
168166
this->time_loop->get_clock());
169167
this->render_passes.push_back(this->hud_renderer->get_render_pass());
168+
169+
for (auto& render_pass : render_passes) {
170+
render_pass->set_stencil_state(renderer::StencilState::USE_STENCIL_TEST);
171+
}
170172

171173
this->init_gui();
172174
this->init_final_render_pass();
173175

176+
// set passes indices
177+
this->index_gui_stencil_pass = this->render_passes.size() - 3;
178+
this->index_gui_render_pass = this->render_passes.size() - 2;
179+
this->index_final_render_pass = this->render_passes.size() - 1;
180+
174181
if (this->simulation) {
175182
auto render_factory = std::make_shared<renderer::RenderFactory>(this->terrain_renderer, this->world_renderer);
176183
this->simulation->attach_renderer(render_factory);
@@ -213,7 +220,10 @@ void Presenter::init_gui() {
213220
qml_root, // directory to watch for qml file changes
214221
qml_assets, // qml data: Engine *, the data directory, ...
215222
this->renderer // openage renderer
216-
);
223+
);
224+
225+
auto stencil_pass = this->gui->get_stencil_pass();
226+
this->render_passes.push_back(stencil_pass);
217227

218228
auto gui_pass = this->gui->get_render_pass();
219229
this->render_passes.push_back(gui_pass);
@@ -309,57 +319,24 @@ void Presenter::init_final_render_pass() {
309319
});
310320
}
311321

312-
void Presenter::init_stencil_test() {
313-
glEnable(GL_STENCIL_TEST);
314-
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
315-
glClear(GL_STENCIL_BUFFER_BIT);
316-
}
317-
318-
void Presenter::enable_stencil_for_gui_mask() {
319-
// Replace stencil value with 1 when depth test passes
320-
glStencilFunc(GL_ALWAYS, 1, 0xFF);
321-
glStencilMask(0xFF);
322-
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
323-
}
324-
325-
void Presenter::enable_stencil_for_world() {
326-
// Only pass if stencil value is not 1
327-
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
328-
glStencilMask(0x00);
329-
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
330-
}
331-
332-
void Presenter::disable_stencil() {
333-
glDisable(GL_STENCIL_TEST);
334-
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
335-
}
336-
337322
void Presenter::render() {
338323
// TODO: Pass current time to update() instead of fetching it in renderer
339324
this->camera_manager->update();
340325
this->terrain_renderer->update();
341326
this->world_renderer->update();
342327
this->hud_renderer->update();
343328

344-
this->init_stencil_test();
345-
346-
// First Pass: Render GUI to stencil buffer
347-
this->enable_stencil_for_gui_mask();
348329
this->gui->render();
330+
this->renderer->render(this->render_passes[this->index_gui_stencil_pass]);
349331

350-
// Second Pass: Render game world with stencil buffer
351-
this->enable_stencil_for_world();
352-
for (size_t i = 0; i < this->render_passes.size() - 2; ++i) {
332+
for (size_t i = 0; i < this->index_gui_stencil_pass; ++i) {
353333
this->renderer->render(this->render_passes[i]);
354334
}
355335

356-
// Third Pass: Render GUI to screen
357-
this->disable_stencil();
358336
this->gui->render();
359-
this->renderer->render(this->render_passes[this->render_passes.size() - 2]);
337+
this->renderer->render(this->render_passes[this->index_gui_render_pass]);
360338

361-
// Fourth Pass: Render screen to window
362-
this->renderer->render(this->render_passes.back());
339+
this->renderer->render(this->render_passes[this->index_final_render_pass]);
363340
}
364341

365342
} // namespace openage::presenter

libopenage/presenter/presenter.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,6 @@ class Presenter {
142142

143143
// void init_audio();
144144

145-
void init_stencil_test();
146-
void enable_stencil_for_gui_mask();
147-
void enable_stencil_for_world();
148-
void disable_stencil();
149-
150145
/**
151146
* Render all configured render passes in sequence.
152147
*/
@@ -235,6 +230,13 @@ class Presenter {
235230
* Input manager.
236231
*/
237232
std::shared_ptr<input::InputManager> input_manager;
233+
234+
/**
235+
* Pass indices.
236+
*/
237+
size_t index_gui_stencil_pass;
238+
size_t index_gui_render_pass;
239+
size_t index_final_render_pass;
238240
};
239241

240242
} // namespace presenter

libopenage/renderer/gui/gui.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ std::shared_ptr<renderer::RenderPass> GUI::get_render_pass() const {
6262
return this->render_pass;
6363
}
6464

65+
std::shared_ptr<renderer::RenderPass> GUI::get_stencil_pass() const {
66+
return this->stencil_pass;
67+
}
68+
6569
void GUI::initialize_render_pass(size_t width,
6670
size_t height,
6771
const util::Path &shaderdir) {
@@ -98,6 +102,10 @@ void GUI::initialize_render_pass(size_t width,
98102
// TODO: Rendering into the FBO is a bit redundant right now because we
99103
// just copy the GUI texture into the output texture
100104
this->render_pass = renderer->add_render_pass({display_obj}, fbo);
105+
106+
auto stencil_pass = renderer->add_render_pass({display_obj}, fbo);
107+
stencil_pass->set_stencil_state(StencilState::WRITE_STENCIL_MASK);
108+
this->stencil_pass = stencil_pass;
101109
}
102110

103111

libopenage/renderer/gui/gui.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@ class GUI {
6363
*/
6464
std::shared_ptr<renderer::RenderPass> get_render_pass() const;
6565

66+
/**
67+
* Get the stencil render pass of the GUI.
68+
*
69+
* @return stencil render pass of the GUI
70+
*/
71+
std::shared_ptr<renderer::RenderPass> get_stencil_pass() const;
72+
6673
/**
6774
* Render the GUI texture.
6875
*/
@@ -139,6 +146,11 @@ class GUI {
139146
* this pass via a \p renderer::resources::Renderable.
140147
*/
141148
std::shared_ptr<renderer::RenderPass> render_pass;
149+
150+
/**
151+
* Render pass for the stencil mask of the GUI.
152+
*/
153+
std::shared_ptr<renderer::RenderPass> stencil_pass;
142154
};
143155

144156
} // namespace gui

libopenage/renderer/opengl/renderer.cpp

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,30 @@ void GlRenderer::optimize(const std::shared_ptr<GlRenderPass> &pass) {
162162
}
163163
}
164164

165+
void GlRenderer::setupStencilWrite() {
166+
glEnable(GL_STENCIL_TEST);
167+
glStencilFunc(GL_ALWAYS, 1, 0xFF);
168+
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
169+
glStencilMask(0xFF);
170+
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
171+
glDepthMask(GL_FALSE);
172+
}
173+
174+
void GlRenderer::setupStencilTest() {
175+
glEnable(GL_STENCIL_TEST);
176+
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
177+
glStencilMask(0x00);
178+
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
179+
glDepthMask(GL_TRUE);
180+
}
181+
182+
void GlRenderer::disableStencilTest() {
183+
glDisable(GL_STENCIL_TEST);
184+
glStencilMask(0xFF);
185+
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
186+
glDepthMask(GL_TRUE);
187+
}
188+
165189
void GlRenderer::check_error() {
166190
// thanks for the global state, opengl!
167191
GlContext::check_error();
@@ -176,7 +200,12 @@ void GlRenderer::render(const std::shared_ptr<RenderPass> &pass) {
176200
// see https://www.khronos.org/opengl/wiki/Vertex_Rendering#Causes_of_rendering_failure
177201
shared_quad_vao->bind();
178202

179-
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
203+
// ensure that an (empty) VAO is bound before rendering geometry
204+
// a bound VAO is required to render bufferless quad geometries by OpenGL
205+
// see https://www.khronos.org/opengl/wiki/Vertex_Rendering#Causes_of_rendering_failure
206+
shared_quad_vao->bind();
207+
208+
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
180209

181210
// TODO: Option for face culling
182211
// glEnable(GL_CULL_FACE);
@@ -198,6 +227,20 @@ void GlRenderer::render(const std::shared_ptr<RenderPass> &pass) {
198227
glClear(GL_DEPTH_BUFFER_BIT);
199228
}
200229

230+
switch (layer.stencil_state) {
231+
case StencilState::WRITE_STENCIL_MASK:
232+
setupStencilWrite();
233+
break;
234+
235+
case StencilState::USE_STENCIL_TEST:
236+
setupStencilTest();
237+
break;
238+
239+
case StencilState::DISABLE_STENCIL:
240+
disableStencilTest();
241+
break;
242+
}
243+
201244
for (auto const &obj : objects) {
202245
if (obj.alpha_blending) {
203246
glEnable(GL_BLEND);

libopenage/renderer/opengl/renderer.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ class GlRenderer final : public Renderer {
6363
/// Optimize the render pass by reordering stuff
6464
static void optimize(const std::shared_ptr<GlRenderPass> &pass);
6565

66+
void setupStencilWrite();
67+
68+
void setupStencilTest();
69+
70+
void disableStencilTest();
71+
6672
/// The GL context.
6773
std::shared_ptr<GlContext> gl_context;
6874

libopenage/renderer/render_pass.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ void RenderPass::add_renderables(Renderable &&renderable, int64_t priority) {
8383
this->add_renderables(std::vector<Renderable>{std::move(renderable)}, priority);
8484
}
8585

86-
void RenderPass::add_layer(int64_t priority, bool clear_depth) {
86+
void RenderPass::add_layer(int64_t priority, bool clear_depth, StencilState stencil_state) {
8787
size_t layer_index = 0;
8888
for (const auto &layer : this->layers) {
8989
if (layer.priority > priority) {
@@ -92,14 +92,20 @@ void RenderPass::add_layer(int64_t priority, bool clear_depth) {
9292
layer_index++;
9393
}
9494

95-
this->add_layer(layer_index, priority, clear_depth);
95+
this->add_layer(layer_index, priority, clear_depth, stencil_state);
9696
}
9797

98-
void RenderPass::add_layer(size_t index, int64_t priority, bool clear_depth) {
99-
this->layers.insert(this->layers.begin() + index, Layer{priority, clear_depth});
98+
void RenderPass::add_layer(size_t index, int64_t priority, bool clear_depth, StencilState stencil_state) {
99+
this->layers.insert(this->layers.begin() + index, Layer{priority, clear_depth, stencil_state});
100100
this->renderables.insert(this->renderables.begin() + index, std::vector<Renderable>{});
101101
}
102102

103+
void RenderPass::set_stencil_state(StencilState state) {
104+
for (auto &layer : this->layers) {
105+
layer.stencil_state = state;
106+
}
107+
}
108+
103109
void RenderPass::clear_renderables() {
104110
// Keep layer definitions, but reset the length of each layer to 0
105111
for (size_t i = 0; i < this->layers.size(); i++) {

libopenage/renderer/render_pass.h

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,18 @@ namespace openage {
1414
namespace renderer {
1515
class RenderTarget;
1616

17+
/**
18+
* Stencil states for the render pass.
19+
*/
20+
enum class StencilState {
21+
/// State for writing GUI elements to stencil buffer.
22+
WRITE_STENCIL_MASK,
23+
/// State for using the mask when rendering scene.
24+
USE_STENCIL_TEST,
25+
/// State for normal rendering (GUI rendering).
26+
DISABLE_STENCIL,
27+
};
28+
1729
/**
1830
* Defines a layer in the render pass. A layer is a slice of the renderables
1931
* that have the same priority. Each layer can have its own settings.
@@ -27,6 +39,8 @@ struct Layer {
2739
int64_t priority;
2840
/// Whether to clear the depth buffer before rendering this layer.
2941
bool clear_depth = true;
42+
/// The state of the stencil buffer for the render pass.
43+
StencilState stencil_state;
3044
};
3145

3246
/**
@@ -95,8 +109,17 @@ class RenderPass {
95109
*
96110
* @param priority Priority of the layer. Layers with higher priority are drawn first.
97111
* @param clear_depth If true clears the depth buffer before rendering this layer.
112+
* @param stencil_state State of the stencil buffer, using to do stencil test.
98113
*/
99-
void add_layer(int64_t priority, bool clear_depth = true);
114+
void add_layer(int64_t priority, bool clear_depth = true,
115+
StencilState stencil_state = StencilState::DISABLE_STENCIL);
116+
117+
/**
118+
* Set the stencil state for the render pass.
119+
*
120+
* @param state The new stencil state.
121+
*/
122+
void set_stencil_state(StencilState state);
100123

101124
/**
102125
* Clear the list of renderables
@@ -137,9 +160,11 @@ class RenderPass {
137160
*
138161
* @param index Index in \p layers member to insert the new layer.
139162
* @param priority Priority of the layer. Layers with higher priority are drawn first.
140-
* @param clear_depth If true clears the depth buffer before rendering this layer.
163+
* @param clear_depth If true clears the depth buffer before rendering this layer.
164+
* @param stencil_state State of the stencil buffer, using to do stencil test.
141165
*/
142-
void add_layer(size_t index, int64_t priority, bool clear_depth = true);
166+
void add_layer(size_t index, int64_t priority, bool clear_depth = true,
167+
StencilState stencil_state = StencilState::DISABLE_STENCIL);
143168

144169
/**
145170
* Render target to write to.

0 commit comments

Comments
 (0)