Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support KHR_lights_punctual, KHR_materials_emissive_strength, EXT_meshopt_compression, EXT_texture_webp #1259

Closed
4 tasks done
andreasplesch opened this issue Feb 26, 2023 · 18 comments

Comments

@andreasplesch
Copy link
Contributor

andreasplesch commented Feb 26, 2023

These glTF extensions are fairly straightforward to implement:

@andreasplesch
Copy link
Contributor Author

andreasplesch commented Aug 18, 2023

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 )

@andreasplesch
Copy link
Contributor Author

andreasplesch commented Aug 19, 2023

Distance Attenuation

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:
image
image
image
image
image
image
image
image
image
image

A parabola shifted by -b/2a in x (distance) and c/a - b^2/(4a^2) in y (1/attenuation).

image
image

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.

@andreasplesch
Copy link
Contributor Author

andreasplesch commented Aug 19, 2023

Angular Attenuation

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

image

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:
image

X3D defaults:
image

glTF defaults:
image

@andreasplesch
Copy link
Contributor Author

andreasplesch commented Aug 21, 2023

pointlight.zip by threejs editor
3js: image
babylonjs: image
khronos:
image
castle:
pointlight_0
x3dom:
image

@andreasplesch
Copy link
Contributor Author

andreasplesch commented Aug 21, 2023

spotlight.zip
threejs:
image

bjs:image

khronos:image

view3dscene:
spotlight_0

x3dom:
image

@andreasplesch
Copy link
Contributor Author

andreasplesch commented Aug 26, 2023

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;
}

KhronosGroup/glTF-Sample-Viewer#486

@andreasplesch
Copy link
Contributor Author

@andreasplesch
Copy link
Contributor Author

PR #1292

@andreasplesch
Copy link
Contributor Author

PR #1293

@brutzman
Copy link

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:

  • [in out] SFBool punctual FALSE

@andreasplesch
Copy link
Contributor Author

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.

@andreasplesch
Copy link
Contributor Author

range attenuation: 0,0,1 at radius 100, identical at 0 to 10m distance
image
at 50 to 100m distance strong attenuation and small differences
image

1,0,0 at radius 100:
at 0 to 10: almost identical, no or very minor attenuation
image
at 50 to 100m distance step with x3d and parabolic transition with gltf
image

infinite range (gltf default):
0,0,1
image
1,0,0
image
for both attenuation settings, gltf and x3d are mathematically the same

@andreasplesch
Copy link
Contributor Author

andreasplesch commented Sep 14, 2023

  1. combine compressed buffer as is with other buffers, update bufferview compressed byteoffsets, isMeshOpt=true (compressor=MeshOopt/Draco/None)
  2. in binarysetup, decompress view

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

@andreasplesch
Copy link
Contributor Author

meshopt_compression is often/almost always used together with mesh_quantization. mesh_quantization was already implemented and needed only to be recognized as supported.
#1292
#1293
#1294
#1295
This concludes efforts on these 'simpler' gltf extensions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants