Closed andreasplesch closed 9 months ago
https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_lights_punctual/README.md
directional -> DirectionalLight point -> PointLight spot -> SpotLight color -> color name -> DEF ? intensity -> intensity, may need adjustment range -> point: radius spot: radius innerConeAngle: beamWidth outerConeAngle: cutOffAngle "Conforming implementations will model this angular attenuation with a curve that follows a steeper decline in brightness before leveling off when moving from the inner to the outer angle." ->
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
attenuation = max( min( 1.0 - ( current_distance / range )^4, 1 ), 0 ) / current_distance^2 -> 1 /max(attenuation[0] + attenuation[1] × r + attenuation[2] × r^2 , 1 )
glTF and x3d treat attenuation with distance somewhat differently. Both have an absolute cutoff and both have inverse square falloff. But glTF additionally has additionally a smoother transition close the absolute cutoff. castle just uses x3d defaults: https://github.com/castle-engine/castle-engine/blob/57a899edd0436bfb0ae990ed1078420ed8d97fb9/src/scene/load/x3dloadinternalgltf.pas#L757
x3d:
A parabola shifted by -b/2a in x (distance) and c/a - b^2/(4a^2) in y (1/attenuation).
It is not possible to approximate the additional gltf falloff near the range in x3d. Therefore x3d attenuation factors should be 0,0,1. So at 10m distance the attenuation is already 0.01. The intensity has be high for spotlights and pointlights.
glTF has effectively fixed attenuation. So for a large scene, it is necessary to have high intensity lights.
X3D:
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
glTF: interpolation of cosines
// These two values can be calculated on the CPU and passed into the shader
float lightAngleScale = 1.0f / max(0.001f, cos(innerConeAngle) - cos(outerConeAngle));
float lightAngleOffset = -cos(outerConeAngle) * lightAngleScale;
// Then, in the shader:
float cd = dot(spotlightDir, normalizedLightVector); // cos(angle)
float angularAttenuation = saturate(cd * lightAngleScale + lightAngleOffset);
// cos(angle) / (cos(beamWidth) - cos(cutOff)) - cos(cufOff)/(cos(beamWidth) - cos(cutOff))
// (cos(angle)-cos(cutOff))/(cos(beamWidth) - cos(cutOff))
angularAttenuation *= angularAttenuation;
In short, X3D interpolates uses angle difference ratios which glTF uses ratio of the differences of the cosines, then squared for sharper falloff.
Let's see how this compares:
X3D defaults:
glTF defaults:
https://github.com/castle-engine/demo-models/tree/master/gltf/punctual_lights https://www.web3d.org/x3d/content/examples/X3dForWebAuthors/Chapter11LightingEnvironmentalEffects/SpotLightVisualizationIndex.html https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/LightsPunctualLamp https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/IridescenceSuzanne
pointlight.zip by threejs editor
3js:
babylonjs:
khronos:
castle:
x3dom:
The Khronos gltf viewer uses a slightly modified version of the recommendation in the glTF spec. for angular attenuation:
float getSpotAttenuation(vec3 pointToLight, vec3 spotDirection, float outerConeCos, float innerConeCos) {
float actualCos = dot(normalize(spotDirection), normalize(-pointToLight));
if (actualCos > outerConeCos) {
if (actualCos < innerConeCos) {
return smoothstep(outerConeCos, innerConeCos, actualCos);
}
return 1.0;
}
return 0.0;
}
https://github.com/KhronosGroup/glTF-Sample-Viewer/issues/486
directional light and point light with specular https://raw.githubusercontent.com/KhronosGroup/glTF/main/extensions/2.0/Khronos/KHR_lights_punctual/schema/examples/lights.gltf https://github.com/andreasplesch/x3dom/raw/lights_punctual/test/functional/media/data/lights.glb
khronos:
view3dscene:
x3dom:
bjs:
threejs:
PR #1292
PR #1293
Wondering if there are corresponding changes in rendering hardware and APIs.
When ready, we should be prepared to propose potential additions for X3D 4.1. Once fully ready for ISO with multiple implementations and corresponding examples, mature capabilities can be designated as an X3D Suggested Practice or Web3D Recommended Practice.
If possible, a single simple parameter for appropriate nodes and node types might be useful. For example, with backwards compatibility:
I believe you are referring to the light attenuation changes of glTF relative to X3D. Not sure API/hardware/OpenGL changes. Perhaps the fixed function pipeline had something related as Michalis was referring to it but that is ancient, eg. prepre webGL history. Certainly no recent changes. glTF made these choices from trends in 3d/game engines I believe. For a single parameter, another option would be a scalar weighting factor [0-1] for mixing x3d and gltf attenuation: 0=x3d, 1=gltf, 0.5 intermediate, SFFloat attenuationSmoothness 0. But a boolean with a good name would be simpler.
range attenuation: 0,0,1 at radius 100, identical at 0 to 10m distance
at 50 to 100m distance strong attenuation and small differences
1,0,0 at radius 100:
at 0 to 10: almost identical, no or very minor attenuation
at 50 to 100m distance step with x3d and parabolic transition with gltf
infinite range (gltf default):
0,0,1
1,0,0
for both attenuation settings, gltf and x3d are mathematically the same
Meshopt example: https://github.com/zeux/meshoptimizer/tree/master/demo https://raw.githubusercontent.com/zeux/meshoptimizer/master/demo/pirate.glb
https://github.com/mrdoob/three.js/blob/e6304f37428aca97fab71ff74d52e73cbc8b90e3/examples/models/gltf/coffeemat.glb https://raw.githubusercontent.com/mrdoob/three.js/e6304f37428aca97fab71ff74d52e73cbc8b90e3/examples/models/gltf/coffeemat.glb but needs ktx extension
https://github.com/rdurnin/DebugMeshesLibrary/tree/d4050c3dfbb93e63ee129f3b13ed3cabc2dbbcf3/optmesh const https://media.githubusercontent.com/media/rdurnin/DebugMeshesLibrary/d4050c3dfbb93e63ee129f3b13ed3cabc2dbbcf3/optmesh/Rage_Q1_NoInstances_packed.glb https://media.githubusercontent.com/media/rdurnin/DebugMeshesLibrary/d4050c3dfbb93e63ee129f3b13ed3cabc2dbbcf3/optmesh/Rage_Q2_Instances_packed.glb https://media.githubusercontent.com/media/rdurnin/DebugMeshesLibrary/d4050c3dfbb93e63ee129f3b13ed3cabc2dbbcf3/optmesh/Rage_Q3_Q4_Combined_packed.glb
https://playground.babylonjs.com/#CIYTF6#0 https://assets.babylonjs.com/meshes/Buggy/glTF-MeshOpt/Buggy.gltf
Or (preferred)
Decompress views in Loader, and reassemble buffer and bufferviews. If no buffer.uri, create empty buffer with byteLength. Combine all buffers, store offset into superbuffer decompress into superbuffer
meshopt_compression is often/almost always used together with mesh_quantization. mesh_quantization was already implemented and needed only to be recognized as supported.
This concludes efforts on these 'simpler' gltf extensions.
These glTF extensions are fairly straightforward to implement: