Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(web): add image-based lighting #519

Merged
merged 29 commits into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
5250fa5
feat: image-based lighting
airslice Jun 23, 2023
311a9f1
refactor: type
airslice Jun 26, 2023
c1a4fba
Merge branch 'main' into feat/visualizer-image-based-lighting
airslice Jun 26, 2023
36e1f5f
Merge branch 'main' into feat/visualizer-image-based-lighting
airslice Jun 26, 2023
e607408
Merge branch 'main' into feat/visualizer-image-based-lighting
airslice Jun 28, 2023
a94c8b0
refactor: update feature component key
airslice Jun 28, 2023
44580b3
Merge branch 'main' into feat/visualizer-image-based-lighting
airslice Jul 4, 2023
e9df6cd
Merge branch 'main' into feat/visualizer-image-based-lighting
airslice Jul 7, 2023
6dc628f
Merge branch 'main' into feat/visualizer-image-based-lighting
airslice Jul 7, 2023
1ab685d
feat: add image based lighting to 3d tiles
airslice Jul 10, 2023
d4ee13b
Merge branch 'main' into feat/visualizer-image-based-lighting
airslice Jul 10, 2023
15212b1
chore: clear
airslice Jul 10, 2023
2bd7ff8
Merge branch 'main' into feat/visualizer-image-based-lighting
airslice Jul 10, 2023
41986cd
Merge branch 'main' into feat/visualizer-image-based-lighting
airslice Jul 12, 2023
63b05a9
Merge branch 'main' into feat/visualizer-image-based-lighting
airslice Jul 12, 2023
afb0028
Merge branch 'main' into feat/visualizer-image-based-lighting
airslice Jul 12, 2023
686fcb9
Merge branch 'main' into feat/visualizer-image-based-lighting
keiya01 Jul 12, 2023
f261706
Update web/src/beta/lib/core/engines/Cesium/Feature/index.tsx
airslice Jul 12, 2023
ec349b9
support IBL for Model
keiya01 Jul 13, 2023
9901f14
update hasNoExpressionObject func
pyshx Jul 13, 2023
e68a259
refactor: featrue component key
airslice Jul 14, 2023
1c4fcb7
refactor: feature use scene property
airslice Jul 14, 2023
c1d1731
refactor: clear code
airslice Jul 14, 2023
13187e5
Merge branch 'main' into feat/visualizer-image-based-lighting
airslice Jul 14, 2023
537629d
refactor: align expression
airslice Jul 14, 2023
1c517d1
Update web/src/beta/lib/core/engines/Cesium/Feature/index.tsx
airslice Jul 14, 2023
e5d2c21
Update model logic
keiya01 Jul 14, 2023
b1c8b09
refactor: separate key for 3d tiles
airslice Jul 14, 2023
50e9751
Merge branch 'main' into feat/visualizer-image-based-lighting
airslice Jul 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion web/src/beta/lib/core/Map/Layer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
DataType,
ComputedFeature,
} from "../../mantle";
import { SceneProperty } from "../types";

import useHooks, { type Atom, type EvalFeature } from "./hooks";

Expand All @@ -27,7 +28,7 @@ export type CommonProps = {

export type FeatureComponentProps = {
layer: ComputedLayer;
sceneProperty?: any;
sceneProperty?: SceneProperty;
onFeatureRequest?: (range: DataRange) => void;
onFeatureFetch?: (features: Feature[]) => void;
onComputedFeatureFetch?: (feature: Feature[], computed: ComputedFeature[]) => void;
Expand Down
3 changes: 3 additions & 0 deletions web/src/beta/lib/core/Map/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,9 @@ export type SceneProperty = {
lightDirectionZ?: number;
lightColor?: string;
lightIntensity?: number;
specularEnvironmentMaps?: string;
sphericalHarmonicCoefficients?: [x: number, y: number, z: number][];
imageBasedLightIntensity?: number;
};
render?: {
antialias?: "low" | "medium" | "high" | "extreme";
Expand Down
102 changes: 98 additions & 4 deletions web/src/beta/lib/core/engines/Cesium/Feature/Model/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { Cartesian3, HeadingPitchRoll, Math as CesiumMath, Transforms } from "cesium";
import { useMemo } from "react";
import { ModelGraphics } from "resium";
import {
Cartesian3,
HeadingPitchRoll,
Math as CesiumMath,
Transforms,
Model as CesiumModel,
ImageBasedLighting,
} from "cesium";
import { useEffect, useMemo, useRef } from "react";
import { ModelGraphics, useCesium } from "resium";

import { toColor } from "@reearth/beta/utils/value";

import type { ModelAppearance } from "../../..";
import { colorBlendMode, heightReference, shadowMode } from "../../common";
import { NonPBRLightingShader } from "../../CustomShaders/NonPBRLightingShader";
import { useSceneEvent } from "../../hooks/useSceneEvent";
import {
EntityExt,
extractSimpleLayerData,
Expand All @@ -23,7 +31,27 @@ export type Property = ModelAppearance & {
height?: number;
};

export default function Model({ id, isVisible, property, geometry, layer, feature }: Props) {
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,
property,
sceneProperty,
geometry,
layer,
feature,
}: Props) {
const data = extractSimpleLayerData(layer);
const isGltfData = data?.type === "gltf";

Expand Down Expand Up @@ -94,6 +122,72 @@ export default function Model({ id, isVisible, property, geometry, layer, featur
[property?.near, property?.far],
);

const imageBasedLighting = useMemo(() => {
const ibl = new ImageBasedLighting();

if (
!property?.specularEnvironmentMaps &&
!property?.sphericalHarmonicCoefficients &&
!sceneProperty?.light?.specularEnvironmentMaps &&
!sceneProperty?.light?.sphericalHarmonicCoefficients
)
return ibl;

const specularEnvironmentMaps =
property?.specularEnvironmentMaps ?? sceneProperty?.light?.specularEnvironmentMaps;
const sphericalHarmonicCoefficients =
property?.sphericalHarmonicCoefficients ??
sceneProperty?.light?.sphericalHarmonicCoefficients;
const imageBasedLightIntensity =
property?.imageBasedLightIntensity ?? sceneProperty?.light?.imageBasedLightIntensity;

if (specularEnvironmentMaps) {
ibl.specularEnvironmentMaps = specularEnvironmentMaps;
}
if (sphericalHarmonicCoefficients) {
ibl.sphericalHarmonicCoefficients = sphericalHarmonicCoefficients?.map((cartesian, index) =>
Cartesian3.multiplyByScalar(
new Cartesian3(...cartesian),
imageBasedLightIntensity ?? 1.0,
sphericalHarmonicCoefficientsScratch[index],
),
);
}
return ibl;
}, [
property?.specularEnvironmentMaps,
property?.sphericalHarmonicCoefficients,
property?.imageBasedLightIntensity,
sceneProperty?.light?.specularEnvironmentMaps,
sceneProperty?.light?.sphericalHarmonicCoefficients,
sceneProperty?.light?.imageBasedLightIntensity,
]);

const { viewer } = useCesium();
const shouldUpdateAfterLoaded = useRef(false);
useSceneEvent("postRender", () => {
const primitives = viewer?.scene.primitives;
const length = primitives?.length ?? 0;

if (!shouldUpdateAfterLoaded.current || !imageBasedLighting) {
return;
}

for (let i = 0; i < length; i++) {
const prim = primitives?.get(i);
if (prim instanceof CesiumModel && prim.id && prim.id.id === id) {
shouldUpdateAfterLoaded.current = false;
prim.imageBasedLighting = imageBasedLighting;
}
}
});

useEffect(() => {
if (imageBasedLighting) {
shouldUpdateAfterLoaded.current = true;
}
}, [imageBasedLighting]);

// if data type is gltf, layer should be rendered. Otherwise only features should be rendererd.
return (isGltfData ? feature : !feature) || !isVisible || !show || !actualUrl ? null : (
<EntityExt
Expand Down
55 changes: 55 additions & 0 deletions web/src/beta/lib/core/engines/Cesium/Feature/Tileset/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
Cesium3DTileFeature,
Model,
Cesium3DTilePointFeature,
ImageBasedLighting,
} from "cesium";
import { isEqual, pick } from "lodash-es";
import { MutableRefObject, useCallback, useEffect, useMemo, useRef, useState } from "react";
Expand Down Expand Up @@ -44,6 +45,18 @@ 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 @@ -297,6 +310,7 @@ export const useHooks = ({
boxId,
isVisible,
property,
sceneProperty,
layer,
feature,
meta,
Expand Down Expand Up @@ -516,11 +530,52 @@ export const useHooks = ({
: null;
}, [isVisible, tileset, url, type, meta]);

const imageBasedLighting = useMemo(() => {
airslice marked this conversation as resolved.
Show resolved Hide resolved
if (
!property?.specularEnvironmentMaps &&
!property?.sphericalHarmonicCoefficients &&
!sceneProperty?.light?.specularEnvironmentMaps &&
!sceneProperty?.light?.sphericalHarmonicCoefficients
)
return;

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;

if (specularEnvironmentMaps) {
ibl.specularEnvironmentMaps = specularEnvironmentMaps;
}
if (sphericalHarmonicCoefficients) {
ibl.sphericalHarmonicCoefficients = sphericalHarmonicCoefficients?.map((cartesian, index) =>
Cartesian3.multiplyByScalar(
new Cartesian3(...cartesian),
imageBasedLightIntensity ?? 1.0,
sphericalHarmonicCoefficientsScratch[index],
),
);
}
return ibl;
}, [
property?.specularEnvironmentMaps,
property?.sphericalHarmonicCoefficients,
property?.imageBasedLightIntensity,
sceneProperty?.light?.specularEnvironmentMaps,
sceneProperty?.light?.sphericalHarmonicCoefficients,
sceneProperty?.light?.imageBasedLightIntensity,
]);

return {
tilesetUrl,
ref,
style,
clippingPlanes,
builtinBoxProps,
imageBasedLighting,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ function Tileset({
}: Props): JSX.Element | null {
const { shadows, colorBlendMode, pbr } = property ?? {};
const boxId = `${layer?.id}_box`;
const { tilesetUrl, ref, style, clippingPlanes, builtinBoxProps } = useHooks({
const { tilesetUrl, ref, style, clippingPlanes, builtinBoxProps, imageBasedLighting } = useHooks({
id,
boxId,
isVisible,
layer,
feature,
property,
sceneProperty,
meta,
evalFeature,
});
Expand All @@ -55,6 +56,7 @@ function Tileset({
shadows={shadowMode(shadows)}
clippingPlanes={clippingPlanes}
colorBlendMode={colorBlendModeFor3DTile(colorBlendMode)}
imageBasedLighting={imageBasedLighting}
/>
{builtinBoxProps && (
<Box
Expand Down
25 changes: 23 additions & 2 deletions web/src/beta/lib/core/engines/Cesium/Feature/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,14 @@ export default function Feature({
const cacheable = !data?.updateInterval;

const renderComponent = (k: keyof AppearanceTypes, f?: ComputedFeature): JSX.Element | null => {
const useSceneSphericalHarmonicCoefficients =
!!props.sceneProperty?.light?.sphericalHarmonicCoefficients;
const useSceneSpecularEnvironmentMaps = !!props.sceneProperty?.light?.specularEnvironmentMaps;

const componentId = generateIDWithMD5(
`${layer.id}_${f?.id ?? ""}_${k}_${isHidden}_${data?.url ?? ""}_${
`${layer.id}_${f?.id ?? ""}_${k}_${isHidden}_${
data?.url ?? ""
}_${useSceneSphericalHarmonicCoefficients}_${useSceneSpecularEnvironmentMaps}_${
JSON.stringify(f?.[k]) ?? ""
}`,
);
Expand Down Expand Up @@ -161,9 +167,24 @@ export default function Feature({
const [C] = components[k] ?? [];
const isVisible = layer.layer.visible !== false && !isHidden;

// NOTE: IBL for 3dtiles is not updated unless Tileset feature component is re-created.
const useSceneSphericalHarmonicCoefficients =
!!props.sceneProperty?.light?.sphericalHarmonicCoefficients;
const useSceneSpecularEnvironmentMaps =
!!props.sceneProperty?.light?.specularEnvironmentMaps;
const use3dtilesSphericalHarmonicCoefficients =
layer?.layer?.type === "simple" &&
!!layer?.layer?.["3dtiles"]?.sphericalHarmonicCoefficients;
const use3dtilesSpecularEnvironmentMaps =
layer?.layer?.type === "simple" && !!layer?.layer?.["3dtiles"]?.specularEnvironmentMaps;

// "noFeature" component should be recreated when the following value is changed.
// data.url, isVisible
const key = generateIDWithMD5(`${layer?.id || ""}_${k}_${data?.url}_${isVisible}`);
const key = generateIDWithMD5(
`${layer?.id || ""}_${k}_${
data?.url
}_${isVisible}_${useSceneSphericalHarmonicCoefficients}_${useSceneSpecularEnvironmentMaps}_${use3dtilesSphericalHarmonicCoefficients}}_${use3dtilesSpecularEnvironmentMaps}`,
);

return (
<C
Expand Down
10 changes: 8 additions & 2 deletions web/src/beta/lib/core/engines/Cesium/Feature/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@ import { type CesiumComponentRef, Entity } from "resium";

import { Data, Layer, LayerSimple, TimeInterval } from "@reearth/beta/lib/core/mantle";

import type { ComputedFeature, ComputedLayer, FeatureComponentProps, Geometry } from "../..";
import type {
ComputedFeature,
ComputedLayer,
FeatureComponentProps,
Geometry,
SceneProperty,
} from "../..";
import type { InternalCesium3DTileFeature } from "../types";

export type FeatureProps<P = any> = {
Expand All @@ -39,7 +45,7 @@ export type FeatureProps<P = any> = {
layer?: ComputedLayer;
feature?: ComputedFeature;
geometry?: Geometry;
sceneProperty?: any;
sceneProperty?: SceneProperty;
keiya01 marked this conversation as resolved.
Show resolved Hide resolved
} & Omit<FeatureComponentProps, "layer">;

export type FeatureComponent = ComponentType<FeatureProps>;
Expand Down
2 changes: 1 addition & 1 deletion web/src/beta/lib/core/mantle/evaluator/simple/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ function hasExpression(e: any): e is ExpressionContainer {

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function hasNonExpressionObject(v: any): boolean {
return typeof v === "object" && v && !("expression" in v);
return typeof v === "object" && v && !("expression" in v) && !Array.isArray(v);
}

function evalExpression(
Expand Down
6 changes: 6 additions & 0 deletions web/src/beta/lib/core/mantle/types/appearance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ export type ModelAppearance = {
near?: number;
far?: number;
pbr?: boolean;
specularEnvironmentMaps?: string;
sphericalHarmonicCoefficients?: [x: number, y: number, z: number][];
imageBasedLightIntensity?: number;
};

export type Cesium3DTilesAppearance = {
Expand All @@ -147,6 +150,9 @@ export type Cesium3DTilesAppearance = {
pointSize?: number;
meta?: unknown;
pbr?: boolean;
specularEnvironmentMaps?: string;
sphericalHarmonicCoefficients?: [x: number, y: number, z: number][];
imageBasedLightIntensity?: number;
};

export type LegacyPhotooverlayAppearance = {
Expand Down