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

发布于 2024-10-24 13:41:23 字数 252 浏览 3 评论 0原文

我可以只用一个三角形渲染三角形渐变,并对每个角使用 glColor 。

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

也许有某种方法可以在着色器中计算这个值吗?

--

编辑: 图像链接已损坏(已删除)。

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

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?

--

Edit: Link to images were broken(removed).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

辞取 2024-10-31 13:41:23

事实上,您想要的渐变类型依赖于每个像素的 4 种颜色,其中 OpenGL 通常仅在三角形上插入输入(因此 3 个输入)。仅使用标准插值器不可能获得完美的梯度。

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

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

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

顶点着色器片段:

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

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.

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

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

Vertex shader snippet:

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);
书间行客 2024-10-31 13:41:23

问题是因为你使用的是四边形。四边形是使用两个三角形绘制的,但三角形的方向不是您需要的。

如果我将四边形顶点定义为:

  • A:左下顶点
  • B:右下顶点
  • C:右上顶点
  • D:左上顶点

我想说四边形由以下三角形组成:

  • AB D
  • DB C

分配给每个顶点的颜色是:

  • A< /em>:黄色
  • B:红色
  • C:黄色
  • D:红色

请记住几何形状(两个三角形)、<之间的像素em>D 和 B 是红色和红色之间插值的结果:确实是红色!

解决方案是具有两个三角形的几何体,但以不同的方式定向:

  • AB C
  • AC D

但您可能无法获得确切的渐变,因为在中间四边形你会得到全黄色,而不是黄色与红色混合。所以,我想你可以使用 4 个三角形(或三角形扇形)获得精确的结果,其中中心顶点是黄色和红色之间的插值。


呜呜!实际上结果并不是我所期望的。我认为渐变是由颜色之间的线性插值产生的,但肯定不是(我真的需要设置 LCD 颜色空间!)。事实上,最具可扩展性的解决方案是使用片段着色器进行渲染。

保留 Bahbar 提出的解决方案。我建议开始实现直通顶点/片段着色器(仅指定顶点和颜色,您应该得到之前的结果);然后,开始使用 mix 函数和传递给顶点着色器的纹理坐标。

您确实需要了解具有可编程着色器的渲染管道:每个顶点调用一次顶点着色器,每个片段调用一次片段着色器(没有多重采样,一个片段就是一个像素;使用多重采样,一个像素是由许多片段组成,这些片段被插值以获得像素颜色)。

顶点着色器采用输入参数(制服和输入;制服对于在 glBegin/glEnd 之间发出的所有顶点而言是恒定的;输入是每个顶点着色器实例的特征(4 个顶点,4 个顶点着色器实例)。

片段着色器将顶点作为输入产生片段的着色器输出(由于三角形、线和点的光栅化)在 Bahbar 答案中,唯一的输出是 uv 变量(两个着色器源共有)。 在您的情况下,顶点着色器输出顶点纹理坐标 UV(“按原样”传递

)。这些 UV 坐标可用于每个片段,并且通过根据片段对顶点着色器输出的值进行插值来计算。 获得这些坐标

后,您只需要两种颜色:您的情况下的红色和黄色(在 Bahbar 中,答案对应于 color0color1 em> 制服)然后,根据特定片段的 UV 坐标混合这些颜色。 (*)

(*) 这就是着色器的强大之处:您可以通过简单地修改着色器源来指定不同的插值方法。线性、双线性或样条插值是通过为片段着色器指定附加制服来实现的。

好习惯!

The problem is because you use a quad. The quad is drawn using two triangles, but the triangles are not in the orientation that you need.

If I define the quad vertices as:

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

I would say that the quad is composed by the following triangles:

  • A B D
  • D B C

The colors assigned to each vertex are:

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

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

The solution would be the a geometry with two triangles, but orientated in a different way:

  • A B C
  • A C D

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


Wooop! Effetively the result is not what I was expecting. I thought the gradient was produced by linear interpolation between colors, but surely is not (I really need to setup the LCD color space!). Indeed, the most scalable solution is rendering using fragment shaders.

Keep the solution proposed by Bahbar. I would advice to start the implementation of a pass-through vertex/fragment shader (specifying only vertices and colors you should get the previous result); then, start playing with the mix function and the texture coordinate passed to the vertex shader.

You really need to understand the rendering pipeline with programmable shaders: vertex shader is called once per vertex, fragment shader is called once per fragment (without multisampling, a fragment is a pixel; with multisampling, a a pixel is composed by a many fragments which are interpolated to get the pixel color).

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

A fragment shader takes as input the vertex shader outputs which has produced the fragment (due the rasterization of triangles, lines and points). In the Bahbar answer the only output is the uv variable (common to both shader sources).

In you case, the vertex shader outputs the vertex texture coordinates UV (passed "as-are"). These UV coordinates are available for each fragment, and they are computed by interpolating the values outputted by the vertex shader depending on the fragment position.

Once you have those coordinates, you only need two colors: the red and the yellow in your case (in Bahbar answer corresponds to color0 and color1 uniforms). Then, mix those colors depending on the UV coordinates of the specific fragment. (*)

(*) Here is the power of shaders: you can specify different interpolation methods by simply modifying the shader source. Linear, Bilinear or Spline interpolation are implemented by specifying additional uniforms to the fragment shader.

Good practice!

池木 2024-10-31 13:41:23

所有顶点是否具有相同的深度 (Z) 值,并且所有三角形都完全在屏幕上吗?如果是这样,那么使用 glColor 在由两个三角形组成的四边形上获得“完美”颜色渐变应该没有问题。如果不是,那么您的 OpenGL 实现可能无法很好地处理颜色。

这让我怀疑您可能有一个非常古老或奇怪的 OpenGL 实现。我建议您告诉我们您正在使用什么平台,以及您拥有什么版本的 OpenGL...?

如果没有更多信息,我建议您尝试编写着色器,并避免告诉 OpenGL 您需要“颜色”。如果可能的话,告诉它你想要一个“texcoord”,但无论如何要把它当作一种颜色。此技巧在某些颜色准确度太低的情况下有效。

Do all of your vertices have the same depth (Z) value, and are all of your triangles completely on-screen? If so, then you should have no problem getting a "perfect" color gradient over a quad made from two triangles with glColor. If not, then it's possible that your OpenGL implementation treats colors poorly.

This leads me to suspect that you may have a very old or strange OpenGL implementation. I recommend that you tell us what platform you're using, and what version of OpenGL you have...?

Without any more information, I recommend you attempt writing a shader, and avoid telling OpenGL that you want a "color." If possible, tell it that you want a "texcoord" but treat it like a color anyway. This trick has worked in some cases where color accuracy is too low.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文