2

I'm trying to use the stencil buffer to draw outline border behind a model.

I'm using 2 render passes, first rendering the model and using GL_ALWAYS to write 1 to the stencil buffer and then I render a slightly scaled up version of the model that uses GL_NOTEQUAL with ref value of 1 so basically to pass only fragments that are outside the original cube thus making the highlight.

I'm getting weird result, the outline is not fully rendered and when I move my camera enough it disappears completely as if the stencil test of the scaled up cube doesn't pass at all. Here's the gist of the rendering loop (shader3 just outputs a color for the border):

while (!glfwWindowShouldClose(window))
{
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClearStencil(0);
    renderer.Clear();    
    ...
    glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)SCREEN_WIDTH / (float)SCREEN_HEIGHT, 0.1f, 100.0f);
    UniformMat4f projectionUniform("u_Projection");
    projectionUniform.SetValues(projection);

    glm::mat4 view = camera.GetView();
    UniformMat4f viewUniform("u_View");
    viewUniform.SetValues(view);

    modelUniform.SetValues(glm::translate(glm::mat4(1.0f), glm::vec3(5.0f, 0.0f, 0.0f)));

    shader1.SetUniform(projectionUniform);
    shader1.SetUniform(viewUniform);
    shader1.SetUniform(modelUniform);

    glStencilFunc(GL_ALWAYS, 1, 0xFF);
    glStencilMask(0xFF);

    renderer.Draw(model, shader1);

    modelUniform.SetValues(glm::scale(glm::translate(glm::mat4(1.0f), glm::vec3(5.0f, 0.0f, 0.0f)), glm::vec3(1.1f, 1.1f, 1.1f)));
    shader3.SetUniform(projectionUniform);
    shader3.SetUniform(viewUniform);
    shader3.SetUniform(modelUniform);

    glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
    glStencilMask(0x00);
    glDisable(GL_DEPTH_TEST);
    renderer.Draw(model, shader3);
    glEnable(GL_DEPTH_TEST);
}

I first tried it with the nanosuit model from crysis and it work as I described not showing all the parts and eventually disappearing when moving the camera. I've decided to try a simple geometry to test this out and tried to outline a simple cube and got the same result: enter image description here

Jorayen
  • 1,737
  • 2
  • 21
  • 52
  • What does `renderer.Clear();`? Does it use [`glClear()`](https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glClear.xml) _with_ `GL_STENCIL_BUFFER_BIT`? – Scheff's Cat Oct 31 '19 at 10:27
  • Yep clear the color, depth and stencil buffer – Jorayen Oct 31 '19 at 10:33
  • There is a nice tutorial in [learnopengl.com](https://learnopengl.com/Advanced-OpenGL/Stencil-testing). You can compare your code concerning subtleties. From my experience with OpenGL, missing small details often have fatal consequences... ;-) – Scheff's Cat Oct 31 '19 at 10:34
  • Thats my resource actually so I'm not sure what went wrong – Jorayen Oct 31 '19 at 10:39

1 Answers1

2

The issue is the call to glStencilMask(0x00); at the end of the main loop. the default value of the stencil mask is 0xff (for 8 bit, see glStencilMask). OpenGL is a state engine. A state is kept, till it is changed again (even beyond frames).
glClear considers the state of the color, depth and stencil mask. If the stencil mask is 0x00, then the stencil buffer isn't cleared at all.

Set the stencil mask 0xff, before you clear the stencil buffer to solve the issue:

glStencilMask(0xFF);
renderer.Clear(); 
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • But isn't `glStencilMask(0xFF);` used only to mask what we're writing to the stencil buffer and not clearing? Why wouldn't it clear when I set `glClearStencil(0);` and call `glClear(...)` ? – Jorayen Oct 31 '19 at 11:35
  • 3
    No, `glStencilMask` specifies the bits which are allowed to be written to. If it is 0 then not any bit of the stencil buffer is allowed to be written and the stencil buffer can't be changed at all, so `glClear(GL_STENCIL_BUFFER_BIT)` does nothing. – Rabbid76 Oct 31 '19 at 11:37