OpenGL (ES) Обработка изображений C ++ - PullRequest
0 голосов
/ 14 мая 2018

В настоящее время я пытаюсь выполнить обработку изображений с использованием OpenGL ES. Я пытаюсь сделать основные эффекты изображения, такие как размытие, переключение цветового пространства и так далее. Я хочу собрать простейшую программу для следующих вещей:

  • Загрузка изображения
  • Обработка изображений (с использованием шейдера)
  • Сохранение изображения

Мне удалось создать следующую настройку:

  • Контекст OpenGL
  • Изображение, которое я хочу использовать для эффектов, загруженных с использованием DevIL.
  • Два шейдера (один вершинный шейдер и один фрагментный шейдер)

Теперь я застрял при использовании загруженного изображения для отправки данных во фрагментный шейдер. То, что я пытаюсь сделать, - это отправить изображение в качестве sampleler2D во фрагментный шейдер и применить к нему обработку.

У меня есть несколько вопросов, таких как:

  • Нужен ли вершинный шейдер, если все, что я хочу сделать - это чистая обработка 2D-изображений?
  • Если я это сделаю, то что нужно сделать в этом вершинном шейдере, так как у меня вообще нет вершин. Должен ли я создавать четырехугольные вершины (например, (0,0) (1, 0) (0, 1) (1, 1))? Если так, то почему?
  • Нужно ли мне использовать такие вещи, как VBO (что, похоже, связано с вершинным шейдером), FBO или что-то подобное?
  • Разве я не могу просто загрузить свое изображение в текстуру и подождать, пока фрагментный шейдер сделает все, что я хочу с этой текстурой?
  • Может кто-нибудь предоставить какой-нибудь простой кусок «чистого» кода, который мог бы помочь мне понять (без каких-либо причудливых классов, которые усложняют понимание)?

Вот как выглядит мой фрагментный шейдер для простой замены цвета:

uniform int width;
uniform int height;
uniform sampler2D texture;

void main() {
    vec2 texcoord = vec2(gl_FragCoord.x/width, gl_FragCoord.y/height);
    vec4 texture_value = texture2D(texture, texcoord);
    gl_FragColor = texture_value.bgra;
}

и мой main.cpp:

int main(int argc, char** argv)
{

    if (argc != 4) {
        std::cerr << "Usage: " << argv[0] << " <vertex shader path> <fragment shader path> <image path>" << std::endl;
        return EXIT_FAILURE;
    }

  // Get an EGL valid display
  EGLDisplay display;
  display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
  if (display == EGL_NO_DISPLAY) {
    std::cerr << "Failed to get EGL Display" << std::endl
              << "Error: " << eglGetError() << std::endl;
    return EXIT_FAILURE;
  }
  else {
    std::cerr << "Successfully get EGL Display." << std::endl;
  }


  // Create a connection to the display
  int minor, major;
  if (eglInitialize(display, &minor, &major) == EGL_FALSE) {
    std::cerr << "Failed to initialize EGL Display" << std::endl
              << "Error: " << eglGetError() << std::endl;
    eglTerminate(display);
    return EXIT_FAILURE;
  }
  else {
    std::cerr << "Successfully intialized display (OpenGL ES version " << minor << "." << major << ")." << std::endl;
  }

  // OpenGL ES Config are used to specify things like multi sampling, channel size, stencil buffer usage, & more
  // See the doc: https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglChooseConfig.xhtml for more informations
  EGLConfig config;
  EGLint num_configs; 
  if (!eglChooseConfig(display, configAttribs, &config, 1, &num_configs)) {
    std::cerr << "Failed to choose EGL Config" << std::endl
              << "Error: " << eglGetError() << std::endl;
    eglTerminate(display);
    return EXIT_FAILURE;
  }
  else {
    std::cerr << "Successfully choose OpenGL ES Config ("<< num_configs << ")." << std::endl;
  }

  // Creating an OpenGL Render Surface with surface attributes defined above.
  EGLSurface surface = eglCreatePbufferSurface(display, config, pbufferAttribs);
  if (surface == EGL_NO_SURFACE) {
    std::cerr << "Failed to create EGL Surface." << std::endl
              << "Error: " << eglGetError() << std::endl;
  }
  else {
    std::cerr << "Successfully created OpenGL ES Surface." << std::endl;
  }

  eglBindAPI(EGL_OPENGL_API);
  EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
  if (context == EGL_NO_CONTEXT) {
    std::cerr << "Failed to create EGL Context." << std::endl
              << "Error: " << eglGetError() << std::endl;
  }
  else {
    std::cerr << "Successfully created OpenGL ES Context." << std::endl;
  }

  //Bind context to surface
  eglMakeCurrent(display, surface, surface, context);

  // Create viewport and check if it has been created correctly
  glViewport(0, 0, WIDTH, HEIGHT);
  GLint viewport[4];
  glGetIntegerv(GL_VIEWPORT, viewport);

  if (viewport[2] != WIDTH || viewport[3] != HEIGHT) {
    std::cerr << "Failed to create the viewport. Size does not match (glViewport/glGetIntegerv not working)." << std::endl
              << "OpenGL ES might be faulty!" << std::endl
              << "If you are on Raspberry Pi, you should not updated EGL as it will install fake EGL." << std::endl;
    eglTerminate(display);
    return EXIT_FAILURE;
  }

  // Clear buffer and get ready to draw some things
  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  // Create a shader program
  GLuint program = load_shaders(std::string(argv[1]), std::string(argv[2]));
  if (program == -1)
  {
      std::cerr << "Failed to create a shader program. See above for more details." << std::endl;
      eglTerminate(display);
      return EXIT_FAILURE;
  }

  /* Initialization of DevIL */
  if (ilGetInteger(IL_VERSION_NUM) < IL_VERSION) {
    std::cerr << "Failed to use DevIL: Wrong version." << std::endl;
    return EXIT_FAILURE;
  }

  ilInit(); 
  ILuint image = load_image(argv[3]);
  GLuint texId;
  glGenTextures(1, &texId); /* Texture name generation */
  glBindTexture(GL_TEXTURE_2D, texId); /* Binding of texture name */
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); /* We will use linear interpolation for magnification filter */
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); /* We will use linear interpolation for minifying filter */
  glTexImage2D(GL_TEXTURE_2D, 0, ilGetInteger(IL_IMAGE_BPP), ilGetInteger(IL_IMAGE_WIDTH), ilGetInteger(IL_IMAGE_HEIGHT), 
               0, ilGetInteger(IL_IMAGE_FORMAT), GL_UNSIGNED_BYTE, ilGetData()); /* Texture specification */

Спасибо.

1 Ответ

0 голосов
/ 14 мая 2018

Нужен ли мне вершинный шейдер, поскольку все, что я хочу сделать, - это чистая обработка 2D-изображений?

Использование вершинных и фрагментных шейдеров обязательно в OpenGL ES 2.

Если я это сделаю, то что нужно сделать в этом вершинном шейдере, так как у меня вообще нет вершин.Должен ли я создавать четырехугольные вершины (например, (0,0) (1, 0) (0, 1) (1, 1))?Если так, то почему?

Да.Потому что так работает OpenGL ES 2.В противном случае вам нужно будет использовать что-то вроде компьютерных шейдеров (поддерживается в OpenGL ES 3.1+) или OpenCL.

Нужно ли использовать такие вещи, как VBO (что, похоже, связано с вершинным шейдером),FBO или что-то в этом роде?

Использование VBO / IBO практически ничего не изменит для вас, поскольку у вас есть только 4 вершины и 2 примитива.Вы можете рендерить текстуру, в зависимости от ваших потребностей.

Разве я не могу просто загрузить свое изображение в текстуру и ждать, пока фрагментный шейдер сделает все, что я хочу с этой текстурой?

...