Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Simple OpenGL question - texture loses its transparency after copying  (Read 1566 times)

0 Members and 1 Guest are viewing this topic.

tjhickey

  • Newbie
  • *
  • Posts: 1
    • View Profile
Beginner here who's just learning OpenGL.

I've created a simple program to demonstrate this issue that's confusing me (code below).  It creates an array of two textures/FBOs and and sets the texture on the first with a PNG that has some transparency in it.

Every time you click the mouse it renders the texture from the current FBO to the other using a basic shader.  Then it renders that target FBO to the screen.  It alternates back and forth, rendering the texture from one FBO to another and displaying it on the screen.

What seems to be happening is that with every copy the transparency in the texture seems to decrease, and pixels are drifting toward zero.  After about eight clicks you can see that there's no smooth edges at all, just jagged edges. (image attached)

What's causing it to lose the smoothness of the edges?  I figured that each copy would be pixel perfect with the original.

#include <SFML/window.hpp>
#include <SFML/system.hpp>
#include <SFML/graphics.hpp>
#include <SFML/main.hpp>

#include <glew.h>
#include <SFML/OpenGL.hpp>

#include <iostream>
#include <windows.h>
#include <vector>

GLuint
fboLayer[2],
vaoLayer, vboLayer, eboLayer,
vaoBrush, vboBrush, eboBrush,
programFBO2FBO, programFBODelete, programScreen;

sf::Texture textureLayer[2],
textureFagen,
textureBrush,
textureAlphabet;


int index = 0;
const int layerWidth = 512, layerHeight = 512;

const int DEFAULT_FBO = 0;

bool drawing = false;

void shaders();
void renderQuad(GLuint vao);
void paintGL();
void initializeGL();

GLuint createShaderProgram(const char* vertexShaderPath, const char* fragmentShaderPath);
void checkShaderCompilation(GLuint shader, const std::string& type);
void checkProgramLinking(GLuint program);
sf::Vector2f drawingXY, lastDrawingXY = sf::Vector2f(-100000000, -1000000000);
sf::Texture loadTextureFromResource(const std::string& resourceName);


int main()
{
    // SFML context settings for OpenGL version 3.3
    sf::ContextSettings settings;
    settings.depthBits = 24;
    settings.stencilBits = 8;
    settings.antialiasingLevel = 0;
    settings.majorVersion = 3;
    settings.minorVersion = 3;
    settings.attributeFlags = sf::ContextSettings::Core;

    // Creating the window
    sf::Window window(sf::VideoMode(layerWidth, layerHeight), "OpenGL 3.3 + SFML", sf::Style::Default, settings);

    // Initialize GLEW
    glewExperimental = GL_TRUE;
    if (GLEW_OK != glewInit())
    {
        std::cout << "Failed to initialize GLEW." << std::endl;
        return -1;
    }

    // OpenGL configuration
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

   
        // =============================================
        // START  
        // =============================================
        initializeGL();
       

    // Event loop
    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
                        switch (event.type) {
                                case sf::Event::Closed:
                                        window.close();
                                        break;
                                case sf::Event::MouseButtonPressed:
                                        if (event.mouseButton.button == sf::Mouse::Left) {
                                                drawing = true;
                                        }
                                        else {
                                                index = index ^ 1;
                                        }
                                        break;
                        }
                }

                paintGL();

        window.display();
    }

    // Cleanup

    return 0;
}

void initializeGL()
{
        if (GLEW_OK != glewInit())
        {
                std::cout << "Failed to initialize GLEW." << std::endl;
                return;
        }

        // Setup OpenGL state
        glEnable(GL_DEBUG_OUTPUT);
        glDisable(GL_MULTISAMPLE);
        glDisable(GL_DEPTH_TEST);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

        // Load and create textures
        textureAlphabet = loadTextureFromResource("ALPHABET-alpha.png");

        // Create Framebuffers
        for (int i = 0; i < 2; i++) {
                textureLayer[i].create(layerWidth, layerHeight);

                glBindTexture(GL_TEXTURE_2D, textureLayer[i].getNativeHandle());
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, layerWidth, layerHeight, 0, GL_RGBA, GL_FLOAT, NULL);

                // framebuffers
                glGenFramebuffers(1, &fboLayer[i]);
                glBindFramebuffer(GL_FRAMEBUFFER, fboLayer[i]);
                glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureLayer[i].getNativeHandle(), 0);
                if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
                        std::cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl;
                else
                        std::cout << "Framebuffer is complete and ready for rendering................................." << std::endl;
        }
        textureLayer[0].update(textureAlphabet);
       

        // Setup vertex data and buffers and configure vertex attributes
        float vertices[] = {
                // positions        // texture coords
                 1.0f,  1.0f, 0.0f, 1.0f, 1.0f, // top right
                 1.0, -1.0f, 0.0f, 1.0f, 0.0f, // bottom right
                -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, // bottom left
                -1.0f,  1.0f, 0.0f, 0.0f, 1.0f  // top left
        };

        unsigned int indices[] = {
                0, 1, 3, // first triangle
                1, 2, 3  // second triangle
        };

        glGenVertexArrays(1, &vaoLayer);
        glGenBuffers(1, &vboLayer);
        glGenBuffers(1, &eboLayer);
        glBindVertexArray(vaoLayer);
        glBindBuffer(GL_ARRAY_BUFFER, vboLayer);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW        );
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboLayer);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
        glEnableVertexAttribArray(1);

        // Load and compile shaders
        shaders();
}

