OpenGL: how to make a perfect rectangular gradient?

I can display a triangular gradient with just one triangle and use glColor for each corner.

But how to make a perfect rectangular gradient? I tried with one quadrant, but the middle will get an ugly seam. I also tried with a 2x2 texture, it was like how it should be done: mix correctly from every angle, but the fetch accuracy of the texture becomes blurry when stretched too much (I started to see pixels larger than 1x1).

Is there a way to calculate this in a shader, maybe?

-

Edit: Here is the problem:

http://img143.imageshack.us/img143/7066/gradients.png

http://img143.imageshack.us/img143/7066/gradients.png http://img143.imageshack.us/img143/7066/gradients.png

+6
c ++ gradient opengl glsl
source share
4 answers

Indeed, the kind of gradient you want uses 4 colors per pixel, where OpenGL usually only interpolates the input signals over the triangles (so there are 3 inputs). Obtaining an ideal gradient is not possible only with standard interpolations.

Now, as you said, a 2x2 texture can do this. If you have seen problems with accuracy, I suggest switching the texture format to something that usually requires more accuracy (for example, floating-point textures).

Finally, as you mentioned in your question, you can solve this with a shader. Let's say you pass an extra per-vertex attribute that matches (u, v) = (0,0) (0,1) (1,0) (1,0), right down to the pixel shader (with the vertex shader just making a pass )

You can do the following in a pixel shader (note that the idea here is sound, but I have not tested the code):

Vertex Shader Fragment:

varying vec2 uv; attribute vec2 uvIn; uv = uvIn; 

Fragment Shader:

 uniform vec3 color0; uniform vec3 color1; varying vec2 uv; // from wikipedia on bilinear interpolation on unit square: // f(x,y) = f(0,0)(1-x)(1-y) + f(1,0)x(1-y) + f(0,1)(1-x)y + f(1,1) xy. // applied here: // gl_FragColor = color0 * ((1-x)*(1-y) + x*y) + color1*(x*(1-y) + (1-x)*y) // gl_FragColor = color0 * (1 - x - y + 2 * x * y) + color1 * (x + y - 2 * x * y) // after simplification: // float temp = (x + y - 2 * x * y); // gl_FragColor = color0 * (1-temp) + color1 * temp; gl_FragColor = mix(color0, color1, uv.u + uv.v - 2 * uv.u * uv.v); 
+7
source share

The problem is that you are using quad. A square is drawn using two triangles, but the triangles are not in the orientation you need.

If I define four vertices in the form:

  • A: bottom left vertex
  • B: bottom right vertex
  • C: upper right vertex
  • D: upper left vertex

I would say that a quad consists of the following triangles:

  • Abd
  • Dbc

Colors assigned to each vertex:

  • A: yellow
  • B: red
  • C: yellow
  • D: red

Bearing in mind the geometry (two triangles), the pixels between D and B are the result of interpolation between red and red: indeed, red!

The solution would be geometry with two triangles, but oriented differently:

  • Abc
  • ACD

But, probably, you will not get the exact gradient, since in the middle of the square you will get full yellow instead of yellow mixed with red. So, I suppose, you can achieve the exact result using 4 triangles (or a triangular fan) in which the centered vertex is the interpolation between yellow and red.


Wooop! Effectively the result is not what I expected. I thought the gradient was created by linear interpolation between the colors, but certainly not (I really need to adjust the color space on the LCD!). In fact, the most scalable solution is rendering using fragmented shaders.

Save the solution proposed by Bahbar. I would advise you to start implementing a through vertex / fragment shader (specifying only the vertices and colors that you should get the previous result); then start playback using the mix function and the coordinate of the texture passed to the vertex shader.

You really need to understand the rendering pipeline with programmable shaders : the vertex shader is called once per vertex, the shader fragment is called once per fragment (without multisampling, the fragment is a pixel, with multisampling, aa pixel consists of many fragments that are interpolated to get the color of the pixels )

The vertex shader accepts input parameters (uniforms and inputs, uniforms are constant for all vertices issued between glBegin / glEnd, inputs are typical for each instance of the vertex shader (4 vertices, 4 instances of the vertex shader).

The fragment shader accepts vertex shaders that produced the fragment as input (due to the rasterization of triangles, lines, and points). In Bahbar's answer, the only way out is the uv variable (common to both shader sources).

In this case, the vertex shader displays the coordinates of the UV vertex texture (transmitted as-are). These UV coordinates are available for each fragment, and they are calculated by interpolating the values โ€‹โ€‹displayed by the vertex shader, depending on the position of the fragment.

When you have these coordinates, you only need two colors: red and yellow in your case (in the answer Bahbar corresponds to the format color0 and color1). Then mix these colors depending on the UV coordinates of a particular fragment. (*)

(*) Here is the power of the shaders: you can specify various interpolation methods by simply changing the source of the shader. Linear, bilinear or spline interpolation is implemented by specifying an additional uniform in the fragment shader.

Good practice!

+7
source share

Do all your vertices have the same depth (Z) and all your triangles are fully displayed on the screen? If so, then you should have no problem getting the โ€œperfectโ€ color gradient over the quad of two triangles with glColor. If not, then it is possible that your OpenGL implementation is bad for colors.

This makes me suspect that you may have a very old or strange OpenGL implementation. I recommend that you tell us which platform you are using and which version of OpenGL you have ??

Without additional information, I recommend that you try writing a shader and not tell OpenGL that you want "color." If possible, say you want texcoord, but treat it like a color anyway. This trick worked in some cases where the color accuracy is too low.

0
source share

Well, I'm not going to give you a specific answer, but you should be able to move forward with that.

First you need to know exactly what you want. You said you can make a triangular gradient. I suspect you mean the code from NeHe: an example image .

Now in our palette there are three primary colors: red, green and blue. What do you think, creating a triangular palette seems logical and easy.

Now, if you want to convert this to a square, you first need to think about what you want it to look like. Where am I going to move the corners? What should be in the middle of the ATV? etc. Once you understand that you have to start coding. I suspect that you immediately started coding without thinking about what exactly you want.

0
source share

All Articles