我正在尝试为项目使用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::RenderTexture
有自己的GL上下文,我想你不能在上下文之间重用数据。所以我必须先生成纹理,然后在生成着色器和网格之前使用texture.setActive()
。这样,上下文就有了这些对象。
现在我只是一个黑屏。我可以将新的sf::RectangleShape
绘制到同一RenderTexture
,但我的GL网格似乎没有绘制。还在调查......
如果有人遇到同样的问题,以下是我必须改变的内容:
// --- 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:绘图问题已解决,请参阅我的回答。
答案 0 :(得分:0)
我找到了答案!感谢&#34; Groogy&#34;在这个帖子中:https://en.sfml-dev.org/forums/index.php?topic=7446.0
我之前已经浏览过该帖子,这促使我在创建texture.setView(texture.getDefaultView());
时添加RenderTexture
。但是这还不够,我不得不用纹理绑定调用glViewport
。 (我认为这是sf::View
在封面下会做的事情,但显然事实并非如此。)
所以我的RenderTexture
创建现在看起来像这样:
sf::RenderTexture texture;
sf::Sprite sprite;
{ // Render Texture
if (!texture.create(800, 600, true)) {
std::cerr << "Failed to create RenderTexture" << std::endl;
}
if (!texture.setActive(true)) {
std::cerr << "Failed to activate texture" << std::endl;
}
sprite.setTexture(texture.getTexture());
glViewport(0, 0, 800, 600); // <-- Required
glCheckError();
if (!texture.setActive(false)) {
std::cerr << "Failed to deactivate texture" << std::endl;
}
}
现在事情正在发挥作用。