#SSAO Moving
354 messages · Page 1 of 1 (latest)
This looks many levels of wrong. What AO technique are you using?
i just sample random positions in a hemsiphere (using depth and normal textures)
wdym by "many levels of wrong" tho
as far as i can tell the only issue is the effect moving around when up-close (and the edges of the screen not having correct ao but thats to be expected)
Alright, so imma warn you against doing that. It’s simple but looks bad, figure out horizons.
How do you reconstruct position from depth?
i do this: (i have reversed infinite far-plane depth)
vec3 reconstructViewPos(vec2 uvPos, float depth) {
vec4 clip = vec4((uvPos*2)-1, depth, 1.f);
vec4 view = inverse(globalUbo.proj)*clip;
return view.xyz/view.w;
}
also i have heard of horizon based ssao but it doesnt look any better to me
maybe im just not looking at the right comparisons tho
It looks much better. I don’t think your average joe can’t tell, though.
Hmm, right. In my time I’ve just did clip, depth remap and multiply xy etc etc, without inverting stuff.
I’d try that
Gimme a sec, imma get you a snippet
i mean i'd think its important to invert the projection matrix when using it to undo a previous projection
Yeah, it’s just perspective though, correct?
yeah
Can avoid that
oh are you meaning like dont use the projection matrix at all?
Just as a test
hm if i dont use the projection matrix than there just no ao appearing at all
float3 getViewPos(float2 uv, float z) {
float fin_z = remap(z, 0., 1., n, f);
float2 norm = (uv - 0.5.xx) * 2.0;
float fl = 2.0 * tan(FOV * 0.5);
float3 pos = float3(norm * fl * fin_z, fin_z);
pos.y *= rcp(BUFFER_ASPECT_RATIO);
return pos;
}```
n is nearplane, f is farplane, fl is focal length
you'll need to reverse it, though
i seem to have misplaced the inversion, but you can figure it out yourself
ngl ive got no idea what that is doin
i dont think anything is wrong with my current reconstruction tho
well, okay, what rng are you using
i use the randomVec for offset and randomFloat for radius (which now that i think about it is redundant)
const vec3 SSAO_NOISE[16] = vec3[](
vec3( 0.7071, 0.7071, 0.0), vec3(-0.3827, 0.9239, 0.0), vec3( 0.9808, -0.1951, 0.0), vec3(-0.8315, -0.5556, 0.0),
vec3( 0.1951, -0.9808, 0.0), vec3( 0.9239, 0.3827, 0.0), vec3(-0.5556, 0.8315, 0.0), vec3(-0.9808, 0.1951, 0.0),
vec3( 0.3827, -0.9239, 0.0), vec3( 0.8315, 0.5556, 0.0), vec3(-0.7071, -0.7071, 0.0), vec3(-0.1951, 0.9808, 0.0),
vec3( 0.5556, -0.8315, 0.0), vec3( 0.9808, 0.1951, 0.0), vec3(-0.3827, -0.9239, 0.0), vec3(-0.9239, -0.3827, 0.0));
vec3 randomVec() {
int x = int(mod(gl_FragCoord.x, 4.0));
int y = int(mod(gl_FragCoord.y, 4.0));
return SSAO_NOISE[x * 4 + y];
}
float randomFloat(int y) {
int x = int(mod(gl_FragCoord.x, 4.0));
return SSAO_NOISE[x * 4 + y].x;
}
boi
?
just use this for now
because its quite likely you're looking at correlations that appear because of
well
mod()
on coords
oh, and also, random inside sphere, right
hmm
wdym
ok so i get a random vector to use as the direction
multiply it by the random float used as radius
and i use the normal to make sure it is a hemisphere
random vector is not the same as random direction
how so?
imagine a cube
that's where the random vector lies
now smooth it out
you'll be cutting away at the corners, while the faces will sort of remain remain
that's my best intuitive explanation
also, not what you need
yeah idk what you mean by any of that
idk what crytek is so i probably aint doin that
you're picking a random point in the volume
where you got this general impl from?
tbh i dont remember, i made this like a month ago
i think i used multiple guides tho
this is what my randomVec looks like visualized
i do see a pattern
so yeah its probably wrong
ok
only for testing, replace with something else later
do AO debug view, output just the AO
im confused about the hashing
one of the methods wants a vec2 and outputs just one float
the other wants a vec3 and outputs a vec3
i do want a vec3 but idk what i would input for the third number
depth maybe?
they are using time in the example
but i dont think i want the ao moving over time
alright
looks about the same to me
still has the moving around problem up-close too
hm
so first off, you want points in a sphere
a simple (but not efficient on GPU) method is to just reroll the RNG until length < 1
the rng is always less than 1 i think
and why would i want the points in a sphere instead of hemisphere?
yeah the rng never makes a value greater than 1
i made it show red if any channel is greater than 1 and green if its not
also not sure what your tryna show here
Ok, what I’m saying is that your random numbers need to be in a sphere with radius one in accordance with crytek ao
Just because each individual component of a vector is < 1, doesn’t mean the vectors length has to be < 1
ohhh
why does the length matter tho i dont currently get the length anywhere
Ok so at the corner of the block, right?
There’s a sharp 90 degree angle
That should dissapear
(I think at least)
ohhh i see
But overall, that’s what the technique requires
couldnt i just do hash = hash/length(hash)
to avoid the >1 length
apparently that doesnt work like i thought it would, still has some greater than 1 lengths
Basically this
You can, but your samples will just be non-uniformly distributed on the surface of a sphere
They need to be (at least for the most part), uniform and inside the sphere
i could just divide the result by 1.5 or smth
whatever the max possible length is (1,1,1)
1.732
bandaid?
However, you can a smaller cube
its reducing it to the right range
Which is, for the exact same reasons, not what you want
Visualization of different methods of generating random points inside a sphere
You need this.
As for why, see if drawing it can help
Same shit in 2D
Or just plt that shit, because numeric experiments are based
plt?
i mean the dividing by 1.732 seems to work
You can just, run that calculation and visually see the clumping near the corners
although none of this affects the up-close moving around issue
also smth i noticed is the hashing made the ao noisy
It’s different sorts of wrong though.
And yeah, when you add noise you get noise.
But before, you had 16 or so predetermined values which caused banding instead
I see no bands here tbh
Oh nvm
You’re not slicing the array with those values, right?
Just using them directly?
im just using them directly
Guh
maybe the kernel is causing it idk
vec3 SSAO_KERNEL[32] = vec3[](vec3( 0.021, 0.183, 0.512), vec3( 0.392, 0.041, 0.734), vec3(-0.221, 0.114, 0.612), vec3( 0.134, -0.287, 0.553), vec3(-0.341, -0.102, 0.487), vec3( 0.287, 0.331, 0.682), vec3(-0.129, 0.412, 0.731), vec3( 0.512, -0.221, 0.612), vec3(-0.412, -0.331, 0.682), vec3( 0.221, 0.129, 0.341), vec3(-0.183, -0.021, 0.512), vec3( 0.331, -0.412, 0.731), vec3(-0.041, 0.392, 0.734), vec3( 0.102, -0.341, 0.487), vec3(-0.287, 0.134, 0.553), vec3( 0.412, 0.287, 0.612), vec3(-0.512, -0.183, 0.512), vec3( 0.341, -0.102, 0.487), vec3(-0.129, 0.221, 0.341), vec3( 0.183, 0.412, 0.731), vec3(-0.392, 0.041, 0.734), vec3( 0.102, -0.512, 0.612), vec3(-0.221, -0.392, 0.682), vec3( 0.331, 0.129, 0.341), vec3(-0.041, -0.183, 0.512), vec3( 0.287, -0.412, 0.731), vec3(-0.134, 0.341, 0.487), vec3( 0.412, -0.287, 0.612), vec3(-0.512, 0.183, 0.512), vec3( 0.341, 0.102, 0.487), vec3(-0.129, -0.221, 0.341), vec3( 0.183, -0.412, 0.731));
for (int i = 0; i < KERNEL_SIZE; i++) {
vec3 sampleVec = TBN * SSAO_KERNEL[i];
hmm
Ok so
maybe i should use the random number instead of a kernel?
If you have tabulated values that are guaranteed correct inside of a sphere, you can roll with those
Though I’m a stickler for actually generating white noise until you run into quality issues with it
But when shit doesn’t look correct, getting it to the most minimal point is the right move
yeah using a random number instead of a kernel got rid of the banding
its even more noisy now tho
and less stable
Yes! But more debugfavle
So, first off, the hash does fract()
So it returns a value from 0-1
right
So just -0.5.xxx that shit
oh yeah true i forgot i need -1 to 1 not 0 to 1
It just needs to be symmetrical around 0, you can skip the *2, that’s just radius for now
ooh that actually looks super good if you ignore the noise
theres highlights on edges
Which is very wrong
but looks good
But, the hemisphere check is supposed to fix that
yeah odd that it isnt
Debatable, I know a lot of people will fry you for that
I like it myself
Though there are probably cheaper ways to achieve it
Also, I think it’s still asymmetrical
You remapped all components of the vector, right?
As in -.5
You need to do that for every component
Or just -0.5.xxx the entire thing
yeah:
vec3 hash(){
vec3 p = vec3(uv.xy, 1);
p = vec3(dot(p, vec3(127.1,311.7, 74.7)), dot(p, vec3(269.5,183.3,246.1)), dot(p, vec3(113.5,271.9,124.6)));
return (fract(sin(p)*43758.5453123)-0.5f)*2;
}
Add the .xxx swizzle so that I may sleep tonight
lol ok
Idk the default behavior for glsl
it multiplies all components
but im confused on what you mean by .xxx
where do i put it, never seen that before
Yeah, though scalar + vector is technically undefined in a mathematical sense
After .5
Ok, good. Explicit is better then implicit (to a point
)
This is the fix.
which one of the solutions there should i do
i could just do the last one it looks pretty uniform
Whatever is the simplest
Last one is good
Avoid the first one, its probably really bad for GPU or something
Fam
maybe i could re-use the hashing for that instead
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math#static_methods
well theres no javascript in glsl
Yeah
So what you need is the information on where the numbers are
They’re probably 0-1
So you can just reuse the hash
what are you talkin about?
Remove the .5.xxx and *2 though
The examples are written in js. This is the documentation for js’s Math.
where what numbers are tho
range of what??
The rng
Yes, you use a hash
right
But you need to match up the example code and GLSL
And just to be sure, you should check what Math.random() does
i need to figure out what range javascript's Math.random() returns
Genau
yeah its the same as the hash perfect
yeah i undid the +0.5 and /2 thing
also do you know what the x:x, y:y, z:z from the example means?
is that some kind of bitwise operation
idk
x:x aint valid in glsl
maybe its just a weird way to make a vec3 in some other language idk
That’s iirc just shorthand init of a class that contains x,y,z
Heard of jsons? Ofc you did, you probably had the fun of fucking around with them because modding.
yeah
Use pow
arent those two different operations?
i could look into how cubic roots are calculated and recreate that method maybe
Elementary school level math
bro they dont teach cubic roots at any stage of education
maybe in college actually
but yeah not in normal schools
A root is just a non-whole power
Just uhh
Pow(…,1./3.)
Wonder if that has edge cases in negative numbers…
Ah, it’s UB
but i aint got negatives from the hash anyways
so should be able to just do pow(hash.z, 0.333333333f);
You can, but in this case since you’d a cube root it’s sign(…)*pow(…, 1./3.)
whats the point in sign if its always 1
ah
Yep, all nice and defined here
implementing the last example from that website doesnt change the results noticably
maybe the issue is that randomVec is the same every time i call it from one pixel
float getAO(float depth, vec3 normal) {
vec3 posVS = reconstructViewPos(uv.xy, depth);
vec3 normalVS = normalize((globalUbo.view * vec4(normal.xyz, 0.f)).xyz);
vec3 randVec = randomVec();
vec3 tangent = normalize(randVec - normalVS * dot(randVec, normalVS));
vec3 bitangent = cross(normalVS, tangent);
mat3 TBN = mat3(tangent, bitangent, normalVS);
float occlusion = 0.f;
for (int i = 0; i < KERNEL_SIZE; i++) {
vec3 sampleVec = TBN * randomVec();//SSAO_KERNEL[i];
sampleVec = posVS + sampleVec * radius;
vec4 offset = globalUbo.proj * vec4(sampleVec, 1.0);
offset.xyz /= offset.w;
vec2 sampleUV = (offset.xy*0.5)+0.5;
if (!(sampleUV.x < 0 || sampleUV.x > 1 || sampleUV.y < 0 || sampleUV.y > 1)) {
vec3 sampleVS = reconstructViewPos(sampleUV, texture(ddaDepth, sampleUV).r);
float rangeCheck = smoothstep(0.f, 1.f, (radius/AO_STRENGTH)/length(sampleVS-posVS));
vec3 viewDirVS = normalize(posVS);
bool occluded = sampleVS.z <= sampleVec.z-0.05f;
occlusion += (occluded ? 1.f : 0.f) * rangeCheck * AO_STRENGTH;
}
radius is just 1
Any reason why you TBN* it?
for the hemisphere
if i dont tbn it then it gets very broken
So it’s not hemisphere then
yeah i get the outlines with the tbn lol
well it was hemisphere
with the old random method
That’s just because the points were distributed as such
radius is just 1
wait disregard that
it wasnt caused by removing the tbn
it was from me making the randomvec inputs different in the loop
this is without tbn
and yeah its perfectly stable now
no asymmetry
Neat!
and the blur should get rid of the noise
once i add blur
so i think its working good
Well, it’s a lot of noise
true
The best way to get rid of noise is to not create it
ah like by going back to the kernel? (nvm that breaks everything)
In other words, steal a GTAO impl
It’s lower integration dimensionality will make your life way easier
… as for noise reduction
- Don’t blur, use a basic À-Trous with a few passes
- Temporal accumulation is really good for quality, but is difficult to get perfect
- avoid white noise and hashes. they’re really good to debug, but ass for production. See blue noise. It’s also easier to do so with GTAO.
Sort of? It is more expensive ALU wise. But it is much higher quality. So you can do less samples. And get better denoising.
Pretty much, it’s worth it, imo
I’ll also tell you to avoid bitmasking for the time being, it’s cool but much more difficult and is missing the ground truth of GTAO if used as-is
i dont think im doin any bitmasking
is it a good idea to change the randomVec in my loop to be different from the one outside of it?
randomVec(uv.xy+(i/100))
like by using the iteration
Every time you need a new random number it should be unique and ideally uncorrelated to an extent. That’s why .nextRandom() is a thing in rng patterns.
Don’t uv+something, use an rng with extra inputs
what inputs tho
index is what i was just using tho
or wait i see what you mean
i shouldnt add the index to the uv
i should make the 3rd parameter in the vec3 the hash uses
Exactly
Should once again avoid correlations, which is usually good
Anyhow, imma log off for now. You should probably shoot me a dm or something if you need me.
i cant find anything good for how to implement gtao
so i think maybe i'll just add taa and hope it hides the noise
(also tried using blue noise but it brought back banding and asymetry / ao moving around up-close)
The paper has pseudocode and looking at an impl will do you about as well. Otherwise read up on (genuinely) SSR and HBAO. Beyond that if you can grasp the math, that’s great. Otherwise, just kinda debug issues with it until it works.