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

Add ground projection support #14470

Merged
merged 8 commits into from Oct 26, 2023
Merged
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
44 changes: 44 additions & 0 deletions packages/dev/core/src/Materials/Background/backgroundMaterial.ts
Expand Up @@ -122,6 +122,11 @@ class BackgroundMaterialDefines extends MaterialDefines implements IImageProcess
*/
public REFLECTIONBGR = false;

/**
* True if ground projection has been enabled.
*/
public PROJECTED_GROUND = false;

public IMAGEPROCESSING = false;
public VIGNETTE = false;
public VIGNETTEBLENDMODEMULTIPLY = false;
Expand Down Expand Up @@ -609,6 +614,29 @@ export class BackgroundMaterial extends PushMaterial {
*/
public switchToBGR: boolean = false;

private _enableGroundProjection: boolean = false;
/**
* Enables the ground projection mode on the material.
* @see https://doc.babylonjs.com/features/featuresDeepDive/environment/skybox#ground-projection
*/
@serialize()
@expandToProperty("_markAllSubMeshesAsMiscDirty")
public enableGroundProjection: boolean = false;

/**
* Defines the radius of the projected ground if enableGroundProjection is true.
* @see https://doc.babylonjs.com/features/featuresDeepDive/environment/skybox#ground-projection
*/
@serialize()
public projectedGroundRadius = 1000;

/**
* Defines the height of the projected ground if enableGroundProjection is true.
* @see https://doc.babylonjs.com/features/featuresDeepDive/environment/skybox#ground-projection
*/
@serialize()
public projectedGroundHeight = 10;

// Temp values kept as cache in the material.
private _renderTargets = new SmartArray<RenderTargetTexture>(16);
private _reflectionControls = Vector4.Zero();
Expand Down Expand Up @@ -838,6 +866,15 @@ export class BackgroundMaterial extends PushMaterial {
this._imageProcessingConfiguration.prepareDefines(defines);
}

if (defines._areMiscDirty) {
if (defines.REFLECTIONMAP_3D && this._enableGroundProjection) {
defines.PROJECTED_GROUND = true;
defines.REFLECTIONMAP_SKYBOX = true;
} else {
defines.PROJECTED_GROUND = false;
}
}

// Misc.
MaterialHelper.PrepareDefinesForMisc(mesh, scene, false, this.pointsCloud, this.fogEnabled, this._shouldTurnAlphaTestOn(mesh), defines);

Expand Down Expand Up @@ -919,6 +956,8 @@ export class BackgroundMaterial extends PushMaterial {

"vDiffuseInfos",
"diffuseMatrix",

"projectedGroundInfos",
];

addClipPlaneUniforms(uniforms);
Expand Down Expand Up @@ -1030,6 +1069,7 @@ export class BackgroundMaterial extends PushMaterial {
this._uniformBuffer.addUniform("alpha", 1);
this._uniformBuffer.addUniform("vBackgroundCenter", 3);
this._uniformBuffer.addUniform("vReflectionControl", 4);
this._uniformBuffer.addUniform("projectedGroundInfos", 2);

this._uniformBuffer.create();
}
Expand Down Expand Up @@ -1159,6 +1199,10 @@ export class BackgroundMaterial extends PushMaterial {
);
}
}

if (defines.PROJECTED_GROUND) {
this._uniformBuffer.updateFloat2("projectedGroundInfos", this.projectedGroundRadius, this.projectedGroundHeight);
}
}

// Clip plane
Expand Down
Expand Up @@ -346,7 +346,6 @@ export abstract class ReflectionTextureBaseBlock extends NodeMaterialBlock {
state.sharedData.bindableBlocks.push(this);

const comments = `//${this.name}`;
state._emitFunction("ReciprocalPI", "#define RECIPROCAL_PI2 0.15915494", "");
state._emitFunctionFromInclude("helperFunctions", comments);
state._emitFunctionFromInclude("reflectionFunction", comments, {
replaceStrings: [{ search: /vec3 computeReflectionCoords/g, replace: "void DUMMYFUNC" }],
Expand Down
Expand Up @@ -26,5 +26,9 @@
#endif

#if defined(REFLECTIONMAP_SPHERICAL) || defined(REFLECTIONMAP_PROJECTION) || defined(REFRACTION)
uniform mat4 view;
uniform mat4 view;
#endif

#ifdef PROJECTED_GROUND
uniform vec2 projectedGroundInfos;
#endif
Expand Up @@ -14,14 +14,9 @@ uniform Material
uniform float pointSize;
uniform float shadowLevel;
uniform float alpha;

#if defined(REFLECTIONFRESNEL) || defined(OPACITYFRESNEL)
uniform vec3 vBackgroundCenter;
#endif

#ifdef REFLECTIONFRESNEL
uniform vec4 vReflectionControl;
#endif
uniform vec3 vBackgroundCenter;
uniform vec4 vReflectionControl;
uniform vec2 projectedGroundInfos;
};

#include<sceneUboDeclaration>
@@ -1,4 +1,6 @@
const float PI = 3.1415926535897932384626433832795;
const float RECIPROCAL_PI = 0.3183098861837907;
const float RECIPROCAL_PI2 = 0.15915494309189535;
const float HALF_MIN = 5.96046448e-08; // Smallest positive half.

const float LinearEncodePowerApprox = 2.2;
Expand Down
@@ -1,7 +1,3 @@
// Constants
#define RECIPROCAL_PI2 0.15915494
#define RECIPROCAL_PI 0.31830988618

// AlphaG epsilon to avoid numerical issues
#define MINIMUMVARIANCE 0.0005

Expand Down
87 changes: 84 additions & 3 deletions packages/dev/core/src/Shaders/background.fragment.fx
Expand Up @@ -7,8 +7,6 @@ precision highp float;
#include<__decl__backgroundFragment>
#include<helperFunctions>

#define RECIPROCAL_PI2 0.15915494

// Input
varying vec3 vPositionW;

Expand Down Expand Up @@ -108,6 +106,83 @@ varying vec3 vNormalW;
}
#endif

#ifdef PROJECTED_GROUND
// From: https://www.shadertoy.com/view/4tsBD7
// keeping for reference the general formula for a disk
// float diskIntersectWithBackFaceCulling(vec3 ro, vec3 rd, vec3 c, vec3 n, float r) {
// float d = dot(rd, n);
// if(d > 0.0) { return 1e6; }
// vec3 o = ro - c;
// float t = -dot(n, o) / d;
// vec3 q = o + rd * t;
// return (dot(q, q) < r * r) ? t : 1e6;
// }
// optimized for a disk on the ground facing up
float diskIntersectWithBackFaceCulling(vec3 ro, vec3 rd, vec3 c, float r) {
float d = rd.y;
if(d > 0.0) { return 1e6; }
vec3 o = ro - c;
float t = -o.y / d;
vec3 q = o + rd * t;
return (dot(q, q) < r * r) ? t : 1e6;
}

// From: https://www.iquilezles.org/www/articles/intersectors/intersectors.htm
// keeping for reference the general formula for a sphere
// float sphereIntersect(vec3 ro, vec3 rd, vec3 ce, float ra) {
// vec3 oc = ro - ce;
// float b = dot(oc, rd);
// float c = dot(oc, oc) - ra * ra;
// float h = b * b - c;
// if(h < 0.0) { return -1.0; }
// h = sqrt(h);
// return - b + h;
// }
// optimized for a sphere centered at the origin
float sphereIntersect(vec3 ro, vec3 rd, float ra) {
float b = dot(ro, rd);
float c = dot(ro, ro) - ra * ra;
float h = b * b - c;

if(h < 0.0) { return -1.0; }

h = sqrt(h);

return - b + h;
}

vec3 project(vec3 viewDirectionW, vec3 eyePosition) {
float radius = projectedGroundInfos.x;
float height = projectedGroundInfos.y;

// reproject the cube ground to a sky sphere
// to help with shadows
// vec3 p = normalize(vPositionW);
vec3 camDir = -viewDirectionW;
float skySphereDistance = sphereIntersect(eyePosition, camDir, radius);
vec3 skySpherePositionW = eyePosition + camDir * skySphereDistance;

vec3 p = normalize(skySpherePositionW);
eyePosition.y -= height;

// Let s remove extra conditions in the following block
// float intersection = sphereIntersect(eyePosition, p, radius);
// if(intersection > 0.0) {
// vec3 h = vec3(0.0, -height, 0.0);
// float intersection2 = diskIntersectWithBackFaceCulling(eyePosition, p, h, radius);
// p = (eyePosition + min(intersection, intersection2) * p) / radius;
// } else {
// p = vec3(0.0, 1.0, 0.0);
// }

float sIntersection = sphereIntersect(eyePosition, p, radius);
vec3 h = vec3(0.0, -height, 0.0);
float dIntersection = diskIntersectWithBackFaceCulling(eyePosition, p, h, radius);
p = (eyePosition + min(sIntersection, dIntersection) * p);

return p;
}
#endif

#define CUSTOM_FRAGMENT_DEFINITIONS

Expand Down Expand Up @@ -145,7 +220,13 @@ void main(void) {
// _____________________________ REFLECTION ______________________________________
vec4 reflectionColor = vec4(1., 1., 1., 1.);
#ifdef REFLECTION
vec3 reflectionVector = computeReflectionCoords(vec4(vPositionW, 1.0), normalW);
#ifdef PROJECTED_GROUND
vec3 reflectionVector = project(viewDirectionW, vEyePosition.xyz);
reflectionVector = vec3(reflectionMatrix * vec4(reflectionVector, 1.));
#else
vec3 reflectionVector = computeReflectionCoords(vec4(vPositionW, 1.0), normalW);
#endif

#ifdef REFLECTIONMAP_OPPOSITEZ
reflectionVector.z *= -1.0;
#endif
Expand Down
3 changes: 0 additions & 3 deletions packages/dev/core/src/Shaders/default.fragment.fx
Expand Up @@ -13,9 +13,6 @@
#extension GL_EXT_frag_depth : enable
#endif

// Constants
#define RECIPROCAL_PI2 0.15915494

// Input
varying vec3 vPositionW;

Expand Down
Expand Up @@ -107,6 +107,36 @@ export class BackgroundMaterialPropertyGridComponent extends React.Component<IBa
</LineContainerComponent>
{this.renderTextures()}
<LineContainerComponent title="RENDERING" closed={true} selection={this.props.globalState}>
<CheckBoxLineComponent
label="Ground Projection"
target={material}
propertyName="enableGroundProjection"
onPropertyChangedObservable={this.props.onPropertyChangedObservable}
/>
{material.enableGroundProjection && (
<div className="fragment">
<SliderLineComponent
lockObject={this.props.lockObject}
label="Ground radius"
target={material}
propertyName="projectedGroundRadius"
minimum={0}
maximum={10000}
step={10}
onPropertyChangedObservable={this.props.onPropertyChangedObservable}
/>
<SliderLineComponent
lockObject={this.props.lockObject}
label="Ground height"
target={material}
propertyName="projectedGroundHeight"
minimum={1}
maximum={1000}
step={10}
onPropertyChangedObservable={this.props.onPropertyChangedObservable}
/>
</div>
)}
<CheckBoxLineComponent label="Enable noise" target={material} propertyName="enableNoise" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
<CheckBoxLineComponent
label="Opacity fresnel"
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions packages/tools/tests/test/visualization/config.json
@@ -1,6 +1,11 @@
{
"root": "https://cdn.babylonjs.com",
"tests": [
{
"title": "Ground Projection",
"playgroundId": "#XG08YC#0",
"referenceImage": "groundProjection.png"
},
{
"title": "Node geometry",
"playgroundId": "#WGZLGJ#9152",
Expand Down