Ich versuche, eine Kombination von SFML und OpenGL für ein Projekt zu verwenden, aber ich habe Probleme beim Rendern auf eine sf::RenderTexture
. Genauer gesagt, wenn ich versuche zu zeichnen, während der RenderTexture
aktiv ist, stürze ich ab. (Sieht aus wie eine Nullzeiger-Dereferenz in glDrawElements
.)Rendering OpenGL Mesh zu SFML RenderTexture
Rendern direkt in das Fenster funktioniert gut. Und wenn ich manuell einen Framebuffer durch OpenGL selbst erstelle, funktioniert das auch gut. Aber ich würde gerne in der Lage sein, RenderTexture
wenn möglich zu verwenden, um eine Menge Code zu vereinfachen.
Ich kann etwas Dummes tun, aber ich bin noch neu bei OpenGL, also bin ich mir nicht sicher. (Besonders bei der Mischung aus SFML und OpenGL scheint es so, als könnten viele Dinge kaputt gehen, wenn der Kontextwechsel nicht korrekt funktioniert.) Ich sehe keine Warnungen von OpenGL oder SFML.
Nachfolgend gibt das Problem ich sehe (Windows 10, Visual Studio 2017, OpenGL 4.5, GLEW 2.1.0, SFML 2.4.0):
#include <iostream>
#include <string>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#define GL_GLEXT_PROTOTYPES
#include <GL/glew.h>
#include <SFML/OpenGL.hpp>
GLenum glCheckError_(const char *file, int line)
{
GLenum errorCode;
while ((errorCode = glGetError()) != GL_NO_ERROR)
{
std::string error;
switch (errorCode)
{
case GL_INVALID_ENUM: error = "INVALID_ENUM"; break;
case GL_INVALID_VALUE: error = "INVALID_VALUE"; break;
case GL_INVALID_OPERATION: error = "INVALID_OPERATION"; break;
case GL_STACK_OVERFLOW: error = "STACK_OVERFLOW"; break;
case GL_STACK_UNDERFLOW: error = "STACK_UNDERFLOW"; break;
case GL_OUT_OF_MEMORY: error = "OUT_OF_MEMORY"; break;
case GL_INVALID_FRAMEBUFFER_OPERATION: error = "INVALID_FRAMEBUFFER_OPERATION"; break;
}
std::cerr << error << " | " << file << " (" << line << ")" << std::endl;
}
return errorCode;
}
#define glCheckError() glCheckError_(__FILE__, __LINE__)
int main()
{
sf::RenderWindow window(sf::VideoMode(800, 600, 32), "test");
glewInit();
std::cout << "Using OpenGL " << window.getSettings().majorVersion << "." << window.getSettings().minorVersion << std::endl;
//std::cout << "Available GL extensions: " << glGetString(GL_EXTENSIONS) << std::endl;
sf::Shader shader;
{ // Shader
const char* vs = R"(
#version 330 core
layout (location = 0) in vec3 pos;
void main()
{
gl_Position = vec4(pos, 1.0);
}
)";
const char* fs = R"(
#version 330 core
out vec4 color;
void main()
{
color = vec4(0.3, 0.8, 0.2, 1.0);
}
)";
shader.loadFromMemory(vs, fs);
}
unsigned int vao;
{ // Mesh
float vertices[] = {
0.3f, 0.5f, 1.0f, // top right
0.5f, -0.5f, -0.5f, // bottom right
-0.5f, -0.5f, -1.0f, // bottom left
-0.3f, 0.5f, 0.5f, // top left
};
unsigned int indices[] = {
0, 3, 1, // first triangle
1, 3, 2, // second triangle
};
unsigned int vbo, ebo;
glGenVertexArrays(1, &vao);
glCheckError();
glGenBuffers(1, &vbo);
glCheckError();
glGenBuffers(1, &ebo);
glCheckError();
glBindVertexArray(vao);
glCheckError();
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glCheckError();
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glCheckError();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glCheckError();
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glCheckError();
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glCheckError();
glEnableVertexAttribArray(0);
glCheckError();
glBindBuffer(GL_ARRAY_BUFFER, 0);
glCheckError();
glBindVertexArray(0);
glCheckError();
}
sf::RenderTexture texture;
sf::Sprite sprite;
{ // Render Texture
if (!texture.create(800, 600, true)) {
std::cerr << "Failed to create RenderTexture" << std::endl;
}
sprite.setTexture(texture.getTexture());
}
int frame = 0;
while (window.isOpen())
{
++frame;
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
window.close();
}
}
window.clear();
if (frame > 1)
{
window.popGLStates();
}
{ // Render to screen
sf::Shader::bind(&shader);
glBindVertexArray(vao);
glCheckError();
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glCheckError();
glBindVertexArray(0);
glCheckError();
sf::Shader::bind(nullptr);
}
window.pushGLStates();
window.display();
// Press space to continue...
bool waiting = true;
while (waiting) {
while (window.pollEvent(event))
{
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Space)
{
waiting = false;
break;
}
}
}
window.clear();
if (frame > 1)
{
window.popGLStates();
}
{ // Render to texture
sf::Shader::bind(&shader);
glBindVertexArray(vao);
glCheckError();
texture.pushGLStates();
if (!texture.setActive(true)) { // TODO Setting the texture as active is causing me to segfault, messing up my state somehow
std::cerr << "Failed to activate RenderTexture" << std::endl;
}
texture.clear();
texture.popGLStates();
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); // <-- Crashes here!
glCheckError();
texture.pushGLStates();
texture.display();
if (!texture.setActive(false)) {
std::cerr << "Failed to deactivate RenderTexture" << std::endl;
}
texture.popGLStates();
glBindVertexArray(0);
glCheckError();
sf::Shader::bind(nullptr);
}
window.pushGLStates();
window.draw(sprite);
window.display();
}
};
Hat Ideen jemand?
EDIT: Nun, ich habe den Absturz Teil gelöst. sf::RenderTexture
s haben ihren eigenen GL-Kontext, und ich denke, Sie können Daten zwischen Kontexten nicht wiederverwenden. Also musste ich zuerst die Textur generieren und dann texture.setActive()
verwenden, bevor ich den Shader und Mesh erzeuge. Auf diese Weise hat der Kontext diese Objekte verfügbar.
Jetzt bekomme ich nur einen schwarzen Bildschirm. Ich bin in der Lage, eine neue sf::RectangleShape
zu demselben RenderTexture
zu zeichnen, aber mein GL-Netz scheint nicht zu zeichnen. Noch ... Untersuchung
Falls jemand das gleiche Problem hat, sind hier Schnipsel von dem, was ich ändern musste:
// --- initialization ---
// Generate the texture first so its context is available
sf::RenderTexture texture;
sf::Sprite sprite;
{ // Render Texture
if (!texture.create(800, 600, true)) {
std::cerr << "Failed to create RenderTexture" << std::endl;
}
sprite.setTexture(texture.getTexture());
}
// Generate the rest of the data within the texture's context
sf::Shader shader;
{ // Shader
if (!texture.setActive(true)) {
std::cerr << "Failed to activate RenderTexture" << std::endl;
}
shader.loadFromMemory(vs, fs);
if (!texture.setActive(false)) {
std::cerr << "Failed to deactivate RenderTexture" << std::endl;
}
}
unsigned int vao;
{ // Mesh
if (!texture.setActive(true)) {
std::cerr << "Failed to activate RenderTexture" << std::endl;
}
unsigned int vbo, ebo;
glGenVertexArrays(1, &vao);
glCheckError();
glGenBuffers(1, &vbo);
glCheckError();
// ...
glBindBuffer(GL_ARRAY_BUFFER, 0);
glCheckError();
glBindVertexArray(0);
glCheckError();
if (!texture.setActive(false)) {
std::cerr << "Failed to deactivate RenderTexture" << std::endl;
}
}
// --- drawing ---
{ // Render to texture
// Make sure we use the appropriate context for all drawing to texture
if (!texture.setActive(true)) {
std::cerr << "Failed to activate RenderTexture" << std::endl;
}
texture.clear();
sf::Shader::bind(&shader);
glBindVertexArray(vao);
glCheckError();
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); // TODO rendering nothing here...
glCheckError();
glBindVertexArray(0);
glCheckError();
sf::Shader::bind(nullptr);
// Drawing to the texture through SFML works fine
texture.pushGLStates();
sf::RectangleShape rect(sf::Vector2f(20, 20));
rect.setFillColor(sf::Color::Cyan);
texture.draw(rect);
texture.popGLStates();
texture.display();
if (!texture.setActive(false)) {
std::cerr << "Failed to deactivate RenderTexture" << std::endl;
}
}
if (!window.setActive(true)) {
std::cerr << "Failed to activate window" << std::endl;
}
window.pushGLStates();
window.draw(sprite);
window.display();
EDIT 2: Zeichnung Probleme gelöst, meine Antwort sehen.