【发布时间】:2018-02-11 16:32:45
【问题描述】:
我正在尝试将 SFML 和 OpenGL 组合用于一个项目,但在渲染到 sf::RenderTexture 时遇到问题。具体来说,如果我在RenderTexture 处于活动状态时尝试绘制,我会崩溃。 (看起来像 glDrawElements 内部的空指针取消引用。)
直接渲染到窗口可以正常工作。如果我自己通过 OpenGL 手动创建帧缓冲区,那也可以正常工作。但如果可能的话,我希望能够使用RenderTexture 来简化很多代码。
我可能在做一些愚蠢的事情,但我还是 OpenGL 的新手,所以我不确定。 (尤其是 SFML 和 OpenGL 的混合,如果您没有正确管理上下文切换,似乎很多东西都会中断。)我没有看到来自 OpenGL 或 SFML 的任何警告。
以下重现了我看到的问题(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();
}
};
有人有什么想法吗?
编辑:好吧,我已经解决了崩溃的部分。 sf::RenderTextures 有自己的 GL 上下文,我猜你不能在上下文之间重用数据。所以我必须先生成纹理并使用texture.setActive(),然后再生成着色器和网格。这样,上下文就可以使用这些对象。
现在我只是黑屏。我可以将新的sf::RectangleShape 绘制到相同的RenderTexture,但我的 GL 网格似乎没有绘制。还在调查中……
如果有人遇到同样的问题,这里是我必须更改的 sn-ps:
// --- 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();
编辑 2: 绘图问题已解决,请参阅我的答案。
【问题讨论】: