OpenGL中的渐变"斜接"在连接处显示接缝

Ben*_*tto 7 opengl gradient

我正在围绕GL中的一些2D工作做一些非常基本的实验.我想在矩形区域周围画一个"相框".我希望框架一直有一致的渐变,所以我用几何形状构造它,看起来像四个四边形,框架的每一边一个,逐渐变细以形成有效地具有斜接连接的梯形.

顶部坐标在"内部"和"外部"矩形上是相同的,并且所有内部和外部的颜色都是相同的,所以我希望看到边缘处的完美混合.

但请注意下图中的连接处角落似乎有一个"接缝",它应该比它应该更轻.

我觉得我在数学中缺少概念性的东西来解释这一点.这个神器是否是梯度斜率的结果?如果我将所有颜色改为不透明蓝色(比方说),我会得到一个完美的蓝色实心框架.

更新:下面添加的代码.对不起有点啰嗦.使用2个三角形风扇作为梯形而不是四边形.

谢谢!

替代文字

glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);

// Prep the color array. This is the same for all trapezoids.
// 4 verts * 4 components/color = 16 values. 
GLfloat colors[16];
colors[0] = 0.0;
colors[1] = 0.0;
colors[2] = 1.0;
colors[3] = 1.0;
colors[4] = 0.0;
colors[5] = 0.0;
colors[6] = 1.0;
colors[7] = 1.0;

colors[8] = 1.0;
colors[9] = 1.0;
colors[10] = 1.0;
colors[11] = 1.0;
colors[12] = 1.0;
colors[13] = 1.0;
colors[14] = 1.0;
colors[15] = 1.0;

// Draw the trapezoidal frame areas. Each one is two triangle fans.
// Fan of 2 triangles = 4 verts = 8 values
GLfloat vertices[8];

float insetOffset = 100;
float frameMaxDimension = 1000;

// Bottom 
vertices[0] = 0;
vertices[1] = 0;
vertices[2] = frameMaxDimension;
vertices[3] = 0;
vertices[4] = frameMaxDimension - insetOffset;
vertices[5] = 0 + insetOffset;
vertices[6] = 0 + insetOffset;
vertices[7] = 0 + insetOffset;
glVertexPointer(2, GL_FLOAT , 0, vertices); 
glColorPointer(4, GL_FLOAT, 0, colors);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);    

// Left
vertices[0] = 0;
vertices[1] = frameMaxDimension;
vertices[2] = 0;
vertices[3] = 0;
vertices[4] = 0 + insetOffset;
vertices[5] = 0 + insetOffset;
vertices[6] = 0 + insetOffset;
vertices[7] = frameMaxDimension - inset;    
glVertexPointer(2, GL_FLOAT , 0, vertices); 
glColorPointer(4, GL_FLOAT, 0, colors);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);    

/* top & right would be as expected... */

glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
Run Code Online (Sandbox Code Playgroud)

Kos*_*Kos 6

正如@Newbie在评论中发表的那样,

@quixoto:在Paint程序中打开你的图像,点击接缝处某处的填充工具,你会看到它在那里形成90度角线...意味着只有1种颜色,在"接缝"中的任何地方都没有亮度.它只是一种幻觉.

真正.虽然我不熟悉OpenGL下的这部分数学,但我相信这是如何执行三角形顶点之间颜色插值的隐含结果......我很肯定它被称为"双线性插值".

那么该怎么做才能解决这个问题?一种可能性是使用纹理并且只绘制纹理四边形(或几个纹理四边形).

但是,在片段着色器中生成这样的边框应该很容易.

使用GLSL着色器的一个很好的解决方案......


假设您正在绘制一个矩形,其左下角的纹理坐标等于(0,0),右上角为(1,1).

然后在片段着色器中以程序方式生成"miter"看起来像这样,如果我是正确的:

varying vec2 coord;

uniform vec2 insetWidth; // width of the border in %, max would be 0.5

void main() {

    vec3 borderColor = vec3(0,0,1);
    vec3 backgroundColor = vec3(1,1,1); 

    // x and y inset, 0..1, 1 means border, 0 means centre

    vec2 insets = max(-coord + insetWidth, vec2(0,0)) / insetWidth;
Run Code Online (Sandbox Code Playgroud)

如果到目前为止我是正确的,那么现在对于每个像素,该值的insets.x值都在该范围内[0..1]

确定给定点水平进入边界的insets.y深度,并具有相似的垂直深度值.

左垂直条insets.y == 0, 的底部水平条有insets.x = 0,,左下角有一对(insets.x,insets.y),覆盖从(0,0)到(1,1)的整个2D范围.为清晰起见,请参见图片:

插图

现在我们想要一个转换,对于给定的(x,y)对,我们将给出一个[0..1]确定如何混合背景和前景色的值.1表示100%边框,0表示0%边框.这可以通过几种方式完成!

该功能应符合要求:

  • 如果x == 0且y == 0,则为0
  • 1如果x == 1或y == 1
  • 两者之间的平滑值.

假设这样的功能:

float bias = max(insets.x,insets.y);
Run Code Online (Sandbox Code Playgroud)

它满足了这些要求.实际上,我很确定这个功能会给你带来与上面相同的"锐利"优势.尝试在纸上计算它,以选择左下方矩形内的坐标.

如果我们想在那里有一个光滑的圆形斜角,我们在这里只需要另一个功能.我认为这样的事情就足够了:

float bias = min( length(insets) , 1 );
Run Code Online (Sandbox Code Playgroud)

length()这里的功能就是sqrt(insets.x*insets.x + insets.y*insets.y).重要的是:这意味着:"距离边界越远(就欧几里德距离而言,边界应该越明显",而min()只是使结果不大于1(= 100%).

请注意,我们的原始函数遵循完全相同的定义 - 但距离是根据Chessboard(Chebyshev)度量而非欧几里德度量来计算的.

这意味着,例如,使用曼哈顿度量,您将拥有第三种可能的斜接形状!它的定义如下:

float bias = min(insets.x+insets.y, 1);
Run Code Online (Sandbox Code Playgroud)

我预测这个也会有一条可见的"对角线",但对角线会在另一个方向("\").

好的,所以对于剩下的代码,当我们有偏差[0..1]时,我们只需要混合背景和前景色:

    vec3 finalColor = mix(borderColor, backgroundColor, bias);
    gl_FragColor = vec4(finalColor, 1); // return the calculated RGB, and set alpha to 1 
}
Run Code Online (Sandbox Code Playgroud)

就是这样!将GLSL与OpenGL结合使用可以简化生活.希望有所帮助!