8
\$\begingroup\$

What the title says.Is it possible to update a texture via a glsl shader ? Something like :

//Read
vec4 Pixel = texture2D(TextureID,gl_TexCoord[TextureIndex].st);

//Write to texture memory ?
vec4 NewPixel = Pixel + vec4(0.0,0.0,0.0,AlphaPatch);

// ?? How to write back to texture memory ??
texture2D(TextureID,gl_TexCoord[TextureIndex].st) = NewPixel; 

??

\$\endgroup\$

3 Answers 3

6
\$\begingroup\$

What you want is to bind a texture to a shader and then render to that texture. I could spit through miles of documentation specification of OpenGL, but I'm going to give the feeling my gut gives me:

I don't think that's possible.

What I do know is possible, however, is that you create a Frame Buffer Object (FBO) and render to that. First, you must generate an FBO and attach a texture to it with the same size as the one you want to update.

GLuint fbo_handle, fbo_texture_handle;

GLuint texture_width = GetTextureWidth();
GLuint texture_height = GetTextureWidth();

// generate texture

glGenTextures(1, &fbo_texture_handle);
glBindTexture(GL_TEXTURE_2D, fbo_texture_handle);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texture_width, texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);

// generate framebuffer

glGenFrameBuffers(1, &fbo_handle);
glBindFrameBuffer(GL_FRAMEBUFFER, fbo_handle);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo_texture_handle, 0);

GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
    LOG_ERROR("Could not validate framebuffer);
}

glBindFramebuffer(GL_FRAMEBUFFER, 0);

When you want to update your texture, you must take the followings steps.

First, you attach the FBO:

glBindFramebuffer(GL_FRAMEBUFFER, fbo_handle);

glPushAttrib(GL_VIEWPORT_BIT);
glViewport(0, 0, texture_width, texture_height);

Now you can render a fullscreen quad with a shader that outputs the updated color:

vec4 frag_texture = texture2D(TextureID, gl_TexCoord[TextureIndex].st);

vec4 frag_updated = frag_texture + vec4(0.0, 0.0, 0.0, AlphaPatch);

gl_FragData[0] = frag_updated;

And then you have to copy the resulting framebuffer to the original texture.

glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_handle);
glBindTexture(GL_TEXTURE_2D, original_texture_handle);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, texture_width, texture_height);

glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);

glPopAttrib();

However, you should consider the following:

  • How often do you update this texture?
  • If you don't have to update it every frame, is this method worth the speed penalty?
  • Can you generate multiple version of this texture and cycle between them?
  • Is there another way to achieve the same effect?
\$\endgroup\$
3
  • \$\begingroup\$ Wow thank you very much for the help! And to answer your questions : 1.I have a special texture for my hero that i have to update every time collision occurs (with dots of blood and other things). 2.Due to the nature of the game you get hit or touch things full of dirt(mud) all the time so i have to update that texture very frequently... 3.That's how i do it right now and im not happy with it(i need dynamic texture patching) 4.Don't know :o \$\endgroup\$ Jun 26, 2012 at 13:58
  • 4
    \$\begingroup\$ Aha! Well that helps in fine-tuning my answer. What you're probably looking for is multi-texturing. You have a texture for your game character and you have a texture that shows dirt and blood and whatnot. In a shader, you can combine them to create the effect you want. This is much cheaper than continually rendering to a frame buffer object and copying the result to a texture. \$\endgroup\$
    – knight666
    Jun 26, 2012 at 14:07
  • \$\begingroup\$ Thank you once again.I'm not quite sure what you mean with multi texturing but i will try to search on stackexchange :)) \$\endgroup\$ Jun 27, 2012 at 6:39
5
\$\begingroup\$

If your hardware supports it, the GL_ARB_shader_image_load_store extension allows you to write to a texture.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ I can tell you right now that my NVIDIA GeForce GTS 250 supports OpenGL 3.3.0, but not GL_EXT_shader_image_load_store. That means your answer is probably correct, but not practical in the field yet. Sucks. :( \$\endgroup\$
    – knight666
    Jun 26, 2012 at 13:24
  • 1
    \$\begingroup\$ Yep, looks like you need a GeForce 400 series or greater for support (it's a GL4/DX11 feature). \$\endgroup\$
    – elFarto
    Jun 26, 2012 at 14:00
1
\$\begingroup\$

Yes it's possible, texture/imageLoad are for reading and imageStore is for writing.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .