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

IES shader #15952

Merged
merged 18 commits into from
Dec 5, 2024
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