Extensible 3D (X3D)
Part 1: Architecture and base components

17 Lighting component

# 17.1 Introduction

## 17.1.1 Name

The name of this component is "Lighting". This name shall be used when referring to this component in the COMPONENT statement (see 7.2.5.4 Component statement).

## 17.1.2 Overview

This clause describes the Lighting component of this part of ISO/IEC 19775. This includes how light sources are defined and positioned as well as how lights effect the rendered image. Table 17.1 provides links to the major topics in this clause.

Table 17.1 — Topics

17.1 Introduction 17.1.1 Name 17.1.2 Overview 17.2 Concepts 17.2.1 Light source semantics 17.2.1.1 Overview 17.2.1.2 Scoping of lights 17.2.2 Lighting model 17.2.2.1 Introduction 17.2.2.2 Lighting 'off' 17.2.2.3 Lighting 'on' 17.2.2.4 Lighting equations 17.2.2.5 References 17.2.2.1 Introduction 17.2.2.2 Texture sampling 17.2.2.3 Common definitions for all lighting models 17.2.2.4 Unlit lighting model 17.2.2.5 Phong lighting model 17.2.2.6 Physical lighting model 17.2.2.7 References 17.2.2.7 Gouraud shading model 17.2.2.8 Shadows 17.3 Abstract types 17.3.1 X3DLightNode 17.4 Node reference 17.4.1 DirectionalLight 17.4.2 EnvironmentLight 17.4.3 PointLight 17.4.4 SpotLight 17.5 Support levels Figure 17.1 — SpotLight node Table 17.1 — Topics Table 17.2 — Unlit colour and alpha mapping Table 17.3 — Lit colour and alpha mapping Table 17.4 — Calculation of the spotlight factor Table 17.5 — Calculation of the fog interpolant Table 17.6 — Lighting component support levels

# 17.2 Concepts

## 17.2.1 Light source semantics

### 17.2.1.1 Overview

The following node types are light source nodes:

- DirectionalLight
- PointLight
- SpotLight

PointLight and SpotLight illuminate all objects in the world that fall within their volume of lighting influence regardless of location within the transformation hierarchy (by default, when their global field is TRUE). PointLight defines this volume of influence as a sphere centred at the light (defined by a radius). SpotLight defines the volume of influence as a solid angle defined by a radius and a cut-off angle. DirectionalLight nodes illuminate only the objects descended from the light's parent grouping node, including any descendent children of the parent grouping nodes (by default, when their global field is FALSE).

Shape nodes are illuminated by the sum of all of the lights in the world that affect them. This includes the contribution of both the direct and ambient illumination from light sources. Ambient illumination results from the scattering and reflection of light originally emitted directly by light sources. The amount of ambient light is associated with the individual lights in the scene. This is a gross approximation to how ambient reflection actually occurs in nature.

Any node used as a source of illumination is derived from X3DLightNode. All light sources contain an intensity, a color, and an ambientIntensity field. The intensity field specifies the brightness of the direct emission from the light, and the ambientIntensity specifies the intensity of the ambient emission from the light. Light intensity may range from 0.0 (no light emission) to 1.0 (full intensity)infinity. The color field specifies the spectral colour properties of both the direct and ambient light emission as an RGB value. The on field specifies whether the light is enabled or disabled. If the value is FALSE, the light is disabled and will not affect any nodes in the scene. If the value is TRUE, the light will affect other nodes according to the 17.2.1.2 Scoping of lights.

Typically lighting intensity values are within range [0,1] for consistent composability of multiple scenes with independent lights.

In the physical lighting model (see 17.2.2.6 Physical lighting model) the intensity value should correspond to:

- luminous intensity in candela (lm/sr) in case of PointLight and SpotLight.
- illuminance in lux (lm/m2) in case of DirectionalLight.

In the physical lighting model, the ambientIntensity value is unused.

Future specification versions may introduce a use for it, therefore we recommend leaving it at 0 (default) in case of physical rendering, to avoid future changes.

In the case of the unlit lighting model all lights are ignored. See the 17.2.2.4 Unlit lighting model.

Do we need to explain allowing lighting values greater than 1? (see X3DLightingNode additions)

### 17.2.1.2 Scoping of lights

Each light type defines a global field that determines whether the light is global or scoped. Global lights illuminate all objects that fall within their volume of lighting influence. Scoped lights only illuminate objects that are in the same transformation hierarchy as the light; i.e., only the children and descendants of its enclosing parent group are illuminated. This allows the creation of realistic effects such as lights that illuminate a single room.

## 17.2.2 Lighting model

### 17.2.2.1 Introduction

The X3D lighting model provides detailed equations that specify the colours to apply to each geometric object. For each object, the values of the Material, color, and/or texture currently being applied to the object are combined with the lights illuminating the object and the currently bound X3DFogObject (if specified). These equations are designed to simulate the physical properties of light striking a surface.

If a programmable shader is defined for an Appearance node, the lighting model shall be disabled and replaced by the functionality implemented by the shader program. See 31 Programmable shaders component for more information.

### 17.2.2.2 Lighting 'off'

A Shape node is unlit if either of the following is true:

1. The shape's appearance field is NULL (default).
2. The material field in the Appearance node is NULL (default).

NOTE Geometry nodes that represent lines or points do not support lighting.

