# How do I add a border to rectangles using a shader?

I want to draw some rectangles with a border. Currently I render the fill with glDrawArrays(Triangles, ...) and the border with glDrawArrays(LineLoop, ...). Is there a neater way?

I'm targeting OpenGL 3.3.

# Details

Illustrative excerpt:

// vertexBuffer   -> VertexAttribArray(0)
// colorBuffer    -> VertexAttribArray(1)
// texCoordBuffer -> VertexAttribArray(2)

GL.DrawArrays(PrimitiveType.Triangles, 0, 12); // 2 test rectangles

GL.DisableVertexAttribArray(1); //Disable color array
GL.DisableVertexAttribArray(2); //Disable texture array

GL.DrawArrays(PrimitiveType.LineLoop, 0, 6); // First rectangle border
GL.DrawArrays(PrimitiveType.LineLoop, 6, 6); // Second rectangle border
// Lots of draw calls for every rectangle,
// if there are more than the 2 test rectangles...


This works fine, but I don't like having to draw the additional LineLoops for every rectangle. How can I improve this?

Could I do this elegantly with a shader? Something like this:

// VertexArributes

GL.DrawArrays(Triangles, 0, vertices.Length); // Shader does the border

// DisableVertexAttributes


## Vertex

#version 330 core

layout(location = 0) in vec4 position;
layout(location = 1) in vec4 color;
layout(location = 2) in vec2 texCoord;

uniform mat4 projMatrix;
uniform mat4 worldMatrix;

out vec4 vColor;
out vec2 texCoords[];

void main()
{
gl_Position = projMatrix * worldMatrix * position;
texCoords[0] = texCoord;
vColor = color;
}


## Fragment

#version 330 core

in vec4 vColor;
in vec2 texCoords[];

uniform sampler2D tex;

out vec4 fColor;

void main(void)
{
fColor = texture2D(tex, texCoords[0].st) * vColor;
}


You can use your texture coordinates, and set a uniform that tells you the aspect ratio of your rect, and the desired width of your border. Then, you can just check to see if your pixel falls within the bounds of your rectangle.

(0, 0)                         (1, 0)
-------------------------------------
|  (0.1, 0.1)  Border  (0.9, 0.1)   |
|  -------------------------------  |
|  |                             |  |
|  |                             |  |
. . .
|  |                             |  |
|  |                             |  |
|  -------------------------------  |
|  (0.1, 0.9)          (0.9, 0.9)   |
-------------------------------------
(0, 1)                         (1, 1)


Then in your pixel shader you can just test to see if the pixel is within the border:

uniform float border_width;
uniform float aspect;  // ratio of width to height
varying vec2 texCoord;

main() {
float maxX = 1.0 - border_width;
float minX = border_width;
float maxY = maxX / aspect;
float minY = minX / aspect;

if (texCoord.x < maxX && texCoord.x > minX &&
texCoord.y < maxY && texCoord.y > minY) {
gl_FragColor = **rect color**;
} else {
gl_FragColor = **border color**;
}
}

• aspect and border_width are dependend from the size of the rectangle. So I need to set the values for the uniforms for every rectangle, right? Then I need to call Uniform1 on the CPU instead of DrawArrays... I can't see the advantage of this against my DrawArrays(LineLoops). Or did I understand it wrong? Sep 16 '14 at 9:21
• You seemed like you were worried about the number of draw calls... this reduces that to one. I assumed you still need to position the quad via some sort of uniform in your vertex shader, so setting a few extras for the border width and aspect ratio wouldn't be too big of a problem. Sep 16 '14 at 13:30
• I edited my question to show you my shaders. I simply put my 3 buffers and let DrawArray (and the shaders) doing the rest. I guess I will get no problems because too much draw calls. I think I could realise your idea with another buffer (and location = 4) but I think this would be more work than my current solution. Sep 16 '14 at 15:16
• I'm not sure what you're asking for then? You have a solution but you want one that's .... what? Sep 17 '14 at 15:48
• I guess I should have asked this question before. I have problems with handling my objects. I want to draw the borders without using more CPU code in my drawing loop to keep the loop as clear as possible. The ideal solution would be a shader without any inputs so that I can reduce the code in my loop to a minimum. Sep 17 '14 at 17:02