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

Posted

技术标签:

【中文标题】OpenGL:如何渲染完美的矩形渐变?【英文标题】:OpenGL: How to render perfect rectangular gradient? 【发布时间】:2011-03-19 00:56:41 【问题描述】:

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

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

也许有一些方法可以在着色器中计算这个?

--

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

【问题讨论】:

您使用的是什么版本的 OpenGL?前向还是兼容上下文? 我希望它与尽可能低的版本兼容。 这看起来像两个三角形而不是一个四边形(中心插在两个角之间,而不是全部四个。 @ben,是的...四边形由两个三角形组成,我怎样才能在opengl中没有三角形...?顺便说一句,我在那里使用了 GL_QUADS。 @Ben Voigt,现在看看我的新图像,它根本不起作用。 【参考方案1】:

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

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

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

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

顶点着色器sn-p:

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

【讨论】:

不应该是gl_FragColor = mix(...) 吗?那些float3和float2也不应该是vec3vec2吗?你能展示我如何传递额外的属性,以及我需要在顶点着色器中进行哪些更改吗?我不太关注这个... @Rookie:确实。我玩的语言太多了……修复了你提到的那些,并在顶点着色器上添加​​了一些。你到底不遵循什么?我们的想法是在着色器中实现您所追求的双线性过滤,使用我们传入的 uv 来确定感兴趣的像素在正方形中的位置。 如果我理解正确,我必须使用 glTexCoord2f() 和 glColor3f() 和 glVertex2f() 吗?但是代码的哪一部分知道 uvIn 意味着纹理坐标……那部分我不明白 我也不明白如何用顶点着色器(或其他地方?)的相应值填充 color0 和 color1 哦,我明白了.. 好吧,您应该告诉您的代码在您的答案中没有计算完美的梯度,(以防其他人想知道为什么它不起作用)。那我就自己算算了……(真的看不懂那个公式XD)【参考方案2】:

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

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

A:左下顶点 B:右下顶点 C:右上角的顶点 D:左上顶点

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

A B D DB C

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

A:黄色 B:红色 C:黄色 D:红色

记住几何形状(两个三角形),DB 之间的像素是红色和红色之间插值的结果:确实,红色!

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

A B C A C D

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


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

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

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

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

片段着色器将产生片段的顶点着色器输出作为输入(由于三角形、线和点的光栅化)。在 Bahbar 答案中,唯一的输出是 uv 变量(两个着色器源通用)。

在您的情况下,顶点着色器输出顶点纹理坐标 UV(“按原样”传递)。这些 UV 坐标可用于每个片段,它们是通过根据片段位置对顶点着色器输出的值进行插值来计算的。

一旦你有了这些坐标,你只需要两种颜色:红色和黄色(在 Bahbar 中,答案对应于 color0color1 制服)。然后,根据特定片段的 UV 坐标混合这些颜色。 (*)

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

好习惯!

【讨论】:

我尝试了任意数量的三角形,即使是您建议的“4 个角和 1 个中间额外点”,但结果始终与使用两个三角形相同(除了形状略有变化,但它仍然显示“接缝”,我仍然不会像我想要的那样完美地得到它)。你能帮我用着色器做这个吗?我在想我可以自己计算渐变,但我遇到的问题是:如何发送片段着色器的 4 个角颜色值(如果这甚至是我应该怎么做?,所以我也需要这方面的建议。 ..) 添加了您建议的版本示例,它仍然无法正常工作,请查看图片(或我留下的链接,以防图片无法正常工作)【参考方案3】:

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

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

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

【讨论】:

是的,它是 2d。是的,它们完全在屏幕上(为什么这很重要?虽然,如果三角形部分超出屏幕,我看到 opengl 渲染在没有驱动程序的情况下变得丑陋,但我为使用 opengl 驱动程序的人这样做) hmm,通过将texcoord视为颜色,您的意思是我会使用它的数值通过使用4个角颜色来混合颜色?你能给我一些建议我该怎么做吗?我不知道,我只知道如何获取纹理颜色值或 glColor 值...但我不知道如何将 4 个颜色值发送到片段着色器(我也不确定这是否应该这样做) . 我现在添加了问题的示例图片。 @Rookie:我没有看到任何图像。您没有足够的代表将它们直接添加到您的问题中,因此请粘贴链接,我们中的一个可以添加图像。 啊,它不应该显示给我,如果没有其他人可以看到它...现在添加一个链接。

以上是关于OpenGL:如何渲染完美的矩形渐变?的主要内容,如果未能解决你的问题,请参考以下文章

如何以与 OpenGL 中的点精灵相同的方式渲染屏幕对齐的矩形

C++ OpenGL渲染两个不同颜色的矩形

使用 OpenGL 进行像素完美的 2D 渲染

用 D 语言用 OpenGL 3 渲染一个简单的矩形

OpenGL 渲染到 Framebuffer 会产生一个白色的矩形纹理

在opengl矩形上使用着色器会导致它消失[关闭]