If the shape is unlit, the colour (Irgb) and alpha (A, 1−transparency) of the shape at each point on the shape's geometry is specified in Table 17.2.

Table 17.2 — Unlit colour and alpha mapping

Texture type | Colour per-vertex or per-face | Colour NULL
--- | --- | ---
No texture | Irgb= IC rgb A = 1 | Irgb= (1, 1, 1) A = 1
Intensity (one-component) | Irgb= IT × IC rgb A = 1 | Irgb = (IT,IT,IT ) A = 1
Intensity+Alpha (two-component) | Irgb= I T × IC rgb A = AT | Irgb= (IT,IT,IT ) A = AT
RGB (three-component) | Irgb= IT rgb A = 1 | Irgb= IT rgb A = 1
RGBA (four-component) | Irgb= IT rgb A = AT | Irgb= IT rgb A = AT

where:

AT = normalized [0, 1] alpha value from 2 or 4 component texture image
IC rgb = interpolated per-vertex colour, or per-face colour, from Color node
IT = normalized [0, 1] intensity from 1 or 2 component texture image
IT rgb= colour from 3-4 component texture image

### 17.2.2.3 Lighting 'on'

If the Shape node is lit (i.e., a Material and an Appearance node are specified for the Shape), the Material and Texture nodes determine the diffuse colour for the lighting equation as specified in Table 17.3.

The Material's diffuseColor field modulates the color in the texture. Hence, a diffuseColor of white results in the pure color of the texture, while a diffuseColor of black results in a black diffuse factor regardless of the texture.

The Material's transparency field modulates the alpha in the texture. Hence, a transparency of 0 results in an alpha equal to that of the texture. A transparency of 1 results in an alpha of 0 regardless of the value in the texture.

Table 17.3 — Lit colour and alpha mapping

Texture type | Colour per-vertex or per-face | Color node NULL
No texture | OD rgb = IC rgb A = 1-TM | OD rgb = ID rgb A = 1-TM
Intensity texture (one-component) | OD rgb = IT × IC rgb A = 1-TM | OD rgb = IT × ID rgb A = 1-TM
Intensity+Alpha texture (two-component) | OD rgb = IT × IC rgb A = AT | OD rgb = IT × ID rgb A = AT
RGB texture (three-component) | OD rgb = IT rgb A = 1-TM | OD rgb = IT rgb A = 1-TM
RGBA texture (four-component) | OD rgb = IT rgb A = AT | OD rgb = IT rgb A = AT

where:

ID rgb = material diffuseColor
OD rgb = diffuse factor, used in lighting equations below
TM = material transparency

All other terms are as defined in 17.2.2.2 Lighting off.

### 17.2.2.4 Lighting equations

An ideal X3D implementation will evaluate the following lighting equation at each point on a lit surface. RGB intensities at each point on a geometry (Irgb) are given by:

Irgb = IF rgb × (1 -f0)
+ f0 × (OE rgb + SUM( oni × attenuationi × spoti × IL rgb
× (ambienti + diffusei + specular i)))

where:

attenuationi = 1 / max(c1 + c2 × dL + c3 × dL2 , 1 )
ambienti = Iia × OD rgb × Oa
diffusei = Ii × OD rgb × ( N · L )
specular i = Ii × OS rgb × ( N · ((L + V) / |L + V|))shininess × 128

and:

· = modified vector dot product:
if dot product < 0, then 0.0, otherwise, dot product
c1 , c2, c 3 = light i attenuation
dV = distance from point on geometry to viewer's position, in coordinate system of current fog node
dL = distance from light to point on geometry, in light's coordinate system
f0 = fog interpolant, see Table 17.5 for calculation
IF rgb = currently bound fog's color
I L rgb = light i color
Ii = light i intensity
Iia = light i ambientIntensity
L = (PointLight/SpotLight) normalized vector from point on geometry to light source i position
L = (DirectionalLight) -direction of light source i
N = normalized normal vector at this point on geometry (interpolated from vertex normals specified in a node derived from X3DNormalNode or calculated by browser)
Oa = X3DMaterialNode ambientIntensity
OD rgb = diffuse colour, from a node derived from X3DMaterialNode, a node derived from X3DColorNode, and/or a texture node
OE rgb = X3DMaterialNode emissiveColor
OS rgb = X3DMaterialNode specularColor
on i = 1, if light source i affects this point on the geometry,
  0, if light source i does not affect this geometry. The following conditions indicate that light source i does not affect this geometry:
  a. if the geometry is farther away than radius for PointLight or SpotLight;
  b. if the geometry is outside the enclosing X3DGroupingNode; and/or
  c. if the on field is FALSE.
shininess = X3DMaterialNode shininess
spotAngle = arccosine(-L · spotDiri)
spot BW = SpotLight i beamWidth
spot CO = SpotLight i cutOffAngle
spot i = spotlight factor, see Table 17.4 for calculation
spotDiri = normalized SpotLight i direction
SUM: sum over all light sources i
V = normalized vector from point on geometry to viewer's position

Table 17.4 — Calculation of the spotlight factor

Condition (in order) | spoti =
--- | ---
lighti is PointLight or DirectionalLight | 1
spotAngle ≥ spotCO | 0
spotAngle ≤ spotBW | 1
spotBW < spotAngle < spot CO | (spotAngle - spotCO ) / (spotBW - spotCO)

