Skip to content

Commit

Permalink
Merge pull request #12316 from CesiumGS/ao-sampling-disc
Browse files Browse the repository at this point in the history
Distance-based Gaussian weighting for Ambient Occlusion
  • Loading branch information
jjspace authored Dec 2, 2024
2 parents 78e24ca + b6a41fe commit f0cec41
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 159 deletions.
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,
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

0 comments on commit f0cec41

Please sign in to comment.