Texture mapping artifacts between fixed function and shader implementations

Hi all

I’m in the process of converting a fixed function (using display lists) OpenGL implementation to shader implementation

Bear with me as I’m new to OpenGL. The application is a radar scanconverter which maps a rectangular texture texture to my mesh of vertices (if that makes sense).

The image on the left is a screenshots of a radar target produced with the fixed function implementation which I’m trying to achieve, the one on the right is produced by the shader implementation

The shader code is below

#version 450 core
// fragment shader

layout(location = 0) out vec4 fragColor;
uniform sampler2D bscan;
in vec2 texCoord;

void main() {
    fragColor = texture(bscan, texCoord.st);
}

What could be causing the artifacts evident in the shader implementation

Thanks

The 2D texture you bound to bscan. What have you set the texture parameters to? In particular, check GL_TEXTURE_MAG_FILTER.

The mag filtter is set to Linear (FYI the texture is 2k x 2k pixels) and min filter to LinearMipMapNearest

HI all

Anyone have any ideas what could be causing this issue?

Thanks

Your texture looked so low res I figured your short was MAGnifying. But if it’s really 2048 x 2048, then you’re probably MINifying.

So a few ideas.

  • Post the base map of the texture as an image attachment so we can see what you’re starting with.
  • Change GL_TEXTURE_MIN_FILTER to GL_LINEAR_MIPMAP_LINEAR
  • Make sure you’ve called glGenerateMipmap() to ensure that you’ve properly set the content of the MIPmaps from the base map.
  • And for good measure, allocate your 2D texture with glTexStorage2D() and specify levels = 12.

Hi

Thanks for the response - I am magnifying - the texture 2k x 2k - please find attached

I have tried the GL_LINEAR_MIPMAP_LINEAR - didn’t make any difference

I’m not sure if I’m drawing the quad strips correctly - see the code at the bottom of post

Show below is the fixed function implemenation

    glNewList(displayList, GL_COMPILE);
    glEnable(GL_TEXTURE_2D);
    GLuint texture = m_radarView->texture()->textureId();
    glBindTexture(GL_TEXTURE_2D, texture);

    for(uint azimuth = 0 ; azimuth < azimuthSamples;  ++azimuth)
    {
        auto azimuth2 = azimuth+1;

        if (azimuth2 >= azimuthSamples)
            azimuth2 = 0;

        glBegin(GL_QUAD_STRIP);
        GLfloat atex = (azimuth * atexd) + (atexd / 2.0), atex2 = azimuth2 != 0 ? (azimuth2 * atexd) + (atexd / 2.0) : 1.0;
        const QList<QPointF> &line1 = watcher->resultAt(azimuth), &line2 = watcher->resultAt(azimuth2);
        for(int range = 0; range < rangeSamples; range++)
        {
            glTexCoord2f((rtexd * range) + (rtexd / 2.0), atex);
            glVertex3f(line1.at(range).x(), line1.at(range).y(), 0.0);
            glTexCoord2f((rtexd * range) + (rtexd / 2.0), atex2);
            glVertex3f(line2.at(range).x(), line2.at(range).y(), 0.0);
        }
        glEnd();
    }
    glBindTexture(GL_TEXTURE_2D, 0);
    glEndList();

The in render I do the

        glCallList(displayList);

This works fine

Using shaders (Qt OpenGL wrappers)

The VBO stores the vertices and texture coordinates - initialisation below

m_vao = new QOpenGLVertexArrayObject(this);,
m_vbo = new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);

m_vao->create();
m_vbo->setUsagePattern(QOpenGLBuffer::DynamicDraw);
m_vbo->create();

auto numVertices = textureSize().width() * textureSize().height() * 2;
auto verticesSize = numVertices * sizeof(QVector2D);
auto textureSize = numVertices * sizeof(QVector2D);
auto textureOffset = verticesSize;

m_vao->bind();
m_vbo->bind();

//the VAO must be bound here - this is part of the VAO state
m_vbo->allocate(verticesSize + textureSize);

shaderProgram()->bind();
const int vertexLocation = 1;
shaderProgram()->enableAttributeArray(vertexLocation);
shaderProgram()->setAttributeBuffer(vertexLocation, GL_FLOAT, 0, 2);
const int texLocation = 2;
shaderProgram()->enableAttributeArray(texLocation);
shaderProgram()->setAttributeBuffer(texLocation, GL_FLOAT, textureOffset, 2);

m_vao->release();
m_vbo->release();
shaderProgram()->release();type or paste code here
  • create vertices and texture coordinates and write to VBO
 //map each cartesian point to a texture coordinate - one quad strip for each azimuth spoke
    for(uint azimuth = 0 ; azimuth < azimuthSamples;  ++azimuth)
    {
        auto azimuth2 = azimuth+1;

        if (azimuth2 >= azimuthSamples)
            azimuth2 = 0;

        const QList<QPointF> &line1 = watcher->resultAt(azimuth), &line2 = watcher->resultAt(azimuth2);

        auto atex = (azimuth * atexd) + (atexd / 2.0), atex2 = azimuth2 != 0 ? (azimuth2 * atexd) + (atexd / 2.0) : 1.0;

        for (uint range = 0; range < rangeSamples; ++range)
        {
            vertices.append(QVector2D(line1.at(range).x(), line1.at(range).y()));
            textCords.append(QVector2D((rtexd * range) + (rtexd / 2.0), atex));
            vertices.append(QVector2D(line2.at(range).x(), line2.at(range).y()));
            textCords.append(QVector2D((rtexd * range) + (rtexd / 2.0), atex2));
            Q_ASSERT(textCords.last().x()<=1.0 && textCords.last().y()<=1.0);
        }
    }

    m_vbo->bind();
    auto verticesSize = vertices.size() * sizeof(QVector2D);
    auto textureSize = textCords.size() * sizeof(QVector2D);
    m_vbo->write(0, vertices.constData(), verticesSize);
    m_vbo->write(verticesSize, textCords.constData(), textureSize);
    m_vbo->release();

Then in render

    shaderProgram()->bind();
    shaderProgram()->setUniformValue(shaderProgram()->uniformLocation("modelViewMatrix"), projection);

    //explicitly set the texture unit 
    shaderProgram()->setUniformValue("bscan", 0);
    texture()->bind(); 
    m_vao->bind();
**pragma message ("TODO - use GL_TRIANGLE_STRIP" - is this correct ????)**
    glDrawArrays(GL_QUAD_STRIP, 0, textureSize().width() * textureSize().height() * 2);

    m_vao->release();
    shaderProgram()->release();
    texture()->release();

The fixed function implementation using display lists creates a quad strip for each spoke/azimuth, the shader implementation calls

    glDrawArrays(GL_QUAD_STRIP, 0, textureSize().width() * textureSize().height() * 2);

I don’t think this is correct

Thanks for your help

What sized GL internal format are you using for the texture?

Also, are you checking for GL errors?

Hi

Using GL_LUMINANCE with GL_UNSIGNED_BYTE

Thanks

Yes I am checking for errors - I do this this GL message on startup

"Buffer detailed info: Buffer object 1 (bound to GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB (1), and GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB (2), usage hint is GL_DYNAMIC_DRAW) will use VIDEO memory as the source for buffer object operations."

Don’t know if its significant

Thanks

Changing glDrawArrays from GL_QUAD_STRIP to GL_QUADS produces this result

image

Thanks

Ok. Those aren’t sized GL internal formats. They’re what you might provide for the format and type parameters of glTexImage2D().

What I’m asking about is what enum you provided to the internalFormat parameter of glTexImage2D() or glTexStorage2D(). The latter requires a sized GL internal format. The former does not.

If you’re providing an integer texture format like GL_R8UI, that’d be bad because they don’t support linear filtering. My guess is you’re just specifying the unsized GL internal format GL_LUMINANCE to glTexImage2D() and so probably getting the legacy sized internal format GL_LUMINANCE8 behind-the-scenes. That should be ok, as it supports linear filtering.

No problem. That’s just the NVIDIA driver letting you know how it’s handling a GL buffer object behind-the-scenes. Its severity is GL_DEBUG_SEVERITY_NOTIFICATION, which means you can typically ignore it.

The ones you definitely do not want to ignore have type == GL_DEBUG_TYPE_ERROR. Those are the problems that would be returned by glGetError().

Hi

Thanks for the response

I’m presently using Qt wrapper for the texture (QOpenGLTexture) - I don’t believe here is an issue with the texture as its used by the displayList implemenation and the texture mapping is correct


    //Initialisationm
    m_texture->setMinificationFilter(QOpenGLTexture::LinearMipMapNearest);
    m_texture->setMagnificationFilter(QOpenGLTexture::Linear);
    m_texture->setWrapMode(QOpenGLTexture::ClampToEdge);
    m_texture->setSize(m_bscanSize.width(), m_bscanSize.height(), 1);
    m_texture->setFormat(QOpenGLTexture::LuminanceFormat);
    m_texture->allocateStorage(QOpenGLTexture::Luminance, QOpenGLTexture::UInt8);
    Q_ASSERT(m_texture->isCreated() && m_texture->isStorageAllocated());


    //Update texture
    m_texture->setData(0, 0, 0, m_bscanSize.width(), m_bscanSize.height(), 0, QOpenGLTexture::Luminance, QOpenGLTexture::UInt8, data);
   

Thanks

Hi

This should be called in a loop - don’t know how it ever sort of worked

once for each quad strip

   auto verticesPerQuadStrip = texture()->height() * 2;
    for (int i = 0; i < textureSize().width(); ++i)
        glDrawArrays(GL_QUAD_STRIP, i * verticesPerQuadstrip, verticesPerQuadstrip);

Thanks for your time - much appreciated

Regards

Miklos