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);
}