diff --git a/src/render/draw_fill_extrusion.js b/src/render/draw_fill_extrusion.js index 32d865821bd..40f1f03dac1 100644 --- a/src/render/draw_fill_extrusion.js +++ b/src/render/draw_fill_extrusion.js @@ -70,9 +70,10 @@ function drawExtrusionTiles(painter, source, layer, coords, depthMode, stencilMo layer.paint.get('fill-extrusion-translate'), layer.paint.get('fill-extrusion-translate-anchor')); + const shouldUseVerticalGradient = layer.paint.get('fill-extrusion-vertical-gradient'); const uniformValues = image ? - fillExtrusionPatternUniformValues(matrix, painter, coord, crossfade, tile) : - fillExtrusionUniformValues(matrix, painter); + fillExtrusionPatternUniformValues(matrix, painter, shouldUseVerticalGradient, coord, crossfade, tile) : + fillExtrusionUniformValues(matrix, painter, shouldUseVerticalGradient); program.draw(context, context.gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.backCCW, diff --git a/src/render/program/fill_extrusion_program.js b/src/render/program/fill_extrusion_program.js index bdb1d63a4b8..080420a9b37 100644 --- a/src/render/program/fill_extrusion_program.js +++ b/src/render/program/fill_extrusion_program.js @@ -24,7 +24,8 @@ export type FillExtrusionUniformsType = {| 'u_matrix': UniformMatrix4f, 'u_lightpos': Uniform3f, 'u_lightintensity': Uniform1f, - 'u_lightcolor': Uniform3f + 'u_lightcolor': Uniform3f, + 'u_vertical_gradient': Uniform1f |}; export type FillExtrusionPatternUniformsType = {| @@ -33,6 +34,7 @@ export type FillExtrusionPatternUniformsType = {| 'u_lightintensity': Uniform1f, 'u_lightcolor': Uniform3f, 'u_height_factor': Uniform1f, + 'u_vertical_gradient': Uniform1f, // pattern uniforms: 'u_texsize': Uniform2f, 'u_image': Uniform1i, @@ -53,7 +55,8 @@ const fillExtrusionUniforms = (context: Context, locations: UniformLocations): F 'u_matrix': new UniformMatrix4f(context, locations.u_matrix), 'u_lightpos': new Uniform3f(context, locations.u_lightpos), 'u_lightintensity': new Uniform1f(context, locations.u_lightintensity), - 'u_lightcolor': new Uniform3f(context, locations.u_lightcolor) + 'u_lightcolor': new Uniform3f(context, locations.u_lightcolor), + 'u_vertical_gradient': new Uniform1f(context, locations.u_vertical_gradient) }); const fillExtrusionPatternUniforms = (context: Context, locations: UniformLocations): FillExtrusionPatternUniformsType => ({ @@ -61,6 +64,7 @@ const fillExtrusionPatternUniforms = (context: Context, locations: UniformLocati 'u_lightpos': new Uniform3f(context, locations.u_lightpos), 'u_lightintensity': new Uniform1f(context, locations.u_lightintensity), 'u_lightcolor': new Uniform3f(context, locations.u_lightcolor), + 'u_vertical_gradient': new Uniform1f(context, locations.u_vertical_gradient), 'u_height_factor': new Uniform1f(context, locations.u_height_factor), // pattern uniforms 'u_image': new Uniform1i(context, locations.u_image), @@ -80,7 +84,8 @@ const extrusionTextureUniforms = (context: Context, locations: UniformLocations) const fillExtrusionUniformValues = ( matrix: Float32Array, - painter: Painter + painter: Painter, + shouldUseVerticalGradient: boolean ): UniformValues => { const light = painter.style.light; const _lp = light.properties.get('position'); @@ -97,18 +102,20 @@ const fillExtrusionUniformValues = ( 'u_matrix': matrix, 'u_lightpos': lightPos, 'u_lightintensity': light.properties.get('intensity'), - 'u_lightcolor': [lightColor.r, lightColor.g, lightColor.b] + 'u_lightcolor': [lightColor.r, lightColor.g, lightColor.b], + 'u_vertical_gradient': +shouldUseVerticalGradient }; }; const fillExtrusionPatternUniformValues = ( matrix: Float32Array, painter: Painter, + shouldUseVerticalGradient: boolean, coord: OverscaledTileID, crossfade: CrossfadeParameters, tile: Tile ): UniformValues => { - return extend(fillExtrusionUniformValues(matrix, painter), + return extend(fillExtrusionUniformValues(matrix, painter, shouldUseVerticalGradient), patternUniformValues(crossfade, painter, tile), { 'u_height_factor': -Math.pow(2, coord.overscaledZ) / tile.tileSize / 8 diff --git a/src/shaders/fill_extrusion.vertex.glsl b/src/shaders/fill_extrusion.vertex.glsl index 3256c08c72e..df1b8556227 100644 --- a/src/shaders/fill_extrusion.vertex.glsl +++ b/src/shaders/fill_extrusion.vertex.glsl @@ -2,6 +2,7 @@ uniform mat4 u_matrix; uniform vec3 u_lightcolor; uniform lowp vec3 u_lightpos; uniform lowp float u_lightintensity; +uniform float u_vertical_gradient; attribute vec2 a_pos; attribute vec4 a_normal_ed; @@ -47,7 +48,11 @@ void main() { // Add gradient along z axis of side surfaces if (normal.y != 0.0) { - directional *= clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - u_lightintensity), 1.0); + // This avoids another branching statement, but multiplies by a constant of 0.84 if no vertical gradient, + // and otherwise calculates the gradient based on base + height + directional *= ( + (1.0 - u_vertical_gradient) + + (u_vertical_gradient * clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - u_lightintensity), 1.0))); } // Assign final color based on surface + ambient light color, diffuse light directional, and light color diff --git a/src/shaders/fill_extrusion_pattern.vertex.glsl b/src/shaders/fill_extrusion_pattern.vertex.glsl index 0acd3bd2a94..4c314f19bff 100644 --- a/src/shaders/fill_extrusion_pattern.vertex.glsl +++ b/src/shaders/fill_extrusion_pattern.vertex.glsl @@ -3,6 +3,7 @@ uniform vec2 u_pixel_coord_upper; uniform vec2 u_pixel_coord_lower; uniform float u_height_factor; uniform vec4 u_scale; +uniform float u_vertical_gradient; uniform vec3 u_lightcolor; uniform lowp vec3 u_lightpos; @@ -62,7 +63,11 @@ void main() { directional = mix((1.0 - u_lightintensity), max((0.5 + u_lightintensity), 1.0), directional); if (normal.y != 0.0) { - directional *= clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - u_lightintensity), 1.0); + // This avoids another branching statement, but multiplies by a constant of 0.84 if no vertical gradient, + // and otherwise calculates the gradient based on base + height + directional *= ( + (1.0 - u_vertical_gradient) + + (u_vertical_gradient * clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - u_lightintensity), 1.0))); } v_lighting.rgb += clamp(directional * u_lightcolor, mix(vec3(0.0), vec3(0.3), 1.0 - u_lightcolor), vec3(1.0)); diff --git a/src/style-spec/reference/v8.json b/src/style-spec/reference/v8.json index a817aa6c8de..9c686f719dd 100644 --- a/src/style-spec/reference/v8.json +++ b/src/style-spec/reference/v8.json @@ -3755,6 +3755,22 @@ ] }, "property-type": "data-driven" + }, + "fill-extrusion-vertical-gradient": { + "type": "boolean", + "default": true, + "doc": "Whether to apply a vertical gradient to the sides of a fill-extrusion layer. If true, sides will be shaded slightly darker farther down.", + "transition": false, + "sdk-support": { + "basic functionality": { + "js": "0.50.0" + } + }, + "expression": { + "interpolated": false, + "parameters": ["zoom"] + }, + "property-type": "data-constant" } }, "paint_line": { diff --git a/src/style-spec/types.js b/src/style-spec/types.js index 2e7c5d30166..5690824235e 100644 --- a/src/style-spec/types.js +++ b/src/style-spec/types.js @@ -333,7 +333,8 @@ export type FillExtrusionLayerSpecification = {| "fill-extrusion-translate-anchor"?: PropertyValueSpecification<"map" | "viewport">, "fill-extrusion-pattern"?: DataDrivenPropertyValueSpecification, "fill-extrusion-height"?: DataDrivenPropertyValueSpecification, - "fill-extrusion-base"?: DataDrivenPropertyValueSpecification + "fill-extrusion-base"?: DataDrivenPropertyValueSpecification, + "fill-extrusion-vertical-gradient"?: PropertyValueSpecification |} |} diff --git a/src/style/style_layer/fill_extrusion_style_layer_properties.js b/src/style/style_layer/fill_extrusion_style_layer_properties.js index c8e27695110..4aa0360fa30 100644 --- a/src/style/style_layer/fill_extrusion_style_layer_properties.js +++ b/src/style/style_layer/fill_extrusion_style_layer_properties.js @@ -26,6 +26,7 @@ export type PaintProps = {| "fill-extrusion-pattern": CrossFadedDataDrivenProperty, "fill-extrusion-height": DataDrivenProperty, "fill-extrusion-base": DataDrivenProperty, + "fill-extrusion-vertical-gradient": DataConstantProperty, |}; const paint: Properties = new Properties({ @@ -36,6 +37,7 @@ const paint: Properties = new Properties({ "fill-extrusion-pattern": new CrossFadedDataDrivenProperty(styleSpec["paint_fill-extrusion"]["fill-extrusion-pattern"]), "fill-extrusion-height": new DataDrivenProperty(styleSpec["paint_fill-extrusion"]["fill-extrusion-height"]), "fill-extrusion-base": new DataDrivenProperty(styleSpec["paint_fill-extrusion"]["fill-extrusion-base"]), + "fill-extrusion-vertical-gradient": new DataConstantProperty(styleSpec["paint_fill-extrusion"]["fill-extrusion-vertical-gradient"]), }); // Note: without adding the explicit type annotation, Flow infers weaker types diff --git a/test/integration/render-tests/fill-extrusion-vertical-gradient/default/expected.png b/test/integration/render-tests/fill-extrusion-vertical-gradient/default/expected.png new file mode 100644 index 00000000000..ce1c4f52c99 Binary files /dev/null and b/test/integration/render-tests/fill-extrusion-vertical-gradient/default/expected.png differ diff --git a/test/integration/render-tests/fill-extrusion-vertical-gradient/default/style.json b/test/integration/render-tests/fill-extrusion-vertical-gradient/default/style.json new file mode 100644 index 00000000000..405797c646a --- /dev/null +++ b/test/integration/render-tests/fill-extrusion-vertical-gradient/default/style.json @@ -0,0 +1,101 @@ +{ + "version": 8, + "metadata": { + "test": { + "height": 256 + } + }, + "light": { + "intensity": 1 + }, + "sources": { + "geojson": { + "type": "geojson", + "data": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + "property": 0 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -0.0003, + -0.0003 + ], + [ + -0.0003, + 0.0003 + ], + [ + 0.0003, + 0.0003 + ], + [ + 0.0003, + -0.0003 + ], + [ + -0.0003, + -0.0003 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "property": 20 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -0.0003, + -0.0003 + ], + [ + -0.0003, + 0.0003 + ], + [ + 0.0003, + 0.0003 + ], + [ + 0.0003, + -0.0003 + ], + [ + -0.0003, + -0.0003 + ] + ] + ] + } + } + ] + } + } + }, + "pitch": 60, + "zoom": 18, + "layers": [ + { + "id": "extrusion", + "type": "fill-extrusion", + "source": "geojson", + "paint": { + "fill-extrusion-height": ["+", ["get", "property"], 18], + "fill-extrusion-base": ["get", "property"], + "fill-extrusion-color": "#999" + } + } + ] +} diff --git a/test/integration/render-tests/fill-extrusion-vertical-gradient/false/expected.png b/test/integration/render-tests/fill-extrusion-vertical-gradient/false/expected.png new file mode 100644 index 00000000000..1ce43e1d89c Binary files /dev/null and b/test/integration/render-tests/fill-extrusion-vertical-gradient/false/expected.png differ diff --git a/test/integration/render-tests/fill-extrusion-vertical-gradient/false/style.json b/test/integration/render-tests/fill-extrusion-vertical-gradient/false/style.json new file mode 100644 index 00000000000..7b7d5a6af3b --- /dev/null +++ b/test/integration/render-tests/fill-extrusion-vertical-gradient/false/style.json @@ -0,0 +1,102 @@ +{ + "version": 8, + "metadata": { + "test": { + "height": 256 + } + }, + "light": { + "intensity": 1 + }, + "sources": { + "geojson": { + "type": "geojson", + "data": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + "property": 0 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -0.0003, + -0.0003 + ], + [ + -0.0003, + 0.0003 + ], + [ + 0.0003, + 0.0003 + ], + [ + 0.0003, + -0.0003 + ], + [ + -0.0003, + -0.0003 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "property": 20 + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -0.0003, + -0.0003 + ], + [ + -0.0003, + 0.0003 + ], + [ + 0.0003, + 0.0003 + ], + [ + 0.0003, + -0.0003 + ], + [ + -0.0003, + -0.0003 + ] + ] + ] + } + } + ] + } + } + }, + "pitch": 60, + "zoom": 18, + "layers": [ + { + "id": "extrusion", + "type": "fill-extrusion", + "source": "geojson", + "paint": { + "fill-extrusion-height": ["+", ["get", "property"], 18], + "fill-extrusion-base": ["get", "property"], + "fill-extrusion-color": "#999", + "fill-extrusion-vertical-gradient": false + } + } + ] +}