2010-04-29 5 views
1

Si j'ai une texture OpenGL et que j'ai besoin d'effectuer des modifications HSL avant de rendre la texture, d'après ce que j'ai entendu, j'ai besoin d'un shader. Le problème est que je ne connais rien aux shaders. Est-ce que quelqu'un sait où je devrais regarder? Je veux écrire une fonction où je peux passer une texture et trois valeurs, un décalage de teinte en degrés, et des multiplicateurs de saturation et de luminosité entre 0 et 2, et ensuite l'appeler un shader qui appliquera ces transformations à la texture avant qu'elle ne se rende. L'interface ressemblerait à quelque chose comme ceci:Comment effectuer une transformation HSL sur une texture?

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

Je ne sais pas ce qui est censé aller à l'intérieur de la routine, cependant. Je comprends les mathématiques de base impliquées dans les conversions HSL/RGB, mais je ne sais pas comment écrire un shader ou comment l'appliquer. Quelqu'un peut me diriger dans la bonne direction? Les exemples Delphi sont préférés, mais je peux aussi lire C si je le dois.

Répondre

4

Ceci est très impliqué, et si vous êtes nouveau pour les shaders et les FBO, il faudra du temps pour le comprendre.Voici donc un peu de code OpenGL non testé qui implémente Danvil's answer. La vérification d'erreur a été omise. c'est assez compliqué comme ça. Si vous appelez cette fonction plusieurs fois, vous devez conserver tout ou partie des objets créés dans ce code.

Si vous ne se soucient pas de la vitesse, VilleK's answer qui fonctionne sur le CPU est beaucoup plus facile ...

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

espoir qui aide. Pour les versions antérieures à la 2.0 OpenGL, vous devez d'abord charger les extensions appropriées, puis cliquer sur EXT s et/ou ARB s ici et là.

Si vous êtes prêt à emprunter cette route et que cela ne fonctionne pas, n'hésitez pas à poster un commentaire décrivant les problèmes que vous rencontrez. Je serai heureux d'aider!

+0

Wow! Vous avez fait du bon travail là-bas :) J'étais trop paresseux pour l'écrire;) – Danvil

2

L'écriture d'un matériau n'est pas aussi difficile que cela en a l'air. Vous devriez regarder quelque chose comme Cg, ou HLSL ... Fondamentalement, vous allez finir par écrire une seule fonction qui prend un pixel et le transforme.

The CG Tutorial

Une fois que vous êtes shader est écrit, vous chargez et le lier avec des fonctions d'extension OpenGL. Il sera ensuite appliqué par le rastériseur lorsque vous poussez la géométrie dans le pipeline. Voici un tutoriel simple qui montre comment écrire et utiliser un shader très basique:

Tutorial - Cg Pixel Shaders in OpenGL

1

Si vous ne souhaitez pas utiliser les shaders (ie temps réel n'est pas nécessaire), vous pouvez obtenir les pixels de la texture et modifiez-le en utilisant le code Delphi. Quelque chose comme ceci:

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

Vous pouvez utiliser GL_UNSIGNED_BYTE si vous voulez travailler avec des octets au lieu de flotteurs. Les performances seront plus lentes que celles des shaders, mais vous aurez probablement du travail en moins de temps car les shaders sont difficiles à écrire, surtout si vous n'en avez jamais écrit un auparavant. Ils doivent également être testés sur le matériel NVIDIA et ATI pour s'assurer qu'ils fonctionnent correctement.

+0

Cela doit être fait en temps réel, malheureusement. Ce serait bien de pouvoir le prérégler, mais ça ne marchera pas. –

1

Utilisation rendre à la texture:

  1. Créer une nouvelle (cible) texture texHSL avec la taille de votre texture Source texRgb
  2. Mettre en place une fenêtre avec la taille de texHSL en utilisant un orthogonal projection
  3. Créer et liez un framebuffer et utilisez texture texHSL comme cible de rendu
  4. Rendu d'un quad de taille d'écran (peut-être plus grand) à l'aide d'un shader. Pour la liaison de shaders texRgb en utilisant un uniforme
  5. Shader reads the texture value for the currently rendered value (voir par réponse là-bas), calcule le RVB-> conversion de HSL et écrit la valeur HSL gl_FragColor
  6. Unbind et supprimer le framebuffer
  7. Voila, votre converti L'image est en texHSL
+0

Aux étapes 3 et 6, avec "render buffer" vous voulez dire "objet framebuffer"? – Thomas

+0

Merci, l'a mis à jour. – Danvil

Questions connexes