From 1da31868ce58b94789ce68cb0b88978cd3f9d26d Mon Sep 17 00:00:00 2001 From: David Manthey Date: Tue, 24 Sep 2019 09:24:41 -0400 Subject: [PATCH] Change the point shaders so that common code is in shared files. The shader-loader allows importing chunks into other files. This allows common code to be placed in a shared file and imported by other files. There are some restrictions: the common chunks must have filenames ending in `glsl`, and the glslangValidator doesn't handle these inclusions on its own. However, sharing code is of substantial benefit for reducing maintenance issues, so it is worth this change. Note that there are other webpack shader loaders (e.g., webpack-glsl-loader) that provide similar include functionality but with a different syntax, as glsl doesn't have its own include statements. As such, if we ever switch the shader loader to another one, the import statements would have to change. --- .travis.yml | 5 ++-- scripts/preprocess_glsl.py | 25 +++++++++++++++++++ src/webgl/pointFeatureFS.glsl | 41 +++++++++++++++++++++++++++++++ src/webgl/pointFeaturePoly.frag | 40 +++--------------------------- src/webgl/pointFeaturePoly.vert | 40 +++++------------------------- src/webgl/pointFeatureSprite.frag | 41 +++---------------------------- src/webgl/pointFeatureSprite.vert | 38 +++------------------------- src/webgl/pointFeatureVS.glsl | 39 +++++++++++++++++++++++++++++ webpack.base.config.js | 9 ++++--- 9 files changed, 130 insertions(+), 148 deletions(-) create mode 100755 scripts/preprocess_glsl.py create mode 100644 src/webgl/pointFeatureFS.glsl create mode 100644 src/webgl/pointFeatureVS.glsl diff --git a/.travis.yml b/.travis.yml index 55a330fcc9..b1707a9bce 100644 --- a/.travis.yml +++ b/.travis.yml @@ -53,11 +53,12 @@ before_script: - export DISPLAY=:99.0 script: + # Use the glslangValidator to validate all .frag and .vert files + - find . \( -name '*.frag' \) -exec sh -c 'for n; do python scripts/preprocess_glsl.py "$n" | bin/glslangValidator --stdin -S frag || exit 1; done' sh {} \+ + - find . \( -name '*.vert' \) -exec sh -c 'for n; do python scripts/preprocess_glsl.py "$n" | bin/glslangValidator --stdin -S vert || exit 1; done' sh {} \+ - npm run build - npm run docs - mkdir -p _build - # Use the glslangValidator to validate all .frag and .vert files - - find . \( -name '*.frag' -o -name '*.vert' \) -exec bin/glslangValidator {} \+ - ctest -S cmake/travis_build.cmake -VV || true - if [ -f _build/test_failed ] ; then false ; fi # Build the website to ensure that it will pass diff --git a/scripts/preprocess_glsl.py b/scripts/preprocess_glsl.py new file mode 100755 index 0000000000..b6b0d58640 --- /dev/null +++ b/scripts/preprocess_glsl.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 + +import argparse +import os +import re +import sys + + +def readSource(source): + data = open(source).read() + parts = re.split('(\\$[-.\\w]+)', data) + for idx, chunk in enumerate(parts): + if chunk.startswith('$') and len(chunk) > 1: + parts[idx] = readSource(os.path.join(os.path.dirname(source), chunk[1:] + '.glsl')) + return ''.join(parts) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description='Preprocess glsl files to handle includes in the same way ' + 'as shader-loader. The output of this can sent to glslangValidator.') + parser.add_argument('source', help='Source file') + args = parser.parse_args() + data = readSource(args.source) + sys.stdout.write(data) diff --git a/src/webgl/pointFeatureFS.glsl b/src/webgl/pointFeatureFS.glsl new file mode 100644 index 0000000000..9085398c0a --- /dev/null +++ b/src/webgl/pointFeatureFS.glsl @@ -0,0 +1,41 @@ +/* pointFeature common fragment shader */ + +#ifdef GL_ES + precision highp float; +#endif +varying vec4 fillColorVar; +varying vec4 strokeColorVar; +varying float radiusVar; +varying float strokeWidthVar; + +void pointFeatureFragment(float rad) { + vec4 strokeColor, fillColor; + float endStep; + // No stroke or fill implies nothing to draw + if (rad > 1.0) + discard; + // If there is no stroke, the fill region should transition to nothing + if (strokeColorVar.a == 0.0) { + strokeColor = vec4(fillColorVar.rgb, 0.0); + endStep = 1.0; + } else { + strokeColor = strokeColorVar; + endStep = radiusVar / (radiusVar + strokeWidthVar); + } + // Likewise, if there is no fill, the stroke should transition to nothing + if (fillColorVar.a == 0.0) + fillColor = vec4(strokeColor.rgb, 0.0); + else + fillColor = fillColorVar; + // Distance to antialias over. First number is in pixels + float antialiasDist = 1.5 / (radiusVar + strokeWidthVar); + if (rad < endStep) { + float step = smoothstep(max(0.0, endStep - antialiasDist), endStep, rad); + vec4 color = mix(fillColor, strokeColor, step); + float step2 = smoothstep(max(0.0, 1.0 - antialiasDist), 1.0, rad); + gl_FragColor = mix(color, vec4(color.rgb, 0.0), step2); + } else { + float step = smoothstep(max(0.0, 1.0 - antialiasDist), 1.0, rad); + gl_FragColor = mix(strokeColor, vec4(strokeColor.rgb, 0.0), step); + } +} diff --git a/src/webgl/pointFeaturePoly.frag b/src/webgl/pointFeaturePoly.frag index 284e20445c..d4b54da447 100644 --- a/src/webgl/pointFeaturePoly.frag +++ b/src/webgl/pointFeaturePoly.frag @@ -1,46 +1,12 @@ /* pointFeature square/triangle fragment shader */ -#ifdef GL_ES - precision highp float; -#endif -uniform float aspect; -varying vec4 fillColorVar; -varying vec4 strokeColorVar; -varying float radiusVar; -varying float strokeWidthVar; +$pointFeatureFS + varying vec3 unitVar; // distinct for square/triangle void main () { - vec4 strokeColor, fillColor; - float endStep; - // No stroke or fill implies nothing to draw if (fillColorVar.a == 0.0 && strokeColorVar.a == 0.0) discard; float rad = length(unitVar.xy); // distinct for square/triangle - if (rad > 1.0) - discard; - // If there is no stroke, the fill region should transition to nothing - if (strokeColorVar.a == 0.0) { - strokeColor = vec4(fillColorVar.rgb, 0.0); - endStep = 1.0; - } else { - strokeColor = strokeColorVar; - endStep = radiusVar / (radiusVar + strokeWidthVar); - } - // Likewise, if there is no fill, the stroke should transition to nothing - if (fillColorVar.a == 0.0) - fillColor = vec4(strokeColor.rgb, 0.0); - else - fillColor = fillColorVar; - // Distance to antialias over. First number is in pixels - float antialiasDist = 1.5 / (radiusVar + strokeWidthVar); - if (rad < endStep) { - float step = smoothstep(max(0.0, endStep - antialiasDist), endStep, rad); - vec4 color = mix(fillColor, strokeColor, step); - float step2 = smoothstep(max(0.0, 1.0 - antialiasDist), 1.0, rad); - gl_FragColor = mix(color, vec4(color.rgb, 0.0), step2); - } else { - float step = smoothstep(max(0.0, 1.0 - antialiasDist), 1.0, rad); - gl_FragColor = mix(strokeColor, vec4(strokeColor.rgb, 0.0), step); - } + pointFeatureFragment(rad); } diff --git a/src/webgl/pointFeaturePoly.vert b/src/webgl/pointFeaturePoly.vert index a145f59fbd..d27b6c2d3f 100644 --- a/src/webgl/pointFeaturePoly.vert +++ b/src/webgl/pointFeaturePoly.vert @@ -1,46 +1,18 @@ /* pointFeature square/triangle vertex shader */ -#ifdef GL_ES - precision highp float; -#endif -attribute vec3 pos; -attribute float radius; -attribute vec3 fillColor; -attribute vec3 strokeColor; -attribute float fillOpacity; -attribute float strokeWidth; -attribute float strokeOpacity; -attribute float fill; -attribute float stroke; -uniform float pixelWidth; -uniform float aspect; -uniform mat4 modelViewMatrix; -uniform mat4 projectionMatrix; -varying vec4 fillColorVar; -varying vec4 strokeColorVar; -varying float radiusVar; -varying float strokeWidthVar; +$pointFeatureVS + +uniform float pixelWidth; // for non-sprite +uniform float aspect; // for non-sprite attribute vec2 unit; // for non-sprite varying vec3 unitVar; // for non-sprite void main(void) { - strokeWidthVar = strokeWidth; - fillColorVar = vec4(fillColor, fillOpacity); - strokeColorVar = vec4(strokeColor, strokeOpacity); - // No stroke or fill implies nothing to draw - if (stroke < 1.0 || strokeWidth <= 0.0 || strokeOpacity <= 0.0) { - strokeColorVar.a = 0.0; - strokeWidthVar = 0.0; - } - if (fill < 1.0 || radius <= 0.0 || fillOpacity <= 0.0) - fillColorVar.a = 0.0; - /* If the point has no visible pixels, skip doing computations on it. */ - if (fillColorVar.a == 0.0 && strokeColorVar.a == 0.0) { - gl_Position = vec4(2, 2, 0, 1); + radiusVar = pointFeaturePrep(); + if (radiusVar == 0.0) { return; } - radiusVar = radius; // for non-sprite unitVar = vec3(unit, 1.0); vec4 p = (projectionMatrix * modelViewMatrix * vec4(pos, 1.0)).xyzw; diff --git a/src/webgl/pointFeatureSprite.frag b/src/webgl/pointFeatureSprite.frag index f463897315..9fd189665c 100644 --- a/src/webgl/pointFeatureSprite.frag +++ b/src/webgl/pointFeatureSprite.frag @@ -1,46 +1,13 @@ /* pointFeature sprite fragment shader */ -#ifdef GL_ES - precision highp float; -#endif -uniform float aspect; -varying vec4 fillColorVar; -varying vec4 strokeColorVar; -varying float radiusVar; -varying float strokeWidthVar; -// the square/triangle shade defines unitVar +$pointFeatureFS + +// the square/triangle shader defines unitVar void main () { - vec4 strokeColor, fillColor; - float endStep; // No stroke or fill implies nothing to draw if (fillColorVar.a == 0.0 && strokeColorVar.a == 0.0) discard; float rad = 2.0 * length(gl_PointCoord - vec2(0.5)); // distinct for sprite - if (rad > 1.0) - discard; - // If there is no stroke, the fill region should transition to nothing - if (strokeColorVar.a == 0.0) { - strokeColor = vec4(fillColorVar.rgb, 0.0); - endStep = 1.0; - } else { - strokeColor = strokeColorVar; - endStep = radiusVar / (radiusVar + strokeWidthVar); - } - // Likewise, if there is no fill, the stroke should transition to nothing - if (fillColorVar.a == 0.0) - fillColor = vec4(strokeColor.rgb, 0.0); - else - fillColor = fillColorVar; - // Distance to antialias over. First number is in pixels - float antialiasDist = 1.5 / (radiusVar + strokeWidthVar); - if (rad < endStep) { - float step = smoothstep(max(0.0, endStep - antialiasDist), endStep, rad); - vec4 color = mix(fillColor, strokeColor, step); - float step2 = smoothstep(max(0.0, 1.0 - antialiasDist), 1.0, rad); - gl_FragColor = mix(color, vec4(color.rgb, 0.0), step2); - } else { - float step = smoothstep(max(0.0, 1.0 - antialiasDist), 1.0, rad); - gl_FragColor = mix(strokeColor, vec4(strokeColor.rgb, 0.0), step); - } + pointFeatureFragment(rad); } diff --git a/src/webgl/pointFeatureSprite.vert b/src/webgl/pointFeatureSprite.vert index e9069c0674..ee0da0f3d3 100644 --- a/src/webgl/pointFeatureSprite.vert +++ b/src/webgl/pointFeatureSprite.vert @@ -1,45 +1,13 @@ /* pointFeature sprite vertex shader */ -#ifdef GL_ES - precision highp float; -#endif -attribute vec3 pos; -attribute float radius; -attribute vec3 fillColor; -attribute vec3 strokeColor; -attribute float fillOpacity; -attribute float strokeWidth; -attribute float strokeOpacity; -attribute float fill; -attribute float stroke; -uniform float pixelWidth; -uniform float aspect; -uniform mat4 modelViewMatrix; -uniform mat4 projectionMatrix; -varying vec4 fillColorVar; -varying vec4 strokeColorVar; -varying float radiusVar; -varying float strokeWidthVar; -// non-sprite has ither definitions. +$pointFeatureVS void main(void) { - strokeWidthVar = strokeWidth; - fillColorVar = vec4(fillColor, fillOpacity); - strokeColorVar = vec4(strokeColor, strokeOpacity); - // No stroke or fill implies nothing to draw - if (stroke < 1.0 || strokeWidth <= 0.0 || strokeOpacity <= 0.0) { - strokeColorVar.a = 0.0; - strokeWidthVar = 0.0; - } - if (fill < 1.0 || radius <= 0.0 || fillOpacity <= 0.0) - fillColorVar.a = 0.0; - /* If the point has no visible pixels, skip doing computations on it. */ - if (fillColorVar.a == 0.0 && strokeColorVar.a == 0.0) { - gl_Position = vec4(2, 2, 0, 1); + radiusVar = pointFeaturePrep(); + if (radiusVar == 0.0) { return; } - radiusVar = radius; // for sprite gl_Position = (projectionMatrix * modelViewMatrix * vec4(pos, 1.0)).xyzw; gl_PointSize = 2.0 * (radius + strokeWidthVar); diff --git a/src/webgl/pointFeatureVS.glsl b/src/webgl/pointFeatureVS.glsl new file mode 100644 index 0000000000..a9cbf1e213 --- /dev/null +++ b/src/webgl/pointFeatureVS.glsl @@ -0,0 +1,39 @@ +/* pointFeature common vertex shader */ + +#ifdef GL_ES + precision highp float; +#endif +attribute vec3 pos; +attribute float radius; +attribute vec3 fillColor; +attribute vec3 strokeColor; +attribute float fillOpacity; +attribute float strokeWidth; +attribute float strokeOpacity; +attribute float fill; +attribute float stroke; +uniform mat4 modelViewMatrix; +uniform mat4 projectionMatrix; +varying vec4 fillColorVar; +varying vec4 strokeColorVar; +varying float radiusVar; +varying float strokeWidthVar; + +float pointFeaturePrep() { + strokeWidthVar = strokeWidth; + fillColorVar = vec4(fillColor, fillOpacity); + strokeColorVar = vec4(strokeColor, strokeOpacity); + // No stroke or fill implies nothing to draw + if (stroke < 1.0 || strokeWidth <= 0.0 || strokeOpacity <= 0.0) { + strokeColorVar.a = 0.0; + strokeWidthVar = 0.0; + } + if (fill < 1.0 || radius <= 0.0 || fillOpacity <= 0.0) + fillColorVar.a = 0.0; + /* If the point has no visible pixels, skip doing computations on it. */ + if (fillColorVar.a == 0.0 && strokeColorVar.a == 0.0) { + gl_Position = vec4(2, 2, 0, 1); + return 0.0; + } + return radius; +} diff --git a/webpack.base.config.js b/webpack.base.config.js index 410046d70f..ccbe2462ef 100644 --- a/webpack.base.config.js +++ b/webpack.base.config.js @@ -135,9 +135,12 @@ module.exports = { ] }, { test: /\.(glsl|vs|fs|vert|frag)$/, - use: [ - 'shader-loader' - ] + use: [{ + loader: 'shader-loader', + options: { + glsl: { chunkPath: 'src/webgl' } + } + }] }, { test: /vgl\.js$/, use: [