void paintGL()
{
        if (drawing) {
                //textureLayer[index ^ 1].update(textureLayer[index]);
                std::cout << "Painting " << index << " to " << (index ^ 1) << std::endl;
               
                glBindFramebuffer(GL_FRAMEBUFFER, fboLayer[index ^ 1]);
                glClearColor(0.0, 0.0, 0.0, 0.0);  // Clear with transparent, since it supports alpha
                glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


                glUseProgram(programFBO2FBO);
                glBindVertexArray(vaoLayer);
                glActiveTexture(GL_TEXTURE0);
                glBindTexture(GL_TEXTURE_2D, textureLayer[index].getNativeHandle());
                glUniform1i(glGetUniformLocation(programFBO2FBO, "texture1"), 0);
                glUniform2f(glGetUniformLocation(programFBO2FBO, "resolution"), 512, 512);
                glViewport(0, 0, 512, 512);
                glEnable(GL_BLEND);
                glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
                renderQuad(vaoLayer);
               
                drawing = false;

                index ^= 1;  // Toggle the index for ping-pong buffering

        }

        glUseProgram(programScreen);

        glBindFramebuffer(GL_FRAMEBUFFER, DEFAULT_FBO);

        glClearColor(0.0, 0.0, 0.0, 0.0);  // Clear with transparent, since it supports alpha
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, textureLayer[index].getNativeHandle());
        glUniform1i(glGetUniformLocation(programScreen, "texture1"), 0);
        glUniform2f(glGetUniformLocation(programScreen, "resolution"), layerWidth, layerHeight);

        glViewport(0, 0, layerWidth, layerHeight);

        //std::cout << "]]]]]]]]]]]]]]]]]]]]] Rendering : " << index << std::endl;

        renderQuad(vaoLayer);  // Render the final result to the screen or further process it

        glBindVertexArray(0);

}

sf::Texture loadTextureFromResource(const std::string& resourceName) {
        sf::Texture texture;
        if (!texture.loadFromFile(resourceName))
        {
                std::cout << "Failed to load texture" << std::endl;
                exit(EXIT_FAILURE);
        }
        else {
                std::cout << "Texture loaded successfully.........................................." << std::endl;
        }

        return texture;
}


void shaders() {

        // Vertex shader
        const char* vertexScreenSource = R"(
                #version 330 core
                layout (location = 0) in vec3 aPos;
                layout (location = 1) in vec2 aTexCoords;
                out vec2 TexCoords;
                void main() {
                        gl_Position = vec4(aPos, 1.0);
                        TexCoords = vec2(aTexCoords.x, 1.0 - aTexCoords.y);
                }
        )"
;

        const char* vertexFBOSource = R"(
        #version 330 core
        layout (location = 0) in vec3 aPos;
        layout (location = 1) in vec2 aTexCoords;
        out vec2 TexCoords;
        void main() {
                        gl_Position = vec4(aPos, 1.0);
                        TexCoords = aTexCoords;
        }
    )"
;

        // Fragment shader
        const char* fragmentPaintSource = R"(
        #version 330 core
        in vec2 TexCoords;
        out vec4 FragColor;
        uniform sampler2D texture1;
        void main() {
            FragColor = texture(texture1, TexCoords);
        }
    )"
;


        programFBO2FBO = createShaderProgram(vertexFBOSource, fragmentPaintSource);
        programScreen = createShaderProgram(vertexScreenSource, fragmentPaintSource);
}

GLuint createShaderProgram(const char* vertexShaderSource, const char* fragmentShaderSource) {
        // Compile vertex shader
        GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
        glCompileShader(vertexShader);
        checkShaderCompilation(vertexShader, "VERTEX");

        // Compile fragment shader
        GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
        glCompileShader(fragmentShader);
        checkShaderCompilation(fragmentShader, "FRAGMENT");

        // Link shaders to shader program
        GLuint shaderProgram = glCreateProgram();
        glAttachShader(shaderProgram, vertexShader);
        glAttachShader(shaderProgram, fragmentShader);
        glLinkProgram(shaderProgram);
        checkProgramLinking(shaderProgram);

        // Clean up shaders; they&#39;re no longer needed once linked into the program
        glDeleteShader(vertexShader);
        glDeleteShader(fragmentShader);

        return shaderProgram;
}


void checkShaderCompilation(GLuint shader, const std::string& type) {
        GLint success;
        GLchar infoLog[1024];
        glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
        if (!success) {
                glGetShaderInfoLog(shader, 1024, NULL, infoLog);
                std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type.c_str() << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
                exit(EXIT_FAILURE);
        }
        else {
                std::cout << "Shader compiled successfully.........................................." << std::endl;
        }
}


void checkProgramLinking(GLuint program) {
        GLint success;
        GLchar infoLog[1024];
        glGetProgramiv(program, GL_LINK_STATUS, &success);
        if (!success) {
                glGetProgramInfoLog(program, 1024, NULL, infoLog);
                std::cout << "ERROR::PROGRAM_LINKING_ERROR:\n" << infoLog << "\n";
                exit(EXIT_FAILURE);
        }
        else {
                std::cout << "Shader program linked successfully.........................................." << std::endl;
        }
}

void renderQuad(GLuint vao) {
        glBindVertexArray(vao);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
        glBindVertexArray(0);
}

« Last Edit: May 05, 2024, 10:27:35 pm by eXpl0it3r »

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
I haven't read your OpenGL code, but if you use the default AlphaBlend blend mode, this is expect behavior.

You're rendering the same transparent item over the other, adds the alpha values, so over multiple iterations the alpha value simple gets to the max value and this becomes fully visible and not transparent anymore.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/