3

I'm trying to make bilinear color interpolation on a quad, i succeeded with the help of my previous question on here, but it has bad performance because its requires me to repeat glBegin() and glEnd() and 4 times glUniform() before glBegin().

The question is: is it anyhow possible to apply bilinear color interpolation on a quad like this:

glBegin(GL_QUADS);
    glColor4f(...); glVertexAttrib2f(uv, 0, 0); glTexCoord2f(...); glVertex3f(...);
    glColor4f(...); glVertexAttrib2f(uv, 1, 0); glTexCoord2f(...); glVertex3f(...);
    glColor4f(...); glVertexAttrib2f(uv, 1, 1); glTexCoord2f(...); glVertex3f(...);
    glColor4f(...); glVertexAttrib2f(uv, 0, 1); glTexCoord2f(...); glVertex3f(...);
... // here can be any amount of quads without repeating glBegin()/glEnd()
glEnd();

To do this, i think i should somehow access the nearby vertex colors, but how? Or is there any other solutions for this?

I need this to work this way so i can easily switch between different interpolation shaders.

Any other solution that works with one glBegin() command is good too, but sending all corner colors per vertex isnt acceptable, unless thats the only solution here?

Edit: The example code uses immediate mode for clarity only. Even with vertex arrays/buffers the problem would be the same: i would have to split the rendering calls into 4 vertices chunks, which causes the whole speed drop here!

Rookie
  • 1,242
  • 3
  • 17
  • 22
  • 1
    Why don't you use vertex arrays/vertex buffer objects? I think the function call overhead causes your performance issues. – datenwolf Mar 22 '11 at 19:23
  • 1
    Practically everything you are doing here is deprecated OpenGL. You should look into Vertex Buffers, as the person above has said – Adam Casey Mar 22 '11 at 19:26
  • becase even with vertex arrays or buffers, the performance will drop because i would have to split the rendering into 4 vertice pieces and then again call glUniform(). also i am using display lists so it doesnt really matter here, it will be converted to VBO anyways. You are missing the point here, its not slow because im using immediate mode, because i already tested with the same rendering code, except by using glUniform() and repeating glBegin() billion times, so the only thing that changes is those two, and thats what im trying to optimize off here, thats the only thing i need to optimize. – Rookie Mar 22 '11 at 20:47

4 Answers4

3

Long story short: You cannot do this with a vertex shader.

The interpolator (or rasterizer) is one of the components of the graphics pipeline that is not programmable. Given how the graphics pipe works, neither a vertex shader nor a fragment shader are allowed access to anything but their vertex (or fragment, respectively), for reasons of speed, simplicity, and parallelism.

The workaround is to use a texture lookup, which has already been noted in previous answers.

In newer versions of OpenGL (3.0 and up I believe?) there is now the concept of a geometry shader. Geometry shaders are more complicated to implement than the relatively simple vertex and fragment shaders, but geometry shaders are given topological information. That is, they execute on a primitive (triangle, line, quad, etc) rather than a single point. With that information, they could create additional geometry in order to resolve your alternate color interpolation method.

However, that's far more complicated than necessary. I'd stick with a 4 texel texture map and implement your logic in your fragment lookup.

Nick Gebbie
  • 518
  • 2
  • 6
2

Under the hood, OpenGL (and all the hardware that it drives) will do everything as triangles, so if you choose to blend colors via vertex interpolation, it will be triangular interpolation because the hardware doesn't work any other way.

If you want "quad" interpolation, you should put your colors into a texture, because in hardware a texture is always "quad" shaped.

  • yeah, i thought of making texture for those colors, but is that really the only way? – Rookie Mar 23 '11 at 01:41
  • You don't need to use a texture, but you do need to store a [ U, V ] coordinate per vertex and pass it into the pixel shader to do *something* with the [ U, V ] coordinate to produce your gradient. A texture is nice because it can store lots of different gradients at once, and you can even paint things on top of the gradient. If you need only one gradient, and you don't care about painting, you can store the gradient as four pixel shader constants, and in the pixel shader do a "quad" interpolation among them using the [ U, V ] coordinate passed down from the vertex shader. –  Mar 25 '11 at 17:13
0

Linear interpolation with colours specified per vertex can be set up efficiently using glColorPointer. Similarly you should use glTexCoordPointer/glVertexAttribPointer/glVertexPointer to replace all those individual per-vertex calls with a single call referencing the data in an array. Then render all your quads with a single (or at most a handful of) glDrawArrays or glDrawElements call. You'll see a huge improvement from this even without VBOs (which just change where the arrays are stored).

You mention you want to change shaders (between ShaderA and ShaderB say) on a quad by quad basis. You should either:

  • Arrange things so you can batch all of the ShaderA quads together and all the ShaderB quads together and render all of each together with a single call. Changing shader is generally quite expensive so you want to minimise the number of changes.

or

  • Implement all the different shader logic you want in a single "unified" shader, but selected by another vertex attribute which selects between the different codepaths. Whether this is anywhere near as efficient as the batching approach (which is preferable) depends on whether or not each "tile" of SIMD shaders tends to have to run a mixture of paths or just one.
timday
  • 24,582
  • 12
  • 83
  • 135
  • how does this glColorPointer() solve anything? the problem is the fragment shader needing to know all 4 corner colors for each vertex. at least thats what i think, but if you have better algorithm than: `mix(mix(color0, color1, uv.x), mix(color2, color3, uv.x), uv.y);` this is fragment shader, and it needs all those 4 colors to calculate the bilinear interpolation, can you advice how to go around this and not need to send all 4 colors there? because uniforms cannot be used here with vertex arrays, unless you want every vertex have the same corner colors. – Rookie Mar 23 '11 at 01:37
  • as i said before, the only solution i can think of is to send all 4 corner colors PER VERTEX, but that will take lots of bandwidth per vertex... and it is very redundant data which makes me personally sick. so im just hoping to see if this can be solved with some fragment shader magic or something. – Rookie Mar 23 '11 at 01:39
  • bmcnett suggested texture for the colors, i think i go with that, if nothing else comes up... – Rookie Mar 23 '11 at 01:44
  • Normally you'd let the hardware do the interpolation for you. It can interpolate colours just as well as it can interpolate texture coordinates. Ah OK I get what you're trying to do now... bilinear interpolation across quads is different from linear interpolation across triangles... hmmm tricky. Personally I'd be trying to get whatever you're trying to do to to work with triangles rather than needing quads... – timday Mar 23 '11 at 23:49
0

If you really think it's the number of draws that cause your performance drop, you can try to use Instancing (Using glDrawArrayInstanced+glVertexAttribDivisor), available in GL 3.1 core.

An alternative might be point sprites, depending on your usage model (mostly, maximum size of your quads, and are they always perpendicular to the view). That's available since GL 2.0 core.

Bahbar
  • 17,760
  • 43
  • 62