Я работаю над небольшим 2D графическим движком openGL 3+ c, используя glfw и рад. Я могу загружать и отображать текстуры из файлов или из памяти. Теперь мне нужно вернуть некоторые данные текстуры из графического процессора в процессор, вырезать небольшую часть этих данных, создать новую текстуру меньшего размера, загрузить ее в графический процессор и отобразить ее.
Я нашел решение, которое работает , используя glGetTexImage
, но оно очень медленное. Я нашел другие решения здесь и там , но я не могу заставить их работать.
Я пробовал несколько вещей:
- Сначала я попробовал с
glgetTextureSubImage
, который, кажется, предназначен именно для того, что я ищу. Говорят, что мне нужен либо контекст openGL 4.5, либо расширение GL_ARB_get_texture_sub_image . Я пробовал оба с одинаковым результатом (не работает). - Затем я попытался с
glReadPixels
из FBO. Результат тот же.
Возможно, я чего-то не понимаю, но не могу понять ... Вот пример, который может отображать текстуру, но не субтекстура . Существует функция init
, которая обрабатывает большинство вещей, связанных с созданием дисплея, и функция exit
. Я включил их, так что пример почти завершен (я не включал код шейдера или код компиляции шейдера, он очень прост и работает как задумано), но часть, которая нас на самом деле интересует, - это функция main
. Объект Texture
- это просто небольшая оболочка, которая может хранить метаданные текстуры.
Я бы ожидал, что этот пример отобразит некоторую часть моей исходной текстуры, но у меня только черный экран.
// Редактировать после комментария: @ Rabbid76 указал, что я мог бы использовать glCopyImageSubData
для непосредственного копирования из одной текстуры в другую. Но моя реальная ситуация предполагает некоторую обработку субтекстуры перед ее повторной загрузкой в графический процессор, поэтому я не могу использовать это.
init code:
// You can skip this one, it's here for completeness
#include <glad/glad.h>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include "resourcemanager.h"
static std::vector<float> vertices;
static std::vector<unsigned int> indices;
static unsigned int VAO;
static unsigned int VBO;
static unsigned int EBO;
static Shader shader;
static GLFWwindow *window;
static void error_callback(int error, const char *description) {
std::cerr << "Error(" << error << "): " << description << std::endl;
}
void init() {
glfwSetErrorCallback(error_callback);
if (!glfwInit())
exit(EXIT_FAILURE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
window = glfwCreateWindow(1280, 720, "subimage test", nullptr, nullptr);
if (!window) {
glfwTerminate();
throw std::runtime_error("Couldn't create window.");
}
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress))) {
glfwTerminate();
throw std::runtime_error("Couldn't initialize glad.");
}
if (!GLAD_GL_ARB_get_texture_sub_image)
throw std::runtime_error("Couldn't load openGL extension "
"GL_ARB_get_texture_sub_image which is needed");
glfwSwapInterval(1);
// This works fine
shader = ResourceManager::loadShader(
"shaders/subimage.vs", "shaders/subimage.fs", nullptr, "subimage");
vertices = {
1.0f, 1.0f, // top right
1.0f, 1.0f, // tex coord
1.0f, -1.0f, // bottom right
1.0f, 0.0f, // tex coord
-1.0f, -1.0f, // bottom left
0.0f, 0.0f, // tex coord
-1.0f, 1.0f, // top left
0.0f, 1.0f // tex coord
};
indices = {
0, 3, 1, // first triangle
1, 3, 2 // second triangle
};
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER,
static_cast<GLsizeiptr>(sizeof(float) * vertices.size()),
vertices.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
static_cast<GLsizeiptr>(sizeof(float) * indices.size()),
indices.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), nullptr);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float),
reinterpret_cast<void *>((2 * sizeof(float))));
glEnableVertexAttribArray(1);
glBindVertexArray(0);
}
[[noreturn]] void exit(void) {
glfwDestroyWindow(window);
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
ResourceManager::cleanup();
glfwTerminate();
exit(EXIT_SUCCESS);
}
основная функция:
// This is where my actual problem is
int main(void) {
init();
// test.png is an image : 1280px x 720px, it can be displayed properly, see
// render loop.
Texture texture =
ResourceManager::loadTexture("textures/test.png", true, "test");
uint8_t *subTextureData = new uint8_t[static_cast<size_t>(160 * 90 * 4)];
memset(subTextureData, 0xFF, 160 * 90 * 4); // should be all white
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture.id);
// Alternative A : use glGetTextureSubImage() to fill subTextureData
glGetTextureSubImage(GL_TEXTURE_2D, 0, 0, 0, 0, 160, 90, 1, GL_RGBA,
GL_UNSIGNED_BYTE, 160 * 90 * 4, subTextureData);
// Alternative B : create an FBO, use glReadPixels() to fill subTextureData
// GLuint fboId = 0;
// glGenFramebuffers(1, &fboId);
// glBindFramebuffer(GL_READ_FRAMEBUFFER, fboId);
// glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
// GL_TEXTURE_2D, texture.id, 0); glReadPixels(0, 0, 160, 90, GL_RGBA,
// GL_UNSIGNED_BYTE, subTextureData); glBindFramebuffer(GL_READ_FRAMEBUFFER,
// 0);
Texture subTexture;
glGenTextures(1, &subTexture.id);
glBindTexture(GL_TEXTURE_2D, subTexture.id);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 160, 90, 0, GL_RGBA, GL_UNSIGNED_BYTE,
subTextureData);
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
glUseProgram(shader.id);
glClearColor(0.5f, 0.5f, 0.5f, 1.0f); // clear color is grey
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArray(VAO);
glActiveTexture(GL_TEXTURE0);
// glBindTexture(GL_TEXTURE_2D, texture.id); // <= using that displays the
// whole texture.
glBindTexture(GL_TEXTURE_2D, subTexture.id); // screen is black
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
glBindVertexArray(0);
glfwSwapBuffers(window);
}
exit();
}