为啥模板测试不丢弃片段?

Posted

技术标签:

【中文标题】为啥模板测试不丢弃片段?【英文标题】:Why stencil test does not discard fragments?为什么模板测试不丢弃片段? 【发布时间】:2019-06-25 22:18:58 【问题描述】:

我想运行模板测试来勾勒立方体。它来自 learnopengl https://learnopengl.com/Advanced-OpenGL/Stencil-testing。

做轮廓的立方体应该有丢弃在纹理立方体上的碎片。这不会发生,轮廓立方体覆盖了整个区域。

static const char* cubeVtx = R"(
#version 460 core
layout (location = 0) in vec3 a_position;
layout (location = 1) in vec2 a_uv;

out vec2 v_uv;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()

    v_uv = a_uv;    
    gl_Position = projection * view * model * vec4(a_position, 1.0f);


)";

static const char* cubeFrag = R"(
#version 460 core

layout (location = 0) out vec4 f_color;

in vec2 v_uv;

uniform sampler2D u_texture1;

void main()
    
    f_color = texture(u_texture1, v_uv);


)";

static const char* singleColorVtx = R"(
#version 460 core
layout (location = 0) in vec3 a_position;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
   
    gl_Position = projection * view * model * vec4(a_position, 1.0f);


)";

static const char* singleColorFrag = R"(
#version 460 core

layout (location = 0) out vec4 f_color;

void main()
    
    f_color = vec4(1.0f, 0.0f, 0.0f, 1.0f);


)";

void Cube::InitAPI()

    shader = Shader::Create();
    ShaderProgram program[2] = 
        shader->ShaderFromSource(cubeVtx, ShaderType::Vertex),
        shader->ShaderFromSource(cubeFrag, ShaderType::Fragment)
    ;
    shader->End(program, 2);

    singleColorShader = Shader::Create();
    ShaderProgram singleColorProgram[2] = 
        singleColorShader->ShaderFromSource(singleColorVtx, ShaderType::Vertex),
        singleColorShader->ShaderFromSource(singleColorFrag, ShaderType::Fragment)
    ;
    singleColorShader->End(singleColorProgram, 2);

    glCreateVertexArrays(1, &vao);
    glBindVertexArray(vao);
    glCreateBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glNamedBufferData(vbo, sizeof(cubeVertices), cubeVertices, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(0, 3, GL_FLOAT, false, 5 * sizeof(float), 0);
    glVertexAttribPointer(1, 2, GL_FLOAT, false, 5 * sizeof(float), (void*)(3 * sizeof(float)));

    glCreateVertexArrays(1, &planeVao);
    glBindVertexArray(planeVao);
    glCreateBuffers(1, &planeVbo);
    glBindBuffer(GL_ARRAY_BUFFER, planeVbo);
    glNamedBufferData(planeVbo, sizeof(planeVertices), planeVertices, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(0, 3, GL_FLOAT, false, 5 * sizeof(float), 0);
    glVertexAttribPointer(1, 2, GL_FLOAT, false, 5 * sizeof(float), (void*)(3 * sizeof(float)));

    texture = Texture::LoadPNG("Media/container2");
    planeTexture = Texture::LoadPNG("Media/matrix");

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);
    glEnable(GL_STENCIL_TEST);
    glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

    shader->UseProgram();
    shader->Uniform1i("u_texture1", 0);


void Cube::Render()

    singleColorShader->UseProgram();
    glm::mat4 model = glm::mat4(1.0f);
    glm::mat4 view = camera.GetViewMatrix();
    glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)1280.0f / (float)720.0f, 0.1f, 100.0f);
    singleColorShader->UniformMatrix4fv("view", view);
    singleColorShader->UniformMatrix4fv("projection", projection);

    shader->UseProgram();
    shader->UniformMatrix4fv("view", view);
    shader->UniformMatrix4fv("projection", projection);

    glStencilMask(0x00);
    glBindVertexArray(planeVao);
    planeTexture->Active();
    planeTexture->Bind();
    glDrawArrays(GL_TRIANGLES, 0, 6);

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

    glBindVertexArray(vao);
    model = glm::translate(model, glm::vec3(-1.0f, 0.0f, -1.0f));
    shader->UniformMatrix4fv("model", model);
    texture->Active();
    texture->Bind();
    glDrawArrays(GL_TRIANGLES, 0, 36);

    model = glm::mat4(1.0f);
    model = glm::translate(model, glm::vec3(2.0f, 0.0f, 0.0f));
    shader->UniformMatrix4fv("model", model);
    glDrawArrays(GL_TRIANGLES, 0, 36);

    glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
    glStencilMask(0x00);
    glDisable(GL_DEPTH_TEST);
    singleColorShader->UseProgram();
    float scale = 1.1;

    glBindVertexArray(vao);
    model = glm::mat4(1.0f);
    model = glm::translate(model, glm::vec3(-1.0f, 0.0f, -1.0f));
    model = glm::scale(model, glm::vec3(scale, scale, scale));
    shader->UniformMatrix4fv("model", model);
    glDrawArrays(GL_TRIANGLES, 0, 36);

    model = glm::mat4(1.0f);
    model = glm::translate(model, glm::vec3(2.0f, 0.0f, 0.0f));
    model = glm::scale(model, glm::vec3(scale, scale, scale));
    shader->UniformMatrix4fv("model", model);
    glDrawArrays(GL_TRIANGLES, 0, 36);

    glBindVertexArray(0);

    glStencilMask(0xFF);
    glEnable(GL_DEPTH_TEST);


