Spaces:
Sleeping
Sleeping
| // @flow | |
| import Texture from './texture'; | |
| import StencilMode from '../gl/stencil_mode'; | |
| import DepthMode from '../gl/depth_mode'; | |
| import CullFaceMode from '../gl/cull_face_mode'; | |
| import { | |
| hillshadeUniformValues, | |
| hillshadeUniformPrepareValues | |
| } from './program/hillshade_program'; | |
| import type Painter from './painter'; | |
| import type SourceCache from '../source/source_cache'; | |
| import type HillshadeStyleLayer from '../style/style_layer/hillshade_style_layer'; | |
| import type {OverscaledTileID} from '../source/tile_id'; | |
| export default drawHillshade; | |
| function drawHillshade(painter: Painter, sourceCache: SourceCache, layer: HillshadeStyleLayer, tileIDs: Array<OverscaledTileID>) { | |
| if (painter.renderPass !== 'offscreen' && painter.renderPass !== 'translucent') return; | |
| const context = painter.context; | |
| const depthMode = painter.depthModeForSublayer(0, DepthMode.ReadOnly); | |
| const colorMode = painter.colorModeForRenderPass(); | |
| const [stencilModes, coords] = painter.renderPass === 'translucent' ? | |
| painter.stencilConfigForOverlap(tileIDs) : [{}, tileIDs]; | |
| for (const coord of coords) { | |
| const tile = sourceCache.getTile(coord); | |
| if (tile.needsHillshadePrepare && painter.renderPass === 'offscreen') { | |
| prepareHillshade(painter, tile, layer, depthMode, StencilMode.disabled, colorMode); | |
| } else if (painter.renderPass === 'translucent') { | |
| renderHillshade(painter, tile, layer, depthMode, stencilModes[coord.overscaledZ], colorMode); | |
| } | |
| } | |
| context.viewport.set([0, 0, painter.width, painter.height]); | |
| } | |
| function renderHillshade(painter, tile, layer, depthMode, stencilMode, colorMode) { | |
| const context = painter.context; | |
| const gl = context.gl; | |
| const fbo = tile.fbo; | |
| if (!fbo) return; | |
| const program = painter.useProgram('hillshade'); | |
| context.activeTexture.set(gl.TEXTURE0); | |
| gl.bindTexture(gl.TEXTURE_2D, fbo.colorAttachment.get()); | |
| const uniformValues = hillshadeUniformValues(painter, tile, layer); | |
| program.draw(context, gl.TRIANGLES, depthMode, stencilMode, colorMode, CullFaceMode.disabled, | |
| uniformValues, layer.id, painter.rasterBoundsBuffer, | |
| painter.quadTriangleIndexBuffer, painter.rasterBoundsSegments); | |
| } | |
| // hillshade rendering is done in two steps. the prepare step first calculates the slope of the terrain in the x and y | |
| // directions for each pixel, and saves those values to a framebuffer texture in the r and g channels. | |
| function prepareHillshade(painter, tile, layer, depthMode, stencilMode, colorMode) { | |
| const context = painter.context; | |
| const gl = context.gl; | |
| const dem = tile.dem; | |
| if (dem && dem.data) { | |
| const tileSize = dem.dim; | |
| const textureStride = dem.stride; | |
| const pixelData = dem.getPixels(); | |
| context.activeTexture.set(gl.TEXTURE1); | |
| context.pixelStoreUnpackPremultiplyAlpha.set(false); | |
| tile.demTexture = tile.demTexture || painter.getTileTexture(textureStride); | |
| if (tile.demTexture) { | |
| const demTexture = tile.demTexture; | |
| demTexture.update(pixelData, {premultiply: false}); | |
| demTexture.bind(gl.NEAREST, gl.CLAMP_TO_EDGE); | |
| } else { | |
| tile.demTexture = new Texture(context, pixelData, gl.RGBA, {premultiply: false}); | |
| tile.demTexture.bind(gl.NEAREST, gl.CLAMP_TO_EDGE); | |
| } | |
| context.activeTexture.set(gl.TEXTURE0); | |
| let fbo = tile.fbo; | |
| if (!fbo) { | |
| const renderTexture = new Texture(context, {width: tileSize, height: tileSize, data: null}, gl.RGBA); | |
| renderTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE); | |
| fbo = tile.fbo = context.createFramebuffer(tileSize, tileSize, true); | |
| fbo.colorAttachment.set(renderTexture.texture); | |
| } | |
| context.bindFramebuffer.set(fbo.framebuffer); | |
| context.viewport.set([0, 0, tileSize, tileSize]); | |
| painter.useProgram('hillshadePrepare').draw(context, gl.TRIANGLES, | |
| depthMode, stencilMode, colorMode, CullFaceMode.disabled, | |
| hillshadeUniformPrepareValues(tile.tileID, dem), | |
| layer.id, painter.rasterBoundsBuffer, | |
| painter.quadTriangleIndexBuffer, painter.rasterBoundsSegments); | |
| tile.needsHillshadePrepare = false; | |
| } | |
| } | |