OpenGL:如何渲染完美的矩形渐变?

2021-12-19 00:00:00 opengl gradient c++ glsl

我可以只用一个三角形并为每个角使用 glColor 来渲染三角形渐变.

I can render triangular gradient with simply just one triangle and using glColor for each corner.

但是如何渲染完美的矩形渐变?我试过一个四边形,但中间会出现难看的接缝.我也尝试过 2x2 大小的纹理,好像应该这样做:从每个角落适当混合,但是当拉伸太多时纹理采样精度变得不精确(我开始看到大于 1x1 大小的像素).

But how to render perfect rectangular gradient? I tried with one quad, but the middle will get ugly seam. I also tried with texture of 2x2 size, it was like it should be done: proper blending from each corner, but the texture sampling precision becomes unprecise when stretched too much (i started to see pixels bigger than 1x1 size).

是否有某种方法可以在着色器中计算此值?

Is there some way of calculating this in a shader perhaps?

--

图像链接已损坏(已删除).

Link to images were broken(removed).

推荐答案

确实,您想要的渐变类型依赖于每个像素的 4 种颜色,而 OpenGL 通常只在三角形上插入输入(因此是 3 个输入).仅使用标准插值法无法获得完美的梯度.

Indeed, the kind of gradient you want relies on 4 colors at each pixel, where OpenGL typically only interpolates input over triangles (so 3 inputs). Getting the perfect gradient is not possible just with the standard interpolants.

现在,正如您提到的,2x2 纹理可以做到.如果您确实看到精度问题,我建议将纹理的格式切换为通常需要更高精度的格式(例如浮动纹理).

Now, as you mentioned, a 2x2 texture can do it. If you did see precision issues, I suggest switching the format of the texture to something that typically requires more precision (like a float texture).

最后,正如您在问题中也提到的,您可以使用着色器解决此问题.假设您将对应于 (u,v) = (0,0) (0,1) (1,0) (1,0) 的额外属性 per-vertex 一直传递到像素着色器(使用顶点着色器只是做一个传递).

Last, and as you mentioned also in your question, you can solve this with a shader. Say you pass an extra attribute per-vertex that corresponds to (u,v) = (0,0) (0,1) (1,0) (1,0) all the way to the pixel shader (with the vertex shader just doing a pass-through).

您可以在像素着色器中执行以下操作(注意,这里的想法是合理的,但我没有测试代码):

You can do the following in the pixel shader (note, the idea here is sound, but I did not test the code):

顶点着色器片段:

varying vec2 uv;
attribute vec2 uvIn;

uv = uvIn;

片段着色器:

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

相关文章