Sign in to follow this  
rabidh

OpenGL Pixel shaders and blending

Recommended Posts

rabidh    122
Hi. I'm currently working on an engine for some sound-reactive visuals - the plan is to use pixel shaders as much as possible instead of the OpenGL fixed pipeline. I've hit a snag to do with blending though. I've been reading around and as far as I can see the output of a pixel shader always goes through the standard OpenGL Blending function (unless disabled). My problem is I want to be able to combine my output pixels with the framebuffer in other ways - specifically: DST = SRC+DST-0.5 I used to be able to do this just fine with the register combiners extension in OpenGL, but I don't think this will work with fragment shaders. So... Is there any way to load the destination colour into the pixel shader? This would be the ideal solution for me. The standard workaround seems to be to render to a texture first, and use that as an input. The whole reason I want the blend function above is for a particle system though, so thats not an option (i'd have to copy the texture back after each particle). Someone suggested that in DirectX you can render to a texture while sampling from that same texture - is there an equivalent in OpenGL (with PBuffers?). thanks in advance, - Gordon

Share this post


Link to post
Share on other sites
Lord Faron    152
I think the only way is to render the framebuffer to the texture and use it in ps. There was a plan to include FB reading in GLSL but I don't know if it was ever implemented.
You can't render to a texture and sample it at the same time.

Share this post


Link to post
Share on other sites
rabidh    122
Thanks! that just got me the magic phrase for google :)

http://www.beyond3d.com/forum/showthread.php?t=32477

It says (referring to 'GL 2.1 / GLSL 1.20):
Quote:
AFAICS, the new features/changes from OpenGL2.0 are in general rather modest:

* sRGB textures
* Centroid Mapping
* Shading-language "invariant" keyword
* It is no longer permitted to turn off perspective correction for plain gouraud shading
* Pixel Buffer Objects (which mainly provides mechanisms for asynchronous texture uploads and framebuffer reads, so that these operatiosn do not produce gigantic pipeline bubbles anymore)


I must admit I couldn't believe that 'GL wouldn't provide something as basic as that in PS - given that the hardware is obviously capable.

... now I just wonder if Cg is going to hack it (I wanted to use it so I could use the same shader for both DirectX and OpenGL)

edit: arg. Just read through most of http://www.opengl.org/registry/specs/ARB/GLSLangSpec.Full.1.20.6.pdf and I can't see a single mention of texture reads :(

edit2: can glBlendFunc and glBlendMode be used at the same time as fragment programs?

[Edited by - rabidh on August 18, 2006 1:04:50 PM]

Share this post


Link to post
Share on other sites
rabidh    122
just went to post this and saw your post. They metion it, but not in a good way :(

1.10 Spec #59 from http://oss.sgi.com/projects/ogl-sample/registry/ARB/GLSLangSpec.Full.1.10.59.pdf:

23) Should the fragment shader be allowed to read the current location in the frame buffer?
DISCUSSION: It may be difficult to specify this properly while taking into account multisampling. It also may be quite difficult for hardware implementors to implement this capability, at least with reasonable performance. But this was one of the top two requested items after the original release of the shading language white paper. ISVs continue to tell us that they need this capability, and that it
must be high performance.
RESOLUTION: Yes. This is allowed, with strong cautions as to performance impacts.
REOPENED on December 10, 2002. There is too much concern about impact to performance and
impracticallity of implementation.
CLOSED on December 10, 2002.

43) What is definition of built-in derivative functions of gl_FB*?
...
Possible resolutions:
a) Don't allow gl_FB* read operations in the fragment processor. (This interacts with Issue (23).)
b) The built-in derivative functions are undefined if a gl_FB* is a parent of an expression. (The builtin
derivative functions are in some cases undefined within the body of a conditional or loop.)
Rejected resolutions:
c) Explicitly define the order which fragments are rasterized by OpenGL.
RESOLUTION: gl_FB* have been removed. Issue 23 has been reopened and closed as disallowing
frame buffer reads.
CLOSED on December 11, 2002.

bummer. It was looking promising for a while.

Share this post


Link to post
Share on other sites
Yann L    1802
Okay, to clear up a few things here:

* FB reads are not allowed in fragment shaders, because current hardware doesn't support them (it would create a huge performance problem). D3D doesn't offer them either.

* The only part of the hardware in the render pipeline that can read from the framebuffer is the blending stage.

* Register combiners could not access the framebuffer values either.

You can use FBOs (framebuffer objects) to render into a texture. Later on, you bind this texture as an input and do the blending in your fragment shader. Sampling a texture while rendering to it is theoretically possible, but will exhibit completely undefined behaviour on current hardware.

Share this post


