From 20c86db71ff6935a0b73df73d9dd6ab9f441322b Mon Sep 17 00:00:00 2001 From: Maciej Suminski Date: Tue, 16 Jul 2013 08:49:12 +0200 Subject: [PATCH] 1 pixel wide lines using OpenGL shaders. --- common/gal/opengl/opengl_gal.cpp | 91 +++++++++++++++----------------- common/gal/opengl/shader.frag | 15 ++++-- common/gal/opengl/shader.vert | 39 +++++++++++++- 3 files changed, 89 insertions(+), 56 deletions(-) diff --git a/common/gal/opengl/opengl_gal.cpp b/common/gal/opengl/opengl_gal.cpp index cc43852dba..7d98f0cd38 100644 --- a/common/gal/opengl/opengl_gal.cpp +++ b/common/gal/opengl/opengl_gal.cpp @@ -783,23 +783,24 @@ void OPENGL_GAL::DrawCircle( const VECTOR2D& aCenterPoint, double aRadius ) color4( fillColor.r, fillColor.g, fillColor.b, fillColor.a ); /* Draw a triangle that contains the circle, then shade it leaving only the circle. - Parameters given to setShader are relative coordinates of the triangle's vertices. + Parameters given to setShader are indices of the triangle's vertices + (if you want to understand more, check the vertex shader source [shader.vert]). Shader uses this coordinates to determine if fragments are inside the circle or not. v2 /\ //\\ v0 /_\/_\ v1 */ - setShader( SHADER_FILLED_CIRCLE, -sqrt( 3.0f ), -1.0f ); - vertex3( aCenterPoint.x - aRadius * sqrt( 3.0f ), // v0 - aCenterPoint.y - aRadius, layerDepth ); + setShader( SHADER_FILLED_CIRCLE, 1.0 ); + vertex3( aCenterPoint.x - aRadius * sqrt( 3.0f ), + aCenterPoint.y - aRadius, layerDepth ); // v0 - setShader( SHADER_FILLED_CIRCLE, sqrt( 3.0f ), -1.0f ); - vertex3( aCenterPoint.x + aRadius * sqrt( 3.0f ), // v1 - aCenterPoint.y - aRadius, layerDepth ); + setShader( SHADER_FILLED_CIRCLE, 2.0 ); + vertex3( aCenterPoint.x + aRadius * sqrt( 3.0f ), + aCenterPoint.y - aRadius, layerDepth ); // v1 - setShader( SHADER_FILLED_CIRCLE, 0.0f, 2.0f ); - vertex3( aCenterPoint.x, aCenterPoint.y + aRadius * 2.0f, layerDepth ); // v2 + setShader( SHADER_FILLED_CIRCLE, 3.0 ); + vertex3( aCenterPoint.x, aCenterPoint.y + aRadius * 2.0f, layerDepth ); // v2 } if( isStrokeEnabled ) @@ -807,30 +808,25 @@ void OPENGL_GAL::DrawCircle( const VECTOR2D& aCenterPoint, double aRadius ) color4( strokeColor.r, strokeColor.g, strokeColor.b, strokeColor.a ); /* Draw a triangle that contains the circle, then shade it leaving only the circle. - Parameters given to setShader are relative coordinates of the triangle's vertices + Parameters given to setShader are indices of the triangle's vertices + (if you want to understand more, check the vertex shader source [shader.vert]). and the line width. Shader uses this coordinates to determine if fragments are inside - the circle or not. Width parameter has to be passed as a ratio of inner radius - to outer radius. + the circle or not. v2 /\ //\\ v0 /_\/_\ v1 */ - float outerRadius = aRadius + ( lineWidth / 2.0f ); - float innerRadius = aRadius - ( lineWidth / 2.0f ); - float relWidth = innerRadius / outerRadius; + setShader( SHADER_STROKED_CIRCLE, 1.0, aRadius, lineWidth ); + vertex3( aCenterPoint.x - aRadius * sqrt( 3.0f ), + aCenterPoint.y - aRadius, layerDepth ); // v0 - setShader( SHADER_STROKED_CIRCLE, -sqrt( 3.0f ), -1.0f, relWidth ); - vertex3( aCenterPoint.x - outerRadius * sqrt( 3.0f ), // v0 - aCenterPoint.y - outerRadius, layerDepth ); + setShader( SHADER_STROKED_CIRCLE, 2.0, aRadius, lineWidth ); + vertex3( aCenterPoint.x + aRadius * sqrt( 3.0f ), + aCenterPoint.y - aRadius, layerDepth ); // v1 - setShader( SHADER_STROKED_CIRCLE, sqrt( 3.0f ), -1.0f, relWidth ); - vertex3( aCenterPoint.x + outerRadius * sqrt( 3.0f ), // v1 - aCenterPoint.y - outerRadius, layerDepth ); - - setShader( SHADER_STROKED_CIRCLE, 0.0f, 2.0f, relWidth ); - vertex3( aCenterPoint.x, // v2 - aCenterPoint.y + outerRadius * 2.0f, layerDepth ); + setShader( SHADER_STROKED_CIRCLE, 3.0, aRadius, lineWidth ); + vertex3( aCenterPoint.x, aCenterPoint.y + aRadius * 2.0f, layerDepth ); // v2 } return; @@ -931,20 +927,21 @@ void OPENGL_GAL::drawFilledSemiCircle( const VECTOR2D& aCenterPoint, double aRad Rotate( aAngle ); /* Draw a triangle that contains the semicircle, then shade it to leave only the semicircle. - Parameters given to setShader are relative coordinates of the triangle's vertices. - Shader uses this coordinates to determine if fragments are inside the semicircle or not. - v2 - /\ - /__\ - v0 //__\\ v1 + Parameters given to setShader are indices of the triangle's vertices + (if you want to understand more, check the vertex shader source [shader.vert]). + Shader uses this coordinates to determine if fragments are inside the semicircle or not. + v2 + /\ + /__\ + v0 //__\\ v1 */ - setShader( SHADER_FILLED_CIRCLE, -3.0f / sqrt( 3.0f ), 0.0f ); + setShader( SHADER_FILLED_CIRCLE, 4.0f ); vertex3( -aRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth ); // v0 - setShader( SHADER_FILLED_CIRCLE, 3.0f / sqrt( 3.0f ), 0.0f ); + setShader( SHADER_FILLED_CIRCLE, 5.0f ); vertex3( aRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth ); // v1 - setShader( SHADER_FILLED_CIRCLE, 0.0f, 2.0f ); + setShader( SHADER_FILLED_CIRCLE, 6.0f ); vertex3( 0.0f, aRadius * 2.0f, layerDepth ); // v2 Restore(); @@ -973,26 +970,22 @@ void OPENGL_GAL::drawStrokedSemiCircle( const VECTOR2D& aCenterPoint, double aRa Rotate( aAngle ); /* Draw a triangle that contains the semicircle, then shade it to leave only the semicircle. - Parameters given to setShader are relative coordinates of the triangle's vertices - and the line width. Shader uses this coordinates to determine if fragments are inside - the semicircle or not. Width parameter has to be passed as a ratio of inner radius - to outer radius. - v2 - /\ - /__\ - v0 //__\\ v1 + Parameters given to setShader are indices of the triangle's vertices + (if you want to understand more, check the vertex shader source [shader.vert]), the + radius and the line width. Shader uses this coordinates to determine if fragments are + inside the semicircle or not. + v2 + /\ + /__\ + v0 //__\\ v1 */ - float outerRadius = aRadius; - float innerRadius = aRadius - lineWidth; - float relWidth = innerRadius / outerRadius; - - setShader( SHADER_STROKED_CIRCLE, -3.0f / sqrt( 3.0f ), 0.0f, relWidth ); + setShader( SHADER_STROKED_CIRCLE, 4.0f, aRadius, lineWidth ); vertex3( -aRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth ); // v0 - setShader( SHADER_STROKED_CIRCLE, 3.0f / sqrt( 3.0f ), 0.0f, relWidth ); + setShader( SHADER_STROKED_CIRCLE, 5.0f, aRadius, lineWidth ); vertex3( aRadius * 3.0f / sqrt( 3.0f ), 0.0f, layerDepth ); // v1 - setShader( SHADER_STROKED_CIRCLE, 0.0f, 2.0f, relWidth ); + setShader( SHADER_STROKED_CIRCLE, 6.0f, aRadius, lineWidth ); vertex3( 0.0f, aRadius * 2.0f, layerDepth ); // v2 Restore(); diff --git a/common/gal/opengl/shader.frag b/common/gal/opengl/shader.frag index 8525e3b0b9..f658fcdae5 100644 --- a/common/gal/opengl/shader.frag +++ b/common/gal/opengl/shader.frag @@ -32,20 +32,25 @@ const float SHADER_FILLED_CIRCLE = 2.0; const float SHADER_STROKED_CIRCLE = 3.0; varying vec4 shaderParams; +varying vec2 circleCoords; void filledCircle( vec2 aCoord ) { - if( dot( aCoord, aCoord ) < 1.0 ) + if( dot( aCoord, aCoord ) < 1.0f ) gl_FragColor = gl_Color; else discard; } -void strokedCircle( vec2 aCoord, float aWidth ) +void strokedCircle( vec2 aCoord, float aRadius, float aWidth ) { + float outerRadius = aRadius; + float innerRadius = aRadius - aWidth; + float relWidth = innerRadius / outerRadius; + if( ( dot( aCoord, aCoord ) < 1.0 ) && - ( dot( aCoord, aCoord ) > aWidth * aWidth ) ) + ( dot( aCoord, aCoord ) > relWidth * relWidth ) ) gl_FragColor = gl_Color; else discard; @@ -56,11 +61,11 @@ void main() { if( shaderParams[0] == SHADER_FILLED_CIRCLE ) { - filledCircle( vec2( shaderParams[1], shaderParams[2] ) ); + filledCircle( circleCoords ); } else if( shaderParams[0] == SHADER_STROKED_CIRCLE ) { - strokedCircle( vec2( shaderParams[1], shaderParams[2] ), shaderParams[3] ); + strokedCircle( circleCoords, shaderParams[2], shaderParams[3] ); } else { diff --git a/common/gal/opengl/shader.vert b/common/gal/opengl/shader.vert index eee54e0ffa..24b12afe45 100644 --- a/common/gal/opengl/shader.vert +++ b/common/gal/opengl/shader.vert @@ -31,8 +31,12 @@ const float SHADER_LINE = 1.0; const float SHADER_FILLED_CIRCLE = 2.0; const float SHADER_STROKED_CIRCLE = 3.0; +// Minimum line width +const float MIN_WIDTH = 1.0; + attribute vec4 attrShaderParams; varying vec4 shaderParams; +varying vec2 circleCoords; void main() { @@ -45,8 +49,8 @@ void main() float worldScale = gl_ModelViewMatrix[0][0]; float scale; - // Make lines appear to be at least 1 pixel width - if( worldScale * lineWidth < 1.0 ) + // Make lines appear to be at least 1 pixel wide + if( worldScale * lineWidth < MIN_WIDTH ) scale = 1.0 / ( worldScale * lineWidth ); else scale = 1.0; @@ -54,6 +58,37 @@ void main() gl_Position = gl_ModelViewProjectionMatrix * ( gl_Vertex + vec4( shaderParams.yz * scale, 0.0, 0.0 ) ); } + else if( ( shaderParams[0] == SHADER_STROKED_CIRCLE ) || + ( shaderParams[0] == SHADER_FILLED_CIRCLE ) ) + { + // Compute relative circle coordinates basing on indices + // Circle + if( shaderParams[1] == 1.0f ) + circleCoords = vec2( -sqrt( 3.0f ), -1.0f ); + else if( shaderParams[1] == 2.0f ) + circleCoords = vec2( sqrt( 3.0f ), -1.0f ); + else if( shaderParams[1] == 3.0f ) + circleCoords = vec2( 0.0f, 2.0f ); + + // Semicircle + else if( shaderParams[1] == 4.0f ) + circleCoords = vec2( -3.0f / sqrt( 3.0f ), 0.0f ); + else if( shaderParams[1] == 5.0f ) + circleCoords = vec2( 3.0f / sqrt( 3.0f ), 0.0f ); + else if( shaderParams[1] == 6.0f ) + circleCoords = vec2( 0.0f, 2.0f ); + + // Make the line appear to be at least 1 pixel wide + float lineWidth = shaderParams[3]; + float worldScale = gl_ModelViewMatrix[0][0]; + float scale; + + // Make lines appear to be at least 1 pixel width + if( worldScale * lineWidth < MIN_WIDTH ) + shaderParams[3] = shaderParams[3] / ( worldScale * lineWidth ); + + gl_Position = ftransform(); + } else { // Pass through the coordinates like in the fixed pipeline