Ok, as promised I've cobbled together something that appears to work and have tried to document.
The testbed for the shaders...
#include <SFML/Graphics.hpp>
#include <cmath>
int main()
{
// Create the main window
sf::RenderWindow window(sf::VideoMode(800, 600), "Glowing Line Shader");
window.setVerticalSyncEnabled(true);
// Slow down the action since I'm tossing out random lines per frame below
window.setFramerateLimit(1);
// Create a new shader object
sf::Shader line_shader;
// Set how thick we need the lines
float thickness = 48.0;
// Create a container to store the points in for the shader
sf::VertexArray line_points;
// Load the vertex shader and also fragment shader
line_shader.loadFromFile("point.vert", "glow.frag");
// This shader is for Quads, the lines are actually chubby rectangles
line_points.setPrimitiveType(sf::Quads);
// The render state will hold blending type and shader
sf::RenderStates render_attributes;
// Set the attributes in our sf::RenderStates object
render_attributes.shader = &line_shader;
// For a nice healthy glow we use additive blending
render_attributes.blendMode = sf::BlendAdd;
// Start the program loop
while (window.isOpen())
{
// Process events
sf::Event event;
while (window.pollEvent(event))
{
// Close window: exit
if (event.type == sf::Event::Closed)
window.close();
if (event.type == sf::Event::KeyPressed)
{
switch (event.key.code)
{
// Escape key: exit
case sf::Keyboard::Escape:
window.close();
break;
default:
break;
}
}
}
// Clear the window
window.clear(sf::Color(0, 0, 0, 255));
// 30 chubby glowers (recall I've forced the FPS speed to be slow above)
for(int i = 0; i < 30; ++i)
{
// line endpoints/verts , this could be moved to GPU...maybe?
// Or Geometry shader once they become more common with drivers...
float x = static_cast<float>(rand() % 800);
float y = static_cast<float>(rand() % 600);
float x2 = static_cast<float>(rand() % 800);
float y2 = static_cast<float>(rand() % 600);
float sub_angle = (2.0 * M_PI) / 8.0;
float theta = atan2(y2 - y, x2 - x);
float theta2 = atan2(y - y2, x - x2);
float radius = .5 * thickness * sqrt(2.0);
// Make a fat rotated rectangle of points this is HORRID
float xx = x + cos(theta2 + sub_angle) * radius;
float yy = y + sin(theta2 + sub_angle) * radius;
float xx2 = x + cos(theta2 - sub_angle) * radius;
float yy2 = y + sin(theta2 - sub_angle) * radius;
float xx3 = x2 + cos(theta - sub_angle) * radius;
float yy3 = y2 + sin(theta - sub_angle) * radius;
float xx4 = x2 + cos(theta + sub_angle) * radius;
float yy4 = y2 + sin(theta + sub_angle) * radius;
// LOL FUN Kolrs
sf::Color col_a = sf::Color(rand() % 255, rand() % 255, rand() % 255);
sf::Color col_b = sf::Color(rand() % 255, rand() % 255, rand() % 255);
// Put verticies into the line_points buffer
line_points.append(sf::Vertex(sf::Vector2f(xx2, yy2), col_a));
line_points.append(sf::Vertex(sf::Vector2f(xx, yy), col_a));
line_points.append(sf::Vertex(sf::Vector2f(xx3, yy3), col_b));
line_points.append(sf::Vertex(sf::Vector2f(xx4, yy4), col_b));
// Shaders use text strings to set their parameters...strange
line_shader.setParameter("line_width", thickness);
// SFML does not share OpenGL's coord system now, subtract y pos from 600
line_shader.setParameter("start", sf::Vector2f(x, 600 - y));
line_shader.setParameter("end", sf::Vector2f(x2, 600 - y2));
window.draw(line_points, render_attributes);
line_points.clear();
}
// Finally, display the rendered frame on screen
window.display();
}
return EXIT_SUCCESS;
}
glow.frag
uniform float line_width;
uniform vec2 start;
uniform vec2 end;
float point_distance(vec2 test)
{
float A = test.x - start.x;
float B = test.y - start.y;
float C = end.x - start.x;
float D = end.y - start.y;
float dot_p = A * C + B * D;
float len_sq = C * C + D * D;
float param = dot_p / len_sq;
float xx = 0.0;
float yy = 0.0;
if (param < 0.0 || (start.x == end.x && start.y == end.y))
{
xx = start.x;
yy = start.y;
}
else if (param > 1.0)
{
xx = end.x;
yy = end.y;
}
else
{
xx = start.x + param * C;
yy = start.y + param * D;
}
float dx = test.x - xx;
float dy = test.y - yy;
return(sqrt(dx * dx + dy * dy));
}
void main()
{
vec4 pixel = gl_Color;
vec4 pos = gl_FragCoord;
float dist = point_distance(pos.xy);
if(dist > line_width * 0.5) // Cut off the corners
{
pixel.a = 0.0;
}
else
{
float intencity = dist / (line_width * 0.5);
if(intencity < 0.1) // If the fragment is in the closest 10% make it full bright
{
pixel.a = mix(1.0, 0.0, intencity);
}
else // Interpolate normally
{
pixel.a = mix(1.0, 0.0, intencity) * .5;
}
}
gl_FragColor = pixel;
}
point.vert
//"out" varyings to our fragment shader
uniform float line_width;
varying vec2 vstart;
varying vec2 vend;
void main()
{
vec4 vertex = gl_ModelViewMatrix * gl_Vertex;
gl_Position = gl_ProjectionMatrix * vertex;
gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
gl_FrontColor = gl_Color;
}