Table 17.5 — Calculation of the fog interpolant

Condition | f0 =
--- | ---
no fog | 1
fogType "LINEAR", dV < fogVisibility | (fogVisibility-dV) / fogVisibility
fogType "LINEAR", dV > fogVisibility | 0
fogType "EXPONENTIAL", dV < fogVisibility | exp(-dV / (fogVisibility-dV ) )
fogType "EXPONENTIAL", dV > fogVisibility | 0

### 17.2.2.5 References

The X3D lighting equations are based on the simple illumination equations given in [FOLEY] and [OPENGL].

### 17.2.2.1 Introduction

The X3D lighting model provides detailed equations that specify the colours to apply to each geometric object. For each object, the values of the material, color, and/or texture currently being applied to the object are combined with the lights illuminating the object and the currently bound X3DFogObject (if specified). These equations are designed to simulate the physical properties of light striking a surface.

If a programmable shader is defined for an Appearance node, the lighting model shall be disabled and replaced by the functionality implemented by the shader program. See 31 Programmable shaders component for more information.

NOTE Geometry nodes that represent lines or points do not support lighting. The lighting equations in X3D 4.0 are backward compatible with X3D 3.3. If you take a combination of X3D 3.3 material and light nodes, and simply use them in X3D 4.0 (leaving the new X3D 4.0 fields at their default values) then the rendering result will be equivalent. The only exception to this statement is differentiating between grayscale and RGB textures, which is discussed in section below in 17.2.2.2 Texture sampling.

### 17.2.2.2 Texture sampling

When sampling any texture, the A grayscale texture is exactly equivalent to using an RGB texture with all 3 components (red, green, blue) equal.

When sampling any texture, the A texture without an alpha channel is exactly equivalent to using a texture with an alpha channel filled with 1 (indicating opaque).

These rules make treatment of the textures simple, and consistent with other 3D authoring software. They are also consistent with how the graphic APIs and GPU shaders query the textures.

The browsers are encouraged to optimize loading of the textures, to not load all textures as 4 channels (RGBA) to the GPU. Intensity texture can be loaded as just 1 channel, intensity + alpha is only 2 channels, RGB texture without alpha is 3 channels. Optimizing this loading is useful to keep GPU memory usage low, and to keep texture loading time smaller. However, this is just an optimization. Performing it is optional, and the rendering result should be the same as if all textures were loaded as full RGBA textures.

Backward compatibility note: In X3D version 3, the treatment of grayscale and RGB textures was not consistent. In some cases (using ImageTexture node, but not inside MultiTexture) grayscale texture resulted in a different rendering result than the equivalent RGB texture (with all red, green, blue components equal). As this was inconsistent (within X3D, and with other software and model formats), uneasy to implement (browsers needed to investigate the image header), needlessly limiting to authors, and the implementation was inconsistent across the existing browsers — in X3D 4.0 it was streamlined.

### 17.2.2.3 Common definitions for all lighting models

The declarations and definitions below are presented using a pseudocode similar to the usual typical shading language code (see 31.2.2.1 Shader language options).

- The function declarations look like this: functionName(typeOfParameter1 parameter1, typeOfParameter2 parameter2, ...). Type names are underlined.
- The types can be X3D nodes, scalars (float), vectors with 2, 3 or 4 components (vector2, vector3, vector4), universal type (vectorAny which can represent any vector or scalar).
- In function definitions, we often extract vector components with syntax like: thisVector.rgb (converts vector4 to vector3, discarding alpha channel) or thisVector.a (converts vector4 to float, extracting alpha channel value).
- The symbol × performs a component-wise multiplication of vectors.
- The symbol · is a modified vector dot product that always returns value >= 0, defined like this:

  x · y = max(0.0, dotProduct(x, y))

The mixTexture(vector4 color, X3DTextureNode texture) function, used in the equations below, takes care of mixing an RGBA color with the RGBA value sampled from the texture at the given shape point.

- If the texture is NULL, then this function just returns unmodified color.
- Otherwise, if the texture is not a MultiTexture, then

  mixTexture(color, texture) = color × textureSample(texture)

  The textureSample(texture) is a function sampling the texture (recovering a single color from an array of pixels), with the correct texture coordinates and transformation.

  In effect the color modulates the color from the texture.

  - color.rgb = white = (1, 1, 1) results in the pure color of the texture,
  - color.rgb = black = (0, 0, 0) results in a black output, regardless of the texture.

  The alpha (opacity) values are multiplied too, hence:

  - color.a equal 1 (transparency equal 0) results in an alpha equal to that of the texture,
  - color.a equal 0 (transparency equal 1) results in an alpha of 0 regardless of the value in the texture.
- Otherwise, if the texture is a MultiTexture, then the mixTexture modifies this color following the MultiTexture mode specification. This is only possible when the MultiTexture is provided in the Appearance.texture field. See 12.2.5 Coexistence of textures specified in material nodes with the "Appearance.texture" field for details when multi-texturing is used.

The lerp(float factor, vectorAny x, vectorAny y) function performs a standard linear interpolation, applicable to scalars or vectors of any dimension:

lerp(factor, x, y) = x * (1 - factor) + y * factor = x + (y - x) * factor

