Skip to content

Commit

Permalink
IES shader (#15952)
Browse files Browse the repository at this point in the history
* initial commit

* .

* loader complete

* .

* Switched to texture loader

* Update packages/dev/core/src/Materials/Textures/Loaders/iesTextureLoader.ts

Co-authored-by: Popov72 <github@evpopov.com>

* Optimize texture storage

* Fix build

* shader

* test + webgpu

* Fix test

---------

Co-authored-by: Popov72 <github@evpopov.com>
  • Loading branch information
deltakosh and Popov72 authored Dec 5, 2024
1 parent 3ad1bca commit af7fcf3
Show file tree
Hide file tree
Showing 19 changed files with 271 additions and 51 deletions.
33 changes: 33 additions & 0 deletions packages/dev/core/src/Lights/spotLight.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,30 @@ export class SpotLight extends ShadowLight {
private _lightAngleScale: number;
private _lightAngleOffset: number;

private _iesProfileTexture: Nullable<BaseTexture> = null;

/**
* Gets or sets the IES profile texture used to create the spotlight
* #UIAXAU#1
*/
public get iesProfileTexture(): Nullable<BaseTexture> {
return this._iesProfileTexture;
}

public set iesProfileTexture(value: Nullable<BaseTexture>) {
if (this._iesProfileTexture === value) {
return;
}

this._iesProfileTexture = value;

if (this._iesProfileTexture && SpotLight._IsTexture(this._iesProfileTexture)) {
this._iesProfileTexture.onLoadObservable.addOnce(() => {
this._markMeshesAsLightDirty();
});
}
}

/**
* Gets the cone angle of the spot light in Radians.
*/
Expand Down Expand Up @@ -385,6 +409,10 @@ export class SpotLight extends ShadowLight {
effect.setMatrix("textureProjectionMatrix" + lightIndex, this._projectionTextureMatrix);
effect.setTexture("projectionLightTexture" + lightIndex, this.projectionTexture);
}

if (this._iesProfileTexture && this._iesProfileTexture.isReady()) {
effect.setTexture("iesLightTexture" + lightIndex, this._iesProfileTexture);
}
return this;
}

Expand Down Expand Up @@ -439,6 +467,10 @@ export class SpotLight extends ShadowLight {
if (this._projectionTexture) {
this._projectionTexture.dispose();
}
if (this._iesProfileTexture) {
this._iesProfileTexture.dispose();
this._iesProfileTexture = null;
}
}

/**
Expand Down Expand Up @@ -473,6 +505,7 @@ export class SpotLight extends ShadowLight {
public prepareLightSpecificDefines(defines: any, lightIndex: number): void {
defines["SPOTLIGHT" + lightIndex] = true;
defines["PROJECTEDLIGHTTEXTURE" + lightIndex] = this.projectionTexture && this.projectionTexture.isReady() ? true : false;
defines["IESLIGHTTEXTURE" + lightIndex] = this._iesProfileTexture && this._iesProfileTexture.isReady() ? true : false;
}
}

Expand Down
10 changes: 9 additions & 1 deletion packages/dev/core/src/Materials/Node/Blocks/Dual/lightBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,15 @@ export class LightBlock extends NodeMaterialBlock {
break;
}
const onlyUpdateBuffersList = state.uniforms.indexOf("vLightData" + lightIndex) >= 0;
PrepareUniformsAndSamplersForLight(lightIndex, state.uniforms, state.samplers, defines["PROJECTEDLIGHTTEXTURE" + lightIndex], uniformBuffers, onlyUpdateBuffersList);
PrepareUniformsAndSamplersForLight(
lightIndex,
state.uniforms,
state.samplers,
defines["PROJECTEDLIGHTTEXTURE" + lightIndex],
uniformBuffers,
onlyUpdateBuffersList,
defines["IESLIGHTTEXTURE" + lightIndex]
);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,15 @@ export class PBRMetallicRoughnessBlock extends NodeMaterialBlock {
break;
}
const onlyUpdateBuffersList = state.uniforms.indexOf("vLightData" + lightIndex) >= 0;
PrepareUniformsAndSamplersForLight(lightIndex, state.uniforms, state.samplers, defines["PROJECTEDLIGHTTEXTURE" + lightIndex], uniformBuffers, onlyUpdateBuffersList);
PrepareUniformsAndSamplersForLight(
lightIndex,
state.uniforms,
state.samplers,
defines["PROJECTEDLIGHTTEXTURE" + lightIndex],
uniformBuffers,
onlyUpdateBuffersList,
defines["IESLIGHTTEXTURE" + lightIndex]
);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export class _ExrTextureLoader implements IInternalTextureLoader {
* @param _createPolynomials will be true if polynomials have been requested
* @param _onLoad defines the callback to trigger once the texture is ready
* @param _onError defines the callback to trigger in case of error
* Cube texture are not supported by .exr files
*/
public loadCubeData(
_data: ArrayBufferView | ArrayBufferView[],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ export class _HDRTextureLoader implements IInternalTextureLoader {

/**
* Uploads the cube texture data to the WebGL texture. It has already been bound.
* Cube texture are not supported by .hdr files
*/
public loadCubeData(): void {
// eslint-disable-next-line no-throw-literal
throw ".env not supported in Cube.";
throw ".hdr not supported in Cube.";
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class _IESTextureLoader implements IInternalTextureLoader {

const textureData = LoadIESData(uint8array);

callback(textureData.width, textureData.height, texture.generateMipMaps, false, () => {
callback(textureData.width, textureData.height, false, false, () => {
const engine = texture.getEngine();
texture.type = Constants.TEXTURETYPE_FLOAT;
texture.format = Constants.TEXTUREFORMAT_R;
Expand Down
17 changes: 15 additions & 2 deletions packages/dev/core/src/Materials/materialHelper.functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -932,14 +932,16 @@ export function PrepareDefinesForCamera(scene: Scene, defines: any): boolean {
* @param projectedLightTexture defines if projected texture must be used
* @param uniformBuffersList defines an optional list of uniform buffers
* @param updateOnlyBuffersList True to only update the uniformBuffersList array
* @param iesLightTexture defines if IES texture must be used
*/
export function PrepareUniformsAndSamplersForLight(
lightIndex: number,
uniformsList: string[],
samplersList: string[],
projectedLightTexture?: any,
uniformBuffersList: Nullable<string[]> = null,
updateOnlyBuffersList = false
updateOnlyBuffersList = false,
iesLightTexture = false
) {
if (uniformBuffersList) {
uniformBuffersList.push("Light" + lightIndex);
Expand Down Expand Up @@ -977,6 +979,9 @@ export function PrepareUniformsAndSamplersForLight(
samplersList.push("projectionLightTexture" + lightIndex);
uniformsList.push("textureProjectionMatrix" + lightIndex);
}
if (iesLightTexture) {
samplersList.push("iesLightTexture" + lightIndex);
}
}

/**
Expand Down Expand Up @@ -1008,7 +1013,15 @@ export function PrepareUniformsAndSamplersList(uniformsListOrOptions: string[] |
if (!defines["LIGHT" + lightIndex]) {
break;
}
PrepareUniformsAndSamplersForLight(lightIndex, uniformsList, samplersList, defines["PROJECTEDLIGHTTEXTURE" + lightIndex], uniformBuffersList);
PrepareUniformsAndSamplersForLight(
lightIndex,
uniformsList,
samplersList,
defines["PROJECTEDLIGHTTEXTURE" + lightIndex],
uniformBuffersList,
false,
defines["IESLIGHTTEXTURE" + lightIndex]
);
}

if (defines["NUM_MORPH_INFLUENCERS"]) {
Expand Down
30 changes: 25 additions & 5 deletions packages/dev/core/src/Shaders/ShadersInclude/lightFragment.fx
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,32 @@
#ifdef SPOTLIGHT{X}
#ifdef LIGHT_FALLOFF_GLTF{X}
preInfo.attenuation = computeDistanceLightFalloff_GLTF(preInfo.lightDistanceSquared, light{X}.vLightFalloff.y);
preInfo.attenuation *= computeDirectionalLightFalloff_GLTF(light{X}.vLightDirection.xyz, preInfo.L, light{X}.vLightFalloff.z, light{X}.vLightFalloff.w);
#ifdef IESLIGHTTEXTURE{X}
preInfo.attenuation *= computeDirectionalLightFalloff_IES(light{X}.vLightDirection.xyz, preInfo.L, iesLightTexture{X});
#else
preInfo.attenuation *= computeDirectionalLightFalloff_GLTF(light{X}.vLightDirection.xyz, preInfo.L, light{X}.vLightFalloff.z, light{X}.vLightFalloff.w);
#endif
#elif defined(LIGHT_FALLOFF_PHYSICAL{X})
preInfo.attenuation = computeDistanceLightFalloff_Physical(preInfo.lightDistanceSquared);
preInfo.attenuation *= computeDirectionalLightFalloff_Physical(light{X}.vLightDirection.xyz, preInfo.L, light{X}.vLightDirection.w);
#ifdef IESLIGHTTEXTURE{X}
preInfo.attenuation *= computeDirectionalLightFalloff_IES(light{X}.vLightDirection.xyz, preInfo.L, iesLightTexture{X});
#else
preInfo.attenuation *= computeDirectionalLightFalloff_Physical(light{X}.vLightDirection.xyz, preInfo.L, light{X}.vLightDirection.w);
#endif
#elif defined(LIGHT_FALLOFF_STANDARD{X})
preInfo.attenuation = computeDistanceLightFalloff_Standard(preInfo.lightOffset, light{X}.vLightFalloff.x);
preInfo.attenuation *= computeDirectionalLightFalloff_Standard(light{X}.vLightDirection.xyz, preInfo.L, light{X}.vLightDirection.w, light{X}.vLightData.w);
#ifdef IESLIGHTTEXTURE{X}
preInfo.attenuation *= computeDirectionalLightFalloff_IES(light{X}.vLightDirection.xyz, preInfo.L, iesLightTexture{X});
#else
preInfo.attenuation *= computeDirectionalLightFalloff_Standard(light{X}.vLightDirection.xyz, preInfo.L, light{X}.vLightDirection.w, light{X}.vLightData.w);
#endif
#else
preInfo.attenuation = computeDistanceLightFalloff(preInfo.lightOffset, preInfo.lightDistanceSquared, light{X}.vLightFalloff.x, light{X}.vLightFalloff.y);
preInfo.attenuation *= computeDirectionalLightFalloff(light{X}.vLightDirection.xyz, preInfo.L, light{X}.vLightDirection.w, light{X}.vLightData.w, light{X}.vLightFalloff.z, light{X}.vLightFalloff.w);
#ifdef IESLIGHTTEXTURE{X}
preInfo.attenuation *= computeDirectionalLightFalloff_IES(light{X}.vLightDirection.xyz, preInfo.L, iesLightTexture{X});
#else
preInfo.attenuation *= computeDirectionalLightFalloff(light{X}.vLightDirection.xyz, preInfo.L, light{X}.vLightDirection.w, light{X}.vLightData.w, light{X}.vLightFalloff.z, light{X}.vLightFalloff.w);
#endif
#endif
#elif defined(POINTLIGHT{X})
#ifdef LIGHT_FALLOFF_GLTF{X}
Expand Down Expand Up @@ -125,7 +141,11 @@
#endif
#else
#ifdef SPOTLIGHT{X}
info = computeSpotLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDirection, diffuse{X}.rgb, light{X}.vLightSpecular.rgb, diffuse{X}.a, glossiness);
#ifdef IESLIGHTTEXTURE{X}
info = computeIESSpotLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDirection, diffuse{X}.rgb, light{X}.vLightSpecular.rgb, diffuse{X}.a, glossiness, iesLightTexture{X});
#else
info = computeSpotLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDirection, diffuse{X}.rgb, light{X}.vLightSpecular.rgb, diffuse{X}.a, glossiness);
#endif
#elif defined(HEMILIGHT{X})
info = computeHemisphericLighting(viewDirectionW, normalW, light{X}.vLightData, diffuse{X}.rgb, light{X}.vLightSpecular.rgb, light{X}.vLightGround, glossiness);
#elif defined(POINTLIGHT{X}) || defined(DIRLIGHT{X})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@
#elif defined(HEMILIGHT{X})
uniform vec3 vLightGround{X};
#endif
#ifdef IESLIGHTTEXTURE{X}
uniform sampler2D iesLightTexture{X};
#endif
#ifdef PROJECTEDLIGHTTEXTURE{X}
uniform mat4 textureProjectionMatrix{X};
uniform sampler2D projectionLightTexture{X};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
vec4 shadowsInfo;
vec2 depthValues;
} light{X};
#ifdef IESLIGHTTEXTURE{X}
uniform sampler2D iesLightTexture{X};
#endif
#ifdef PROJECTEDLIGHTTEXTURE{X}
uniform mat4 textureProjectionMatrix{X};
uniform sampler2D projectionLightTexture{X};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,38 +44,77 @@ lightingInfo computeLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData,
return result;
}

lightingInfo computeSpotLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec4 lightDirection, vec3 diffuseColor, vec3 specularColor, float range, float glossiness) {
lightingInfo result;
float getAttenuation(float cosAngle, float exponent) {
return max(0., pow(cosAngle, exponent));
}

float getIESAttenuation(float cosAngle, sampler2D iesLightSampler) {
float angle = acos(cosAngle) / PI;
return texture2D(iesLightSampler, vec2(angle, 0.), 0.).r;
}

lightingInfo basicSpotLighting(vec3 viewDirectionW, vec3 lightVectorW, vec3 vNormal, float attenuation, vec3 diffuseColor, vec3 specularColor, float glossiness) {
lightingInfo result;

// Diffuse
float ndl = max(0., dot(vNormal, lightVectorW));
#ifdef NDOTL
result.ndl = ndl;
#endif
result.diffuse = ndl * diffuseColor * attenuation;
#ifdef SPECULARTERM
// Specular
vec3 angleW = normalize(viewDirectionW + lightVectorW);
float specComp = max(0., dot(vNormal, angleW));
specComp = pow(specComp, max(1., glossiness));

result.specular = specComp * specularColor * attenuation;
#endif
return result;
}

lightingInfo computeIESSpotLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec4 lightDirection, vec3 diffuseColor, vec3 specularColor, float range, float glossiness, sampler2D iesLightSampler) {
vec3 direction = lightData.xyz - vPositionW;
vec3 lightVectorW = normalize(direction);
float attenuation = max(0., 1.0 - length(direction) / range);

// diffuse
float cosAngle = max(0., dot(lightDirection.xyz, -lightVectorW));
float dotProduct = dot(lightDirection.xyz, -lightVectorW);
float cosAngle = max(0., dotProduct);

if (cosAngle >= lightDirection.w)
{
cosAngle = max(0., pow(cosAngle, lightData.w));
attenuation *= cosAngle;
{
attenuation *= getIESAttenuation(dotProduct, iesLightSampler);
return basicSpotLighting(viewDirectionW, lightVectorW, vNormal, attenuation, diffuseColor, specularColor, glossiness);
}

// Diffuse
float ndl = max(0., dot(vNormal, lightVectorW));
lightingInfo result;
result.diffuse = vec3(0.);
#ifdef SPECULARTERM
result.specular = vec3(0.);
#endif
#ifdef NDOTL
result.ndl = ndl;
result.ndl = 0.;
#endif
result.diffuse = ndl * diffuseColor * attenuation;
#ifdef SPECULARTERM
// Specular
vec3 angleW = normalize(viewDirectionW + lightVectorW);
float specComp = max(0., dot(vNormal, angleW));
specComp = pow(specComp, max(1., glossiness));

result.specular = specComp * specularColor * attenuation;
#endif
return result;
return result;
}

lightingInfo computeSpotLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec4 lightDirection, vec3 diffuseColor, vec3 specularColor, float range, float glossiness) {
vec3 direction = lightData.xyz - vPositionW;
vec3 lightVectorW = normalize(direction);
float attenuation = max(0., 1.0 - length(direction) / range);

// diffuse
float cosAngle = max(0., dot(lightDirection.xyz, -lightVectorW));

if (cosAngle >= lightDirection.w)
{
attenuation *= getAttenuation(cosAngle, lightData.w);
return basicSpotLighting(viewDirectionW, lightVectorW, vNormal, attenuation, diffuseColor, specularColor, glossiness);
}

lightingInfo result;
result.diffuse = vec3(0.);
#ifdef SPECULARTERM
result.specular = vec3(0.);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ float computeDirectionalLightFalloff_Standard(vec3 lightDirection, vec3 directio
return falloff;
}

float computeDirectionalLightFalloff_IES(vec3 lightDirection, vec3 directionToLightCenterW, sampler2D iesLightSampler)
{
float cosAngle = dot(-lightDirection, directionToLightCenterW);
float angle = acos(cosAngle) / PI;
return texture2D(iesLightSampler, vec2(angle, 0.)).r;
}

float computeDirectionalLightFalloff_Physical(vec3 lightDirection, vec3 directionToLightCenterW, float cosHalfAngle)
{
const float kMinusLog2ConeAngleIntensityRatio = 6.64385618977; // -log2(0.01)
Expand Down
Loading

0 comments on commit af7fcf3

Please sign in to comment.