@deck.gl/core
Version:
deck.gl core library
228 lines (219 loc) • 8.55 kB
JavaScript
// deck.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
import { COORDINATE_SYSTEM, PROJECTION_MODE } from "../../lib/constants.js";
import project from "../project/project.js";
import { Vector3, Matrix4 } from '@math.gl/core';
import memoize from "../../utils/memoize.js";
import { pixelsToWorld } from '@math.gl/web-mercator';
const uniformBlock = /* glsl */ `
uniform shadowUniforms {
bool drawShadowMap;
bool useShadowMap;
vec4 color;
highp int lightId;
float lightCount;
mat4 viewProjectionMatrix0;
mat4 viewProjectionMatrix1;
vec4 projectCenter0;
vec4 projectCenter1;
} shadow;
`;
const vertex = /* glsl */ `
const int max_lights = 2;
out vec3 shadow_vPosition[max_lights];
vec4 shadow_setVertexPosition(vec4 position_commonspace) {
mat4 viewProjectionMatrices[max_lights];
viewProjectionMatrices[0] = shadow.viewProjectionMatrix0;
viewProjectionMatrices[1] = shadow.viewProjectionMatrix1;
vec4 projectCenters[max_lights];
projectCenters[0] = shadow.projectCenter0;
projectCenters[1] = shadow.projectCenter1;
if (shadow.drawShadowMap) {
return project_common_position_to_clipspace(position_commonspace, viewProjectionMatrices[shadow.lightId], projectCenters[shadow.lightId]);
}
if (shadow.useShadowMap) {
for (int i = 0; i < max_lights; i++) {
if(i < int(shadow.lightCount)) {
vec4 shadowMap_position = project_common_position_to_clipspace(position_commonspace, viewProjectionMatrices[i], projectCenters[i]);
shadow_vPosition[i] = (shadowMap_position.xyz / shadowMap_position.w + 1.0) / 2.0;
}
}
}
return gl_Position;
}
`;
const vs = `
${uniformBlock}
${vertex}
`;
const fragment = /* glsl */ `
const int max_lights = 2;
uniform sampler2D shadow_uShadowMap0;
uniform sampler2D shadow_uShadowMap1;
in vec3 shadow_vPosition[max_lights];
const vec4 bitPackShift = vec4(1.0, 255.0, 65025.0, 16581375.0);
const vec4 bitUnpackShift = 1.0 / bitPackShift;
const vec4 bitMask = vec4(1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0, 0.0);
float shadow_getShadowWeight(vec3 position, sampler2D shadowMap) {
vec4 rgbaDepth = texture(shadowMap, position.xy);
float z = dot(rgbaDepth, bitUnpackShift);
return smoothstep(0.001, 0.01, position.z - z);
}
vec4 shadow_filterShadowColor(vec4 color) {
if (shadow.drawShadowMap) {
vec4 rgbaDepth = fract(gl_FragCoord.z * bitPackShift);
rgbaDepth -= rgbaDepth.gbaa * bitMask;
return rgbaDepth;
}
if (shadow.useShadowMap) {
float shadowAlpha = 0.0;
shadowAlpha += shadow_getShadowWeight(shadow_vPosition[0], shadow_uShadowMap0);
if(shadow.lightCount > 1.0) {
shadowAlpha += shadow_getShadowWeight(shadow_vPosition[1], shadow_uShadowMap1);
}
shadowAlpha *= shadow.color.a / shadow.lightCount;
float blendedAlpha = shadowAlpha + color.a * (1.0 - shadowAlpha);
return vec4(
mix(color.rgb, shadow.color.rgb, shadowAlpha / blendedAlpha),
blendedAlpha
);
}
return color;
}
`;
const fs = `
${uniformBlock}
${fragment}
`;
const getMemoizedViewportCenterPosition = memoize(getViewportCenterPosition);
const getMemoizedViewProjectionMatrices = memoize(getViewProjectionMatrices);
const DEFAULT_SHADOW_COLOR = [0, 0, 0, 1.0];
const VECTOR_TO_POINT_MATRIX = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0];
function screenToCommonSpace(xyz, pixelUnprojectionMatrix) {
const [x, y, z] = xyz;
const coord = pixelsToWorld([x, y, z], pixelUnprojectionMatrix);
if (Number.isFinite(z)) {
return coord;
}
return [coord[0], coord[1], 0];
}
function getViewportCenterPosition({ viewport, center }) {
return new Matrix4(viewport.viewProjectionMatrix).invert().transform(center);
}
function getViewProjectionMatrices({ viewport, shadowMatrices }) {
const projectionMatrices = [];
const pixelUnprojectionMatrix = viewport.pixelUnprojectionMatrix;
const farZ = viewport.isGeospatial ? undefined : 1;
const corners = [
[0, 0, farZ], // top left ground
[viewport.width, 0, farZ], // top right ground
[0, viewport.height, farZ], // bottom left ground
[viewport.width, viewport.height, farZ], // bottom right ground
[0, 0, -1], // top left near
[viewport.width, 0, -1], // top right near
[0, viewport.height, -1], // bottom left near
[viewport.width, viewport.height, -1] // bottom right near
].map(pixel =>
// @ts-expect-error z may be undefined
screenToCommonSpace(pixel, pixelUnprojectionMatrix));
for (const shadowMatrix of shadowMatrices) {
const viewMatrix = shadowMatrix.clone().translate(new Vector3(viewport.center).negate());
const positions = corners.map(corner => viewMatrix.transform(corner));
const projectionMatrix = new Matrix4().ortho({
left: Math.min(...positions.map(position => position[0])),
right: Math.max(...positions.map(position => position[0])),
bottom: Math.min(...positions.map(position => position[1])),
top: Math.max(...positions.map(position => position[1])),
near: Math.min(...positions.map(position => -position[2])),
far: Math.max(...positions.map(position => -position[2]))
});
projectionMatrices.push(projectionMatrix.multiplyRight(shadowMatrix));
}
return projectionMatrices;
}
/* eslint-disable camelcase */
// eslint-disable-next-line complexity
function createShadowUniforms(opts) {
const { shadowEnabled = true, project: projectProps } = opts;
if (!shadowEnabled || !projectProps || !opts.shadowMatrices || !opts.shadowMatrices.length) {
return {
drawShadowMap: false,
useShadowMap: false,
shadow_uShadowMap0: opts.dummyShadowMap,
shadow_uShadowMap1: opts.dummyShadowMap
};
}
const projectUniforms = project.getUniforms(projectProps);
const center = getMemoizedViewportCenterPosition({
viewport: projectProps.viewport,
center: projectUniforms.center
});
const projectCenters = [];
const viewProjectionMatrices = getMemoizedViewProjectionMatrices({
shadowMatrices: opts.shadowMatrices,
viewport: projectProps.viewport
}).slice();
for (let i = 0; i < opts.shadowMatrices.length; i++) {
const viewProjectionMatrix = viewProjectionMatrices[i];
const viewProjectionMatrixCentered = viewProjectionMatrix
.clone()
.translate(new Vector3(projectProps.viewport.center).negate());
if (projectUniforms.coordinateSystem === COORDINATE_SYSTEM.LNGLAT &&
projectUniforms.projectionMode === PROJECTION_MODE.WEB_MERCATOR) {
viewProjectionMatrices[i] = viewProjectionMatrixCentered;
projectCenters[i] = center;
}
else {
viewProjectionMatrices[i] = viewProjectionMatrix
.clone()
.multiplyRight(VECTOR_TO_POINT_MATRIX);
projectCenters[i] = viewProjectionMatrixCentered.transform(center);
}
}
const uniforms = {
drawShadowMap: Boolean(opts.drawToShadowMap),
useShadowMap: opts.shadowMaps ? opts.shadowMaps.length > 0 : false,
color: opts.shadowColor || DEFAULT_SHADOW_COLOR,
lightId: opts.shadowLightId || 0,
lightCount: opts.shadowMatrices.length,
shadow_uShadowMap0: opts.dummyShadowMap,
shadow_uShadowMap1: opts.dummyShadowMap
};
for (let i = 0; i < viewProjectionMatrices.length; i++) {
uniforms[`viewProjectionMatrix${i}`] = viewProjectionMatrices[i];
uniforms[`projectCenter${i}`] = projectCenters[i];
}
for (let i = 0; i < 2; i++) {
uniforms[`shadow_uShadowMap${i}`] =
(opts.shadowMaps && opts.shadowMaps[i]) || opts.dummyShadowMap;
}
return uniforms;
}
export default {
name: 'shadow',
dependencies: [project],
vs,
fs,
inject: {
'vs:DECKGL_FILTER_GL_POSITION': `
position = shadow_setVertexPosition(geometry.position);
`,
'fs:DECKGL_FILTER_COLOR': `
color = shadow_filterShadowColor(color);
`
},
getUniforms: createShadowUniforms,
uniformTypes: {
drawShadowMap: 'f32',
useShadowMap: 'f32',
color: 'vec4<f32>',
lightId: 'i32',
lightCount: 'f32',
viewProjectionMatrix0: 'mat4x4<f32>',
viewProjectionMatrix1: 'mat4x4<f32>',
projectCenter0: 'vec4<f32>',
projectCenter1: 'vec4<f32>'
}
};
//# sourceMappingURL=shadow.js.map