Link to post
Share on other sites
rabidh    122
Cheers Yann. Thinking about it I must have done the A+B-0.5 in the register combiner using textures :(

What exactly can I do in the blending stage while using pixel shaders? do glBlendMode/glBlendFunc still work as expected - I was pretty sure glBlendFunc didn't work for me.

Can I still do glBlendFunc(GL_ONE,GL_ONE) while using pixel shaders? What happens if I don't clamp the outputs of the pixel shader so I can get negative values out? (is it even possible?) Will that subtract from the destination surface?

Share this post


Link to post
Share on other sites
Yann L    1802
Quote:
Original post by rabidh
What exactly can I do in the blending stage while using pixel shaders? do glBlendMode/glBlendFunc still work as expected - I was pretty sure glBlendFunc didn't work for me.

Alpha blending is not dependent on the fragment shading model, it usually works as expected even when a fragment shader is enabled. I said 'usually', because behaviour is dependent on the destination surface type. When writing to a standard RGBA surface (such as the framebuffer), blending works exactly the same way as it does with the fixed function pipeline. However, when writing to a floating point target surface, blending might or might not be available, depending on your hardware. Although all the latest graphics chipsets from both NV and ATI now support floating point blending.

Quote:
Original post by rabidh
Can I still do glBlendFunc(GL_ONE,GL_ONE) while using pixel shaders?

Yes, sure.

Quote:

What happens if I don't clamp the outputs of the pixel shader so I can get negative values out? (is it even possible?) Will that subtract from the destination surface?

It depends on the type of target surface. When using a 32bit RGBA surface, then the values will be automatically clamped to the [0..1] range. When using a floating point surface, then negative values are possible, and will in fact subtract from the target surface.

Also, keep in mind that the blending stage has been extended multiple times in the past. This additional functionality is exposed as extensions. EXT_blend_subtract, for example. Or EXT_blend_func_separate, NV_blend_square, or EXT_blend_equation_separate. Maybe one of these will help you achieve the results you need (support for these extensions will vary amongst chipsets, though, so check your extension string).

Share this post


Link to post
Share on other sites
clapton    234
Quote:
Original post by rabidh
Can I still do glBlendFunc(GL_ONE,GL_ONE) while using pixel shaders?

Of course you can. Blending appears after pixel shading is done. Fell free to use all blending functions you like.

Quote:
What happens if I don't clamp the outputs of the pixel shader so I can get negative values out? (is it even possible?)

Most probably GPU will clamp it to the valid range [0; 1].

Cheers!

Share this post


Link to post
Share on other sites
rabidh    122
Thanks for the replies everyone - that looks really promising.

Do I have to do anything special to allow negative values into the blendfunc when writing to a floating point buffer, or does it all just happen automatically?

From what I could gather NVidia card generally won't blend to 32 bit floating point buffers, but will to 16 bit ones. Whats the state of play for ATI? (specifically the 9600 - i have a feeling I may need an upgrade!)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Similar Content

    • By Zaphyk
      I am developing my engine using the OpenGL 3.3 compatibility profile. It runs as expected on my NVIDIA card and on my Intel Card however when I tried it on an AMD setup it ran 3 times worse than on the other setups. Could this be a AMD driver thing or is this probably a problem with my OGL code? Could a different code standard create such bad performance?
    • By Kjell Andersson
      I'm trying to get some legacy OpenGL code to run with a shader pipeline,
      The legacy code uses glVertexPointer(), glColorPointer(), glNormalPointer() and glTexCoordPointer() to supply the vertex information.
      I know that it should be using setVertexAttribPointer() etc to clearly define the layout but that is not an option right now since the legacy code can't be modified to that extent.
      I've got a version 330 vertex shader to somewhat work:
      #version 330 uniform mat4 osg_ModelViewProjectionMatrix; uniform mat4 osg_ModelViewMatrix; layout(location = 0) in vec4 Vertex; layout(location = 2) in vec4 Normal; // Velocity layout(location = 3) in vec3 TexCoord; // TODO: is this the right layout location? out VertexData { vec4 color; vec3 velocity; float size; } VertexOut; void main(void) { vec4 p0 = Vertex; vec4 p1 = Vertex + vec4(Normal.x, Normal.y, Normal.z, 0.0f); vec3 velocity = (osg_ModelViewProjectionMatrix * p1 - osg_ModelViewProjectionMatrix * p0).xyz; VertexOut.velocity = velocity; VertexOut.size = TexCoord.y; gl_Position = osg_ModelViewMatrix * Vertex; } What works is the Vertex and Normal information that the legacy C++ OpenGL code seem to provide in layout location 0 and 2. This is fine.
      What I'm not getting to work is the TexCoord information that is supplied by a glTexCoordPointer() call in C++.
      Question:
      What layout location is the old standard pipeline using for glTexCoordPointer()? Or is this undefined?
       
      Side note: I'm trying to get an OpenSceneGraph 3.4.0 particle system to use custom vertex, geometry and fragment shaders for rendering the particles.
    • By markshaw001
      Hi i am new to this forum  i wanted to ask for help from all of you i want to generate real time terrain using a 32 bit heightmap i am good at c++ and have started learning Opengl as i am very interested in making landscapes in opengl i have looked around the internet for help about this topic but i am not getting the hang of the concepts and what they are doing can some here suggests me some good resources for making terrain engine please for example like tutorials,books etc so that i can understand the whole concept of terrain generation.
       
    • By KarimIO
      Hey guys. I'm trying to get my application to work on my Nvidia GTX 970 desktop. It currently works on my Intel HD 3000 laptop, but on the desktop, every bind textures specifically from framebuffers, I get half a second of lag. This is done 4 times as I have three RGBA textures and one depth 32F buffer. I tried to use debugging software for the first time - RenderDoc only shows SwapBuffers() and no OGL calls, while Nvidia Nsight crashes upon execution, so neither are helpful. Without binding it runs regularly. This does not happen with non-framebuffer binds.
      GLFramebuffer::GLFramebuffer(FramebufferCreateInfo createInfo) { glGenFramebuffers(1, &fbo); glBindFramebuffer(GL_FRAMEBUFFER, fbo); textures = new GLuint[createInfo.numColorTargets]; glGenTextures(createInfo.numColorTargets, textures); GLenum *DrawBuffers = new GLenum[createInfo.numColorTargets]; for (uint32_t i = 0; i < createInfo.numColorTargets; i++) { glBindTexture(GL_TEXTURE_2D, textures[i]); GLint internalFormat; GLenum format; TranslateFormats(createInfo.colorFormats[i], format, internalFormat); // returns GL_RGBA and GL_RGBA glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, createInfo.width, createInfo.height, 0, format, GL_FLOAT, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); DrawBuffers[i] = GL_COLOR_ATTACHMENT0 + i; glBindTexture(GL_TEXTURE_2D, 0); glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, textures[i], 0); } if (createInfo.depthFormat != FORMAT_DEPTH_NONE) { GLenum depthFormat; switch (createInfo.depthFormat) { case FORMAT_DEPTH_16: depthFormat = GL_DEPTH_COMPONENT16; break; case FORMAT_DEPTH_24: depthFormat = GL_DEPTH_COMPONENT24; break; case FORMAT_DEPTH_32: depthFormat = GL_DEPTH_COMPONENT32; break; case FORMAT_DEPTH_24_STENCIL_8: depthFormat = GL_DEPTH24_STENCIL8; break; case FORMAT_DEPTH_32_STENCIL_8: depthFormat = GL_DEPTH32F_STENCIL8; break; } glGenTextures(1, &depthrenderbuffer); glBindTexture(GL_TEXTURE_2D, depthrenderbuffer); glTexImage2D(GL_TEXTURE_2D, 0, depthFormat, createInfo.width, createInfo.height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glBindTexture(GL_TEXTURE_2D, 0); glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthrenderbuffer, 0); } if (createInfo.numColorTargets > 0) glDrawBuffers(createInfo.numColorTargets, DrawBuffers); else glDrawBuffer(GL_NONE); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) std::cout << "Framebuffer Incomplete\n"; glBindFramebuffer(GL_FRAMEBUFFER, 0); width = createInfo.width; height = createInfo.height; } // ... // FBO Creation FramebufferCreateInfo gbufferCI; gbufferCI.colorFormats = gbufferCFs.data(); gbufferCI.depthFormat = FORMAT_DEPTH_32; gbufferCI.numColorTargets = gbufferCFs.size(); gbufferCI.width = engine.settings.resolutionX; gbufferCI.height = engine.settings.resolutionY; gbufferCI.renderPass = nullptr; gbuffer = graphicsWrapper->CreateFramebuffer(gbufferCI); // Bind glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); // Draw here... // Bind to textures glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, textures[0]); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, textures[1]); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, textures[2]); glActiveTexture(GL_TEXTURE3); glBindTexture(GL_TEXTURE_2D, depthrenderbuffer); Here is an extract of my code. I can't think of anything else to include. I've really been butting my head into a wall trying to think of a reason but I can think of none and all my research yields nothing. Thanks in advance!
    • By Adrianensis
      Hi everyone, I've shared my 2D Game Engine source code. It's the result of 4 years working on it (and I still continue improving features ) and I want to share with the community. You can see some videos on youtube and some demo gifs on my twitter account.
      This Engine has been developed as End-of-Degree Project and it is coded in Javascript, WebGL and GLSL. The engine is written from scratch.
      This is not a professional engine but it's for learning purposes, so anyone can review the code an learn basis about graphics, physics or game engine architecture. Source code on this GitHub repository.
      I'm available for a good conversation about Game Engine / Graphics Programming
  • Popular Now