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

Volumetric spotlight #15046

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/dev/core/src/Lights/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from "./directionalLight";
export * from "./hemisphericLight";
export * from "./pointLight";
export * from "./spotLight";
export * from "./simpleVolumetricSpotLight";
106 changes: 106 additions & 0 deletions packages/dev/core/src/Lights/simpleVolumetricSpotLight.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { SpotLight } from "./spotLight";
import { Scene } from "core/scene";
import { Axis } from "core/Maths/math.axis";
import { CreateCylinder } from "core/Meshes/Builders/cylinderBuilder";
import { Mesh } from "core/Meshes/mesh";
import { ShaderMaterial } from "core/Materials/shaderMaterial";
import "../Shaders/simpleVolumetricSpot.fragment";
import "../Shaders/simpleVolumetricSpot.vertex";
import { Observer, RenderTargetTexture, TmpVectors } from "..";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is not allowed to import from a folder, import every element from their respective files.



export class SimpleVolumetricSpotLight {
private _diameterTop: number;
private _diameterBottom: number;
private _rayLength: number;
private _softRadius: number;
private _scene: Scene;
private spotLight: SpotLight;
private _volumetricMaterial: ShaderMaterial;
private _lightCone: Mesh;
private _observer: Observer<any>;
private _depthTexture: RenderTargetTexture;

constructor(spotLight: SpotLight, diameterTop: number, diameterBottom: number, rayLength: number, scene: Scene) {
this.spotLight = spotLight;
this._diameterTop = diameterTop;
this._diameterBottom = diameterBottom;
this._rayLength = rayLength;
this._scene = scene;
this.createSimpleVolumetricSpotLight();

this._observer = this._scene.onBeforeRenderObservable.add(() => {
this._update();
});
}

public get lightCone(): Mesh {
return this._lightCone;
}

public get lightMaterial(): ShaderMaterial {
return this._volumetricMaterial;
}

public set softRadius(value: number){
this._softRadius = value;
}

public set depthTexture(value: RenderTargetTexture){
this._depthTexture = value;
}

private createSimpleVolumetricSpotLight(){
const spotLightCone = CreateCylinder("spotLightCone", {diameterTop: this._diameterTop, diameterBottom: this._diameterBottom, height: this._rayLength, cap: Mesh.CAP_END}, this._scene);
spotLightCone.rotate(Axis.X, -Math.PI / 2);
spotLightCone.translate(Axis.Y, -this._rayLength / 2); //pivot at the bottom
spotLightCone.bakeCurrentTransformIntoVertices();

this._volumetricMaterial = new ShaderMaterial('volumetricSpotLightMaterial', this._scene, 'simpleVolumetricSpot', {
attributes: ["position", "normal"],
uniforms: ["world", "worldViewProjection", "view"],
needAlphaBlending: true,
});

const renderer = this._scene.enableDepthRenderer();

this._depthTexture = renderer.getDepthMap();

this._updateUniforms();

spotLightCone.material = this._volumetricMaterial;
this._lightCone = spotLightCone;
}

private _updateUniforms(){
this._volumetricMaterial.setFloat("exponent", this.spotLight.exponent);
this._volumetricMaterial.setFloat("angle", this.spotLight.angle / 100)
this._volumetricMaterial.setColor3("diffuse", this.spotLight.diffuse);
this._volumetricMaterial.setVector3("lightPos", this.spotLight.position);
this._volumetricMaterial.setFloat("intensity", this.spotLight.intensity);
this._volumetricMaterial.setFloat("cameraNear", this._scene.activeCamera!.minZ);
this._volumetricMaterial.setFloat("cameraFar", this._scene.activeCamera!.maxZ);
this._volumetricMaterial.setFloat("softRadius", this._softRadius || 0.5);
this._volumetricMaterial.setTexture("depthTexture", this._depthTexture);
const resolution = TmpVectors.Vector2[0].set(this._scene.getEngine().getRenderWidth(), this._scene.getEngine().getRenderHeight());
this._volumetricMaterial.setVector2("resolution", resolution);
}

private _update(){
const lightPos = TmpVectors.Vector3[0].copyFrom(this.spotLight.position);
const dir = TmpVectors.Vector3[1];
lightPos.subtractToRef(this.spotLight.direction, dir);

const len = dir.length();
this._lightCone.lookAt(lightPos.subtract(this.spotLight.direction.normalize().scale(-len)));
this._lightCone.position.copyFrom(lightPos.add(this.spotLight.direction.normalize().scale(-1/(len * this._rayLength))));

this._updateUniforms();
}

public dispose(){
this._scene.onBeforeRenderObservable.remove(this._observer);
this._lightCone.dispose();
this._volumetricMaterial.dispose();
}
}
42 changes: 42 additions & 0 deletions packages/dev/core/src/Shaders/simpleVolumetricSpot.fragment.fx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
precision highp float;
varying vec3 vPositionW;
varying vec3 vNormalW;
varying float viewZ;
uniform float exponent;
uniform float angle;
uniform vec3 lightPos;
uniform vec3 diffuse;
uniform float intensity;
uniform sampler2D depthTexture;
uniform float cameraNear;
uniform float cameraFar;
uniform float softRadius;
uniform vec2 resolution;

float perspectiveDepthToViewZ(float depth, float near, float far) {
return (near * far) / ((far - near) * depth - far);
}

void main() {
float rayIntensity = distance(vPositionW, lightPos) / exponent;
rayIntensity = 1.0 - clamp(rayIntensity, 0.0, 1.0);

vec3 normal = vNormalW;
normal.z = abs(normal.z);

vec3 forward = vec3(0., 0., 1.0);
float angleIntensity = dot(normal, forward);

//smooth the intensity
angleIntensity = pow(angleIntensity, angle);
rayIntensity *= angleIntensity;

//soft intersection
vec2 uv = gl_FragCoord.xy / resolution;
float d = texture2D(depthTexture, uv).r;

rayIntensity *= smoothstep(0.0, 1.0, (viewZ - perspectiveDepthToViewZ(d, cameraNear, cameraFar)) / softRadius);


gl_FragColor = vec4(diffuse, rayIntensity * intensity);
}
19 changes: 19 additions & 0 deletions packages/dev/core/src/Shaders/simpleVolumetricSpot.vertex.fx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
precision highp float;
attribute vec3 position;
attribute vec3 normal;
uniform mat4 worldViewProjection;
uniform mat4 world;
uniform mat4 view;
varying vec3 vNormalW;
varying vec3 vPositionW;
varying float viewZ;

void main() {
vec4 p = vec4(position, 1.);
vec3 viewPos = vec3(view * vec4(position, 1.0));
gl_Position = worldViewProjection * p;

vNormalW = normalize(vec3(world * vec4(normal, 0.0)));
vPositionW = vec3(world * vec4(position, 1.0));
viewZ = viewPos.z;
}