When creating 2 windows using sfml, I found out that only either of the window shows the triangle depending on window.setActive() function called in the code below. One of the window is completely a solid color. Here is the code that replicated the issue
#include <GL/glew.h>
#include <SFML/Window.hpp>
#include <iostream>
// Vertex Shader Source
const char* vertexShaderSource = R"(
#version 460 core
layout (location = 0) in vec3 position;
void main()
{
gl_Position = vec4(position, 1.0);
}
)";
// Fragment Shader Source
const char* fragmentShaderSource = R"(
#version 460 core
out vec4 color;
void main()
{
color = vec4(0.2, 0.8, 0.2, 1.0); // Green color
}
)";
// Triangle Vertex Data
const float triangleVertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
// Compile and link shaders
GLuint CompileShaderProgram() {
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
glCompileShader(vertexShader);
GLint success;
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success) {
char infoLog[512];
glGetShaderInfoLog(vertexShader, 512, nullptr, infoLog);
std::cerr << "Vertex Shader Compilation Error: " << infoLog << std::endl;
return 0;
}
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success) {
char infoLog[512];
glGetShaderInfoLog(fragmentShader, 512, nullptr, infoLog);
std::cerr << "Fragment Shader Compilation Error: " << infoLog << std::endl;
return 0;
}
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
char infoLog[512];
glGetProgramInfoLog(shaderProgram, 512, nullptr, infoLog);
std::cerr << "Shader Program Linking Error: " << infoLog << std::endl;
return 0;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return shaderProgram;
}
int main() {
// Context settings for OpenGL 4.6
sf::ContextSettings settings;
settings.majorVersion = 4;
settings.minorVersion = 6;
settings.attributeFlags = sf::ContextSettings::Core;
// Create the first window
sf::Window window1(sf::VideoMode(800, 600), "Window 1", sf::Style::Default, settings);
// Set the first window's context as active
window1.setActive(true);
// Initialize GLEW after activating the first context
glewExperimental = GL_TRUE;
GLenum err = glewInit();
if (GLEW_OK != err) {
std::cerr << "GLEW Initialization Error: " << glewGetErrorString(err) << std::endl;
return -1;
}
// OpenGL Initialization
GLuint shaderProgram = CompileShaderProgram();
if (!shaderProgram) return -1;
GLuint VAO, VBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(triangleVertices), triangleVertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
// Create the second window, sharing the context of the first window
sf::Window window2(sf::VideoMode(800, 600), "Window 2", sf::Style::Default, settings);
window1.setActive(true); // Ensure the second window's context is active after creation
bool running1 = true, running2 = true;
sf::Event event;
while (running1 || running2) {
if (running1) {
window1.setActive(true);
// Event handling
while (window1.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
running1 = false;
window1.close();
}
}
// Render triangle in window 1
glClearColor(0.1f, 0.1f, 0.4f, 1.0f); // Blue background
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
window1.display();
}
if (running2) {
window2.setActive(true);
// Event handling
while (window2.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
running2 = false;
window2.close();
}
}
// Render triangle in window 2
glClearColor(0.4f, 0.1f, 0.1f, 1.0f); // Red background
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
window2.display();
}
}
// Cleanup
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteProgram(shaderProgram);
return 0;
}
I was expecting the triangle to be drawn in both the windows but only one of them showed. I changed the window1.setActive(true) to window2.setActive(true) which resulted in the second window to show the triangle and not the first one.
EDIT 1: As answered by rafix07, a VAO is unshareable object and we need to duplicate it.
All to answer to your issue is in reference:
Another thing to know is that all the OpenGL contexts created by SFML share their resources. This means that you can create a texture or vertex buffer with any context active, and use it with any other. This also means that you don't have to reload all your OpenGL resources when you recreate your window. Only shareable OpenGL resources can be shared among contexts. An example of an unshareable resource is a vertex array object.
The most important is the last sentence. VAO is not shareable. So after creating the second window, make its context as current, create new VAO and fill it by previosuly created VBO which is shareable:
// Create the second window, sharing the context of the first window
sf::Window window2(sf::VideoMode(800, 600), "Window 2", sf::Style::Default, settings);
window2.setActive(true);
GLuint VAO2;
glGenVertexArrays(1, &VAO2);
glBindVertexArray(VAO2);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(triangleVertices), triangleVertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
window1.setActive(true); // Ensure the second window's context is active after creation
Don't forget to enable second VAO in main loop code when doing the rendering for second window:
glUseProgram(shaderProgram);
glBindVertexArray(VAO2);
glDrawArrays(GL_TRIANGLES, 0, 3);
This all gives you two triangles being rendering, as desired.