Skip to content

Documentation / scene / SkinSSS

Class: SkinSSS ​

Defined in: libs/scene/src/posteffect/skinsss.ts:49

Stylized screen-space skin scattering pass.

Remarks ​

SkinMaterial writes the diffusible part of its lit color - the stylized diffuse ramp plus back-lit transmission, without specular - into a side buffer. This pass diffuses that buffer with a channel-dependent kernel and swaps it back in:

result = base + (diffused - original) * scatterTint * strength + diffused * glow

Because the term added back replaces exactly the term subtracted, the redistribution is energy conserving: light that appears on the dark side of the terminator is light that left the lit side, rather than light invented on top of a finished image. With scatterTint white and glow zero the total radiance is preserved to within the kernel's normalization error.

Two things follow from that formulation and are worth knowing:

  • Nothing happens where the diffusion changes nothing, so a disabled or zero-radius pass is a true no-op, and quantization from an 8-bit side buffer largely cancels between the subtracted and added terms instead of banding across the whole face.
  • scatterTint colors only the light that actually moved. Tinting the whole diffuse - what the material used to do with scatterColor - is what made the old look read as red haze.

Stylization survives in two places. The material owns the direct lighting ramp (wrap, softness, shadow tint, brightening) and this pass diffuses whatever ramp it produced; and profile selects the channel radii, so the wax and jade presets are as reachable as skin.

This is intentionally separate from the profile-based SSS pass, which supports per-pixel profiles via a profile-slot MRT. Here a single global profile lives in uniforms.

Extends ​

Constructors ​

Constructor ​

new SkinSSS(): SkinSSS

Defined in: libs/scene/src/posteffect/skinsss.ts:72

Returns ​

SkinSSS

Overrides ​

AbstractPostEffect.constructor

Properties ​

_enabled ​

protected _enabled: boolean

Defined in: libs/scene/src/posteffect/posteffect.ts:118

Inherited from ​

AbstractPostEffect._enabled


_layer ​

protected _layer: PostEffectLayer

Defined in: libs/scene/src/posteffect/posteffect.ts:119

Inherited from ​

AbstractPostEffect._layer

Accessors ​

disposed ​

Get Signature ​

get disposed(): boolean

Defined in: libs/base/dist/index.d.ts:6107

Returns ​

boolean

Inherited from ​

AbstractPostEffect.disposed


enabled ​

Get Signature ​

get enabled(): boolean

Defined in: libs/scene/src/posteffect/posteffect.ts:130

Whether this post effect is enabled

Returns ​

boolean

Set Signature ​

set enabled(val): void

Defined in: libs/scene/src/posteffect/posteffect.ts:133

Parameters ​
val ​

boolean

Returns ​

void

Inherited from ​

AbstractPostEffect.enabled


layer ​

Get Signature ​

get layer(): PostEffectLayer

Defined in: libs/scene/src/posteffect/posteffect.ts:137

Whether this post effect will be rendered at opaque phase

Returns ​

PostEffectLayer

Inherited from ​

AbstractPostEffect.layer


profile ​

Get Signature ​

get profile(): SubsurfaceProfile

Defined in: libs/scene/src/posteffect/skinsss.ts:109

Subsurface profile supplying the per-channel scatter radii and falloff.

Remarks ​

Defaults to SubsurfaceProfile.getDefaultSkinProfile when unset. Only the channel ratios of scatterRadius and the falloffColor are read; the absolute distance comes from SkinSSS.scatterRadius, so switching profiles changes the color of the bleed without changing how far it reaches.

The channel ratios are what make skin read as skin - red travels several times further than blue, which is the red-to-orange-to-yellow gradient at the terminator. They are also the main stylization lever: the wax and jade presets are the same code path with different ratios.

Returns ​

SubsurfaceProfile

Set Signature ​

set profile(val): void

Defined in: libs/scene/src/posteffect/skinsss.ts:112

Parameters ​
val ​

SubsurfaceProfile

Returns ​

void


strength ​

Get Signature ​

get strength(): number

Defined in: libs/scene/src/posteffect/skinsss.ts:117

