Как получить некоторую часть GL_TEXTURE_2D и поместить ее в другой GL_TEXTURE_2D (glGetTexImage, glReadPixels, glGetTextureSubImage) - PullRequest
0 голосов
/ 01 апреля 2020

Я работаю над небольшим 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();
}

...