如何对纹理执行 HSL 变换?

发布于 2024-08-30 18:36:35 字数 398 浏览 3 评论 0原文

如果我有一个 OpenGL 纹理,并且我需要在渲染纹理之前对其执行 HSL 修改,据我所知我需要一个着色器。问题是,我对着色器一无所知。有谁知道我需要去哪里看?

我想编写一个函数,在其中可以传入纹理和三个值、色调偏移度数以及 0 到 2 之间的饱和度和亮度乘数,然后让它调用一个着色器,将这些变换应用到之前的纹理呈现。界面看起来像这样:

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

不过,我不知道例程中应该包含什么内容。我了解 HSL/RGB 转换中涉及的基本数学,但我不知道如何编写着色器或如何应用它。有人能指出我正确的方向吗?首选 Delphi 示例,但如果需要的话我也可以阅读 C。

If I have an OpenGL texture, and I need to perform HSL modifications on it before rendering the texture, from what I've heard I need a shader. Problem is, I know nothing about shaders. Does anyone know where I would need to look?

I want to write a function where I can pass in a texture and three values, a hue shift in degrees, and saturation and lightness multipliers between 0 and 2, and then have it call a shader that will apply these transformations to the texture before it renders. The interface would look something like this:

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

I have no idea what's supposed to go inside the routine, though. I understand the basic math involved in HSL/RGB conversions, but I don't know how to write a shader or how to apply it. Can someone point me in the right direction? Delphi examples preferred, but I can also read C if I have to.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

伤感在游骋 2024-09-06 18:36:35

这非常复杂,如果您不熟悉着色器和 FBO,则需要一些时间来理解它。所以这里有一些最小的、未经测试的OpenGL代码,它实现了丹维尔的回答。错误检查已被省略;它本身就够复杂的了。如果多次调用此函数,则应保留此代码中创建的任何或所有对象。

如果您不关心速度,VilleK 的答案 在 CPU 上运行要容易得多...

// 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;

希望有帮助。对于 2.0 之前的 OpenGL,您需要首先加载适当的扩展,然后在各处添加一些 EXT 和/或 ARB

如果您愿意走这条路,但没有成功,请随时发表评论来描述您遇到的问题。我很乐意提供帮助!

This is quite involved, and if you're new to shaders and FBO's it will take some time to understand it. So here is some minimal, untested OpenGL code that implements Danvil's answer. Error checking has been left out; it's complicated enough as it is. If you call this function many times, you should hold on to any or all of the objects created in this code.

If you don't care about speed, VilleK's answer that runs on the CPU is a lot easier...

// 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;

Hope that helps. For pre-2.0 OpenGL, you need to load the proper extensions first, and slap on some EXTs and/or ARBs here and there.

If you're willing to go down this road, and it doesn't work out, don't hesitate to post a comment describing the problems you encounter. I'll be happy to help!

隐诗 2024-09-06 18:36:35

编写着色器并不像听起来那么难。您应该研究诸如 Cg 或 HLSL 之类的东西...基本上,您最终将编写一个接受像素并对其进行转换的函数。

CG 教程

编写着色器后,您将加载并将其与 OpenGL 扩展函数绑定。当您将几何图形推入管道时,它将由光栅化器应用。这是一个简单的教程,展示了如何编写和使用非常基本的着色器:

教程 - OpenGL 中的 Cg 像素着色器< /a>

Writing a shader is not as hard as it sounds. You should look into something like Cg, or HLSL... Basically, you're going to end up writing a single function that takes a pixel and transforms it.

The CG Tutorial

Once you're shader is written, you'll load and bind it with OpenGL extension functions. It will then be applied by the rasterizer when you push geometry into the pipeline. Here is a simple tutorial that shows how to write and use a very basic shader:

Tutorial - Cg Pixel Shaders in OpenGL

羁拥 2024-09-06 18:36:35

如果您不想使用着色器(即不需要实时),您可以获取纹理的像素并使用 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 硬件上进行测试,以确保它们正常工作。

If you don't want to use shaders (i.e. real time is not needed) you can get the pixels of the texture and modify it using Delphi code. Something like this:

  //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);

You can use GL_UNSIGNED_BYTE if you want to work with bytes instead of floats.

It will be slower performance than shaders but you will probably get the work done in less times because shaders are tricky to write, especially if you've never written one before. They also need to be tested on both NVIDIA and ATI hardware to be sure they work properly.

江城子 2024-09-06 18:36:35

使用渲染到纹理:

  1. 使用源纹理 texRgb 的大小创建一个新的(目标)纹理 texHSL
  2. 使用 正交投影
  3. 创建并绑定一个 framebuffer a> 并使用纹理 texHSL 作为渲染目标
  4. 使用着色器渲染屏幕尺寸四边形(可能更大)。对于着色器,使用统一的着色器绑定 texRgb
  5. 着色器 读取当前渲染值的纹理值(参见此处的答案),计算 RGB->HSL 转换并将 HSL 值写入 gl_FragColor
  6. 取消绑定并删除帧缓冲区
  7. 瞧,转换后的图像位于 texHSL 中

Use render to texture:

  1. Create a new (target) texture texHSL with the size of your source texture texRgb
  2. Set up a viewport with size of texHSL using an orthogonal projection
  3. Create and bind a framebuffer and use texture texHSL as a render target
  4. Render a screen size quad (may be larger) using a shader. For the shader bind texRgb using a uniform
  5. The shader reads the texture value for the currently rendered value (see by answer there), computes the RGB->HSL conversion and writes the HSL value to gl_FragColor
  6. Unbind and delete the framebuffer
  7. Voila, your converted image is in texHSL
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文