简体   繁体   中英

Basic GLSL: Two fragment shaders behind each other, but first one (drawing a texture) is overwritten

I'm semi-new to OpenGL ES and looks like I'm missing one basic concept. I'm using some QT classes in my code, but it's near plain OpenGL ES.

I want to do the following per rendered frame:

  1. Render a fullscreen simple TRIANLGE_STRIP-based rectangle with a texture on it. You can see it as a background image. This already works on it's own. An own vertex and fragment shader (wrapped by QOpenGLShaderProgram) is used to render it. The texture originally comes from a QOpenGLFramebufferObject and this itself is re-rendered sometimes, but not every frame.
  2. After TRIANGLE_STRIP from (1.) is drawn, I want to render another basic shape on top. Currently also a small rectangle on the bottom. This uses another vertex and frament shader. This part also works on it's own.

My problem is: Both steps together don't work. I only see the the small rectangle on the bottom of the screen from (2.), but the texture is gone. The area, where the texture should be shown, is filled with the clear color.

My assumption is, the both frament shaders conflict somehow with each other or I'm missing something completely with states.. I'd be pleased for a hint on what is missing here.

Thanks in advance!

Here's some of my code:

This is the vertex and frament shader code of step (1):

static const char* vertexShaderSource =
    "attribute highp vec4 triangleCoords;\n"
    "attribute lowp vec2 textureCoords;\n"
    "varying lowp vec2 v_textureCoords;\n"
    "void main() {\n"
    "   v_textureCoords = textureCoords;\n"
    "   gl_Position = triangleCoords;\n"
    "}\n";

static const char* fragmentShaderSource =
    "varying lowp vec2 v_textureCoords;\n"
    "uniform sampler2D sampler;\n"
    "void main() {\n"
    "   gl_FragColor = vec4(texture2D(sampler, v_textureCoords).rgb, 1.0);\n"
    "}\n";

m_textureProgram = new QOpenGLShaderProgram();
m_textureProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
m_textureProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);   

This is the vertex and frament shader code of step (2):

static const char* vertexShaderSource =
    "attribute highp vec4 position;\n"
    "uniform lowp vec4 color;\n"
    "void main() {\n"
    "   gl_Position = position;\n"
    "}\n";

static const char* fragmentShaderSource =
    "uniform lowp vec4 color;\n"
    "void main() {\n"
    "   gl_FragColor = color;\n"
    "}\n";

m_shapeProgram->addShaderFromSourceCode( QOpenGLShader::Vertex, vertexShaderSource );
m_shapeProgram->addShaderFromSourceCode( QOpenGLShader::Fragment, fragmentShaderSource );

And the main rendering function, a little bit stripped down (I'm skipping the initialization part, where the shader programs are setup and attribute locations bound):

m_context->makeCurrent(w);
glClear( GL_COLOR_BUFFER_BIT );

// Step (1.) begins here
m_textureProgram->bind();
glBindTexture(GL_TEXTURE_2D, textureId);
m_textureProgram->enableAttributeArray( LocationTriangleTextureCoords );
m_textureProgram->enableAttributeArray( LocationTextureCoords );
m_textureProgram->setAttributeArray( LocationTriangleTextureCoords, GL_FLOAT, s_triangleStripCoords, 2 );
m_textureProgram->setAttributeArray( LocationTextureCoords, GL_FLOAT, s_textureCoords, 2);
f.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
m_textureProgram->disableAttributeArray( LocationTriangleTextureCoords );
m_textureProgram->disableAttributeArray( LocationTextureCoords );
m_textureProgram->release();

// Step (2.) begins here:
m_shapeProgram->bind();
m_shapeProgram->setUniformValue( m_colorUniformId, m_color1 );
QPointF p0( -1, -0.7);
QPointF p1( -1, -1.0 );
QPointF p2( 0, -1.0 );
QPointF p3( 0, -0.7 );
GLfloat vertices[12] = { GLfloat(p0.x()), GLfloat(p0.y()),
                         GLfloat(p1.x()), GLfloat(p1.y()),
                         GLfloat(p2.x()), GLfloat(p2.y()),
                         GLfloat(p0.x()), GLfloat(p0.y()),
                         GLfloat(p3.x()), GLfloat(p3.y()),
                         GLfloat(p2.x()), GLfloat(p2.y())
                      };

m_buffer->bind();
m_buffer->write( 0, vertices, sizeof(vertices) );
m_shapeProgram->setAttributeBuffer( LocationShapePosition, GL_FLOAT, 0, 2 );
m_shapeProgram->enableAttributeArray( LocationShapePosition );
f.glDrawArrays( GL_TRIANGLES, 0, 6 );
m_shapeProgram->release();

// Done frame.
m_context->swapBuffers(w);

Since you are saying that the two steps work correctly independently I would say the issue must be somewhere else.

It might be a bit unclear from your question but if the following is true:

  • You are drawing a fullscreen rectangle with texture which shows correctly when drawn on its own (fills the whole screen)
  • You are drawing a non-fullscreen smaller shape which shows correctly when drawn on its own (you see a clear color where the shape is not drawn)
  • When you try to render both you should see a full screen texture in background and the part with the small shape should show the small shape
  • Your result is exactly the same as if only the second step was executing (you see a clear color where the shape is not drawn)

Then I see 2 possible issues. First (I am pretty sure you must have checked) might be that you are clearing the color buffer in between the 2 rendering calls. Double check for that anyway.

The second is that the 2 programs are somehow in conflict. If this code executes on every frame it might make sense. A quick test would be to only draw it once and see if the result is drawn correctly.

So what may happen is that some value is being set in the second part which destroys the first part. For instance:

If texture is destroyed by the second part then the next time you call it would be possible that each texel fetched returns a color of (0,0,0,0) which with blending may produce a transparent background and you think you see a background cleared color. You may easily test this by modifying the textured shader to only output a solid color just to see if it gets drawn there.

Another problem might be some values are set to a wrong program and the vertex data for the first call gets overwritten by the second call. In this case the texture would be exactly behind the small rectangle. You could test that by having a small rectangle (semi) transparent and see if the texture shows behind it.

The only thing that draws attention in your code though is

m_textureProgram->release();
m_shapeProgram->release();

Is this correct? I would expect the shaders to be persistent. If not then where are they created?

It seems your assumption is that the second part somehow overwrites other pixels to clear buffer. I assure you that is not possible. When drawing triangles there is no way to influence other pixels (at least without geometry shader I guess), ONLY the pixels within the points will have assigned fragment shaders and only those may be changed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM