How to use fragment program with SDL/OpenGL

I need to display YV12 video onto an OpenGL texture, and I have
found this fragment program that allegedly accomplishes this using
multitexturing (one texture for each input color plane, Y, U and V):

!!ARBfp1.0
ATTRIB position = fragment.texcoord[0];
TEMP R0, R1;
TEX R0, position, texture[0], 2D;
MAD R0, R0, 1.164, -0.073;
TEX R1.r, position, texture[1], 2D;
TEX R1.g, position, texture[2], 2D;
SUB R1.rg, R1, { 0.5, 0.5 };
MAD R0.rgb, { 0, -0.391, 2.018 }, R1.rrra, R0;
MAD R0.rgb, { 1.596, -0.813, 0 }, R1.ggga, R0;
MUL result.color, fragment.color, R0;
END

Unfortunately, I hardly know anything about OpenGL, so I have no
idea how to integrate this into my application. What I have now is
a method that handles RGB textures (minimalistic sample given below).

Is it easy to rewrite this so that it loads a picture in YV12
format, uploads the three color planes into three different
textures, runs the fragment program to generate a single RGB texture
based on the three Y,U,V textures, and display that?

A huge thank you to anyone who can help me with this!

//
/* Example program that needs to be rewritten to use the fragment shader. */
/
/
#include <stdio.h>
#include <unistd.h>
#include <SDL.h>
#include <SDL_opengl.h>

int main(int argc, char *argv[])
{
if (SDL_Init(SDL_INIT_VIDEO) != 0)
return 1;

  SDL_Surface *screen = SDL_SetVideoMode(0,0,24,SDL_OPENGL|SDL_FULLSCREEN);
  SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
  glEnable(GL_TEXTURE_2D);
  glViewport(0, 0, screen->w, screen->h);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0, 1, 1, 0, -1, 1);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  GLuint texture;
  glGenTextures(1, &texture);
  glBindTexture(GL_TEXTURE_2D, texture);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

  SDL_Surface *img = SDL_LoadBMP(argv[1]);
  glTexImage2D(GL_TEXTURE_2D, 0, 3, img->w, img->h, 0,
          GL_BGR, GL_UNSIGNED_BYTE, img->pixels);
  SDL_FreeSurface(img);

  glClearColor(0, 0, 0, 0);
  glClear(GL_COLOR_BUFFER_BIT);

  glBegin(GL_QUADS);
  {
      glTexCoord2i(0, 0);
      glVertex3f(0, 0, 0);

      glTexCoord2i(1, 0);
      glVertex3f(1, 0, 0);

      glTexCoord2i(1, 1);
      glVertex3f(1, 1, 0);

      glTexCoord2i(0, 1);
      glVertex3f(0, 1, 0);
  }
  glEnd();
  SDL_GL_SwapBuffers();
  sleep(5);

  glDeleteTextures(1, &texture);
  SDL_Quit();
  return 0;

}
/*****************************************************************************/–
Haakon

I need to display YV12 video onto an OpenGL texture, and I have
found this fragment program that allegedly accomplishes this using
multitexturing (one texture for each input color plane, Y, U and
V): […]

Nevermind, I think I figured out how to do this. Will post either
the solution (for reference), or a new question if I fail. :-)–
Haakon

I need to display YV12 video onto an OpenGL texture, and I have
found this fragment program that allegedly accomplishes this using
multitexturing (one texture for each input color plane, Y, U and
V): […]

Nevermind, I think I figured out how to do this. Will post either
the solution (for reference), or a new question if I fail. :slight_smile:

I was right, it works. For reference:

#include <stdio.h>
#include <unistd.h>

#include <SDL.h>
#include <SDL_opengl.h>
#include <SDL_video.h>

PFNGLGENPROGRAMSARBPROC glGenProgramsARB;
PFNGLBINDPROGRAMARBPROC glBindProgramARB;
PFNGLPROGRAMSTRINGARBPROC glProgramStringARB;

int main(int argc, char *argv[])
{
SDL_Init(SDL_INIT_VIDEO);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_Surface *screen = SDL_SetVideoMode(0,0,24, SDL_OPENGL|SDL_FULLSCREEN);

  glGenProgramsARB = (PFNGLGENPROGRAMSARBPROC) SDL_GL_GetProcAddress("glGenProgramsARB");
  glBindProgramARB = (PFNGLBINDPROGRAMARBPROC) SDL_GL_GetProcAddress("glBindProgramARB");
  glProgramStringARB = (PFNGLPROGRAMSTRINGARBPROC) SDL_GL_GetProcAddress("glProgramStringARB");

  glEnable(GL_TEXTURE_2D);
  glViewport(0, 0, screen->w, screen->h);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0, 1, 1, 0, -1, 1);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  int img_w = 512, img_h = 512;               // image width and height
  unsigned char buf[img_w * img_h * 12 / 8];  // buffer for image data
  read(0, buf, sizeof(buf));                  // read raw YUV img from stdin

  unsigned char *Y = buf;                     //
  unsigned char *U = buf + img_w * img_h;     // pointers to the Y,U,V planes
  unsigned char *V = U + img_w * img_h / 4;   //

  GLuint tex0, tex1, tex2;

  // Load Y texture.
  glGenTextures(1, &tex0);
  glBindTexture(GL_TEXTURE_2D, tex0);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexImage2D(GL_TEXTURE_2D, 0, 1, img_w, img_h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, Y);

  // Load U texture.
  glGenTextures(1, &tex1);
  glBindTexture(GL_TEXTURE_2D, tex1);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexImage2D(GL_TEXTURE_2D, 0, 3, img_w/2, img_h/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, U);

  // Load V texture.
  glGenTextures(1, &tex2);
  glBindTexture(GL_TEXTURE_2D, tex2);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexImage2D(GL_TEXTURE_2D, 0, 3, img_w/2, img_h/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, V);

  glActiveTextureARB(GL_TEXTURE0_ARB);  //
  glBindTexture(GL_TEXTURE_2D, tex0);   // Enable multi-texturing for Y
  glEnable(GL_TEXTURE_2D);              //

  glActiveTextureARB(GL_TEXTURE1_ARB);  //
  glBindTexture(GL_TEXTURE_2D, tex1);   // Enable multi-texturing for U
  glEnable(GL_TEXTURE_2D);              //

  glActiveTextureARB(GL_TEXTURE2_ARB);  //
  glBindTexture(GL_TEXTURE_2D, tex2);   // Enable multi-texturing for V
  glEnable(GL_TEXTURE_2D);              //

  // Install fragment program that converts planar YUV (YV12) to RGB.
  char const *fragprog =
      "!!ARBfp1.0"
      "ATTRIB position = fragment.texcoord[0];"
      "TEMP R0, R1; "
      "TEX R0, position, texture[0], 2D;"
      "MAD R0, R0, 1.164, -0.073;"
      "TEX R1.r, position, texture[1], 2D;"
      "TEX R1.g, position, texture[2], 2D;"
      "SUB R1.rg, R1, { 0.5, 0.5 };"
      "MAD R0.rgb, { 0, -0.391, 2.018 }, R1.rrra, R0;"
      "MAD R0.rgb, { 1.596, -0.813, 0 }, R1.ggga, R0;"
      "MUL result.color, fragment.color, R0;"
      "END";
  GLuint result;
  glGenProgramsARB(1, &result);
  glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, result);
  glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB,
                     GL_PROGRAM_FORMAT_ASCII_ARB,
                     strlen(fragprog), fragprog);
  glEnable(GL_FRAGMENT_PROGRAM_ARB);

  glClearColor(0, 0, 0, 0);
  glClear(GL_COLOR_BUFFER_BIT);

  glBegin(GL_QUADS);
  {
      glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0, 0.0);
      glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0, 0.0);
      glMultiTexCoord2fARB(GL_TEXTURE2_ARB, 0.0, 0.0);
      glVertex3f(0, 0, 0);

      glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0, 0.0);
      glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0, 0.0);
      glMultiTexCoord2fARB(GL_TEXTURE2_ARB, 1.0, 0.0);
      glVertex3f(1, 0, 0);

      glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0, 1.0);
      glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0, 1.0);
      glMultiTexCoord2fARB(GL_TEXTURE2_ARB, 1.0, 1.0);
      glVertex3f(1, 1, 0);

      glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0, 1.0);
      glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0, 1.0);
      glMultiTexCoord2fARB(GL_TEXTURE2_ARB, 0.0, 1.0);
      glVertex3f(0, 1, 0);
  }
  glEnd();
  SDL_GL_SwapBuffers();
  sleep(5);

  glDeleteTextures(1, &tex0);
  glDeleteTextures(1, &tex1);
  glDeleteTextures(1, &tex2);
  SDL_Quit();
  return 0;

}–
Haakon

  glTexImage2D(GL_TEXTURE_2D, 0, 3, img_w/2, img_h/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, U);
  glTexImage2D(GL_TEXTURE_2D, 0, 3, img_w/2, img_h/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, V);

Oops, the third argument is supposed to be 1, not 3. I’m surprised
this even worked with that typo; glTexImage2D must have inferred
the right number based on the GL_LUMINANCE argument which implies
a single color component.–
Haakon