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

Distance-based Gaussian weighting for Ambient Occlusion #12316

Merged
merged 21 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 24 additions & 21 deletions Apps/Sandcastle/gallery/Ambient Occlusion.html
Original file line number Diff line number Diff line change
Expand Up @@ -62,38 +62,38 @@
</td>
</tr>
<tr>
<td>Step Size</td>
<td>Step Count</td>
<td>
<input
type="range"
min="1"
max="10"
step="0.01"
data-bind="value: stepSize, valueUpdate: 'input'"
max="64"
step="1"
data-bind="value: stepCount, valueUpdate: 'input'"
/>
</td>
</tr>
<tr>
<td>Bias</td>
<td>Direction Count</td>
<td>
<input
type="range"
min="0"
max="1"
step="0.01"
data-bind="value: bias, valueUpdate: 'input'"
min="1"
max="16"
step="1"
data-bind="value: directionCount, valueUpdate: 'input'"
/>
</td>
</tr>
<tr>
<td>Blur Step Size</td>
<td>Bias</td>
<td>
<input
type="range"
min="0"
max="4"
max="1"
step="0.01"
data-bind="value: blurStepSize, valueUpdate: 'input'"
data-bind="value: bias, valueUpdate: 'input'"
/>
</td>
</tr>
Expand All @@ -106,7 +106,11 @@
//Sandcastle_Begin
const viewer = new Cesium.Viewer("cesiumContainer");

viewer.clock.currentTime = Cesium.JulianDate.fromIso8601("2022-08-01T00:00:00Z");
const { canvas, camera, clock, scene } = viewer;
camera.frustum.near = 1.0;
scene.debugShowFramesPerSecond = true;

clock.currentTime = Cesium.JulianDate.fromIso8601("2022-08-01T00:00:00Z");

if (!Cesium.PostProcessStageLibrary.isAmbientOcclusionSupported(viewer.scene)) {
window.alert(
Expand All @@ -119,9 +123,9 @@
ambientOcclusionOnly: false,
intensity: 3.0,
bias: 0.1,
lengthCap: 0.03,
stepSize: 1.0,
blurStepSize: 0.86,
lengthCap: 0.26,
directionCount: 8,
stepCount: 32,
};

Cesium.knockout.track(viewModel);
Expand All @@ -134,7 +138,7 @@
}

function updatePostProcess() {
const ambientOcclusion = viewer.scene.postProcessStages.ambientOcclusion;
const ambientOcclusion = scene.postProcessStages.ambientOcclusion;
ambientOcclusion.enabled =
Boolean(viewModel.show) || Boolean(viewModel.ambientOcclusionOnly);
ambientOcclusion.uniforms.ambientOcclusionOnly = Boolean(
Expand All @@ -143,12 +147,11 @@
ambientOcclusion.uniforms.intensity = Number(viewModel.intensity);
ambientOcclusion.uniforms.bias = Number(viewModel.bias);
ambientOcclusion.uniforms.lengthCap = Number(viewModel.lengthCap);
ambientOcclusion.uniforms.stepSize = Number(viewModel.stepSize);
ambientOcclusion.uniforms.blurStepSize = Number(viewModel.blurStepSize);
ambientOcclusion.uniforms.directionCount = Number(viewModel.directionCount);
ambientOcclusion.uniforms.stepCount = Number(viewModel.stepCount);
}
updatePostProcess();

const camera = viewer.scene.camera;
camera.position = new Cesium.Cartesian3(
1234127.2294710164,
-5086011.666443127,
Expand All @@ -173,7 +176,7 @@
try {
// Power Plant design model provided by Bentley Systems
const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(2464651);
viewer.scene.primitives.add(tileset);
scene.primitives.add(tileset);
} catch (error) {
console.log(`Error loading tileset: ${error}`);
} //Sandcastle_End
Expand Down
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
##### Breaking Changes :mega:

- `Rectangle.validate` has been removed.
- `PostProcessStageCollection.ambientOcclusion` has been updated with a new algorithm to provide better results at all scales, with tunable performance cost. To approximate the appearance and performance of the old algorithm, set the following values for `scene.postProcessStages.ambientOcclusion.uniforms`: `{ lengthCap: 0.02, directionCount: 6, stepCount: 8 }`. For best results at long distances, consider setting `Viewer.camera.frustum.near` to `1.0` or more, to improve precision in the depth buffer.

##### Fixes :wrench:

Expand Down
31 changes: 14 additions & 17 deletions packages/engine/Source/Scene/PostProcessStage.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,16 @@ import PostProcessStageSampleMode from "./PostProcessStageSampleMode.js";
function PostProcessStage(options) {
options = defaultValue(options, defaultValue.EMPTY_OBJECT);
const {
name = createGuid(),
fragmentShader,
uniforms,
textureScale = 1.0,
forcePowerOfTwo = false,
sampleMode = PostProcessStageSampleMode.NEAREST,
Comment on lines +101 to +102
Copy link
Contributor

Choose a reason for hiding this comment

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

This method of setting default values is not equivalent to the previous defaultValue calls when the value is null. I just want to make sure that's intended
2024-11-27_16-12

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jjspace agreed. I think we are already doing this in other parts of the code, with the assumption that null is only an issue when we might be passing the result of a document.getElement?? call or similar.
(and for others reading this, we have more discussion in #12196).

pixelFormat = PixelFormat.RGBA,
pixelDatatype = PixelDatatype.UNSIGNED_BYTE,
clearColor = Color.BLACK,
scissorRectangle,
} = options;

//>>includeStart('debug', pragmas.debug);
Expand All @@ -113,19 +120,13 @@ function PostProcessStage(options) {
//>>includeEnd('debug');

this._fragmentShader = fragmentShader;
this._uniforms = options.uniforms;
this._uniforms = uniforms;
this._textureScale = textureScale;
this._forcePowerOfTwo = defaultValue(options.forcePowerOfTwo, false);
this._sampleMode = defaultValue(
options.sampleMode,
PostProcessStageSampleMode.NEAREST,
);
this._forcePowerOfTwo = forcePowerOfTwo;
this._sampleMode = sampleMode;
this._pixelFormat = pixelFormat;
this._pixelDatatype = defaultValue(
options.pixelDatatype,
PixelDatatype.UNSIGNED_BYTE,
);
this._clearColor = defaultValue(options.clearColor, Color.BLACK);
this._pixelDatatype = pixelDatatype;
this._clearColor = clearColor;

this._uniformMap = undefined;
this._command = undefined;
Expand All @@ -143,18 +144,14 @@ function PostProcessStage(options) {
const passState = new PassState();
passState.scissorTest = {
enabled: true,
rectangle: defined(options.scissorRectangle)
? BoundingRectangle.clone(options.scissorRectangle)
rectangle: defined(scissorRectangle)
? BoundingRectangle.clone(scissorRectangle)
: new BoundingRectangle(),
};
this._passState = passState;

this._ready = false;

let name = options.name;
if (!defined(name)) {
name = createGuid();
}
this._name = name;

this._logDepthChanged = undefined;
Expand Down
20 changes: 4 additions & 16 deletions packages/engine/Source/Scene/PostProcessStageCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,32 +155,20 @@ Object.defineProperties(PostProcessStageCollection.prototype, {
* surface receives light and regardless of the light's position.
* </p>
* <p>
* The uniforms have the following properties: <code>intensity</code>, <code>bias</code>, <code>lengthCap</code>,
* <code>stepSize</code>, <code>frustumLength</code>, <code>ambientOcclusionOnly</code>,
* <code>delta</code>, <code>sigma</code>, and <code>blurStepSize</code>.
* </p>
* The uniforms have the following properties:
* <ul>
* <li><code>intensity</code> is a scalar value used to lighten or darken the shadows exponentially. Higher values make the shadows darker. The default value is <code>3.0</code>.</li>
*
* <li><code>bias</code> is a scalar value representing an angle in radians. If the dot product between the normal of the sample and the vector to the camera is less than this value,
* sampling stops in the current direction. This is used to remove shadows from near planar edges. The default value is <code>0.1</code>.</li>
*
* <li><code>lengthCap</code> is a scalar value representing a length in meters. If the distance from the current sample to first sample is greater than this value,
* sampling stops in the current direction. The default value is <code>0.26</code>.</li>
*
* <li><code>stepSize</code> is a scalar value indicating the distance to the next texel sample in the current direction. The default value is <code>1.95</code>.</li>
*
* <li><code>frustumLength</code> is a scalar value in meters. If the current fragment has a distance from the camera greater than this value, ambient occlusion is not computed for the fragment.
* The default value is <code>1000.0</code>.</li>
*
* <li><code>directionCount</code> is the number of directions along which the ray marching will search for occluders. The default value is <code>8</code>.</li>
* <li><code>stepCount</code> is the number of steps the ray marching will take along each direction. The default value is <code>32</code>.</li>
* <li><code>randomTexture</code> is a texture where the red channel is a random value in [0.0, 1.0]. The default value is <code>undefined</code>. This texture needs to be set.</li>
* <li><code>ambientOcclusionOnly</code> is a boolean value. When <code>true</code>, only the shadows generated are written to the output. When <code>false</code>, the input texture is modulated
* with the ambient occlusion. This is a useful debug option for seeing the effects of changing the uniform values. The default value is <code>false</code>.</li>
* </ul>
* <p>
* <code>delta</code>, <code>sigma</code>, and <code>blurStepSize</code> are the same properties as {@link PostProcessStageLibrary#createBlurStage}.
* The blur is applied to the shadows generated from the image to make them smoother.
* </p>
* <p>
* When enabled, this stage will execute before all others.
* </p>
*
Expand Down
64 changes: 13 additions & 51 deletions packages/engine/Source/Scene/PostProcessStageLibrary.js
Original file line number Diff line number Diff line change
Expand Up @@ -476,27 +476,19 @@ PostProcessStageLibrary.createBloomStage = function () {
* surface receives light and regardless of the light's position.
* </p>
* <p>
* The uniforms have the following properties: <code>intensity</code>, <code>bias</code>, <code>lengthCap</code>,
* <code>stepSize</code>, <code>frustumLength</code>, <code>randomTexture</code>, <code>ambientOcclusionOnly</code>,
* <code>delta</code>, <code>sigma</code>, and <code>blurStepSize</code>.
* </p>
* The uniforms have the following properties:
* <ul>
* <li><code>intensity</code> is a scalar value used to lighten or darken the shadows exponentially. Higher values make the shadows darker. The default value is <code>3.0</code>.</li>
* <li><code>bias</code> is a scalar value representing an angle in radians. If the dot product between the normal of the sample and the vector to the camera is less than this value,
* sampling stops in the current direction. This is used to remove shadows from near planar edges. The default value is <code>0.1</code>.</li>
* <li><code>lengthCap</code> is a scalar value representing a length in meters. If the distance from the current sample to first sample is greater than this value,
* sampling stops in the current direction. The default value is <code>0.26</code>.</li>
* <li><code>stepSize</code> is a scalar value indicating the distance to the next texel sample in the current direction. The default value is <code>1.95</code>.</li>
* <li><code>frustumLength</code> is a scalar value in meters. If the current fragment has a distance from the camera greater than this value, ambient occlusion is not computed for the fragment.
* The default value is <code>1000.0</code>.</li>
* <li><code>directionCount</code> is the number of directions along which the ray marching will search for occluders. The default value is <code>8</code>.</li>
* <li><code>stepCount</code> is the number of steps the ray marching will take along each direction. The default value is <code>32</code>.</li>
* <li><code>randomTexture</code> is a texture where the red channel is a random value in [0.0, 1.0]. The default value is <code>undefined</code>. This texture needs to be set.</li>
* <li><code>ambientOcclusionOnly</code> is a boolean value. When <code>true</code>, only the shadows generated are written to the output. When <code>false</code>, the input texture is modulated
* with the ambient occlusion. This is a useful debug option for seeing the effects of changing the uniform values. The default value is <code>false</code>.</li>
* </ul>
* <p>
* <code>delta</code>, <code>sigma</code>, and <code>blurStepSize</code> are the same properties as {@link PostProcessStageLibrary#createBlurStage}.
* The blur is applied to the shadows generated from the image to make them smoother.
* </p>
* @return {PostProcessStageComposite} A post-process stage that applies an ambient occlusion effect.
*
* @private
Expand All @@ -509,24 +501,18 @@ PostProcessStageLibrary.createAmbientOcclusionStage = function () {
intensity: 3.0,
bias: 0.1,
lengthCap: 0.26,
stepSize: 1.95,
frustumLength: 1000.0,
directionCount: 8,
stepCount: 32,
randomTexture: undefined,
},
});
const blur = createBlur("czm_ambient_occlusion_blur");
blur.uniforms.stepSize = 0.86;
const generateAndBlur = new PostProcessStageComposite({
name: "czm_ambient_occlusion_generate_blur",
stages: [generate, blur],
});

const ambientOcclusionModulate = new PostProcessStage({
name: "czm_ambient_occlusion_composite",
fragmentShader: AmbientOcclusionModulate,
uniforms: {
ambientOcclusionOnly: false,
ambientOcclusionTexture: generateAndBlur.name,
ambientOcclusionTexture: generate.name,
},
});

Expand Down Expand Up @@ -556,20 +542,20 @@ PostProcessStageLibrary.createAmbientOcclusionStage = function () {
generate.uniforms.lengthCap = value;
},
},
stepSize: {
directionCount: {
get: function () {
return generate.uniforms.stepSize;
return generate.uniforms.directionCount;
},
set: function (value) {
generate.uniforms.stepSize = value;
generate.uniforms.directionCount = value;
},
},
frustumLength: {
stepCount: {
get: function () {
return generate.uniforms.frustumLength;
return generate.uniforms.stepCount;
},
set: function (value) {
generate.uniforms.frustumLength = value;
generate.uniforms.stepCount = value;
},
},
randomTexture: {
Expand All @@ -580,30 +566,6 @@ PostProcessStageLibrary.createAmbientOcclusionStage = function () {
generate.uniforms.randomTexture = value;
},
},
delta: {
get: function () {
return blur.uniforms.delta;
},
set: function (value) {
blur.uniforms.delta = value;
},
},
sigma: {
get: function () {
return blur.uniforms.sigma;
},
set: function (value) {
blur.uniforms.sigma = value;
},
},
blurStepSize: {
get: function () {
return blur.uniforms.stepSize;
},
set: function (value) {
blur.uniforms.stepSize = value;
},
},
ambientOcclusionOnly: {
get: function () {
return ambientOcclusionModulate.uniforms.ambientOcclusionOnly;
Expand All @@ -616,7 +578,7 @@ PostProcessStageLibrary.createAmbientOcclusionStage = function () {

return new PostProcessStageComposite({
name: "czm_ambient_occlusion",
stages: [generateAndBlur, ambientOcclusionModulate],
stages: [generate, ambientOcclusionModulate],
inputPreviousStageTexture: false,
uniforms: uniforms,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ float czm_reverseLogDepth(float logZ)
float near = czm_currentFrustum.x;
float far = czm_currentFrustum.y;
float log2Depth = logZ * czm_log2FarDepthFromNearPlusOne;
float depthFromNear = pow(2.0, log2Depth) - 1.0;
float depthFromNear = exp2(log2Depth) - 1.0;
return far * (1.0 - near / (depthFromNear + near)) / (far - near);
#endif
return logZ;
Expand Down
2 changes: 1 addition & 1 deletion packages/engine/Source/Shaders/ConvolveSpecularMapFS.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ void main() {

float NdotL = max(dot(V, L), 0.0);
if (NdotL > 0.0) {
color += vec4(texture(u_radianceTexture, L).rgb, 1.0) * NdotL;
color += vec4(czm_textureCube(u_radianceTexture, L).rgb, 1.0) * NdotL;
weight += NdotL;
}
}
Expand Down
Loading
Loading