Skip to content

Commit

Permalink
Merge pull request #41 from readyplayerme/ACT-108-Enhance-bloom-effect
Browse files Browse the repository at this point in the history
Feature - enhanced bloom effect for materials
  • Loading branch information
Zaehiel authored Aug 4, 2023
2 parents 6b62662 + 1b682c9 commit 64f37fb
Show file tree
Hide file tree
Showing 21 changed files with 106 additions and 63 deletions.
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
- "feature/**"
- "feat/**"
- "hotfix/**"
- "ACT-**"

concurrency:
group: ${{ github.ref }}
Expand Down
2 changes: 1 addition & 1 deletion .storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ module.exports = {
options: {}
},
docs: {
autodocs: true,
autodocs: true
}
};
2 changes: 1 addition & 1 deletion .storybook/theme.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ import { getStoryAssetPath } from '../src/services';
export default create({
base: 'light',
brandImage: getStoryAssetPath('logo.png'),
brandUrl: 'https://readyplayer.me',
brandUrl: 'https://readyplayer.me'
});
Binary file added public/male-emissive.glb
Binary file not shown.
30 changes: 22 additions & 8 deletions src/components/Avatar/Avatar.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@ import React, { Suspense, FC, useMemo, CSSProperties, ReactNode, useEffect } fro
import { Vector3 } from 'three';
import { CameraLighting } from 'src/components/Scene/CameraLighting.component';
import { Environment } from 'src/components/Scene/Environment.component';
import { LightingProps, BaseModelProps, EnvironmentProps } from 'src/types';
import { LightingProps, BaseModelProps, EnvironmentProps, BloomConfiguration } from 'src/types';
import { BaseCanvas } from 'src/components/BaseCanvas';
import { AnimationModel, HalfBodyModel, StaticModel, PoseModel } from 'src/components/Models';
import { isValidGlbFormat, triggerCallback } from 'src/services';
import { Dpr } from '@react-three/fiber';
import { EffectComposer } from '@react-three/postprocessing';
import Capture, { CaptureType } from 'src/components/Capture/Capture.component';
import Box, { Background } from 'src/components/Background/Box/Box.component';
import { Box, Background } from 'src/components/Background/Box/Box.component';
import { BackgroundColor } from 'src/components/Background';
import Shadow from 'src/components/Shadow/Shadow.component';
import Loader from 'src/components/Loader';
import Bloom, { BloomConfiguration } from 'src/components/Bloom/Bloom.component';
import Bloom from 'src/components/Bloom/Bloom.component';