Final blend strength of the conserving redistribution. 1 fully diffuses the skin diffuse.

Returns ​

number

Set Signature ​

set strength(val): void

Defined in: libs/scene/src/posteffect/skinsss.ts:120

Parameters ​
val ​

number

Returns ​

void


glow ​

Get Signature ​

get glow(): number

Defined in: libs/scene/src/posteffect/skinsss.ts:133

Additive, deliberately non-conserving bleed layered on top of the conserving result.

Remarks ​

0 (the default) keeps the pass energy conserving. Raising it adds the diffused term a second time without subtracting anything, which is the glow-heavy look the pass produced before it conserved energy; around 1 approximates it. Use it when the art direction wants skin to read as lit from within rather than as a physical surface.

Returns ​

number

Set Signature ​

set glow(val): void

Defined in: libs/scene/src/posteffect/skinsss.ts:136

Parameters ​
val ​

number

Returns ​

void


scatterTint ​

Get Signature ​

get scatterTint(): Vector4

Defined in: libs/scene/src/posteffect/skinsss.ts:149

Tint applied to the redistributed light.

Remarks ​

White (the default) leaves the pass energy conserving. A warm tint pushes the classic reddish terminator further than the profile's channel radii do on their own. Because it multiplies only the difference between the diffused and original diffuse, it cannot wash the whole surface - only the light that moved.

Returns ​

Vector4

Set Signature ​

set scatterTint(val): void

Defined in: libs/scene/src/posteffect/skinsss.ts:152

Parameters ​
val ​

Vector4

Returns ​

void


opacity ​

Get Signature ​

get opacity(): number

Defined in: libs/scene/src/posteffect/skinsss.ts:157

Skin mask coverage threshold. Scattering fades in as the blurred mask coverage exceeds this.

Returns ​

number

Set Signature ​

set opacity(val): void

Defined in: libs/scene/src/posteffect/skinsss.ts:160

Parameters ​
val ​

number

Returns ​

void


sampleStep ​

Get Signature ​

get sampleStep(): number

Defined in: libs/scene/src/posteffect/skinsss.ts:174

Maximum pixel spacing between blur taps. Caps the projected scatter radius for close-ups.

Remarks ​

With SKIN_SSS_TAPS taps per side the kernel reaches sampleStep * SKIN_SSS_TAPS pixels, so this is the real limit on scatter width. The separable form makes taps cheap enough that the default no longer has to be tight: the old single-pass 9x9 kernel capped out at 8 pixels, and a face in close-up projects a 2 cm scatter radius far past that - the shot that needs the most scattering used to get the least.

Returns ​

number

Set Signature ​

set sampleStep(val): void

Defined in: libs/scene/src/posteffect/skinsss.ts:177

Parameters ​
val ​

number

Returns ​

void


scatterRadius ​

Get Signature ​

get scatterRadius(): number

Defined in: libs/scene/src/posteffect/skinsss.ts:182

World-space scatter radius. The blur width shrinks with distance to keep this constant.

Returns ​

number

Set Signature ​

set scatterRadius(val): void

Defined in: libs/scene/src/posteffect/skinsss.ts:185

Parameters ​
val ​

number

Returns ​

void


smoothness ​

Get Signature ​

get smoothness(): number

Defined in: libs/scene/src/posteffect/skinsss.ts:199

Skin smoothing amount ("beauty filter"). Blends the lit color toward a mask- and depth-weighted blur of itself on skin pixels, removing pore and albedo detail while facial features stay sharp wherever the skin mask excludes them (eyes, brows, lips). 0 disables smoothing.

Remarks ​

This is separate from the diffusion and stacks on top of it. The diffusion blurs lighting only; albedo detail such as pores and blemishes passes through it untouched, which is correct but is not what a beauty filter is for.

Returns ​

number

Set Signature ​

set smoothness(val): void

Defined in: libs/scene/src/posteffect/skinsss.ts:202

Parameters ​
val ​

number

Returns ​

void


depthScale ​

Get Signature ​

get depthScale(): number

Defined in: libs/scene/src/posteffect/skinsss.ts:207

