

Purpose of the Sample:
Demonstrates how to use the stencil buffer to mask draw calls. Over time, a portion of the man’s body will be rendered in wireframe. The effect is achieved like so.
- Draw Filled Man
- Write 0 to the stencil buffer for each fragment rendered to by the filled man.
 
- Render an invisible quad in front of the man.
- Write 1 to the stencil buffer for each fragment rendered to by the quad.
 
- Render the Wireframe Man
- Only display the wireframe man where the stencil buffer value is 1 which is wherever the quad was rendered.
 
Download RenderDoc Frame Capture
Captured with RenderDoc Version 1.2 (x86) (x64)
CGame.h
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | #ifndef __CGAME__ #define __CGAME__ #include <glad/glad.h> #include "shader_s.h" class Model; class CGame { public:     CGame() {};     void Initialize(unsigned int screenWidth, unsigned int screenHeight);     void Shutdown();     void Render();     void OnResize(int screenWidth, int screenHeight); private:     void SetupGPUProgram();     void SetupModel();     Shader* m_pShaderPass0;     Shader* m_pShaderPass1;     Shader* m_pShaderStencilPass;     Model* m_pManModel;     Model* m_pStencilModel;     unsigned int m_Width;     unsigned int m_Height; }; #endif | 
CGame.cpp
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | #include "CGame.h" #define STB_IMAGE_IMPLEMENTATION #include "stb_image/stb_image.h" #include "Model.h" #include "Mesh.h" #include "glm/mat4x4.hpp" #include "glm/vec3.hpp" #include "glm/glm.hpp" #include "glm/gtc/matrix_transform.hpp" #include "glm/gtc/type_ptr.hpp" #include <chrono> void CGame::Initialize(unsigned int screenWidth, unsigned int screenHeight) {     m_Width = screenWidth;     m_Height = screenHeight;     SetupGPUProgram();     SetupModel();     glEnable(GL_DEPTH_TEST);     glDepthFunc(GL_LEQUAL);     glEnable(GL_STENCIL_TEST); } void CGame::Shutdown() { } void CGame::OnResize(int width, int height) {     m_Width = width;     m_Height = height;     glViewport(0, 0, m_Width, m_Height);     glScissor(0, 0, m_Width, m_Height); } void CGame::Render() {     static auto startTime = std::chrono::high_resolution_clock::now();     auto currentTime = std::chrono::high_resolution_clock::now();     float time = std::chrono::duration<float, std::chrono::seconds::period>(currentTime - startTime).count();     glClearColor(0.2f, 0.3f, 0.3f, 1.0f);     glClearDepth(1.0f);     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);     glColorMask(1, 1, 1, 1);     // Draw the Filled Man     glEnable(GL_STENCIL_TEST);     glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);     glStencilFunc(GL_ALWAYS, 0, 0xFF); // all fragments should update the stencil buffer     glStencilMask(0xFF); // enable writing to the stencil buffer     glm::mat4 model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 1.0f, 0.0f));     glm::mat4 view;     view = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, -2.0f, -10.0f));     glm::mat4 projection;     projection = glm::perspective(glm::radians(60.0f), m_Width / static_cast<float>(m_Height), 0.03f, 100.0f);     glm::mat4 mvp = projection * view * model;     m_pShaderPass0->use();     m_pShaderPass0->setMatrix4x4("modelMatrix", glm::value_ptr(model));     m_pShaderPass0->setMatrix4x4("viewMatrix", glm::value_ptr(view));     m_pShaderPass0->setMatrix4x4("projectionMatrix", glm::value_ptr(projection));     m_pShaderPass0->setMatrix4x4("mvpMatrix", glm::value_ptr(mvp));     m_pShaderPass0->setFloat("time", time);     glDepthFunc(GL_LEQUAL);     glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);     m_pManModel->meshes[0].Draw(*m_pShaderPass0);     // Draw the Stencil Quad     glStencilMask(0xFF);     model = glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f));     model = glm::translate(model, glm::vec3(0.0f, 4.0f, -2.5f));     model = glm::scale(model, glm::vec3(1.0f, 1.0f, 1.0f));     mvp = projection * view * model;     m_pShaderStencilPass->use();     m_pShaderStencilPass->setFloat("time", time);     m_pShaderStencilPass->setMatrix4x4("mvpMatrix", glm::value_ptr(mvp));     glDepthFunc(GL_ALWAYS);     glDepthMask(0);     glStencilFunc(GL_ALWAYS, 1, 0xFF);     glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);     glColorMask(0, 0, 0, 0);     m_pStencilModel->meshes[0].Draw(*m_pShaderStencilPass);     glDepthFunc(GL_LEQUAL);     glDepthMask(1);     glColorMask(1, 1, 1, 1);     // Draw the Wireframe man     model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 1.0f, 0.0f));     view = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, -2.0f, -10.0f));     projection = glm::perspective(glm::radians(60.0f), m_Width / static_cast<float>(m_Height), 0.03f, 100.0f);     mvp = projection * view * model;     m_pShaderPass1->use();     m_pShaderPass1->setMatrix4x4("modelMatrix", glm::value_ptr(model));     m_pShaderPass1->setMatrix4x4("viewMatrix", glm::value_ptr(view));     m_pShaderPass1->setMatrix4x4("projectionMatrix", glm::value_ptr(projection));     m_pShaderPass1->setMatrix4x4("mvpMatrix", glm::value_ptr(mvp));     m_pShaderPass1->setFloat("time", time);     glStencilFunc(GL_EQUAL, 1, 0xFF);     glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);     m_pManModel->meshes[0].Draw(*m_pShaderPass1); } void CGame::SetupModel() {     m_pManModel = new Model();     m_pManModel->Load("man.obj");     m_pStencilModel = new Model();     m_pStencilModel->Load("quad.obj"); } void CGame::SetupGPUProgram() {     m_pShaderPass0 = new Shader("vertexPass0.glsl", "fragmentPass0.glsl");     m_pShaderPass1 = new Shader("vertexPass1.glsl", "fragmentPass1.glsl");     m_pShaderStencilPass = new Shader("vertexStencil.glsl", "fragmentStencil.glsl"); } | 
vertexPass0.glsl – Draw the Filled Man
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #version 330 core layout (location = 0) in vec3 position; layout (location = 1) in vec3 normal; uniform mat4 modelMatrix; uniform mat4 viewMatrix; uniform mat4 projectionMatrix; uniform mat4 mvpMatrix; uniform float time; out vec4 fsNormal; void main() {     vec4 pos = mvpMatrix * vec4(position.xyz, 1);     fsNormal = vec4(normal.xyz, 0);     gl_Position = pos; } | 
fragmentPass0.glsl – Shade the Filled Man with his Normals
| 1 2 3 4 5 6 7 8 9 10 | #version 330 core out vec4 FragColor; in vec4 fsNormal; void main() {     vec3 color = vec3(1,0,0);     FragColor = vec4(fsNormal.rgb, 1.0f); } | 
vertexStencil.glsl – Render the Quad Mask
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #version 330 core layout (location = 0) in vec3 position; uniform float time; uniform mat4 mvpMatrix; out vec4 fsNormal; void main() {     vec3 newModelPos = position + vec3(0, 0, sin(time * 0.4) * 3.0);     vec4 pos = mvpMatrix * vec4(newModelPos, 1);     gl_Position = pos; } | 
fragmentStencil.glsl – Render the Quad Mask
| 1 2 3 4 5 6 7 | #version 330 core out vec4 FragColor; void main() {     FragColor = vec4(0f, 0f, 0f, 1f); } | 
vertexPass1.glsl – Render the Wireframe Man
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #version 330 core layout (location = 0) in vec3 position; layout (location = 1) in vec3 normal; uniform mat4 modelMatrix; uniform mat4 viewMatrix; uniform mat4 projectionMatrix; uniform mat4 mvpMatrix; uniform float time; out vec4 fsNormal; void main() {     vec4 pos = mvpMatrix * vec4(position.xyz, 1);     fsNormal = vec4(normal.xyz, 0);     gl_Position = pos; } | 
fragmentPass1.glsl – Render the Wireframe Man
| 1 2 3 4 5 6 7 8 9 10 | #version 330 core out vec4 FragColor; in vec4 fsNormal; void main() {     vec3 color = vec3(0,1,0);     FragColor = vec4(color.rgb, 1.0f); } | 
Download Sample App

 
	