渲染OpenGL的网格来SFML渲染纹理
我试图使用SFML和OpenGL的组合为一个项目,但我有麻烦渲染到sf::RenderTexture
。具体来说,如果我试图在RenderTexture
处于活动状态时进行绘制,则会崩溃。 (貌似内glDrawElements
空指针。)渲染OpenGL的网格来SFML渲染纹理
直接渲染到窗口工作正常。如果我自己通过OpenGL手动创建帧缓冲区,那也可以正常工作。但是,如果可能的话,我希望能够使用RenderTexture
来简化很多代码。
我可能会做一些愚蠢的事,但我还是新OpenGL的,所以我也不太清楚。 (特别是随着SFML和OpenGL的混合,好像很多东西可以打破,如果你不管理上下文切换正确的),我没有看到从OpenGL或SFML任何警告。
下再现我看到的问题(的Windows 10时,Visual Studio 2017年时,OpenGL 4.5,GLEW 2.1.0,2.4.0 SFML):
#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:解决拉丝问题,请参阅我的答案。
我找到了答案!这一切都归功于“Groogy”在这个线程:https://en.sfml-dev.org/forums/index.php?topic=7446.0
我脱脂线程早些时候,这促使我创建RenderTexture
时添加texture.setView(texture.getDefaultView());
。然而,这还不够,我不得不打电话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;
}
}
现在一切正常。