Skip to content

Commit

Permalink
feat(web): support IBL for terrain (#529)
Browse files Browse the repository at this point in the history
  • Loading branch information
keiya01 committed Jul 19, 2023
1 parent 99f5ad8 commit 7439637
Show file tree
Hide file tree
Showing 20 changed files with 618 additions and 55 deletions.
4 changes: 3 additions & 1 deletion web/package.json
Expand Up @@ -107,6 +107,7 @@
"@sentry/browser": "7.51.2",
"@seznam/compose-react-refs": "1.0.6",
"@turf/turf": "6.5.0",
"@types/escape-string-regexp": "2.0.1",
"@ungap/event-target": "0.2.4",
"apollo-link-sentry": "3.2.3",
"apollo-upload-client": "17.0.0",
Expand All @@ -122,6 +123,7 @@
"date-fns": "2.30.0",
"dayjs": "1.11.7",
"detect-browser": "5.3.0",
"escape-string-regexp": "5.0.0",
"fast-xml-parser": "4.2.2",
"formik": "2.2.9",
"github-markdown-css": "5.2.0",
Expand Down Expand Up @@ -175,4 +177,4 @@
"use-file-input": "1.0.0",
"uuid": "9.0.0"
}
}
}
3 changes: 3 additions & 0 deletions web/src/beta/lib/core/Map/types/index.ts
Expand Up @@ -249,6 +249,7 @@ export type TerrainProperty = {
terrainCesiumIonAccessToken?: string;
terrainCesiumIonUrl?: string;
terrainUrl?: string;
terrainNormal?: boolean;
};

