我正在围绕 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);
最佳答案
正如@Newbie 在评论中发布的那样,
真的。虽然我不熟悉 OpenGL 下的这部分数学,但我相信这是三角形顶点之间颜色插值的隐含结果......我很肯定它被称为“双线性插值”。
那么该怎么做才能解决呢?一种可能性是使用纹理并仅绘制带纹理的四边形(或多个带纹理的四边形)。
但是,在 片段着色器 中生成这样的边框应该很容易。
使用 GLSL 着色器的一个不错的解决方案......
假设您正在绘制一个矩形,其左下角的纹理坐标等于 (0,0),右上角的纹理坐标等于 (1,1)。
然后在片段着色器中以程序方式生成“斜接”看起来像这样,如果我是正确的:
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;
如果到目前为止我是正确的,那么现在对于每个像素,
insets.x
的值都在 [0..1]
范围内确定给定点水平进入边界的深度,
insets.y
具有相似的垂直深度值。左边的竖条有
insets.y == 0,
底部水平条有 insets.x = 0,
,左下角有一对 (insets.x, insets.y) 覆盖从 (0,0) 到 (1,1) 的整个二维范围。为清楚起见,请参见图片:现在我们想要一个转换,对于给定的 (x,y) 对会给我们一个值
[0..1]
决定如何混合背景和前景色。 1 表示 100% 边框,0 表示 0% 边框。这可以通过多种方式完成!该功能应符合要求:
假设这样的功能:
float bias = max(insets.x,insets.y);
它满足这些要求。实际上,我很确定这个函数会给你和上面一样的“锐利”边缘。尝试在纸上计算它以选择左下角矩形内的坐标。
如果我们想在那里有一个平滑的圆形斜接,我们只需要在这里添加另一个函数。我认为这样的事情就足够了:
float bias = min( length(insets) , 1 );
这里的
length()
函数就是 sqrt(insets.x*insets.x + insets.y*insets.y)
。重要的是:这转化为:“我们离边界越远(就欧几里得距离而言),边界应该越明显”,而 min() 只是为了使结果不大于 1 (= 100%)。请注意,我们的原始函数遵循完全相同的定义 - 但距离是根据 Chessboard (Chebyshev) 度量计算的,而不是欧几里得度量。
这意味着使用例如曼哈顿度量,您将拥有第三种可能的斜接形状!它将像这样定义:
float bias = min(insets.x+insets.y, 1);
我预测这个也会有一条可见的“对角线”,但对角线会在另一个方向(
"\"
)。好的,对于其余的代码,当我们有偏差 [0..1] 时,我们只需要混合背景色和前景色:
vec3 finalColor = mix(borderColor, backgroundColor, bias);
gl_FragColor = vec4(finalColor, 1); // return the calculated RGB, and set alpha to 1
}
就是这样!使用 GLSL 和 OpenGL 让生活更简单。希望有帮助!