Test::Test()

    //tried many patterns and stencil values

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6);
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

    SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
    window = SDL_CreateWindow("window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL);

    context = SDL_GL_CreateContext(window);
    SDL_GL_MakeCurrent(window, context);

    glewExperimental = true;
    glewInit();

    cube.InitAPI();


void Test::MainLoop()

    while (!quit) 
        while (SDL_PollEvent(&event)) 
            if (event.type == SDL_QUIT) 
                quit = true;
            
        

        glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

        cube.Render();

        SDL_GL_SwapWindow(window);
    


我仍然完全没有得到深度测试的东西,当我开始认为我正在接受模板测试时,一切都搞砸了,因为它不起作用。

是的,我会在其他地方清除颜色、深度和模板缓冲区,但在 Render() 方法之前。

我得到了什么:

我期望的是红色轮廓,而不是整个立方体

您能否像我是个白痴一样解释为什么它不丢弃1 的片段

【问题讨论】:

通常您的设置应该可以工作。你确定默认的帧缓冲甚至有模板缓冲区吗?这可以解释这个问题,因为如果没有模板缓冲区,那么从模板缓冲区读取的值是 0,glStencilFunc(GL_NOTEQUAL, 1, 0xFF) 永远不会失败。 是的,我做的第一件事就是创作?带有`SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE,24)的模板缓冲区;` 可能模板尺寸为 24 是行不通的。试试SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); 还是一样的结果,能否检查一下模板缓冲区大小是否设置正确? SDL_GL_SetAttributeSDL_CreateWindow 之前被调用,不是吗? 【参考方案1】:

在调用任何其他 SDL 函数之前调用 SDL_Init

SDL_Init(SDL_INIT_EVERYTHING);

查看SDL_Init()的文档:

使用此函数初始化 SDL 库。这必须在使用大多数其他 SDL 函数之前调用。

要使模板缓冲区工作,必须设置SDL_GL_STENCIL_SIZE 属性。默认情况下,此属性为 0。通常,模板缓冲区有 8 位。例如:

SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);

SDL_GL_SetAttribute 必须在 SDL_CreateWindow 之前调用,否则不会有任何效果。

查看SDL_GL_SetAttribute的文档:

在窗口创建前使用该函数设置OpenGL窗口属性。

【讨论】:

这是有道理的,但它也不起作用:P。检查更新的代码。 @Shout 注意所有属性必须在SDL_CreateWindow之前设置。 好的,好的,我明白了,但它没有做任何改变。仍然模板缓冲区不起作用。 @Shout 我无法重现该问题。 SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8) 的返回值是多少?它应该是 0。 无法设置属性 - -1。伊玛检查真快这是什么意思。这很奇怪。【参考方案2】:

原代码在SDL_GL_SetAttribute()之后调用SDL_Init 上面的代码甚至没有调用SDL_Init。初始化 SDL 可以解决所有问题。

【讨论】:

以上是关于为啥模板测试不丢弃片段?的主要内容,如果未能解决你的问题,请参考以下文章

◮OpenGL-模板测试

◮OpenGL-模板测试

高级openg 混合,一个完整程序

OpenGL ES之“深度测试”与“模板测试”的使用流程

如何在片段着色器中进行自定义模板测试

为啥有时我们使用特定的位来进行模板测试?