export const CAMERA = {
TARGET: {
Expand Down Expand Up @@ -75,7 +76,7 @@ export interface AvatarProps extends LightingProps, EnvironmentProps, Omit<BaseM
*/
cameraInitialDistance?: number;
/**
* Pass styling to canvas.
* Apply styling to canvas DOM element.
*/
style?: CSSProperties;
/**
Expand All @@ -87,7 +88,8 @@ export interface AvatarProps extends LightingProps, EnvironmentProps, Omit<BaseM
*/
emotion?: Emotion;
/**
* Applies Box background for canvas, make sure that image is loadable to prevent bg errors.
* Applies Box background in the scene with a provided image.
* Make sure that image is loadable to prevent bg errors.
*/
background?: Background;
/**
Expand Down Expand Up @@ -172,6 +174,7 @@ export const Avatar: FC<AvatarProps> = ({
idleRotation={idleRotation}
onLoaded={onLoaded}
headMovement={headMovement}
bloom={bloom}
/>
);
}
Expand All @@ -185,16 +188,26 @@ export const Avatar: FC<AvatarProps> = ({
idleRotation={idleRotation}
onLoaded={onLoaded}
headMovement={headMovement}
bloom={bloom}
/>
);
}

if (isValidGlbFormat(poseSrc)) {
return <PoseModel emotion={emotion} modelSrc={modelSrc} scale={scale} poseSrc={poseSrc!} onLoaded={onLoaded} />;
return (
<PoseModel
emotion={emotion}
modelSrc={modelSrc}
scale={scale}
poseSrc={poseSrc!}
onLoaded={onLoaded}
bloom={bloom}
/>
);
}

return <StaticModel modelSrc={modelSrc} scale={scale} onLoaded={onLoaded} emotion={emotion} />;
}, [halfBody, animationSrc, modelSrc, scale, poseSrc, idleRotation, emotion, onLoaded, headMovement]);
return <StaticModel modelSrc={modelSrc} scale={scale} onLoaded={onLoaded} emotion={emotion} bloom={bloom} />;
}, [halfBody, animationSrc, modelSrc, scale, poseSrc, idleRotation, emotion, onLoaded, headMovement, bloom]);

useEffect(() => triggerCallback(onLoading), [modelSrc, animationSrc, onLoading]);

Expand Down Expand Up @@ -227,6 +240,7 @@ export const Avatar: FC<AvatarProps> = ({
{shadows && <Shadow />}
{background?.src && <Box {...background} />}
{capture && <Capture {...capture} />}
{style?.background && <BackgroundColor color={style.background as string} />}
<EffectComposer disableNormalPass>
<Bloom
luminanceThreshold={bloom?.luminanceThreshold}
Expand Down
21 changes: 11 additions & 10 deletions src/components/Avatar/Avatar.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,20 @@ Static.args = {
cameraZoomTarget: CAMERA.CONTROLS.FULL_BODY.ZOOM_TARGET,
cameraTarget: CAMERA.TARGET.FULL_BODY.FEMALE,
cameraInitialDistance: CAMERA.CONTROLS.FULL_BODY.MAX_DISTANCE,
emotion: emotions.smile,
style: { background: '#ccc' },
/* eslint-disable no-console */
onLoaded: () => console.info('EVENT: static avatar loaded'),
onLoading: () => console.info('EVENT: loading static avatar'),
/* eslint-enable no-console */
bloom: {
luminanceThreshold: 1.0,
luminanceSmoothing: 1.0,
mipmapBlur: false,
mipmapBlur: true,
kernelSize: 1,
intensity: 1.0
}
intensity: 1.0,
materialIntensity: 3.3
},
emotion: emotions.smile,
style: { background: 'rgb(9,20,26)' },
/* eslint-disable no-console */
onLoaded: () => console.info('EVENT: static avatar loaded'),
onLoading: () => console.info('EVENT: loading static avatar')
/* eslint-enable no-console */
};
Static.argTypes = {
headMovement: { control: false },
Expand All @@ -72,7 +73,7 @@ export const Animated: StoryFn<typeof Avatar> = Template.bind({});
Animated.args = {
...Static.args,
emotion: undefined,
modelSrc: getStoryAssetPath('male.glb'),
modelSrc: getStoryAssetPath('male-emissive.glb'),
animationSrc: getStoryAssetPath('male-idle.glb'),
cameraTarget: CAMERA.TARGET.FULL_BODY.MALE,
cameraInitialDistance: CAMERA.CONTROLS.FULL_BODY.MAX_DISTANCE,
Expand Down
4 changes: 1 addition & 3 deletions src/components/Background/Box/Box.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { MeshProps } from '@react-three/fiber/dist/declarations/src/three-types'

export type Background = { src?: string } & MeshProps;

const Box: FC<Background> = ({ src = '', ...baseProps }) => {
export const Box: FC<Background> = ({ src = '', ...baseProps }) => {
const ref = useRef<Mesh>(null);
const texture = useLoader(TextureLoader, src);

Expand All @@ -16,5 +16,3 @@ const Box: FC<Background> = ({ src = '', ...baseProps }) => {
</mesh>
);
};

export default Box;
10 changes: 10 additions & 0 deletions src/components/Background/Color/Color.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React, { FC } from 'react';

export type BackgroundColorProps = {
/**
* CSS-style string. For example: '#000' or 'red'.
*/
color: string;
};

export const BackgroundColor: FC<BackgroundColorProps> = ({ color }) => <color attach="background" args={[color]} />;
2 changes: 2 additions & 0 deletions src/components/Background/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { BackgroundColor } from './Color/Color.component';
export { Box } from './Box/Box.component';
2 changes: 1 addition & 1 deletion src/components/BaseCanvas/BaseCanvas.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const BaseCanvas: FC<BaseCanvasProps> = ({
dpr={dpr}
camera={{ fov, position }}
resize={{ scroll: true, debounce: { scroll: 50, resize: 0 } }}
style={{ ...style }}
style={{ ...style, background: 'transparent' }}
>
{children}
</Canvas>
Expand Down
26 changes: 2 additions & 24 deletions src/components/Bloom/Bloom.component.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,12 @@
import React, { FC } from 'react';
import { Bloom as BloomPostProcessing } from '@react-three/postprocessing';

export type BloomConfiguration = {
/**
* The luminance threshold. Raise this value to mask out darker elements in the scene. Range is [0, 1].
*/
luminanceThreshold?: number;
/**
* Controls the smoothness of the luminance threshold. Range is [0, 1].
*/
luminanceSmoothing?: number;
/**
* Enables or disables mipmap blur.
*/
mipmapBlur?: boolean;
/**
* The intensity of the bloom.
*/
intensity?: number;
/**
* The kernel size of the blur. Values are 0, 1, 2, 3, 4.
*/
kernelSize?: number;
};
import { BloomConfiguration } from 'src/types';

const Bloom: FC<BloomConfiguration> = ({
luminanceThreshold = 1,
luminanceSmoothing = 1,
mipmapBlur = true,
intensity = 0.1,
intensity = 1.0,
kernelSize = 0
}) => (
<BloomPostProcessing
Expand Down
2 changes: 2 additions & 0 deletions src/components/Exhibit/Exhibit.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { StaticModel } from 'src/components/Models/StaticModel';
import { BoundsModel } from 'src/components/Models/BoundsModel';
import { BaseCanvas } from 'src/components/BaseCanvas';
import Capture, { CaptureType } from 'src/components/Capture/Capture.component';
import { BackgroundColor } from 'src/components/Background';

export interface ExhibitProps extends CameraProps, EnvironmentProps, Omit<BaseModelProps, 'setModelFallback'> {
/**
Expand Down Expand Up @@ -110,6 +111,7 @@ export const Exhibit: FC<ExhibitProps> = ({
<Environment environment={environment} />
</Suspense>
{capture && <Capture {...capture} />}
{style?.background && <BackgroundColor color={style.background as string} />}
</BaseCanvas>
);
};
4 changes: 2 additions & 2 deletions src/components/Exhibit/__snapshots__/Exhibit.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ exports[`Exhibit component should change background color 1`] = `
<div>
<div
class="base-canvas "
style="position: relative; width: 100%; height: 100%; overflow: hidden; pointer-events: auto; background: rgb(0, 128, 128);"
style="position: relative; width: 100%; height: 100%; overflow: hidden; pointer-events: auto; background: transparent;"
>
<div
style="width: 100%; height: 100%;"
Expand All @@ -21,7 +21,7 @@ exports[`Exhibit component should mount without errors 1`] = `
<div>
<div
class="base-canvas "
style="position: relative; width: 100%; height: 100%; overflow: hidden; pointer-events: auto;"
style="position: relative; width: 100%; height: 100%; overflow: hidden; pointer-events: auto; background: transparent;"
>
<div
style="width: 100%; height: 100%;"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ export const AnimationModel: FC<AnimationModelProps> = ({
idleRotation = false,
setModelFallback,
onLoaded,
headMovement = false
headMovement = false,
bloom
}) => {
const ref = useRef<Group>(null);
const { scene } = useGltfLoader(modelSrc);
Expand Down Expand Up @@ -57,5 +58,5 @@ export const AnimationModel: FC<AnimationModelProps> = ({
useHeadMovement({ nodes, enabled: headMovement });
useFallback(nodes, setModelFallback);

return <Model modelRef={ref} scene={scene} scale={scale} onLoaded={onLoaded} />;
return <Model modelRef={ref} scene={scene} scale={scale} onLoaded={onLoaded} bloom={bloom} />;
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export interface FloatingModelProps extends BaseModelProps {
scale?: number;
}

export const FloatingModel: FC<FloatingModelProps> = ({ modelSrc, scale = 1.0, onLoaded }) => {
export const FloatingModel: FC<FloatingModelProps> = ({ modelSrc, scale = 1.0, onLoaded, bloom }) => {
const ref = useRef<Group>(null);
const { scene } = useGltfLoader(modelSrc);

Expand All @@ -23,5 +23,5 @@ export const FloatingModel: FC<FloatingModelProps> = ({ modelSrc, scale = 1.0, o
}
});

return <Model modelRef={ref} scale={scale} scene={scene} onLoaded={onLoaded} />;
return <Model modelRef={ref} scale={scale} scene={scene} onLoaded={onLoaded} bloom={bloom} />;
};
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ export const HalfBodyModel: FC<HalfBodyModelProps> = ({
emotion,
setModelFallback,
onLoaded,
headMovement = false
headMovement = false,
bloom
}) => {
const ref = useRef<Group>(null);
const { scene } = useGltfLoader(modelSrc);
Expand Down Expand Up @@ -61,5 +62,5 @@ export const HalfBodyModel: FC<HalfBodyModelProps> = ({
useEmotion(nodes, emotion);
useFallback(nodes, setModelFallback);

return <Model modelRef={ref} scene={scene} scale={scale} onLoaded={onLoaded} />;
return <Model modelRef={ref} scene={scene} scale={scale} onLoaded={onLoaded} bloom={bloom} />;
};
4 changes: 2 additions & 2 deletions src/components/Models/Model/Model.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ interface ModelProps extends BaseModelProps {
scale?: number;
}

export const Model: FC<ModelProps> = ({ scene, scale = 1, modelRef, onLoaded }) => {
export const Model: FC<ModelProps> = ({ scene, scale = 1, modelRef, onLoaded, bloom }) => {
const { materials } = useGraph(scene);
normaliseMaterialsConfig(materials);
normaliseMaterialsConfig(materials, bloom);
scene.traverse((object) => {
const node = object;

Expand Down
5 changes: 3 additions & 2 deletions src/components/Models/PoseModel/PoseModel.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ export const PoseModel: FC<PoseModelProps> = ({
scale = 1,
emotion,
setModelFallback,
onLoaded
onLoaded,
bloom
}) => {
const { scene } = useGltfLoader(modelSrc);
const { nodes } = useGraph(scene);
Expand All @@ -32,5 +33,5 @@ export const PoseModel: FC<PoseModelProps> = ({
useEmotion(nodes, emotion);
useFallback(nodes, setModelFallback);

return <Model modelRef={modelRef} scene={scene} scale={scale} onLoaded={onLoaded} />;
return <Model modelRef={modelRef} scene={scene} scale={scale} onLoaded={onLoaded} bloom={bloom} />;
};
5 changes: 3 additions & 2 deletions src/components/Models/StaticModel/StaticModel.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ export const StaticModel: FC<StaticModelProps> = ({
scale = 1,
setModelFallback,
onLoaded,
emotion
emotion,
bloom
}) => {
const { scene } = useGltfLoader(modelSrc);
const { nodes } = useGraph(scene);

useEmotion(nodes, emotion);
useFallback(nodes, setModelFallback);

return <Model modelRef={modelRef} scene={scene} scale={scale} onLoaded={onLoaded} />;
return <Model modelRef={modelRef} scene={scene} scale={scale} onLoaded={onLoaded} bloom={bloom} />;
};
7 changes: 6 additions & 1 deletion src/services/Models.service.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import type { ObjectMap } from '@react-three/fiber';
import { GLTF, GLTFLoader } from 'three-stdlib';
import { suspend } from 'suspend-react';
import { Emotion } from 'src/components/Avatar/Avatar.component';
import { BloomConfiguration } from 'src/types';

export interface CustomNode extends Object3D {
geometry: BufferGeometry;
Expand Down Expand Up @@ -70,14 +71,18 @@ export const lerp = (start: number, end: number, time = 0.05): number => start *
/**
* Avoid texture pixelation and add depth effect.
*/
export const normaliseMaterialsConfig = (materials: Record<string, Material>) => {
export const normaliseMaterialsConfig = (materials: Record<string, Material>, bloomConfig?: BloomConfiguration) => {
Object.values(materials).forEach((material) => {
const mat = material as MeshStandardMaterial;
if (mat.map) {
mat.map.minFilter = LinearFilter;
mat.depthWrite = true;
mat.toneMapped = false;
}

if (mat.emissiveMap) {
mat.emissiveIntensity = bloomConfig?.materialIntensity || 2.0;
}
});
};

Expand Down
Loading

0 comments on commit 64f37fb

Please sign in to comment.