The occlusion(vector4 color) function, used in the equations below for Phong and physical lighting model, is used to apply the occlusionTexture effect that can be specified in these nodes.

- If the occlusionTexture was not provided (left NULL) then this function just returns unmodified color.
- If the occlusionTexture was provided, then

  occlusion(color) = lerp(occlusionStrength, color, color * textureSample(occlusionTexture).r)

  In effect, the occlusionTexture multiplies the input color when occlusionStrength is 1.0. the occlusionTexture has no effect when when occlusionStrength is 0.0. Values in-between of occlusionStrength allow to smoothly interpolate between these two states.

The applyColorPerVertex(vector4 color) is used to change the color in case geometry uses Color or ColorRGBA nodes. All the lighting models use this function, although it affects a different parameter: emissiveParameter in case of unlit model, diffuseParameter in case of Phong model, and baseParameter in case of physical model. The function returns:

- The interpolated per-vertex colour, or per-face colour, from the Color node, if the Color node is provided in the geometry color field.

  Resulting rgb is derived from the values in the Color node.

  Resulting a (alpha component) is taken from input color.a in this case.

- Otherwise, the interpolated per-vertex colour, or per-face colour, from the ColorRGBA node, if the ColorRGBA node is provided in the geometry color field.

  Resulting rgba vector is derived from the values in the ColorRGBA node.

- Otherwise (if the geometry color field is empty) then it returns unmodified color.

The future X3D versions may introduce an option for Color and ColorRGBA to multiply the input color, instead of replacing it.

Moreover we define the following vectors:

- N = normalized normal vector at this point on geometry. This vector is interpolated from vertex normals specified in a node derived from X3DNormalNode or calculated by the browser. It is modified by the normalTexture providing normals in the tangent space (see X3DOneSidedMaterialNode definition).
- V = normalized vector from point on geometry to viewer's position.

The following definitions are specific to a light source i, which is indicated by a subscript in this text:

- Li is, conceptually, direction to the light i. It is precisely defined like this:
  - Li = (PointLight/SpotLight) normalized vector from point on geometry to light source i position
  - Li = (DirectionalLight) negated and normalized direction of light source i

- attenuationi = 1 / max(c1 + c2 × lightDistance + c3 × lightDistance2 , 1 )

  where

  c1, c2, c3 are the values from light i attenuation field.

  lightDistance is the distance from light to point on geometry, in light's coordinate system.

- oni = 1, if light source i affects this point on the geometry or 0 if it doesn't.

  The following conditions indicate that light source i does not affect this geometry:

  1. if the geometry is farther away than radius for PointLight or SpotLight
  2. if the geometry is outside the enclosing X3DGroupingNode in case of lights with global FALSE
  3. if the lightSource.on field is FALSE.

- shadowTesti scales down the light contribution if the light source is obscured by a shape casting shadow.

  - shadowTesti is 1.0 if the light source i has shadows field equal FALSE.

  - Otherwise, shadowTesti is 1.0 if the light source i has shadows field equal TRUE but nothing obscures the light. That is, there is no Shape (that has visible and castShadow equal TRUE) between the light source position and the given point. The DirectionalLight is treated like a point in infinity for this purpose, which means that shadow rays are all parallel to the light direction.

  - Otherwise, shadowTesti is 1.0 - shadowIntensity. This case occurs when shadows field equals TRUE and the light is obscured. For example, shadowIntensity equal 1.0 (the default) means that light contribution drops to zero when the light is obscured by the shadow caster.

- lightColori equals the color field of light source i.
- spoti is the spotlight factor. It calculates intensity within the SpotLight cone. Table 17.4 specifies how it is calculated. It relies on the following terms:
  - spotDirectioni = normalized SpotLight i direction field.
  - spotAngle = arcCosine(-L ·spotDirectioni)

Table 17.4 — Calculation of the spotlight factor

Condition (in order) | spoti =
--- | ---
lighti is PointLight or DirectionalLight | 1
spotAngle ≥ SpotLight.cutOffAngle | 0
spotAngle ≤ SpotLight.beamWidth | 1
SpotLight.beamWidth < spotAngle < lightSource.cutOffAngle | (spotAngle - SpotLight.cutOffAngle) / (SpotLight.beamWidth - SpotLight.cutOffAngle)

The applyFog(vector4 color) is used to change the color using the fog. Is it used by all lighting models, as the last operation performed on the color. The definition is:

applyFog(color) = lerp(fogInterpolant(fogDistance), fogColor, color)

where:

- fogInterpolant is the fog interpolant, see Table 17.5 for calculation.
- fogDistance is the distance from point on geometry to viewer's position, in coordinate system of current fog node.
- fogColor is the currently bound fog's color.

Table 17.5 — Calculation of the fogInterpolant(FogDistance) function:

Condition | fogInterpolant(fogDistance) =
--- | ---
no fog | 1
fogType "LINEAR", fogDistance < fogVisibility | (fogVisibility - fogDistance) / fogVisibility
fogType "LINEAR", fogDistance > fogVisibility | 0
fogType "EXPONENTIAL", fogDistance < fogVisibility | exp(-FogDistance / (fogVisibility - fogDistance ) )
fogType "EXPONENTIAL", fogDistance > fogVisibility | 0

