Painter 的方法在 OpenGL 4 中失败了吗?

Painter's method failing with OpenGL 4?

我会留下这个问题作为对其他人的警示故事。简短的回答是 GLSL 中的统一变量需要在 glUseProgram 之后绑定到当前程序。他们不会自己神奇地找到着色器。我加了一个 glUniformMatrix4fv(gWorldLocation, 1, GL_FALSE, glm::value_ptr(投影)); 在每个 glUseProgram 和事情改进之后。

~

我正在绘制平面图。这是一个正射投影。在渲染回调中,首先我用一个图案绘制地板 (z=0),现在我用那个图案填充整个屏幕。都好。现在是时候粉刷墙壁了,它只是许多切成三角形的细长方形,因为显然画粗线应该是这样。

最初我用一个片段着色器同时处理地板和墙壁。我会绘制地板三角形,为着色器设置一个标志,然后绘制瘦墙三角形。片段着色器通过使用不同的颜色来纪念旗帜,我满意地绘制了顶部的地板和墙壁。

但是随着我添加功能,我将在地板上绘制越来越多的东西,所以我决定将片段着色器分开 - 一个用于地板,一个用于墙壁,还有其他的用于其他特性。我为地板调用 glUSeProgram(),绘制我的三角形,然后调用 glUseProgram() [这里似乎是龙] 设置墙壁着色器并绘制墙壁(在 z=0.5,只是为了确定)。一切都以一种有趣的方式崩溃了:如果我注释掉第二个 glUseProgram 和相关的绘制,我可以看到地板。如果我放回第 2 个使用和绘制操作,我只会看到墙壁。如果我画墙但不操作墙着色器 (void main() {return;}),我什么也得不到。通过测试,我确定仅调用第二个 glUSeProgram() 就会造成损害。即使我没有画墙并且在那之后有一个无操作片段着色器,地板也不见了并且 window 是空白的。所以它不是离开 rails.

的墙片段着色器

我有一个疯狂的理论。我猜这都是异步的,第二个 UseProgram 会破坏第一个 UseProgram 正在进行的任何事情(也许还没有开始绘制?)如果那是真的,我需要某种方式说“好的,等待第一个程序到处完成”。如果那是错误的,我将完全不知所措,因为我没有看到任何迹象表明 glUSeProgram() 将彻底改变。

我哪里做错了?理论上我可以在一个片段着色器中做所有事情,但这将是巨大的,我知道你不需要那样做。

这是渲染回调中的代码; #if 01 仅表示我关闭和打开以尝试的东西。

    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);  //start clean

    mapData.selectShader(mapData.programDMFloor); //does glUseProgram (see below)
    glEnableVertexAttribArray(0); //needed, no idea why?
    glBindBuffer(GL_ARRAY_BUFFER, VBOS); //2 triangles (that for now cover the window)
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO); //indexed draw for these 
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); //paint the window with the floor
    glDisableVertexAttribArray(0); //needed, no idea why?

    //If we stop here, we see the floor pattern as expected. But..!

#if 01
    mapData.selectShader(mapData.programDMWall);
    //The floor is now gone (or maybe never happened)

    //this draws the walls
    glEnableVertexAttribArray(0); //needed, don't know why
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); //no indexed draw for these 
    glBindBuffer(GL_ARRAY_BUFFER, mapData.wallBuffer_); //Select a lot of skinny triangles
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    //std::cout << "Wall draw " << mapData.triangles_.size() * 3 << " floats from buf " << mapData.wallBuffer_ << '\n';
    glDrawArrays(GL_TRIANGLES, 0, mapData.triangles_.size() * 3); //no indexed draw for this one
    glDisableVertexAttribArray(0);
    //we see the walls, but no floor!
#else
    //we see the floor, and of course no walls.
    //Need both!
#endif

    glutSwapBuffers();

至于mapData.selectShader(),就是

void selectShader(GLuint ShaderProgram) //in Map::
{
    glUseProgram(ShaderProgram);
    //All the shaders should be sharing gWorld, so I probably don't need
    // to set this over and over, since it's not going to change. But it needs
    // to follow a glUseProgram so for now it's here. Needed each time?
    gWorldLocation = glGetUniformLocation(ShaderProgram, "gWorld");
    assert(gWorldLocation != 0xFFFFFFFF);
}

这实际上应该如何工作?

附录:墙的 .vs:

#version 430
//buffer indexes
#define BI_LIGHTS 0
#define BI_WALLS 1
#define BI_WALLWALK 2
#define BI_DOORS 3
#define BI_WALLINDEXES 4

layout (location = 0) in vec3 Position;

uniform mat4 gWorld;

layout (std430, binding=BI_WALLINDEXES) buffer wallIndexesSkip
{
    unsigned int wallIndexes[];
};

out vec4 Color; //unused; the fragment shader calculates all colors
//but maybe we pass the initial floor texture here someday

flat out unsigned int wallIndex;

void main()
{
    gl_Position = gWorld * vec4(Position, 1.0);
    //We must the fragment shader which wall it should NOT use to
    // occlude walls, otherwise the wall currently being drawing
    // will be occluded by itself!
    wallIndex = wallIndexes[gl_VertexID/9];
}

和带有声明的 .fs 的开头:

#version 430

#define BI_LIGHTS 0
#define BI_WALLS 1
#define BI_WALLWALK 2
#define BI_DOORS 3
#define BI_WALLINDEXES 4

#define  AMB_NONE 0
#define  AMB_SUNLIGHT 1
#define  AMB_CLOUDY 2
#define  AMB_RAIN 3
#define  AMB_FOG 4
#define  AMB_DIM 5
#define  AMB_TEST 6

#define LIT_NONE 0
#define LIT_MAGICAL 1
#define LIT_FIRE 2
#define LIT_INFRAVISION 3

layout (std430, binding=BI_LIGHTS) buffer lights
{
    unsigned int lightCount;
    //12 bytes wasted in this packing
    vec4 lightData[]; //x, y, radius, typeflag
};
layout (std430, binding=BI_WALLS) buffer walls
{
    vec2 wp[];
};
layout (std430, binding=BI_WALLWALK) buffer wallRuns
{
    // 0 this wall is ok
    //>0 number to skip including this one
    //0xffffffff marks end
    unsigned int skipWalls[];
};
layout (std430, binding=BI_DOORS) buffer doors
{
    unsigned int doorCount;
    //12 bytes wasted in this packing
    vec4 dp[]; //hinge x, hinge y, end x, end y
};

flat in unsigned int wallIndex;
in vec4 Color;
out vec4 FragColor;

我会留下这个问题作为对其他人的警示故事。简短的回答是 GLSL 中的统一变量需要在 glUseProgram 之后绑定到当前程序。他们不会自己神奇地找到着色器。我添加了一个 glUniformMatrix4fv(gWorldLocation, 1, GL_FALSE, glm::value_ptr(projection));在每个 glUseProgram 和事情改进之后。

感谢评论中的人们,他们在创纪录的时间内解决了这个问题。