Open happyfeet1996 opened 2 years ago
@happyfeet1996 I'm unable to reproduce on MacOS in Chrome. Can you provide some more details about your system by visiting this page and taking a screenshot?
@happyfeet1996 I'm unable to reproduce on MacOS in Chrome. Can you provide some more details about your system by visiting this page and taking a screenshot?
it works today,maybe my network go wrong that day
Reopening this issues since it does seem to occur on certain hardware. On my MacBook Pro (16-inch, 2019) running macOS 12.2.1, I see the following results:
Browser | Sandcastle | WebGL Report |
---|---|---|
Chrome 100.0.4896.60 | ||
Firefox 98.0.2 | ||
Safari Version 15.3 (17612.4.9.1.8) |
I also reproduce the same issue on macOS Chrome with eGPU. Specifying fragment alpha in AmbientOcclusionModulate seems to resolve it.
- out_FragColor.rgb = ambientOcclusionOnly ? ao : ao * color;
+ out_FragColor = vec4(ambientOcclusionOnly ? ao : ao * color, 1.0);
Thanks for the info @shotamatsuda.
@jjhembd Could you please verify the potential fix in the comment above, and recommend a PR if appropriate?
Hi @shotamatsuda, thanks for the tip. However, on my machine (Chrome on Windows), I get the same image even if I set the alpha to 0.0
out_FragColor.rgb = ambientOcclusionOnly ? ao : ao * color;
out_FragColor.a = 0.0;
Does zero alpha also work on your machine, or is the 1.0
value important?
@jjhembd In my specific hardware, setting the alpha to zero results in a black screen, as well as not setting the value at all.
The importance of 1.0
depends on the blending function of the framebuffer. I haven't fully traced where blending is applied for framebuffers used in the post-processing stage, but setting true
as the default value for PassState.blendingEnabled
also resolves the issue.
https://github.com/CesiumGS/cesium/blob/1.104/packages/engine/Source/Renderer/PassState.js#L36
This suggests to me that the blending function might not be defined, leading to a hardware or driver-dependent issue. Specifying the alpha value explicitly compensates for it.
For the record, my setup is macOS 13.3.1, Chrome 112.0.5615.49, and AMD Radeon RX 6800 XT GPU. I encountered the issue before Chrome 100 on macOS Monterey with the same eGPU, and it doesn't reproduce in Safari or Firefox. I first noticed the issue around Cesium 1.89.0, but I'm unsure if it is caused by the Chrome version update.
Hi @shotamatsuda, thanks again for all the info.
I think it was a mistake in AmbientOcclusionModulate.glsl
to leave out_FragColor.a
undefined. We don't do this in any other shaders.
There are two input textures in AmbientOcclusionModulate
:
colorTexture
is the rendered scene before the ambient occlusion stage.ambientOcclusionTexture
is the shading factor computed in AmbientOcclusionGenerate.glsl
ambientOcclusionTexture
has its alpha value set to a constant 1.0. I think it would make sense to preserve the alpha of colorTexture
(which will usually also be 1.0, but not always). This would simplify AmbientOcclusionModulate
to read as follows:
vec4 color = texture(colorTexture, v_textureCoordinates);
vec4 ao = texture(ambientOcclusionTexture, v_textureCoordinates);
out_FragColor = ambientOcclusionOnly ? ao : ao * color;
The above code gives the same result as before on my setup. Can you please verify if it works on your hardware? If so, we should be ready for a PR.
@jjhembd Yes, I confirmed it works with my hardware, and that should solve OP's issue. Deriving alpha from colorTexture
makes sense to me.
Requested on the forum: https://community.cesium.com/t/improve-ambient-occlusion/9639/4
Major problem is in the quality of stochatic noise and the lack of denoise. I made a library that implements Nvidia's HBAO and cross-bilateral filtering for Cesium. It's not a generic library, but you will get the concept. https://github.com/takram-design-engineering/plateau-view/tree/main/libs/cesium-hbao Live demo: https://plateau.takram.com
HBAO with cross-bilateral filter:
HBAO without cross-bilateral filter:
No HBAO:
Awesome demo @shotamatsuda! Would you have any interest in contributing this AO algorithm implementation?
One issue with our current implementation: it computes a non-zero obscurance on flat surfaces.
This may be related to error in the depth buffer. We are computing obscurance based on the "horizon angle" relative to the local surface normal, as reconstructed from screen position and depth buffer. On a flat surface, the horizon angle should be 90° from the normal, and obscurance should be zero. However, the reconstructed normals don't look right. Here is a rendering of the difference between the reconstructed normals at adjacent pixels.
It shows non-zero values at the edges/curves of the model, as expected. But flat surfaces also show significant noise.
The reconstructed normals are better if we don't use a log depth buffer.
Current result, with log depth buffer:
After forcing Scene.defaultLogDepthBuffer = false
:
I hope AO will have better effects and performance. It is a good way to improve the effect of Cesium models. At present, it needs to be optimized in terms of performance and effects. Thank you for your efforts.
As discussed offline, the depth buffer for post-processing has a precision problem. To address this, we will try the following (in order):
DEPTH24_STENCIL8
format, corresponding to the UNSIGNED_INT_24_8
data type, which is available in both WebGL2 and WebGL1 (with the WEBGL_depth_texture extension. In a WebGL2 context, we can try the DEPTH32F_STENCIL8
format, corresponding to the FLOAT_32_UNSIGNED_INT_24_8_REV
data type.Here is an updated Sandcastle displaying the gradient of the depth buffer.
Log depth shows strong (flickering) noise in the distance, as well as some spuriously large gradients on smooth surfaces nearer the camera.
Linear depth reduces the noise nearer the camera. However, only the frustum nearest the camera is valid. The back frustum is washed out.
I updated the depth buffer to use DEPTH32F_STENCIL8
when in a WebGL2 context. Note that this doubles the size of the depth buffer--because of alignment issues, the values are stored as 64 bits per pixel.
Unfortunately, it does not have any effect on the noise.
DEPTH24_STENCIL8
:
DEPTH32F_STENCIL8
:
See the depth-precision
branch.
@ggetz highlighted this commit, before which AO did not show a noisy background on flat surfaces. Here is a quick screenshot of AO from a prior commit (70fc40e258ce5592d7b00abd0d52cb10e90828c0): Note the absence of any occlusion on flat surfaces, even with the "Bias" set to zero.
For comparison, here is a screenshot from the current main
branch:
And here is the result using the DEPTH32F_STENCIL8
format and a 1m near plane (instead of 0.1 m), both of which reduce the noise in the depth buffer:
While some minor artifacts are reduced by the DEPTH32F_STENCIL8
format and a 1m near plane, the main problem is still there: a background noisiness on flat surfaces. This appears to be a problem introduced in https://github.com/CesiumGS/cesium/pull/9966.
I also captured the gradient of the depth buffer prior to #9966. It does not look significantly different from the depth buffer from the current main branch.
The background noise problem appears to be something more fundamental--perhaps a depth buffer is being overwritten somewhere?
The background noise is generated when the HBAO stepping is along a direction not aligned with screen X/Y.
Here is the AO output with stepping only along X/Y. This is achieved by overriding the "randomVal" texture and using a constant rotation of 0 at every sample.
Note the significant reduction in the background on flat surfaces. The remaining background is the result of the noise in the depth buffer. It is reduced by setting the near plane to 1m (instead of 0.1m):
This is already approaching the result before #9966. One possible explanation is that before #9966, we had 2 bugs canceling each other:
Then #9966 fixed the first bug, exposing the second bug. But this is speculation--actually tracing texture handling through the major refactor in #9966 would take significant time.
Here is the result using a constant rotation of czm_piOverFour
:
Note the (incorrect) non-zero values on every flat surface. The current noisy result in main
is a random mix of various non-zero values at different rotations.
A remarkably similar-looking bug, which appears to be related to incorrect normals. In this case, I think world-space geometry was leaking into the normal calculation. https://www.reddit.com/r/GraphicsProgramming/comments/sd17k0/trying_to_implement_hbao_shader/ The similar result was what made me suspect our normals, and therefore the depth buffer from which they were computed.
There were some incorrect geometry assumptions in our shader. For example,
//Reconstruct Normal Without Edge Removation
vec3 getNormalXEdge(vec3 posInCamera, float depthU, float depthD, float depthL, float depthR, vec2 pixelSize)
{
vec4 posInCameraUp = clipToEye(v_textureCoordinates - vec2(0.0, pixelSize.y), depthU);
vec4 posInCameraDown = clipToEye(v_textureCoordinates + vec2(0.0, pixelSize.y), depthD);
Based on what the code is doing, the comment should actually read something like this:
// Reconstruct normal with edge removal
But this line:
vec4 posInCameraUp = clipToEye(v_textureCoordinates - vec2(0.0, pixelSize.y), depthU);
appears to assume v_textureCoordinates
is oriented with y = 0
at the top of the screen, when it actually starts from the bottom of the screen.
Overall, I think our current prototype AO is pretty rough. The choice of algorithm is good, it just needs a rewrite, with careful checking of the input and output of each step.
We now know the depth buffer input is somewhat problematic, but:
Some notes from an offline discussion: Our HBAO is generating wrong values along any sampling axis that is not aligned with screen X/Y. This matters because each pixel rotates the sampling axes by a value that is randomized across neighboring pixels.
The error along rotated axes is much larger than the depth buffer error. It is not clear whether:
We will spend a little time reworking and testing each step of the AO implementation, under the preliminary assumption that the depth buffer is not the main problem. Initial tests/QCs will include:
One possible explanation is that before https://github.com/CesiumGS/cesium/pull/9966, we had 2 bugs canceling each other
I tried to confirm this, but can't. Before that PR, the result looks fine even with a constant rotation of czm_piOverFour
:
Our first test was to check the angular difference between reconstructed normals at adjacent pixels.
Here are some images showing the sine of the angle between the normal at a given screen pixel, and the normal at the pixel just above it (Y + 1 pixel). The sine value is scaled by 11.47, which will make the image fully white if the angular difference exceeds 5 degrees.
From best to worst:
Using linear depth with near plane at 1m Note how the image is black on flat surfaces, with white outlining the edges of the model. This is as expected.
Using log depth with near plane at 1m Some linear noise appears on the far-away terrain, but it represents angular differences of less than 5 degrees. There are also some strange artifacts near the edges of the model. But overall, the result is close enough. The depth errors are small enough that we can move ahead with further debugging of the rest of the AO code.
Using log depth with near plane 0.1m (current default setting) The error between normals at adjacent pixels exceeds 5 degrees in many places. This will be a problem for AO.
I think we can move ahead for now and debug the rest of the AO process, using a near plane at 1m. We will need to come back later to revisit the errors with log depth and closer near planes.
Some additional checks just to be sure.
Angular difference between current pixel and the pixel to the right (X + 1 pixel)
Normals computed from tangents along the screen diagonal, difference along x = -y
axis:
Normals computed from tangents along the screen diagonal, difference along x = y
axis:
Difference between normals computed along screen axes vs. along diagonals
I found what changed in #9966: the depth stencil texture was changed from linear to nearest sampling.
We can recreate the old result in the current code by making 2 changes:
Current result with a WebGL2 context:
Current main with a WebGL1 context, nearest sampling. Note how some of the artifacts are reduced.
Current main with WebGL1 context, and linear sampling of the depth texture.
Just for interest: here's the linear sampling result with more angular sampling (16 instead of 4)
I don't think we want to require WebGL1 for AO. Two possible options:
One thing that is not clear to me yet: why does log depth produce fewer artifacts in WebGL1 than in WebGL2?
I don't think we want to require WebGL1 for AO
I agree. Is doing linear interpolation in the shader a possibility?
@lilleyse Would you be able to weigh in on the solution here?
I'm surprised linear sampling looks that much better. Is it because coordinates aren't snapped?
I'm surprised linear sampling looks that much better. Is it because coordinates aren't snapped?
Correct, the current shader does not snap coordinates to the texture grid. I am trying a snapping solution now.
Is doing linear interpolation in the shader a possibility?
Yes, though this would add to the cost. I think the main performance hit right now is the texture access, and a manually-coded linear interpolation would make that 4x. Snapping would be faster, if we can correctly account for the snapped geometry everywhere.
Here's a quick test fixing the coordinates to account for nearest sampling. This uses some WebGL2-only features like texelFetch
.
The WebGL2 result still has more artifacts than WebGL1.
We now have 4 actionable steps to improve AO:
ao-debug
branch). I will spend another day to see if we can get WebGL1 working too.lengthCap
) is currently a fixed value in meters, which makes the AO effect disappear when zoomed out to city-wide scale. This value should scale with distance.Other issues arising:
Item 1 above is addressed by #12201. Items 2 and 3 are linked, and I think should be addressed together.
At each output pixel, we currently step rays outward along several angles to find the angle of the "horizon". The maximum distance of the ray-stepping defines the distance around the point where we allow nearby geometry to "occlude" the ambient light.
We currently have 3 factors defining this maximum occlusion distance:
1 - (distance / lengthCap) ** 2
.lengthCap
uniform. As we step to pixels away from the output pixel, we compute the Cartesian distance between the stepping point and the output. If it is above "lengthCap" in meters, the contribution of that pixel is ignored and further stepping along that angle is aborted. Unfortunately, this truncates the occlusion if the raymarching path is interrupted by a small foreground object (such as an overhead wire).lengthCap
.The main limitation of this approach is that parameters that work at one scale do not work when zooming out or in. For example, when zoomed on some details of the Power Plant model, an occlusion radius of less than 1m makes sense. But when zooming out to city scale, we might reasonably expect a skyscraper or other large building to occlude ambient light on buildings 10m or more away.
I first tried a naive approach: scale the occlusion radius based on distance from the camera. However, this makes a given feature become darker overall as it moves away from the camera, since the shadowed areas maintain their darkness, but their size grows linearly with distance.
One better approach: model the occlusion as something that blurs with distance--i.e., shadowed areas become wider, but less dark. In this way the overall darkness of a given feature would remain constant.
We can achieve a blurring effect by simplifying the flow as follows:
scalar * gaussianVariance / maxStepCount
. This ensures that every point will sample the entire intended radius.We do not need to abort the stepping if a given sample point is too far away from the output point, because the Gaussian weighting will already make the contribution from that point negligible.
I have a preliminary version of the Gaussian-weighted AO in the ao-sampling-disc
branch. The same parameter choices give reasonably consistent results at a wide range of scales.
Here is a quick comparison on the NYC buildings tileset. Some notes:
No AO:
With AO:
AO term by itself:
A much closer view of the same scene, with the same parameters.
No AO:
With AO:
AO term by itself:
@jjhembd Screenshot look fantastic!
I don't see the ao-sampling-disc
branch on GitHub. Do you need to push?
Sandcastle example:Ambient Occlusion
Browser:Chrome
Operating System: macos
when i check ambient occlusion,full screen change to black and can not look anything!