### 17.2.2.4 Unlit lighting model

A Shape node is unlit if either of the following is true:

1. The shape's appearance field is NULL (default).
2. The material field in the Appearance node is NULL (default).
3. The material field in the Appearance node contains a node of type UnlitMaterial.

In the first two cases above, the rendering is exactly equivalent as if the Appearance node was provided (not NULL), and the material field inside contained an UnlitMaterial with all the fields at their default. Effectively, it means a white untextured unlit material is the default.

NOTE Geometry nodes that represent lines or points do not support lighting if the normal vectors for them are not provided.

If the shape is unlit, the RGBA color of the shape at each point on the shape's geometry is calculated using this equation:

fragmentColor = applyFog(mixTexture(applyColorPerVertex(emissiveParameter), emissiveTextureParameter))

where:

- emissiveParameter.rgb (RGB channels) are taken from UnlitMaterial.emissiveColor.
- emissiveParameter.a (alpha channel) is taken from 1 - UnlitMaterial.transparency.
- emissiveTextureParameter is equal to:
  - emissiveTexture of the UnlitMaterial node, if it is not NULL.
  - Otherwise, Appearance.texture, if the UnlitMaterial has emissiveTexture equal NULL, but Appearance.texture is not NULL. See 12.2.5 Coexistence of textures specified in material nodes with the "Appearance.texture" field.
  - Otherwise (if both the UnlitMaterial.emissiveTexture and Appearance.texture are NULL) then emissiveTextureParameter is NULL. In other words, the mixTexture(...) function used above simply returns the unmodified applyColorPerVertex(emissiveParameter).

### 17.2.2.5 Phong lighting model

The Shape node is lit with a Phong lighting model if the Appearance node is specified for the Shape, and the material field contains a Material node.

NOTE This node is simply called Material for historical reasons. Conceptually, you should think about it now as a PhongMaterial.

The rendered fragment (pixel) color is determined by these equations:

fragmentColor = applyFog(emissiveParameter + occlusion(sumOverAllLights(lightContributioni)))

lightContributioni = oni × shadowTesti × lightColori × attenuationi × spoti × (ambienti + diffusei + speculari)

An ideal X3D implementation will evaluate the following lighting equation at each point on a lit surface. The means that we advise using Phong shading and assume it when writing equations below. For implementations that perform Gouraud shading see 17.2.2.8 Gouraud shading section.

The meaning of all the terms is explained below.

Material parameters

First, the parameters whose value doesn't depend on the light source:

The material diffuseParameter is calculated as follows:

diffuseParameter = mixTexture(applyColorPerVertex(diffuseParameter), diffuseTextureParameter) where:

- diffuseParameter.a (RGB channels) is taken from Material.diffuseColor.
- diffuseParameter.a (alpha channel) is taken from 1 - Material.transparency.
- diffuseTextureParameter is equal to:
  - diffuseTexture of the Material node, if it is not NULL.
  - Otherwise, Appearance.texture, if the Material has diffuseTexture equal NULL, but Appearance.texture is not NULL. See 12.2.5 Coexistence of textures specified in material nodes with the "Appearance.texture" field.
  - Otherwise (if both the Material.diffuseTexture and Appearance.texture are NULL) then diffuseTextureParameter is NULL. In other words, the mixTexture(...) function used above simply returns the unmodified applyColorPerVertex(diffuseParameter).

The remaining parameters are defined below:

- ambientParameter = ambientIntensity × diffuseColor × textureSample(ambientTexture).rgb

- emissiveParameter = emissiveColor × textureSample(emissiveTexture).rgb

- specularParameter = specularColor × textureSample(specularTexture).rgb

- shininessParameter = shininess × textureSample(shininessTexture).a × 128

In the above equations, if the given texture is NULL then behave as if the textureSample(texture) returned a white opaque value (1, 1, 1, 1).

Light parameters

Now we can define terms that depend on the light source:

- ambienti = lightSource.ambientIntensity × ambientParameter

- diffusei = lightSource.intensity × diffuseParameter × (N · L)

- speculari = lightSource.intensity × specularParameter × (N · ((L + V) / |L + V|))shininessParameter

The Phong lighting equations are based on the simple illumination equations given in [FOLEY] and [OPENGL].

### 17.2.2.6 Physical lighting model

The Shape node is lit with a Physical lighting model if the Appearance node are specified for the Shape, and the material field contains a PhysicalMaterial node. We perform in this case a physically-based rendering.

The rendered fragment (pixel) color is determined by these equations:

fragmentColor = applyFog(emissiveParameter + occlusion(sumOverAllLights(lightContributioni)))

lightContributioni = oni × shadowTesti × lightColori × attenuationi × spoti × physicalLightContributioni

The input values used by the physical lighting equation are as follows:

baseParameter = mixTexture(applyColorPerVertex(baseParameter), baseTextureParameter) where:

- baseParameter.a (RGB channels) is taken from PhysicalMaterial.baseColor.
- baseParameter.a (alpha channel) is taken from 1 - PhysicalMaterial.transparency.
- baseTextureParameter is equal to:
  - baseTexture of the PhysicalMaterial node, if it is not NULL.
  - Otherwise, Appearance.texture, if the PhysicalMaterial has baseTexture equal NULL, but Appearance.texture is not NULL. See 12.2.5 Coexistence of textures specified in material nodes with the "Appearance.texture" field.
  - Otherwise (if both the PhysicalMaterial.baseTexture and Appearance.texture are NULL) then baseTextureParameter is NULL. In other words, the mixTexture(...) function used above simply returns the unmodified applyColorPerVertex(baseParameter).

metallicParameter = PhysicalMaterial.metallic × textureSample(PhysicalMaterial.metallicRoughnessTexture).b

roughnessParameter = PhysicalMaterial.roughness × textureSample(PhysicalMaterial.metallicRoughnessTexture).g

If the metallicRoughnessTexture is NULL, then metallicParameter and roughnessParameter are just equal to (respectively) metallic and roughness values given by the PhysicalMaterial node.

The physical lighting model uses the baseParameter, metallicParameter and roughnessParameter to calculate the physicalLightContributioni value in accordance with the physically based rendering equations as specified in 2.[GLTF].

Using the baseParameter, metallicParameter and roughnessParameter, the physical lighting model performs the exact same computations as the recommended glTF lighting model, to calculate physicalLightContributioni value. See glTF 2.0 specification section about lighting equations and glTF-Sample-Viewer implementation of it. Future revisions of this draft will contain the final recommended equations.

The physical lighting equations are specified in 2.[GLTF].

### 17.2.2.7 References

### 17.2.2.7 Gouraud shading model

An ideal X3D implementation will evaluate the lighting equation at each point on a lit surface. The lighting equations in previous sections assume that you use Phong shading (not to be confused with Phong lighting model). In case of Phong shading, lighting is calculated at each fragment (pixel of the screen).

However, some implementations perform Gouraud shading, either by default, or as an option for the user (for efficiency), or as a fallback for an older GPUs. In such case, the lighting equations given above cannot be reproduced precisely. In Gouraud shading, you calculate lighting per-vertex, which means that it doesn't make sense to use texture information to modify material parameters.

In case when Gouraud shading is used we recommend this algorithm:

- The vertex shader calculates lighting equations, ignoring any texture information (as if all the textures were NULL).
- The fragment shader multiplies the resulting interpolated color by the main texture. The main texture is:
  - Material.diffuseTexture, if Phong Material node is used.
  - UnlitMaterial.emissiveTexture, if UnlitMaterial node is used.
  - PhysicalMaterial.baseTexture, if PhysicalMaterial node is used.

  If the given texture is NULL, then use the Appearance.texture. If the Appearance.texture is also NULL, no texture is used.

  All channels (RGB and alpha) are affected by the main texture.

  This method of determining the main texture is deliberately 100% consistent with:

  1. The way how the Appearance.texture cooperates with textures inside the materials, following section 12.2.5 Coexistence of textures specified in material nodes with the "Appearance.texture" field. Appearance.texture may be used in place of Material.diffuseTexture, or UnlitMaterial.emissiveTexture, or PhysicalMaterial.baseTexture.
  2. The determination which texture contains the alpha channel that is multiplied by material transparency. We take it from alpha channel of Material.diffuseTexture, or UnlitMaterial.emissiveTexture, or PhysicalMaterial.baseTexture.

This recommendation tries to preserve the intended look as much as possible in Gouraud shading.

NOTE This recommendation actually doesn't change anything in case of UnlitMaterial. And this is correct, Gouraud shading actually doesn't change how the UnlitMaterial can be implemented.

NOTE When using PhysicalMaterial on older hardware, some implementations may fall back to the Phong lighting model. If this is necessary, we recommend using PhysicalMaterial.baseColor as the Phong diffuse factor, and PhysicalMaterial.baseTexture as the texture to multiply the resulting color.

### 17.2.2.8 Shadows

Geometric objects can cast shadows, meaning that the amount of light reaching a given surface is decreased if a light source is obscured by occluding geometry. Intervening geometry is only considered for occlusion effects when the X3DShapeNode field castShadow is TRUE.

Multiple light sources can have shadows enabled. Lighting contributions from each light source, and the intensity of corresponding shadows that reduce light available at end points, are each computed independently before reaching lit surface points.

High-fidelity physical effects such as umbra, penumbra and antumbra may be optionally applied during shadowIntensity computations. Browser implementations are allowed some variations in shadow rendering. Secondary Shape geometry that is already occluded behind an intervening Shape casting a shadow is not required to cast a secondary shadow.

# 17.3 Abstract types

## 17.3.1 X3DLightNode

    X3DLightNode : X3DChildNode {
      SFFloat [in,out] ambientIntensity 10     [0,1]
      SFColor [in,out] color            1 1 1 [0,1]
      SFBool  [in,out] global           FALSE
      SFFloat [in,out] intensity        1     [0,1]
      SFFloat [in,out] intensity        1     [0,∞) # Consider maintaining nominal upper limit 1
      SFNode  [in,out] metadata         NULL  [X3DMetadataObject]
      SFBool  [in,out] on               TRUE
      SFBool  [in,out] shadows          FALSE
      SFFloat [in,out] shadowIntensity  1     [0,1]
    }

The X3DLightNode abstract node type is the base type from which all node types that serve as light sources are derived. A description of the ambientIntensity, color, intensity, and on fields is in 17.2.1 Light source semantics. A description of the global field is in 17.2.1.2 Scoping of lights.

The shadows field indicates whether or not lighting from this node casts a shadow behind illuminated X3DShapeNode geometry.

The shadowIntensity field defines how much light is obscured by shapes that cast shadows, ranging from 0 (light not obscured, no visible shadows) to 1 (light completely obscured, full-intensity shadows). The shadowIntensity field has no effect when the shadows field is FALSE.

# 17.4 Node reference

## 17.4.1 DirectionalLight

    DirectionalLight : X3DLightNode {
      SFFloat [in,out] ambientIntensity 0      [0,1]
      SFColor [in,out] color            1 1 1  [0,1]
      SFVec3f [in,out] direction        0 0 -1 (-∞,∞)
      SFBool  [in,out] global           FALSE
      SFFloat [in,out] intensity        1      [0,1]
      SFFloat [in,out] intensity        1      [0,∞)
      SFNode  [in,out] metadata         NULL   [X3DMetadataObject]
      SFBool  [in,out] on               TRUE
      SFBool  [in,out] shadows          FALSE
      SFFloat [in,out] shadowIntensity  1     [0,1]
    }

The DirectionalLight node defines a directional light source that illuminates along rays parallel to a given 3-dimensional vector. A description of the ambientIntensity, color, intensity, and on fields is in 17.2.1 Light source semantics. A description of the global field is in 17.2.1.2 Scoping of lights.

The direction field specifies the direction vector of the illumination emanating from the light source in the local coordinate system. Light is emitted along parallel rays from an infinite distance away. A directional light source illuminates only the objects in its enclosing parent group. The light may illuminate everything within this coordinate system, including all children and descendants of its parent group. The accumulated transformations of the parent nodes affect the light.

DirectionalLight nodes do not attenuate with distance. A precise description of X3D's lighting equations is contained in 17.2.2 Lighting model.

## 17.4.2 EnvironmentLight

Editors NOTE This preliminary node definition indicates intent to implement Image Based Lighting (IBL) as defined by Khronos glTF specification. Current activity by X3D practitioners is focused on converged design, implementation and evaluation using the open-source Castle Game Engine and X3DOM browsers. Further improvements are expected.

    EnvironmentLight : X3DLightNode {
      SFFloat    [in,out] ambientIntensity     0        [0,1]
      SFColor    [in,out] color                1 1 1    [0,1]
      SFNode     [in,out] diffuse              NULL     [X3DSingleTextureNode] # .DDS format
      SFNode     [in,out] diffuseTexture       NULL     [X3DEnvironmentTextureNode]
      MFFloat    [in,out] diffuseCoefficients  []
      SFBool     [in,out] global               FALSE
      SFFloat    [in,out] intensity            1        [0,∞)
      SFNode     [in,out] metadata             NULL     [X3DMetadataObject]
      SFBool     [in,out] on                   TRUE
      SFRotation [in,out] rotation             0 0 1 0  [-1,1] or (-∞,∞)
      SFBool     [in,out] shadows              FALSE
      SFFloat    [in,out] shadowIntensity      1        [0,1]
      SFNode     [in,out] specularTexture      NULL     [X3DEnvironmentTextureNode]
    }

The EnvironmentLight node supports Image Based Lighting (IBL) by specifying light-source intensity around a given location (i.e. the environment) as a cube map. EnvironmentLight defines both specular radiance and diffuse irradiance.

The diffuseTexture and specularTexture fields define diffuse and specular lighting modifications, respectively.

The diffuseCoefficients field provides a 3 x 9 array of float values that declares spherical harmonic coefficients for irradiance up to l=2, corresponding to glTF irradianceCoefficients field. The diffuseCoefficients field overrides the diffuseTexture field if both are provided.

## 17.4.3 PointLight

    PointLight : X3DLightNode {
      SFFloat [in,out] ambientIntensity 0     [0,1]
      SFVec3f [in,out] attenuation      1 0 0 [0,∞)
      SFColor [in,out] color            1 1 1 [0,1]
      SFBool  [in,out] global           TRUE
      SFFloat [in,out] intensity        1     [0,1]
      SFFloat [in,out] intensity        1     [0,∞)
      SFVec3f [in,out] location         0 0 0 (-∞,∞)
      SFNode  [in,out] metadata         NULL  [X3DMetadataObject]
      SFBool  [in,out] on               TRUE
      SFFloat [in,out] radius           100   [0,∞)
      SFBool  [in,out] shadows          FALSE
      SFFloat [in,out] shadowIntensity  1     [0,1]
    }

The PointLight node specifies a point light source at a 3D location in the local coordinate system. A point light source emits light equally in all directions; that is, it is omnidirectional. PointLight nodes are specified in the local coordinate system and are affected by ancestor transformations. A description of the global field is in 17.2.1.2 Scoping of lights.

Subclause 17.2.1 Light source semantics, contains a detailed description of the ambientIntensity, color, and intensity fields.

A PointLight node illuminates geometry within radius length base units of its location. Both radius and location are affected by ancestors' transformations (scales affect radius and transformations affect location). The radius field shall be greater than or equal to zero.

PointLight node's illumination falls off with distance as specified by three attenuation coefficients. The attenuation factor is:

1/max(attenuation[0] + attenuation[1] × r + attenuation[2] × r2, 1)