export type SceneProperty = {
Expand Down Expand Up @@ -296,6 +297,8 @@ export type SceneProperty = {
brightness_shift?: number;
hue_shift?: number;
surturation_shift?: number;
globeShadowDarkness?: number;
globeImageBasedLighting?: boolean;
};
timeline?: {
animation?: boolean;
Expand Down
31 changes: 8 additions & 23 deletions web/src/beta/lib/core/engines/Cesium/Feature/Model/index.tsx
Expand Up @@ -13,8 +13,9 @@ import { toColor } from "@reearth/beta/utils/value";

import type { ModelAppearance } from "../../..";
import { colorBlendMode, heightReference, shadowMode } from "../../common";
import { NonPBRLightingShader } from "../../CustomShaders/NonPBRLightingShader";
import { arrayToCartecian3 } from "../../helpers/sphericalHaromic";
import { useSceneEvent } from "../../hooks/useSceneEvent";
import { NonPBRLightingShader } from "../../Shaders/CustomShaders/NonPBRLightingShader";
import {
EntityExt,
extractSimpleLayerData,
Expand All @@ -31,18 +32,6 @@ export type Property = ModelAppearance & {
height?: number;
};

const sphericalHarmonicCoefficientsScratch = [
new Cartesian3(),
new Cartesian3(),
new Cartesian3(),
new Cartesian3(),
new Cartesian3(),
new Cartesian3(),
new Cartesian3(),
new Cartesian3(),
new Cartesian3(),
];

export default function Model({
id,
isVisible,
Expand Down Expand Up @@ -135,23 +124,19 @@ export default function Model({

const specularEnvironmentMaps =
property?.specularEnvironmentMaps ?? sceneProperty?.light?.specularEnvironmentMaps;
const sphericalHarmonicCoefficients =
property?.sphericalHarmonicCoefficients ??
sceneProperty?.light?.sphericalHarmonicCoefficients;
const imageBasedLightIntensity =
property?.imageBasedLightIntensity ?? sceneProperty?.light?.imageBasedLightIntensity;
const sphericalHarmonicCoefficients = arrayToCartecian3(
property?.sphericalHarmonicCoefficients ??
sceneProperty?.light?.sphericalHarmonicCoefficients,
imageBasedLightIntensity,
);

if (specularEnvironmentMaps) {
ibl.specularEnvironmentMaps = specularEnvironmentMaps;
}
if (sphericalHarmonicCoefficients) {
ibl.sphericalHarmonicCoefficients = sphericalHarmonicCoefficients?.map((cartesian, index) =>
Cartesian3.multiplyByScalar(
new Cartesian3(...cartesian),
imageBasedLightIntensity ?? 1.0,
sphericalHarmonicCoefficientsScratch[index],
),
);
ibl.sphericalHarmonicCoefficients = sphericalHarmonicCoefficients;
}
return ibl;
}, [
Expand Down
29 changes: 7 additions & 22 deletions web/src/beta/lib/core/engines/Cesium/Feature/Tileset/hooks.ts
Expand Up @@ -30,6 +30,7 @@ import type {
Cesium3DTilesAppearance,
} from "../../..";
import { layerIdField, sampleTerrainHeightFromCartesian } from "../../common";
import { arrayToCartecian3 } from "../../helpers/sphericalHaromic";
import type { InternalCesium3DTileFeature } from "../../types";
import { lookupFeatures, translationWithClamping } from "../../utils";
import {
Expand All @@ -45,18 +46,6 @@ import { useClippingBox } from "./useClippingBox";

import { Property } from ".";

const sphericalHarmonicCoefficientsScratch = [
new Cartesian3(),
new Cartesian3(),
new Cartesian3(),
new Cartesian3(),
new Cartesian3(),
new Cartesian3(),
new Cartesian3(),
new Cartesian3(),
new Cartesian3(),
];

const useData = (layer: ComputedLayer | undefined) => {
return useMemo(() => {
const data = extractSimpleLayerData(layer);
Expand Down Expand Up @@ -542,23 +531,19 @@ export const useHooks = ({
const ibl = new ImageBasedLighting();
const specularEnvironmentMaps =
property?.specularEnvironmentMaps ?? sceneProperty?.light?.specularEnvironmentMaps;
const sphericalHarmonicCoefficients =
property?.sphericalHarmonicCoefficients ??
sceneProperty?.light?.sphericalHarmonicCoefficients;
const imageBasedLightIntensity =
property?.imageBasedLightIntensity ?? sceneProperty?.light?.imageBasedLightIntensity;
const sphericalHarmonicCoefficients = arrayToCartecian3(
property?.sphericalHarmonicCoefficients ??
sceneProperty?.light?.sphericalHarmonicCoefficients,
imageBasedLightIntensity,
);

if (specularEnvironmentMaps) {
ibl.specularEnvironmentMaps = specularEnvironmentMaps;
}
if (sphericalHarmonicCoefficients) {
ibl.sphericalHarmonicCoefficients = sphericalHarmonicCoefficients?.map((cartesian, index) =>
Cartesian3.multiplyByScalar(
new Cartesian3(...cartesian),
imageBasedLightIntensity ?? 1.0,
sphericalHarmonicCoefficientsScratch[index],
),
);
ibl.sphericalHarmonicCoefficients = sphericalHarmonicCoefficients;
}
return ibl;
}, [
Expand Down
Expand Up @@ -3,7 +3,7 @@ import { Cesium3DTileset } from "resium";

import type { Cesium3DTilesAppearance, ComputedLayer } from "../../..";
import { colorBlendModeFor3DTile, shadowMode } from "../../common";
import { NonPBRLightingShader } from "../../CustomShaders/NonPBRLightingShader";
import { NonPBRLightingShader } from "../../Shaders/CustomShaders/NonPBRLightingShader";
import Box from "../Box";
import { type FeatureComponentConfig, type FeatureProps } from "../utils";

Expand Down
@@ -0,0 +1,57 @@
// This file refers this implementation:
// https://github.com/takram-design-engineering/plateau-view/blob/8ea8bf1d5ef64319d92d0eb05b936cca7f1a2e8f/libs/cesium/src/shaders/imageBasedLightingStage.glsl

// Derived from:
// https://github.com/CesiumGS/cesium/blob/1.106/packages/engine/Source/Shaders/Model/ImageBasedLightingStageFS.glsl
// Specular term was removed, as I never apply it on terrain.
vec3 reearth_imageBasedLightingStage(vec3 positionEC, vec3 normalEC,
vec3 lightDirectionEC, vec3 lightColorHdr,
czm_pbrParameters pbrParameters) {
vec3 v = -positionEC;
vec3 n = normalEC;
vec3 l = normalize(lightDirectionEC);
vec3 h = normalize(v + l);
float NdotV = abs(dot(n, v)) + 0.001;
float VdotH = clamp(dot(v, h), 0.0, 1.0);

const mat3 yUpToZUp = mat3(-1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 1.0, 0.0);
// Reference frame matrix can be computed only by world position and normal.
mat3 referenceFrameMatrix =
czm_transpose(czm_eastNorthUpToEyeCoordinates(v_positionMC, normalEC));
vec3 cubeDir =
normalize(yUpToZUp * referenceFrameMatrix * normalize(reflect(-v, n)));
vec3 diffuseIrradiance =
czm_sphericalHarmonics(cubeDir, u_reearth_sphericalHarmonicCoefficients);

return pbrParameters.diffuseColor * diffuseIrradiance;
}

vec4 reearth_computeImageBasedLightingColor(vec4 color) {
if (u_reearth_globeImageBasedLighting) {
czm_pbrParameters pbrParameters;
pbrParameters.diffuseColor = color.rgb;

vec3 normalEC = normalize(v_normalEC);
vec3 lighting =
czm_pbrLighting(v_positionEC, normalEC, czm_lightDirectionEC,
czm_lightColorHdr, pbrParameters);
lighting += reearth_imageBasedLightingStage(
v_positionEC, normalEC, czm_lightDirectionEC,
czm_lightColorHdr, pbrParameters) *
u_vertexShadowDarkness;

#ifdef HDR
lighting = czm_acesTonemapping(lighting);
lighting = czm_linearToSrgb(lighting);
#endif

return vec4(color.rgb * lighting, color.a);
} else {
float diffuseIntensity = clamp(
czm_getLambertDiffuse(czm_lightDirectionEC, normalize(v_normalEC)) *
u_lambertDiffuseMultiplier +
u_vertexShadowDarkness,
0.0, 1.0);
return vec4(color.rgb * czm_lightColor * diffuseIntensity, color.a);
}
}
@@ -0,0 +1,4 @@
uniform vec3 u_reearth_sphericalHarmonicCoefficients[9];
uniform bool u_reearth_globeImageBasedLighting;

vec4 reearth_computeImageBasedLightingColor(vec4 color);
@@ -0,0 +1,13 @@
# OverriddenShaders

This is a set of shaders to override Cesium's shader.

The structure have to be as follow.

```
ShaderName/
Customs.glsl ... Define your own custom functions
Definitions.glsl ... Definition for each variable or function.
```

You must set `reearth_` prefix to the name of the function like `reearth_own_function()`. Also you must set `reearth_` to the name of the uniform and attribute like `u_reearth_variable` or `v_reearth_variable`.
23 changes: 17 additions & 6 deletions web/src/beta/lib/core/engines/Cesium/core/Globe.tsx
Expand Up @@ -31,6 +31,7 @@ export default function Globe({ property, cesiumIonAccessToken }: Props): JSX.El
const opts = {
terrain: terrainProperty?.terrain,
terrainType: terrainProperty?.terrainType,
terrainNormal: terrainProperty?.terrainNormal,
terrainCesiumIonAccessToken:
terrainProperty?.terrainCesiumIonAccessToken || cesiumIonAccessToken,
terrainCesiumIonAsset: terrainProperty?.terrainCesiumIonAsset,
Expand All @@ -44,6 +45,7 @@ export default function Globe({ property, cesiumIonAccessToken }: Props): JSX.El
terrainProperty?.terrainCesiumIonAccessToken,
terrainProperty?.terrainCesiumIonAsset,
terrainProperty?.terrainCesiumIonUrl,
terrainProperty?.terrainNormal,
cesiumIonAccessToken,
]);

Expand Down Expand Up @@ -82,32 +84,41 @@ const terrainProviders: {
| ((
opts: Pick<
TerrainProperty,
"terrainCesiumIonAccessToken" | "terrainCesiumIonAsset" | "terrainCesiumIonUrl"
| "terrainCesiumIonAccessToken"
| "terrainCesiumIonAsset"
| "terrainCesiumIonUrl"
| "terrainNormal"
>,
) => TerrainProvider | null);
} = {
cesium: ({ terrainCesiumIonAccessToken }) =>
cesium: ({ terrainCesiumIonAccessToken, terrainNormal }) =>
// https://github.com/CesiumGS/cesium/blob/main/Source/Core/createWorldTerrain.js
new CesiumTerrainProvider({
url: IonResource.fromAssetId(1, {
accessToken: terrainCesiumIonAccessToken,
}),
requestVertexNormals: false,
requestVertexNormals: terrainNormal,
requestWaterMask: false,
}),
arcgis: () =>
arcgis: ({ terrainNormal }) =>
new ArcGISTiledElevationTerrainProvider({
url: "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer",
requestVertexNormals: terrainNormal,
}),
cesiumion: ({ terrainCesiumIonAccessToken, terrainCesiumIonAsset, terrainCesiumIonUrl }) =>
cesiumion: ({
terrainCesiumIonAccessToken,
terrainCesiumIonAsset,
terrainCesiumIonUrl,
terrainNormal,
}) =>
terrainCesiumIonAsset
? new CesiumTerrainProvider({
url:
terrainCesiumIonUrl ||
IonResource.fromAssetId(parseInt(terrainCesiumIonAsset, 10), {
accessToken: terrainCesiumIonAccessToken,
}),
requestVertexNormals: true,
requestVertexNormals: terrainNormal,
})
: null,
};
@@ -0,0 +1,33 @@
import { Cartesian3 } from "cesium";
import { expect, test } from "vitest";

import { arrayToCartecian3 } from "./sphericalHaromic";

test("arrayToCartecian3", () => {
expect(
arrayToCartecian3(
[
[1, 1, 1],
[1, 1, 1],
[1, 1, 1],
[1, 1, 1],
[1, 1, 1],
[1, 1, 1],
[1, 1, 1],
[1, 1, 1],
[1, 1, 1],
],
2,
),
).toEqual([
new Cartesian3(2, 2, 2),
new Cartesian3(2, 2, 2),
new Cartesian3(2, 2, 2),
new Cartesian3(2, 2, 2),
new Cartesian3(2, 2, 2),
new Cartesian3(2, 2, 2),
new Cartesian3(2, 2, 2),
new Cartesian3(2, 2, 2),
new Cartesian3(2, 2, 2),
]);
});
27 changes: 27 additions & 0 deletions web/src/beta/lib/core/engines/Cesium/helpers/sphericalHaromic.ts
@@ -0,0 +1,27 @@
import { Cartesian3 } from "cesium";

const sphericalHarmonicCoefficientsScratch = [
new Cartesian3(),
new Cartesian3(),
new Cartesian3(),
new Cartesian3(),
new Cartesian3(),
new Cartesian3(),
new Cartesian3(),
new Cartesian3(),
new Cartesian3(),
];

export const arrayToCartecian3 = (
sphericalHarmonicCoefficients: [x: number, y: number, z: number][] | undefined,
intensity: number | undefined,
) =>
sphericalHarmonicCoefficients?.length === 9
? sphericalHarmonicCoefficients.map((cartesian, index) =>
Cartesian3.multiplyByScalar(
new Cartesian3(...cartesian),
intensity ?? 1.0,
sphericalHarmonicCoefficientsScratch[index],
),
)
: undefined;

0 comments on commit 7439637

Please sign in to comment.