Beginner OpenGL question: Textures losing alpha values when rendering

Started by
2 comments, last by dpadam450 3 days, 13 hours ago

I've asked this question on a few other forums and no one's offered any help. I'm wondering if its a dumb question or it's too complicated to make sense of.

I've created a simple program to demonstrate the issue, originally using Qt and then using SFML to see if it made a difference. SFML code is attached.

What the program does:

  • creates two Framebuffer objects with attached textures
  • loads a PNG with transparency (a hole with smoothed edges) into the first FBO/Texture
  • when the mouse is clicked it renders that first texture to the second FBO/Texture, using a generic shader
  • it then renders that second FBO/Texture to the screen
  • I immediately notice that the transparent area of the texture looks different - the edges of the hole are less smooth - I expected that the texture would be reproduced pixel-perfect
  • when the mouse is clicked again it renders from the second texture back to the first texture and displays it.
  • every click of the mouse I go back and forth rendering the texture between the two framebuffers
  • every copy back-and-forth the texture's transparency changes, it's as though any alpha values between 0.0 and 1.0 are slowly decreasing to 0.0
  • after several clicks the hole in the texture no longer has smooth edges, all pixels are either alpha 0.0 or alpha 1.0.

You can see the change progressing in these three screenshots: beginning, middle, end:

https://ibb.co/J3nfYdC

It was suggested that I disable GL_BLENDING but that just caused the hole to lose all transparency:

https://ibb.co/7YwHBvc

#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;
						drawingXY = sf::Vector2f(static_cast<float>(event.mouseButton.x), static_cast<float>(event.mouseButton.y));
					}
					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'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);
}

#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;
						drawingXY = sf::Vector2f(static_cast<float>(event.mouseButton.x), static_cast<float>(event.mouseButton.y));
					}
					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'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);
}

#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;
						drawingXY = sf::Vector2f(static_cast<float>(event.mouseButton.x), static_cast<float>(event.mouseButton.y));
					}
					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'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);
}
Advertisement

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

My guess would start with this. You are copying and reblending with the same image. In photoshop if you copy and past an image like yours multiple times and allow blending I would expect at some point it becomes fully opaque. Think of a semi-transparent piece of paper. Stack it 10 times, eventually its not transparent.

I think if you use GL_SRC_ALPHA, GL_SRC_ALPHA then it will work. The first blend a 0.5 alpha puts 0.5 into the FBO output with a 0.0 background. Next time its blending a 0.5 alpha png onto a 0.5 alpha background, I cant check this off the top of my head but I think that is what is happening.

Also check glBlendEquation and glBlendFuncSeparate. You may need to handle alpha separately.

NBA2K, Madden, Maneater, Killing Floor, Sims http://www.pawlowskipinball.com/pinballeternal

“Initially, both the RGB blend equation and the alpha blend equation are set to GL_FUNC_ADD. ”

Think you just need to use MIX blend and it likely will work. Default it it will ADD both components together. SRC and DEST get added. So every time you blend, the alpha will be added up with previous value.

NBA2K, Madden, Maneater, Killing Floor, Sims http://www.pawlowskipinball.com/pinballeternal

Advertisement