Как выполнить преобразование HSL для текстуры? - PullRequest
1 голос
/ 29 апреля 2010

Если у меня есть текстура OpenGL, и мне нужно выполнить модификации HSL на ней перед рендерингом текстуры, из того, что я слышал, мне нужен шейдер. Проблема в том, что я ничего не знаю о шейдерах. Кто-нибудь знает, где мне нужно искать?

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

procedure HSLTransform(texture: GLuint; hShift: integer; sMult, lMult: GLfloat);

Я понятия не имею, что должно происходить внутри рутины. Я понимаю основную математику, связанную с преобразованиями HSL / RGB, но я не знаю, как написать шейдер или как его применить. Может ли кто-нибудь указать мне правильное направление? Примеры Delphi предпочтительнее, но я также могу прочитать C, если придется.

Ответы [ 4 ]

4 голосов
/ 30 апреля 2010

Это довольно сложная задача, и если вы новичок в шейдерах и FBO, потребуется некоторое время, чтобы понять это. Итак, вот некоторый минимальный, непроверенный код OpenGL, который реализует ответ Danvil . Проверка ошибок была исключена; это достаточно сложно, как есть. Если вы вызываете эту функцию много раз, вы должны удерживать любой или все объекты, созданные в этом коде.

Если вам не важна скорость, Ответ VilleK , который работает на процессоре, будет намного проще ...

// Create the target texture object.
GLuint target;
glGenTextures(1, &target);

// Bind the target texture.
glBindTexture(GL_TEXTURE_2D, target);

// Allocate texture memory. width and height must be the size of the original texture.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

// Create a framebuffer object.
GLuint fbo;
glGenFramebuffers(1, &fbo);

// Bind the framebuffer object.
glBindFramebuffer(GL_FRAMEBUFFER, fbo);

// Attach the target texture as the render target.
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target, 0);

// Check framebuffer status.
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    throw "Framebuffer incomplete.";

// Create fragment shader to do the transformation.
GLuint frag = glCreateShader(GL_FRAGMENT_SHADER);

// Set the shader's source code.
char const *source =
    "uniform sampler2D input;\n"
    "uniform float hShift, sMult, lMult;\n"
    "void main() {\n"
    "    vec4 color = texture2D(input, gl_FragCoord.xy);\n"
    "    /* Do your HSL transformations here... */\n"
    "    gl_FragColor = color;\n"
    "}\n";
glShaderSource(frag, 1, &source, 0);

// Compile the shader.
glCompileShader(frag);

// Check compilation result. Here, you probably want to use glGetShaderInfoLog() to get any compilation errors.
GLint status;
glGetShader(frag, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
    throw "Shader compilation failed";

// Create the program.
GLuint program = glCreateProgram();

// Attach the shader to the program.
glAttachShader(program, frag);

// Link the program.
glLinkProgram(program);

// Check link result. Here, you probably want to use glGetProgramInfoLog() to get any link errors.
glGetProgram(program, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
    throw "Program linking failed"

// Use the program for subsequent rendering.
glUseProgram(program);

// Set the values of the uniform parameters.
glUniform1i(glUniformLocation(program, "input"), 0);
glUniform1f(glUniformLocation(program, "hShift"), hShift);
glUniform1f(glUniformLocation(program, "sMult"), sMult);
glUniform1f(glUniformLocation(program, "lMult"), lMult);

// Bind the source texture to read from.
glBindTexture(GL_TEXTURE_2D, texture);

// Set up the viewport and matrices.
glPushAttrib(GL_VIEWPORT_BIT | GL_TRANSFORM_BIT);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0, 1, 0, 1, 0, 1);
glViewport(0, 0, width, height);

// Render a quad.
glBegin(GL_QUADS);
glVertex2i(0, 0);
glVertex2i(1, 0);
glVertex2i(1, 1);
glVertex2i(0, 1);
glEnd();

// Restore the matrices and viewport.
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glPopAttrib();

// Stop using the program.
glUseProgram(0);

// Stop using the framebuffer object.
glBindFramebuffer(GL_FRAMEBUFFER, 0);

// Delete created objects.
glDeleteProgram(program);
glDeleteShader(frag);
glDeleteFramebuffers(1, &fbo);

// Optionally, delete the old, untransformed texture.
glDeleteTextures(1, &texture);

// Return the new texture.
return target;

Надеюсь, это поможет. Для OpenGL версии до 2.0 вам необходимо сначала загрузить нужные расширения и добавить несколько EXT s и / или ARB s здесь и там.

Если вы готовы пойти по этому пути, и это не сработает, не стесняйтесь оставлять комментарии с описанием проблем, с которыми вы сталкиваетесь. Я буду рад помочь!

2 голосов
/ 29 апреля 2010

Написание шейдера не так сложно, как кажется. Вы должны рассмотреть что-то вроде Cg или HLSL ... По сути, вы в конечном итоге напишите одну функцию, которая берет пиксель и преобразует его.

Учебник по компьютерной графике

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

Учебник - Cg пиксельные шейдеры в OpenGL

1 голос
/ 30 апреля 2010

Использовать рендер для текстуры:

  1. Создайте новую (целевую) текстуру texHSL с размером исходной текстуры texRgb
  2. Настройка области просмотра с размером texHSL с использованием ортогональной проекции
  3. Создайте и свяжите кадровый буфер и используйте текстуру texHSL в качестве цели рендеринга
  4. Визуализация квадрата размера экрана (может быть больше) с использованием шейдера. Для шейдера связываем texRgb используя униформу
  5. Шейдер читает значение текстуры для текущего отображаемого значения (см. Ответ там), вычисляет преобразование RGB-> HSL и записывает значение HSL в gl_FragColor
  6. Отмена привязки и удаление кадрового буфера
  7. Вуаля, ваше преобразованное изображение в формате texHSL
1 голос
/ 30 апреля 2010

Если вы не хотите использовать шейдеры (т. Е. В реальном времени не требуется), вы можете получить пиксели текстуры и изменить ее, используя код Delphi. Примерно так:

  //Get copy of pixels. P is a pointer to floats (^single)
  GetMem(P,TextureHeight * TextureWidth * 4 * SizeOf(single));
  glGetTexImage(GL_TEXTURE_2D,0,GL_RGBA,GL_FLOAT,P);
  ... loop and modify pixels here. 4 float values per pixels: Red, green, blue and alpha
  //Update texture with modified pixels
  glTexImage2D(GL_TEXTURE_2D, 0, 4, TextureWidth, TextureHeight , 0, GL_RGBA, GL_FLOAT, P);
  FreeMem(P);

Вы можете использовать GL_UNSIGNED_BYTE, если хотите работать с байтами, а не с плавающей точкой.

Это будет медленнее, чем шейдеры, но вы, вероятно, будете выполнять работу за меньшее время, потому что шейдеры сложнее писать, особенно если вы никогда не писали раньше. Их также необходимо протестировать на оборудовании NVIDIA и ATI, чтобы убедиться в их правильной работе.

...