Wide GPU Lines v1.3

// ------------------------------------------------------------------------------------------------
// PointsLinesAndQuads.glsl 
// - See the chapter "Fast Prefiltered Lines" in GPU Gems 2 (freely available online)
// - See the "Modern OpenGL" presentation from Siggraph Asia 2008
// - Requires OpenGL 3.2 / GLSL 1.5 hardware 
// ------------------------------------------------------------------------------------------------

// ------------------------------------------------------------------------------------------------
// Compiler options
// ------------------------------------------------------------------------------------------------
#version 150 core

//#pragma optimize(off)
//#pragma debug(on)
//#pragma STDGL invariant(all) 

layout(shared, row_major) uniform;

// ------------------------------------------------------------------------------------------------
// Uniforms
// ------------------------------------------------------------------------------------------------

uniform CameraBlock {
	mat4 ObjectToWorld;
	mat4 WorldToObject;
	mat4 CameraToWorld;
	mat4 WorldToCamera;
	mat4 WorldToClip;
	mat4 WorldToScreen;
	mat4 CameraToClip;
	mat4 ClipToScreen;
	mat4 ScreenToClip; 
	vec4 Viewport;  
} Camera;

uniform ObjectBlock {
	mat4 ObjectToWorld;
	mat4 WorldToObject;
} Object;

uniform StateBlock {
	float LineWidth;
	float LineFilterWidth;
	float LineSoftness;
} State;

uniform sampler2D Sampler;


// ------------------------------------------------------------------------------------------------
// Math
// ------------------------------------------------------------------------------------------------

void ClipSegmentToPlane(inout vec4 p0, inout vec4 p1, vec4 plane)
{   
	float dist0 = dot(p0, plane);
	float dist1 = dot(p1, plane);
	bool in0 = dist0 >= 0.0;
	bool in1 = dist1 >= 0.0;
	if (!in0 && !in1)
	{
		p0 = vec4(0, 0, 0, -1);
		p1 = vec4(0, 0, 0, -1);
	}
	else if (in0 != in1)
	{
		float t = dist0 / (dist0 - dist1);
		if (in1) p0 = mix(p0, p1, t);
		else     p1 = mix(p0, p1, t); 
	}
}


// ------------------------------------------------------------------------------------------------
// Point
// - Simple screen-space quad expansion of a point
// - Alternative to the point-sprite, which has limitations 
//   such as point clipping & size constraints
// ------------------------------------------------------------------------------------------------
#ifdef PointVS
in vec4 VertPosition;
in vec4 VertColor;
out vec4 GeoColor;
void main()
{	
	GeoColor = VertColor; 
	//gl_PointSize = VertPosition.w; // Or set with glPointSize()
	gl_Position = vec4(VertPosition.xyz, 1) * Camera.WorldToClip;
}
#endif

#ifdef PointGS
layout(points) in;
layout(triangle_strip, max_vertices = 4) out;
in vec4 GeoColor[1];
out vec4 FragColor;
out vec2 FragTexCoord;
void main()
{
	// Project point into screen space 
	vec4 p = gl_in[0].gl_Position * Camera.ClipToScreen; p /= p.w; 

	// Build quad (CCW from upper left) in screen space
	float radius = gl_in[0].gl_PointSize / 2.0;
	vec4 q0 = vec4(p.xy + vec2(-radius, -radius), p.zw);
	vec4 q1 = vec4(p.xy + vec2(-radius, +radius), p.zw);
	vec4 q2 = vec4(p.xy + vec2(+radius, +radius), p.zw);
	vec4 q3 = vec4(p.xy + vec2(+radius, -radius), p.zw);

	// Transform quad back into clip space
	vec4 v0 = q0 * Camera.ScreenToClip; v0 /= v0.w; 
	vec4 v1 = q1 * Camera.ScreenToClip; v1 /= v1.w; 
	vec4 v2 = q2 * Camera.ScreenToClip; v2 /= v2.w; 
	vec4 v3 = q3 * Camera.ScreenToClip; v3 /= v3.w; 

	// Emit strip (CCW from upper left)
	FragColor = GeoColor[0]; FragTexCoord = vec2(0, 0); gl_Position = v0; EmitVertex();
	FragColor = GeoColor[0]; FragTexCoord = vec2(0, 1); gl_Position = v1; EmitVertex();
	FragColor = GeoColor[0]; FragTexCoord = vec2(1, 0); gl_Position = v3; EmitVertex();
	FragColor = GeoColor[0]; FragTexCoord = vec2(1, 1); gl_Position = v2; EmitVertex();
}
#endif

