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.