Stencil用法

genius
2021-12-15 / 0 评论 / 25 阅读 / 正在检测是否收录...

模板测试

opengl的stencil称为模板测试,和深度测试一样,是Opengl中常用的技术。概念就不解释了,直接演示用法。我们实现对一个立方体盒子进行高亮描边。

stencil用法

stencil最简单的用法是使用下面三个函数:

  • glStencilMask(GLuint value): 位遮罩, 0x00设置模板缓冲不可写入;value=0xFF设置可以写入。
  • glStencilFunc(GLenum func, GLint ref, GLuint mask): 用ref值与存储的模板值比较,func为比较方法。
  • glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass): 模板值的写入策略

简单来说,glStencilMask是开关,开了才可以写入模板值;glStencilFunc是写入模板值的前提条件;glStencilOp是模板值的具体写入方法。

示例代码

普通流程

我们先准备好了画一个立方体盒子的代码:

GLuint boxProgramID;

void display( GLFWwindow* window )
{
    glClearColor( 0, 0, 0.4, 0 );
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // 设置好坐标变化的mvp
    glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float) 5 / (float)5, 0.1f, 100.0f);
    glm::mat4 view = glm::lookAt(glm::vec3(1,2,3), glm::vec3(0,0,0), glm::vec3(0,1,0));
    glm::mat4 model = glm::mat4(1.0f);
    glm::mat4 mvp = projection * view * model;

    // 画立方体
    glUseProgram(boxProgramID);
    GLuint mvpId = glGetUniformLocation(boxProgramID, "MVP");
    glUniformMatrix4fv(mvpId, 1, GL_FALSE, &mvp[0][0]);
    glDrawArrays(GL_TRIANGLES, 0, 36);
}

void prepare() {
    boxProgramID = LoadShaders( "data/shader/03.MVP.vs", "data/shader/03.MVP.fs" );

    // 立方体的顶点坐标,纹理坐标
    const GLfloat g_vertex_buffer_data[] = {
            -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
            0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
            0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
            0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
            -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
            -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

            -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
            0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
            0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
            0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
            -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
            -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

            -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
            -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
            -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
            -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
            -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
            -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

            0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
            0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
            0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
            0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
            0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
            0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

            -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
            0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
            0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
            0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
            -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
            -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

            -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
            0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
            0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
            0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
            -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
            -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
    };

    // VAO、VBO的数据
    GLuint VAO1;
    GLuint VBO1;
    glGenVertexArrays(1, &VAO1);
    glBindVertexArray(VAO1);
    glGenBuffers(1, &VBO1);
    glBindBuffer(GL_ARRAY_BUFFER, VBO1);
    glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (void*)0);

    // 纹理数据
    GLuint texture;
    glGenTextures(1, &texture);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);

    // 环绕方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // 过滤方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    // 加载图片数据
    int width, height, channels;
    stbi_set_flip_vertically_on_load(true);
    unsigned char* data = stbi_load("data/img/test.jpg", &width, &height, &channels, 0);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
    stbi_image_free(data);
    // 纹理坐标
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (void*)(3 * sizeof(GLfloat)));

    glEnable(GL_DEPTH_TEST);
}

int main( int argc, char** argv )
{
    glfwInit();
    GLFWwindow* window = glfwCreateWindow( 512, 512, "OpenGL_Tutorial", NULL, NULL );
    glfwMakeContextCurrent( window );
    gladLoadGLLoader( (GLADloadproc)glfwGetProcAddress );
    glfwSwapInterval( 1 );

    glfwSetKeyCallback( window, keyboard );
    glfwSetCursorPosCallback( window, motion );
    glfwSetMouseButtonCallback( window, mouse );

    prepare();

    while (!glfwWindowShouldClose(window)) {
        display(window);
        glfwPollEvents();
        glfwSwapBuffers(window);
    }

    glfwTerminate();
    return 0;
}

显示效果如图:

1639578757898

使用模板测试功能

加入模板测试功能,流程描述:

  1. 开启模板测试
  2. 清除所有模板值
  3. 画一个立方体盒子,将盒子区域的模板值设置可写,值为1
  4. 画一个稍大点的盒子(盒子放大1.05倍),不贴图,fs里直接设置颜色固定值,进行模板测试,模板测试条件为模板值 != 1,设置模板值不可写。

具体代码:

void display( GLFWwindow* window )
{
    // 1.开启stencil功能
    glEnable(GL_STENCIL_TEST);
    // 设置模板值可写,下面的clear GL_STENCIL_BUFFER_BIT操作才能执行
    glStencilMask(0xFF);
    // 设置模板测试成功的写入方式为替换,即测试成功,使用glStencilFunc中的ref值写入模板值
    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
    glClearColor( 0, 0, 0.4, 0 );
    // 2.清除深度、模板值
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    // 3.画立方体
    glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float) 5 / (float)5, 0.1f, 100.0f);
    glm::mat4 view = glm::lookAt(glm::vec3(1,2,3), glm::vec3(0,0,0), glm::vec3(0,1,0));
    glm::mat4 model = glm::mat4(1.0f);
    glm::mat4 mvp = projection * view * model;

    // 设置模板值的写入策略,GL_ALWAYS表示无条件写入
    glStencilFunc(GL_ALWAYS, 1, 0xFF);
    glUseProgram(boxProgramID);
    GLuint mvpId = glGetUniformLocation(boxProgramID, "MVP");
    glUniformMatrix4fv(mvpId, 1, GL_FALSE, &mvp[0][0]);
    glDrawArrays(GL_TRIANGLES, 0, 36);

    // 4.画描边
    model = glm::mat4(1.0f);
    // 放大盒子为原来的1.05倍
    model = glm::scale(model, glm::vec3(1.05, 1.05, 1.05));
    mvp = projection * view * model;

    // 进行模板测试,只有不等于1的模板值才测试成功
    glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
    // 设置模板值不可写,此处无需更新模板值
    glStencilMask(0x00);
    glUseProgram(stencilProgramID);
    mvpId = glGetUniformLocation(stencilProgramID, "MVP");
    glUniformMatrix4fv(mvpId, 1, GL_FALSE, &mvp[0][0]);
    glDrawArrays(GL_TRIANGLES, 0, 36);

}

现在的效果:

1639579682967

0

评论 (0)

取消