#ifdef PointFS
in vec4 FragColor;
in vec2 FragTexCoord;
out vec4 FragData[8];
void main()
{    
	FragData[0] = texture(Sampler, FragTexCoord) * FragColor;
}
#endif


// ------------------------------------------------------------------------------------------------
// Line
// - Quad expansion of a line segment
// - See the chapter "Fast Prefiltered Lines" in GPU Gems 2 (freely available online)
// ------------------------------------------------------------------------------------------------

#define LINE_SCREEN_ALIGNED  

#ifdef LineVS
in vec4 VertPosition;
in vec4 VertColor;
out vec4 GeoColor;
void main()
{	
	GeoColor = VertColor;
	gl_Position = VertPosition * Camera.WorldToClip; 
}
#endif

#ifdef LineGS
layout(lines) in;
layout(triangle_strip, max_vertices = 4) out;
in vec4 GeoColor[2];
flat out vec3 FragPlanes[4];
out vec4 FragColor;
void main()
{   
	// Clip the input clip-space segment to the near plane 
	vec4 p0 = gl_in[0].gl_Position;
	vec4 p1 = gl_in[1].gl_Position;
	ClipSegmentToPlane(p0, p1, vec4(0, 0, 1, 1));

	// Project clipped segment into screen space 
	p0 *= Camera.ClipToScreen; p0 /= p0.w;
	p1 *= Camera.ClipToScreen; p1 /= p1.w;

	// Compute segment direction L and perpendicular P then scale for line width
	float R = (State.LineWidth / 2.0 + State.LineFilterWidth);
	vec2 L = normalize(p1.xy - p0.xy);
	vec2 P = vec2(-L.y, L.x);  
#ifdef LINE_SCREEN_ALIGNED
	vec2 LR = vec2(0, 0);
#else
	vec2 LR = L * R;
#endif
	vec2 PR = P * R;

#ifdef LINE_SCREEN_ALIGNED
	// Sheer PR so that it's orthogonal to L's major axis (so that ends are screen-aligned)
	vec2 Lmajor = abs(L.y) >= abs(L.x) ? vec2(0, Sign(L.y)) : vec2(Sign(L.x), 0);
	PR -= dot(PR, Lmajor) / dot(L, Lmajor) * L;
#endif

	// Compute the screen-space quad corners 
	vec2 q0 = p0.xy - LR - PR;
	vec2 q1 = p1.xy + LR - PR;
	vec2 q2 = p1.xy + LR + PR;
	vec2 q3 = p0.xy - LR + PR;

	// Move the final screen-space quad back into clip-space
	vec4 v0 = vec4(q0, p0.zw) * Camera.ScreenToClip; 
	vec4 v1 = vec4(q1, p1.zw) * Camera.ScreenToClip; 
	vec4 v2 = vec4(q2, p1.zw) * Camera.ScreenToClip; 
	vec4 v3 = vec4(q3, p0.zw) * Camera.ScreenToClip; 

	// Compute the screen-space planes used to determine fragment distances from the quad's boundary
	vec3 planes[4] = vec3[] (
		vec3(+P, -(dot(p0.xy, +P) - R)) / State.LineFilterWidth,
		vec3(-P, -(dot(p1.xy, -P) - R)) / State.LineFilterWidth,
#ifndef LINE_SCREEN_ALIGNED
		vec3(+L, -(dot(p0.xy, +L) - R)) / State.LineFilterWidth,
		vec3(-L, -(dot(p1.xy, -L) - R)) / State.LineFilterWidth
#else	
		vec3(+Lmajor, -(dot(p0.xy, +Lmajor) - State.LineFilterWidth)) / State.LineFilterWidth,
		vec3(-Lmajor, -(dot(p1.xy, -Lmajor) - State.LineFilterWidth)) / State.LineFilterWidth
#endif
		);

	// Emit the quad's tri-strip 
	FragColor = GeoColor[0]; FragPlanes = planes; gl_Position = v0; EmitVertex();
	FragColor = GeoColor[0]; FragPlanes = planes; gl_Position = v3; EmitVertex();
	FragColor = GeoColor[1]; FragPlanes = planes; gl_Position = v1; EmitVertex();
	FragColor = GeoColor[1]; FragPlanes = planes; gl_Position = v2; EmitVertex();
}
#endif

