Skip to content

Commit

Permalink
Implement layer shader hooks (visgl#3295)
Browse files Browse the repository at this point in the history
  • Loading branch information
Pessimistress authored Jun 28, 2019
1 parent 15db4b9 commit bbe0ecd
Show file tree
Hide file tree
Showing 37 changed files with 429 additions and 186 deletions.
1 change: 1 addition & 0 deletions docs/developer-guide/custom-layers/subclassed-layers.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ returns the shaders and modules used by the layer in an object:
* `vs`: string, GLSL source of the vertex shader
* `fs`: string, GLSL source of the fragment shader
* `modules`: Array, list of shader modules to be used
* `inject`: Object, map from injection points to custom GLSL code to be injected

Read about [writing your own shaders](/docs/developer-guide/custom-layers/writing-shaders.md).

Expand Down
97 changes: 97 additions & 0 deletions docs/developer-guide/custom-layers/writing-shaders.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,103 @@ Note that for geospatial projection, deck.gl v6.1 introduced a "hybrid" 32-bit p
Picking is supported using luma.gl [picking shader module](https://github.com/uber/luma.gl/blob/master/docs/api-reference/shadertools/shader-module-picking.md).


### Standard Shader Hooks

When [subclassing](/docs/developer-guide/custom-layers/subclassed-layers.md) an official deck.gl layer with minor feature additions, it is possible to inject custom code into predefined locations into the original shaders. These hooks are considered the public API of layers that will work consistently cross minor releases.

```js
const shaders = this.getShaders();

const model = new Model(gl, {
...this.getShaders(),
inject: {
'fs:decl': `
uniform float coverage;
`
'fs:DECKGL_FILTER_COLOR': `
if (abs(geometry.uv.x) > coverage) discard;
`
}
});
```

##### vs:#decl

Inject into the top of the vertex shader (declarations).

##### vs:#main-start

Inject into the the very beginning of the main function in the vertex shader.

##### vs:#main-end

Inject into the the very end of the main function in the vertex shader.

##### vs:DeckGL_FILTER_SIZE

Inject into a function in the vertex shader to manipulate the size of a geometry. Called before projection.

Arguments:

- `inout vec3 size` - offset of the current vertex from `geometry.worldPosition` in common space.
- `VertexGeometry geometry` - descriptor of the current geometry


##### vs:DeckGL_FILTER_GL_POSITION

Inject into a function in the vertex shader to manipulate the projected position of the current vertex. Called after projection.

Arguments:

- `inout vec4 position` - position of the current vertex in clipspace
- `VertexGeometry geometry` - descriptor of the current geometry


##### vs:DeckGL_FILTER_COLOR

Inject into a function in the vertex shader to manipulate the color of the current geometry. Called after projection.

Arguments:

- `inout vec4 color` - color of the current geometry, RGBA in the `[0, 1]` range
- `VertexGeometry geometry` - descriptor of the current geometry


##### fs:#decl

Inject into the top of the fragment shader (declarations).

##### fs:#main-start

Inject into the the very beginning of the main function in the fragment shader.

##### fs:#main-end

Inject into the the very end of the main function in the fragment shader.

##### fs:DeckGL_FILTER_COLOR

Inject into a function in the vertex shader to manipulate the color of the current geometry. Called after projection.

Arguments:

- `inout vec4 color` - color of the current geometry, RGBA in the `[0, 1]` range
- `FragmentGeometry geometry` - descriptor of the current geometry


### VertexGeometry struct

- `vec3 worldPosition` - The world position of the current geometry, usually populated from a `getPosition` accessor.
- `vec3 worldPositionAlt` - The secondary world position of the current geometry. This property is populated if the geometry is instanced between a source position and a target position, for example `ArcLayer`.
- `vec3 normal` - The normal at the current vertex in common space. Only populated for 3D layers.
- `vec2 uv` - The uv position at the current vertex.
- `vec4 position` - The position of the current vertex in common space. Populated during projection.

### FragmentGeometry struct

- `vec2 uv` - The uv position at the current vertex.


## Shader Techniques and Ideas

### Filtering and Brushing (Vertex and Fragment Shaders)
Expand Down
2 changes: 1 addition & 1 deletion modules/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"prepublishOnly": "npm run build-bundle && npm run build-bundle -- --env.dev"
},
"dependencies": {
"@luma.gl/core": "^7.2.0-alpha.1",
"@luma.gl/core": "^7.2.0-alpha.2",
"@loaders.gl/core": "^1.1.6",
"@loaders.gl/images": "^1.1.6",
"gl-matrix": "^3.0.0",
Expand Down
33 changes: 28 additions & 5 deletions modules/core/src/shaderlib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,17 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import {registerShaderModules, setDefaultShaderModules} from '@luma.gl/core';
import {
registerShaderModules,
setDefaultShaderModules,
createShaderHook,
createModuleInjection
} from '@luma.gl/core';
import {fp32, fp64, picking, gouraudlighting, phonglighting} from '@luma.gl/core';
import project from '../shaderlib/project/project';
import project32 from '../shaderlib/project32/project32';
import project64 from '../shaderlib/project64/project64';
import geometry from './misc/geometry';
import project from './project/project';
import project32 from './project32/project32';
import project64 from './project64/project64';

export function initializeShaderModules() {
registerShaderModules([
Expand All @@ -36,7 +42,24 @@ export function initializeShaderModules() {
picking
]);

setDefaultShaderModules([project]);
setDefaultShaderModules([geometry, project]);

createShaderHook('vs:DECKGL_FILTER_SIZE(inout vec3 size, VertexGeometry geometry)');
createShaderHook('vs:DECKGL_FILTER_GL_POSITION(inout vec4 position, VertexGeometry geometry)');
createShaderHook('vs:DECKGL_FILTER_COLOR(inout vec4 color, VertexGeometry geometry)');
createShaderHook('fs:DECKGL_FILTER_COLOR(inout vec4 color, FragmentGeometry geometry)');

createModuleInjection('picking', {
hook: 'fs:DECKGL_FILTER_COLOR',
order: 99,
injection: `
// use highlight color if this fragment belongs to the selected object.
color = picking_filterHighlightColor(color);
// use picking color if rendering to picking FBO.
color = picking_filterPickingColor(color);
`
});
}

export {fp32, fp64, picking, project, project64, gouraudlighting, phonglighting};
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,20 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

export default `\
highp float random(vec2 co) {
highp float a = 12.9898;
highp float b = 78.233;
highp float c = 43758.5453;
highp float dt= dot(co.xy ,vec2(a,b));
highp float sn= mod(dt,3.14);
return fract(sin(sn) / c) - .5;
}
const vs = `
struct VertexGeometry {
vec4 position;
vec3 worldPosition;
vec3 worldPositionAlt;
vec3 normal;
vec2 uv;
} geometry;
`;

const fs = `
struct FragmentGeometry {
vec2 uv;
} geometry;
`;

export default {name: 'geometry', vs, fs};
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ uniform float widthMinPixels;
uniform float widthMaxPixels;
varying vec4 vColor;
varying vec2 uv;
// offset vector by strokeWidth pixels
// offset_direction is -1 (left) or 1 (right)
Expand All @@ -45,10 +46,7 @@ vec2 getExtrusionOffset(vec2 line_clipspace, float offset_direction, float width
// rotate by 90 degrees
dir_screenspace = vec2(-dir_screenspace.y, dir_screenspace.x);
vec2 offset_screenspace = dir_screenspace * offset_direction * width / 2.0;
vec2 offset_clipspace = project_pixel_size_to_clipspace(offset_screenspace);
return offset_clipspace;
return dir_screenspace * offset_direction * width / 2.0;
}
float getSegmentRatio(float index) {
Expand Down Expand Up @@ -86,8 +84,13 @@ vec2 interpolate (vec2 source, vec2 target, float angularDist, float t) {
}
void main(void) {
geometry.worldPosition = vec3(instancePositions.xy, 0.0);
geometry.worldPositionAlt = vec3(instancePositions.zw, 0.0);
float segmentIndex = positions.x;
float segmentRatio = getSegmentRatio(segmentIndex);
uv = vec2(segmentRatio, positions.y);
geometry.uv = uv;
// if it's the first point, use next - current as direction
// otherwise use current - prev
Expand All @@ -105,7 +108,7 @@ void main(void) {
vec2 currPos64Low = mix(instancePositions64Low.xy, instancePositions64Low.zw, segmentRatio);
vec2 nextPos64Low = mix(instancePositions64Low.xy, instancePositions64Low.zw, nextSegmentRatio);
vec4 curr = project_position_to_clipspace(currPos, currPos64Low, vec3(0.0));
vec4 curr = project_position_to_clipspace(currPos, currPos64Low, vec3(0.0), geometry.position);
vec4 next = project_position_to_clipspace(nextPos, nextPos64Low, vec3(0.0));
// Multiply out width and clamp to limits
Expand All @@ -116,12 +119,17 @@ void main(void) {
);
// extrude
vec2 offset = getExtrusionOffset((next.xy - curr.xy) * indexDir, positions.y, widthPixels);
gl_Position = curr + vec4(offset, 0.0, 0.0);
vec3 offset = vec3(
getExtrusionOffset((next.xy - curr.xy) * indexDir, positions.y, widthPixels),
0.0);
DECKGL_FILTER_SIZE(offset, geometry);
gl_Position = curr + vec4(project_pixel_size_to_clipspace(offset.xy), 0.0, 0.0);
DECKGL_FILTER_GL_POSITION(gl_Position, geometry);
vec4 color = mix(instanceSourceColors, instanceTargetColors, segmentRatio) / 255.0;
vColor = vec4(color.rgb, color.a * opacity);
DECKGL_FILTER_COLOR(vColor, geometry);
// Set color to be rendered to picking fbo (also used to check for selection highlight).
picking_setPickingColor(instancePickingColors);
}
Expand Down
2 changes: 1 addition & 1 deletion modules/geo-layers/src/trips-layer/trips-layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ if(vTime > currentTime || vTime < currentTime - trailLength) {
}
`,
// Fade the color (currentTime - 100%, end of trail - 0%)
'gl_FragColor = vColor;': 'gl_FragColor.a *= 1.0 - (currentTime - vTime) / trailLength;'
'fs:DECKGL_FILTER_COLOR': 'color.a *= 1.0 - (currentTime - vTime) / trailLength;'
};
return shaders;
}
Expand Down
8 changes: 3 additions & 5 deletions modules/layers/src/arc-layer/arc-layer-fragment.glsl.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,12 @@ export default `\
precision highp float;
varying vec4 vColor;
varying vec2 uv;
void main(void) {
gl_FragColor = vColor;
geometry.uv = uv;
// use highlight color if this fragment belongs to the selected object.
gl_FragColor = picking_filterHighlightColor(gl_FragColor);
// use picking color if rendering to picking FBO.
gl_FragColor = picking_filterPickingColor(gl_FragColor);
DECKGL_FILTER_COLOR(gl_FragColor, geometry);
}
`;
21 changes: 15 additions & 6 deletions modules/layers/src/arc-layer/arc-layer-vertex-64.glsl.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ uniform float widthMinPixels;
uniform float widthMaxPixels;
varying vec4 vColor;
varying vec2 uv;
vec2 paraboloid_fp64(vec2 source[2], vec2 target[2], float ratio) {
Expand All @@ -59,10 +60,7 @@ vec2 getExtrusionOffset(vec2 line_clipspace, float offset_direction, float width
// rotate by 90 degrees
dir_screenspace = vec2(-dir_screenspace.y, dir_screenspace.x);
vec2 offset_screenspace = dir_screenspace * offset_direction * width / 2.0;
vec2 offset_clipspace = project_pixel_size_to_clipspace(offset_screenspace);
return offset_clipspace;
return dir_screenspace * offset_direction * width / 2.0;
}
float getSegmentRatio(float index) {
Expand All @@ -89,6 +87,9 @@ void get_pos_fp64(vec2 source[2], vec2 target[2], float segmentRatio, out vec2 p
}
void main(void) {
geometry.worldPosition = vec3(instancePositions.xy, 0.0);
geometry.worldPositionAlt = vec3(instancePositions.zw, 0.0);
vec2 projected_source_coord[2];
vec2 projected_target_coord[2];
Expand All @@ -115,6 +116,9 @@ void main(void) {
vec4 curr_pos_clipspace = project_common_position_to_clipspace_fp64(curr_pos_modelspace);
vec4 next_pos_clipspace = project_common_position_to_clipspace_fp64(next_pos_modelspace);
geometry.position = vec4(curr_pos_modelspace[0].x, curr_pos_modelspace[1].x, curr_pos_modelspace[2].x, 1.0);
uv = positions.xy;
geometry.uv = uv;
// Multiply out width and clamp to limits
// mercator pixels are interpreted as screen pixels
Expand All @@ -123,12 +127,17 @@ void main(void) {
widthMinPixels, widthMaxPixels
);
vec2 offset = getExtrusionOffset(next_pos_clipspace.xy - curr_pos_clipspace.xy, positions.y, widthPixels);
vec3 offset = vec3(
getExtrusionOffset(next_pos_clipspace.xy - curr_pos_clipspace.xy, positions.y, widthPixels),
0.0);
DECKGL_FILTER_SIZE(offset, geometry);
gl_Position = curr_pos_clipspace + vec4(offset, 0.0, 0.0);
gl_Position = curr_pos_clipspace + vec4(project_pixel_size_to_clipspace(offset.xy), 0.0, 0.0);
DECKGL_FILTER_GL_POSITION(gl_Position, geometry);
vec4 color = mix(instanceSourceColors, instanceTargetColors, segmentRatio) / 255.;
vColor = vec4(color.rgb, color.a * opacity);
DECKGL_FILTER_COLOR(vColor, geometry);
// Set color to be rendered to picking fbo (also used to check for selection highlight).
picking_setPickingColor(instancePickingColors);
Expand Down
Loading

0 comments on commit bbe0ecd

Please sign in to comment.