Depth rejection scale. The reference shader uses 80.

Returns ​

number

Set Signature ​

set depthScale(val): void

Defined in: libs/scene/src/posteffect/skinsss.ts:210

Parameters ​
val ​

number

Returns ​

void


colorBoost ​

Get Signature ​

get colorBoost(): number

Defined in: libs/scene/src/posteffect/skinsss.ts:215

Multiplier applied to the diffused irradiance before compositing.

Returns ​

number

Set Signature ​

set colorBoost(val): void

Defined in: libs/scene/src/posteffect/skinsss.ts:218

Parameters ​
val ​

number

Returns ​

void

Methods ​

on() ​

on<K>(type, listener, context?): void

Defined in: libs/base/dist/index.d.ts:630

IEventTarget.on

Type Parameters ​

K ​

K extends "dispose"

Parameters ​

type ​

K

listener ​

EventListener<{ dispose: []; }, K>

context? ​

unknown

Returns ​

void

Inherited from ​

AbstractPostEffect.on


once() ​

once<K>(type, listener, context?): void

Defined in: libs/base/dist/index.d.ts:634

IEventTarget.once

Type Parameters ​

K ​

K extends "dispose"

Parameters ​

type ​

K

listener ​

EventListener<{ dispose: []; }, K>

context? ​

unknown

Returns ​

void

Inherited from ​

AbstractPostEffect.once


off() ​

off<K>(type, listener?, context?): void

Defined in: libs/base/dist/index.d.ts:638

IEventTarget.off

Type Parameters ​

K ​

K extends "dispose"

Parameters ​

type ​

K

listener? ​

EventListener<{ dispose: []; }>

context? ​

unknown

Returns ​

void

Inherited from ​

AbstractPostEffect.off


dispatchEvent() ​

dispatchEvent<K>(type, ...args): void

Defined in: libs/base/dist/index.d.ts:642

IEventTarget.dispatchEvent

Type Parameters ​

K ​

K extends "dispose"

Parameters ​

type ​

K

args ​

...object[K]

Returns ​

void

Inherited from ​

AbstractPostEffect.dispatchEvent


dispose() ​

dispose(): void

Defined in: libs/base/dist/index.d.ts:6108

Returns ​

void

Inherited from ​

AbstractPostEffect.dispose


needFlip() ​

needFlip(device): boolean

Defined in: libs/scene/src/posteffect/posteffect.ts:145

Check if the post effect should be rendered upside down.

Parameters ​

device ​

AbstractDevice

The device object

Returns ​

boolean

true if the post effect should be rendered upside down

Inherited from ​

AbstractPostEffect.needFlip


requireDepthAttachment() ​

requireDepthAttachment(_ctx): boolean

Defined in: libs/scene/src/posteffect/posteffect.ts:159

Checks whether this post effect requires the scene depth buffer

Parameters ​

_ctx ​

DrawContext

Returns ​

boolean

true if the scene depth buffer is required.

Inherited from ​

AbstractPostEffect.requireDepthAttachment


requireMotionVectorTexture() ​

requireMotionVectorTexture(_ctx): boolean

Defined in: libs/scene/src/posteffect/posteffect.ts:166

Checks whether this post effect requires the motion vector texture

Parameters ​

_ctx ​

DrawContext

Returns ​

boolean

true if the motion vector texture is required.

Inherited from ​

AbstractPostEffect.requireMotionVectorTexture


requireHiZTexture() ​

requireHiZTexture(_ctx): boolean

Defined in: libs/scene/src/posteffect/posteffect.ts:170

Checks whether this post effect requires the Hi-Z depth pyramid.

Parameters ​

_ctx ​

DrawContext

Returns ​

boolean

Inherited from ​

AbstractPostEffect.requireHiZTexture


requireHiZNearest() ​

requireHiZNearest(_ctx): boolean

Defined in: libs/scene/src/posteffect/posteffect.ts:181

Checks whether this post effect requires the Hi-Z pyramid's nearest-depth channel, which answers "how close is the nearest surface in this screen region" rather than the farthest-depth channel's occlusion question.

Implies AbstractPostEffect.requireHiZTexture; an effect that needs the nearest channel does not have to request the pyramid separately.

Parameters ​

_ctx ​

DrawContext

Returns ​

boolean

Inherited from ​

AbstractPostEffect.requireHiZNearest


requireSceneNormalTexture() ​

requireSceneNormalTexture(_ctx): boolean

Defined in: libs/scene/src/posteffect/posteffect.ts:185

Checks whether this post effect requires opaque-scene world normals.

Parameters ​

_ctx ​

DrawContext

Returns ​

boolean

Inherited from ​

AbstractPostEffect.requireSceneNormalTexture


requireSceneRoughnessTexture() ​

requireSceneRoughnessTexture(_ctx): boolean

Defined in: libs/scene/src/posteffect/posteffect.ts:189

Checks whether this post effect requires opaque-scene roughness data.

Parameters ​

_ctx ​

DrawContext

Returns ​

boolean

Inherited from ​

AbstractPostEffect.requireSceneRoughnessTexture


requireShadowMask() ​

requireShadowMask(_ctx): boolean

Defined in: libs/scene/src/posteffect/posteffect.ts:200

Checks whether this post effect requires the screen-space shadow mask.

When true and the mask was produced this frame, the effect can sample DrawContext.shadowMaskTexture in its apply() body; the graph keeps the mask alive for the effect's pass.

Parameters ​

_ctx ​

DrawContext

Returns ​

boolean

true if the shadow mask is required.

Inherited from ​

AbstractPostEffect.requireShadowMask


setup() ​

setup(s): RGHandle

Defined in: libs/scene/src/posteffect/posteffect.ts:239

Declare this effect's passes on the render graph.

The default implementation wraps AbstractPostEffect.apply into a single graph pass, so effects only overriding apply() work unchanged. Multi-pass effects override this method to declare each internal step as its own pass, calling PostEffectSetupContext.createOutput inside the final pass.

Parameters ​

s ​

PostEffectSetupContext

Build-time setup context.

Returns ​

RGHandle

The effect's output color handle.

Inherited from ​

AbstractPostEffect.setup


passThrough() ​

protected passThrough(ctx, inputColorTexture, srgbOutput, renderStates?): void

Defined in: libs/scene/src/posteffect/posteffect.ts:344

Parameters ​

ctx ​

DrawContext

Draw context

inputColorTexture ​

Texture2D

Input color texture

srgbOutput ​

boolean

Whether the result should be gamma corrected

renderStates? ​

RenderStateSet

Returns ​

void

Inherited from ​

AbstractPostEffect.passThrough


drawFullscreenQuad() ​

protected drawFullscreenQuad(renderStateSet?): void

Defined in: libs/scene/src/posteffect/posteffect.ts:363

Draws a fullscreen quad

Parameters ​

renderStateSet? ​

RenderStateSet

Render states that will be used when drawing the fullscreen quad.

Returns ​

void

Inherited from ​

AbstractPostEffect.drawFullscreenQuad


onDispose() ​

protected onDispose(): void

Defined in: libs/scene/src/posteffect/posteffect.ts:376

Returns ​

void

Inherited from ​

AbstractPostEffect.onDispose


requireLinearDepthTexture() ​

requireLinearDepthTexture(): boolean

Defined in: libs/scene/src/posteffect/skinsss.ts:222

Checks whether this post effect requires the linear depth texture

Returns ​

boolean

true if the linear depth texture is required.

Overrides ​

AbstractPostEffect.requireLinearDepthTexture


apply() ​

apply(ctx, inputColorTexture, sceneDepthTexture, srgbOutput): void

Defined in: libs/scene/src/posteffect/skinsss.ts:226

Apply the post effect

Parameters ​

ctx ​

DrawContext

inputColorTexture ​

Texture2D

The previous scene color texture

sceneDepthTexture ​

Texture2D

The linear scene depth texture

srgbOutput ​

boolean

Whether the result should be gamma corrected

Returns ​

void

Remarks ​

The frame buffer of the post effect is already set when apply() is called.

Overrides ​

AbstractPostEffect.apply

Released under the MIT License.