#ifdef LineFS
layout(origin_upper_left) in vec4 gl_FragCoord;
flat in vec3 FragPlanes[4];
in vec4 FragColor;
out vec4 FragData[8];
void main()
{
	// Get fragment distances to quad boundary
	vec3 pos = vec3(gl_FragCoord.xy, 1.0); 
	vec4 dist = vec4(dot(pos, FragPlanes[0]), dot(pos, FragPlanes[1]), dot(pos, FragPlanes[2]), dot(pos, FragPlanes[3]));
	if (any(lessThan(dist, vec4(0))))
	{
		// Fragment is behind at least 1 plane and therefore outside of the quad
		discard;
	}
	else
	{
		// Alpha for framebuffer blending is a filter weight based on the 
		// distance of the fragment to the quad's boundary.
		// We're using the inverted Gaussian 
		//		f(d) = 1 - exp(-d * d * s),
		// where d is the minimum distance to the quad boundary and s 
		// is chosen such that f(1) = 1 - c, for a sufficiently small c. 
		// For s we have 
		//		f(1) = 1 - exp(-s) = 1 - c => s = -ln c, 
		// and so
		//		f(d) = 1 - exp(d * d * ln(c)) = 1 - c^(d^2),
		// where c, the measure of "softness", is in the range [0, 1].
		// Quick & simple for testing - use a LUT as prescribed by the GPU Gem
		float d = min(dist.x, dist.y); 
		float f = 1.0 - pow(State.LineSoftness, d * d);
		
		FragData[0] = FragColor;
		FragData[0].w *= f;
	}
}
#endif


// ------------------------------------------------------------------------------------------------
// Quad
// - GL_QUADS primitive replacement and optional bilinear color 
//   interpolation via GL_LINES_ADJACENCY
// - See the "Modern OpenGL" presentation from Siggraph Asia 2008 
// ------------------------------------------------------------------------------------------------

vec4 BilerpColors(vec4 colors[4], vec2 texcoords)
{
	vec4 c00 = mix(colors[0], colors[1], texcoords.y);
	vec4 c01 = mix(colors[3], colors[2], texcoords.y);
	return mix(c00, c01, texcoords.x);
}

#ifdef QuadVS
in vec4 VertPosition;
in vec4 VertColor;
in vec2 VertTexCoord;
out vec4 GeoColor;
out vec2 GeoTexCoord;
void main()
{	
    GeoColor = VertColor;
    GeoTexCoord = VertTexCoord.xy;
	gl_Position = VertPosition * Camera.WorldToClip;
}
#endif

#ifdef QuadGS
layout(lines_adjacency) in;
layout(triangle_strip, max_vertices = 4) out;
in vec4 GeoColor[4];
in vec2 GeoTexCoord[4];
out vec4 FragColor[4];
out vec2 FragTexCoord;
out vec2 FragBilerp;
void main()
{   
	FragColor = GeoColor; FragTexCoord = GeoTexCoord[0]; FragBilerp = vec2(0, 0); gl_Position = gl_in[0].gl_Position; EmitVertex();
	FragColor = GeoColor; FragTexCoord = GeoTexCoord[1]; FragBilerp = vec2(0, 1); gl_Position = gl_in[1].gl_Position; EmitVertex();
	FragColor = GeoColor; FragTexCoord = GeoTexCoord[3]; FragBilerp = vec2(1, 0); gl_Position = gl_in[3].gl_Position; EmitVertex();
	FragColor = GeoColor; FragTexCoord = GeoTexCoord[2]; FragBilerp = vec2(1, 1); gl_Position = gl_in[2].gl_Position; EmitVertex();
}
#endif

#ifdef QuadFS
in vec4 FragColor[4];
in vec2 FragTexCoord;
in vec2 FragBilerp;
out vec4 FragData[8];
void main()
{
	FragData[0] = texture(Sampler, FragTexCoord);
	FragData[0] *= BilerpColors(FragColor, FragBilerp); 
}
#endif
Advertisements

Post a Comment

Required fields are marked *

*
*

%d bloggers like this: