【问题标题】:Painter's method failing with OpenGL 4?Painter 的方法在 OpenGL 4 中失败?
【发布时间】:2021-08-12 22:18:49
【问题描述】:

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

~

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

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

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

我有一个疯狂的理论。我猜这都是异步的,第二个 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;

【问题讨论】:

  • “我有一个疯狂的理论。我猜这都是异步的,第二个 UseProgram 会吹走第一个正在进行的任何事情(也许还没有开始绘制? )" - 不,这个理论绝对是错误的。您不必同步绘图调用。
  • 除了“gWorld”之外,您还有其他统一变量吗?统一变量存储在程序对象的默认统一块中。统一位置取决于着色器程序。即使统一变量同名,在不同程序中也可能有不同的位置。
  • 您使用的是哪种着色器(听起来像片段和顶点,还有其他的)?你知道你交换了整个着色器管道(一个着色器程序将它们链接在一起),你不会认为你可以交换片段着色器而单独留下顶点着色器,反之亦然,是吗?您是否在编译和链接步骤对所有不同的着色器和着色器程序进行了错误检查?
  • 你是如何上传数据的?看起来一个可能是通过 VBO,另一个是直接访问客户端内存?
  • 我的(不是那么疯狂)的理论是,您以某种方式将第二次绘制调用的状态泄漏到第一次调用(因此它会中断下一帧)。很难说它到底是什么,因为给出的代码不完整。你的制服设置完全不见了。

标签: c++ opengl glut


【解决方案1】:

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

向 cmets 中的人们表示敬意,他们在创纪录的时间内解决了这个问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-21
    • 1970-01-01
    • 2014-05-10
    • 1970-01-01
    • 2014-05-13
    相关资源
    最近更新 更多