where r is the distance from the light to the surface being illuminated. The default is no attenuation. An attenuation value of (0, 0, 0) is identical to (1, 0, 0). Attenuation values shall be greater than or equal to zero. A detailed description of X3D's lighting equations is contained in 17.2.2 Lighting model.

## 17.4.4 SpotLight

    SpotLight : X3DLightNode {
      SFFloat [in,out] ambientIntensity 0        [0,1]
      SFVec3f [in,out] attenuation      1 0 0    [0,∞)
      SFFloat [in,out] beamWidth        π/4      (0,π/2]
      SFColor [in,out] color            1 1 1    [0,1]
      SFFloat [in,out] cutOffAngle      π/2      (0,π/2]
      SFVec3f [in,out] direction        0 0 -1   (-∞,∞)
      SFBool  [in,out] global           TRUE
      SFFloat [in,out] intensity        1        [0,1]
      SFFloat [in,out] intensity        1        [0,∞)
      SFVec3f [in,out] location         0 0 0    (-∞,∞)
      SFNode  [in,out] metadata         NULL     [X3DMetadataObject]
      SFBool  [in,out] on               TRUE
      SFFloat [in,out] radius           100      [0,∞)
      SFBool  [in,out] shadows          FALSE
      SFFloat [in,out] shadowIntensity  1        [0,1]
    }

The SpotLight node defines a light source that emits light from a specific point along a specific direction vector and constrained within a solid angle. Spotlights may illuminate geometry nodes that respond to light sources and intersect the solid angle defined by the SpotLight. Spotlight nodes are specified in the local coordinate system and are affected by ancestors' transformations. A description of the global field is in 17.2.1.2 Scoping of lights.

A detailed description of ambientIntensity, color, intensity, and the lighting equations of X3D is provided in 17.2.1 Light source semantics. More information on lighting concepts can be found in 17.2.2 Lighting model, including a detailed description of the X3D lighting equations.

The location field specifies a translation offset of the centre point of the light source from the light's local coordinate system origin. This point is the apex of the solid angle which bounds light emission from the given light source. The direction field specifies the direction vector of the light's central axis defined in the local coordinate system.

The on field specifies whether the light source emits light. If on is TRUE, the light source is emitting light and may illuminate geometry in the scene. If on is FALSE, the light source does not emit light and does not illuminate any geometry.

The radius field specifies the radial extent of the solid angle and the maximum distance from location that may be illuminated by the light source. The light source does not emit light outside this radius. The radius shall be greater than or equal to zero.

Both radius and location are affected by ancestors' transformations (scales affect radius and transformations affect location).

The cutOffAngle field specifies the outer bound of the solid angle. The light source does not emit light outside of this solid angle. The beamWidth field specifies an inner solid angle in which the light source emits light at uniform full intensity. The light source's emission intensity drops off from the inner solid angle (beamWidth) to the outer solid angle (cutOffAngle) as described in the following equations:

        angle = the angle between the Spotlight's direction vector
                and the vector from the Spotlight location to the point
                to be illuminated

        if (angle ≥ cutOffAngle):
            multiplier = 0
        else if (angle ≤ beamWidth):
            multiplier = 1
        else:
            multiplier = (angle - cutOffAngle) / (beamWidth - cutOffAngle)
        intensity(angle) = SpotLight.intensity × multiplier

If the beamWidth is greater than the cutOffAngle, beamWidth is defined to be equal to the cutOffAngle and the light source emits full intensity within the entire solid angle defined by cutOffAngle. Both beamWidth and cutOffAngle shall be greater than 0.0 and less than or equal to π/2. Figure 17.1 depicts the beamWidth, cutOffAngle, direction, location, and radius fields of the SpotLight node.

Figure 17.1 — SpotLight node

SpotLight illumination falls off with distance as specified by three attenuation coefficients. The attenuation factor is:

1/max(attenuation[0] + attenuation[1] × r + attenuation[2] × r2 , 1)

where r is the distance from the light to the surface being illuminated. The default is no attenuation. An attenuation value of (0, 0, 0) is identical to (1, 0, 0). Attenuation values shall be greater than or equal to zero. A detailed description of X3D's lighting equations is contained in 17.2.2 Lighting model.

# 17.5 Support levels

The Lighting component provides three levels of support as specified in Table 17.6.

Table 17.6 — Lighting component support levels

Level | Prerequisites | Nodes/Features | Support
--- | --- | --- | ---
1 | Core 1 Shape 1 |  |
|  | X3DLightNode (abstract) | n/a
|  | DirectionalLight | Not scoped by parent Group or Transform.
|  |  | Support for Gouraud and Phong shading.
2 | Core 1 Shape 1 |  |
|  | All Level 1 Lighting nodes | All fields as supported in Level 1.
|  | PointLight | radius optionally supported. Linear attenuation.
|  | SpotLight | beamWidth optionally supported. radius optionally supported. Linear attenuation.
3 | Core 1 Shape 1 2 |  |
|  | All Level 2 Lighting nodes | All fields fully supported.
|  | EnvironmentLight | All fields fully supported.
|  |  | Support for Physical and Unlit lighting models.
4 | Core 1 Shape 2 |  |
|  | All nodes | Support for Unlit lighting model. All fields and capabilities fully supported.