UNPKG

mapbox-gl

Version:
250 lines (196 loc) 9.97 kB
// @flow import Color from '../style-spec/util/color.js'; import StencilMode from '../gl/stencil_mode.js'; import DepthMode from '../gl/depth_mode.js'; import {default as ColorMode, ZERO, ONE, ONE_MINUS_SRC_ALPHA} from '../gl/color_mode.js'; import CullFaceMode from '../gl/cull_face_mode.js'; import { globeToMercatorTransition, globeUseCustomAntiAliasing } from './../geo/projection/globe_util.js'; import {atmosphereUniformValues} from '../terrain/globe_raster_program.js'; import {AtmosphereBuffer} from '../render/atmosphere_buffer.js'; import {degToRad, mapValue, clamp} from '../util/util.js'; import {mat3, vec3, mat4, quat} from 'gl-matrix'; import Fog from '../style/fog.js'; import SegmentVector from '../data/segment.js'; import {TriangleIndexArray, StarsVertexArray} from '../data/array_types.js'; import {starsLayout} from './stars_attributes.js'; import {starsUniformValues} from '../terrain/stars_program.js'; import {mulberry32} from '../style-spec/util/random.js'; import type Painter from './painter.js'; import type {Vec3} from 'gl-matrix'; import type IndexBuffer from '../gl/index_buffer.js'; import type VertexBuffer from '../gl/vertex_buffer.js'; import type {DynamicDefinesType} from './program/program_uniforms.js'; function generateUniformDistributedPointsOnSphere(pointsCount: number): Array<Vec3> { const sRand = mulberry32(30); const points: Array<Vec3> = []; for (let i = 0; i < pointsCount; ++i) { const lon = 2 * Math.PI * sRand(); const lat = Math.acos(1 - 2 * sRand()) - Math.PI * 0.5; points.push(vec3.fromValues(Math.cos(lat) * Math.cos(lon), Math.cos(lat) * Math.sin(lon), Math.sin(lat))); } return points; } class StarsParams { starsCount: number; sizeMultiplier: number; constructor() { this.starsCount = 16000; this.sizeMultiplier = 0.15; } } class Atmosphere { atmosphereBuffer: ?AtmosphereBuffer; allocatedStarsCount: number; starsVx: ?VertexBuffer; starsIdx: ?IndexBuffer; starsSegments: SegmentVector; colorModeAlphaBlendedWriteRGB: ColorMode; colorModeWriteAlpha: ColorMode; params: StarsParams; constructor(painter: Painter) { this.colorModeAlphaBlendedWriteRGB = new ColorMode([ONE, ONE_MINUS_SRC_ALPHA, ONE, ONE_MINUS_SRC_ALPHA], Color.transparent, [true, true, true, false]); this.colorModeWriteAlpha = new ColorMode([ONE, ZERO, ONE, ZERO], Color.transparent, [false, false, false, true]); this.params = new StarsParams(); this.allocatedStarsCount = 0; painter.tp.registerParameter(this.params, ["Stars"], "starsCount", {min:100, max: 16000, step:1}); painter.tp.registerParameter(this.params, ["Stars"], "sizeMultiplier", {min:0.01, max: 2.0, step:0.01}); } update(painter: Painter) { const context = painter.context; if (!this.atmosphereBuffer || this.allocatedStarsCount !== this.params.starsCount) { this.atmosphereBuffer = new AtmosphereBuffer(context); this.allocatedStarsCount = this.params.starsCount; // Part of internal stlye spec, not exposed to gl-js const sizeRange = 100.0; const intensityRange = 200.0; const stars = generateUniformDistributedPointsOnSphere(this.allocatedStarsCount); const sRand = mulberry32(300); const vertices = new StarsVertexArray(); const triangles = new TriangleIndexArray(); let base = 0; for (let i = 0; i < stars.length; ++i) { const star = vec3.scale([], stars[i], 200.0); const size = Math.max(0, 1.0 + 0.01 * sizeRange * (-0.5 + 1.0 * sRand())); const intensity = Math.max(0, 1.0 + 0.01 * intensityRange * (-0.5 + 1.0 * sRand())); vertices.emplaceBack(star[0], star[1], star[2], -1, -1, size, intensity); vertices.emplaceBack(star[0], star[1], star[2], 1, -1, size, intensity); vertices.emplaceBack(star[0], star[1], star[2], 1, 1, size, intensity); vertices.emplaceBack(star[0], star[1], star[2], -1, 1, size, intensity); triangles.emplaceBack(base + 0, base + 1, base + 2); triangles.emplaceBack(base + 0, base + 2, base + 3); base += 4; } this.starsVx = context.createVertexBuffer(vertices, starsLayout.members); this.starsIdx = context.createIndexBuffer(triangles); this.starsSegments = SegmentVector.simpleSegment(0, 0, vertices.length, triangles.length); } } destroy() { if (this.atmosphereBuffer) { this.atmosphereBuffer.destroy(); } if (this.starsVx) { this.starsVx.destroy(); } if (this.starsIdx) { this.starsIdx.destroy(); } } drawAtmosphereGlow(painter: Painter, fog: Fog) { const context = painter.context; const gl = context.gl; const tr = painter.transform; const depthMode = new DepthMode(gl.LEQUAL, DepthMode.ReadOnly, [0, 1]); const transitionT = globeToMercatorTransition(tr.zoom); const fogColor = fog.properties.get('color').toArray01(); const highColor = fog.properties.get('high-color').toArray01(); const spaceColor = fog.properties.get('space-color').toArray01PremultipliedAlpha(); // https://www.desmos.com/calculator/oanvvpr36d // Ensure horizon blend is 0-exclusive to prevent division by 0 in the shader const minHorizonBlend = 0.0005; const horizonBlend = mapValue(fog.properties.get('horizon-blend'), 0.0, 1.0, minHorizonBlend, 0.25); // Use a slightly smaller size of the globe to account for custom // antialiasing that reduces the size of the globe of two pixels // https://www.desmos.com/calculator/xpgmzghc37 const globeRadius = globeUseCustomAntiAliasing(painter, context, tr) && horizonBlend === minHorizonBlend ? tr.worldSize / (2.0 * Math.PI * 1.025) - 1.0 : tr.globeRadius; const temporalOffset = (painter.frameCounter / 1000.0) % 1; const globeCenterInViewSpace = (((tr.globeCenterInViewSpace): any): Array<number>); const globeCenterDistance = vec3.length(globeCenterInViewSpace); const distanceToHorizon = Math.sqrt(Math.pow(globeCenterDistance, 2.0) - Math.pow(globeRadius, 2.0)); const horizonAngle = Math.acos(distanceToHorizon / globeCenterDistance); const draw = (alphaPass: boolean) => { const defines = tr.projection.name === 'globe' ? ['PROJECTION_GLOBE_VIEW', 'FOG'] : ['FOG']; if (alphaPass) { defines.push("ALPHA_PASS"); } const program = painter.getOrCreateProgram('globeAtmosphere', {defines: ((defines: any): DynamicDefinesType[])}); const uniforms = atmosphereUniformValues( tr.frustumCorners.TL, tr.frustumCorners.TR, tr.frustumCorners.BR, tr.frustumCorners.BL, tr.frustumCorners.horizon, transitionT, horizonBlend, fogColor, highColor, spaceColor, temporalOffset, horizonAngle); painter.uploadCommonUniforms(context, program); const buffer = this.atmosphereBuffer; const colorMode = alphaPass ? this.colorModeWriteAlpha : this.colorModeAlphaBlendedWriteRGB; const name = alphaPass ? "atmosphere_glow_alpha" : "atmosphere_glow"; if (buffer) { program.draw(painter, gl.TRIANGLES, depthMode, StencilMode.disabled, colorMode, CullFaceMode.backCW, uniforms, name, buffer.vertexBuffer, buffer.indexBuffer, buffer.segments); } }; // Write atmosphere color draw(false); // Write atmosphere alpha - transparent areas need to be explicitly marked in a separate pass draw(true); } drawStars(painter: Painter, fog: Fog) { const starIntensity = clamp(fog.properties.get('star-intensity'), 0.0, 1.0); if (starIntensity === 0) { return; } const context = painter.context; const gl = context.gl; const tr = painter.transform; const program = painter.getOrCreateProgram('stars'); const orientation = quat.identity([]); quat.rotateX(orientation, orientation, -tr._pitch); quat.rotateZ(orientation, orientation, -tr.angle); quat.rotateX(orientation, orientation, degToRad(tr._center.lat)); quat.rotateY(orientation, orientation, -degToRad(tr._center.lng)); const rotationMatrix = mat4.fromQuat(new Float32Array(16), orientation); const mvp = mat4.multiply([], tr.starsProjMatrix, rotationMatrix); const modelView3 = mat3.fromMat4([], rotationMatrix); const modelviewInv = mat3.invert([], modelView3); const camUp = [0, 1, 0]; vec3.transformMat3(camUp, camUp, modelviewInv); vec3.scale(camUp, camUp, this.params.sizeMultiplier); const camRight = [1, 0, 0]; vec3.transformMat3(camRight, camRight, modelviewInv); vec3.scale(camRight, camRight, this.params.sizeMultiplier); const uniforms = starsUniformValues( mvp, camUp, camRight, starIntensity); painter.uploadCommonUniforms(context, program); if (this.starsVx && this.starsIdx) { program.draw(painter, gl.TRIANGLES, DepthMode.disabled, StencilMode.disabled, this.colorModeAlphaBlendedWriteRGB, CullFaceMode.disabled, uniforms, "atmosphere_stars", this.starsVx, this.starsIdx, this.starsSegments); } } } export default Atmosphere;