<p>Offset.xy is definatelly off because it just return XY coordinates from screenspace:
<a href="https://i.sstatic.net/pTk24.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/pTk24.png" alt="enter image description here"></a></p>

<p>Clamping solved 'small copies of model' on the model itself, thanks!</p>

<p>sampleDepth actually looks like this and indeed is not in viewspace and it may really explain those little models now:
<a href="https://i.sstatic.net/edGRY.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/edGRY.png" alt="enter image description here"></a></p>

<p>Using viewspace (<em>offset = mul(projection, offset) ---> offset = mul(view, offset)</em>) solves depth problem but final buffer is clear white. No effects are actually visible. What next step do you suggest? If depth comparing is correct now, what might be off?
<a href="https://i.sstatic.net/cEhHh.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/cEhHh.png" alt="enter image description here"></a></p>

<p><strong>UPDATE:</strong>
Comparision is working but in a wrong way. Below line results in just fully white buffer screen:</p>

<pre><code>occlusion += (sampleDepth &gt;= sample.z + bias ? 1.0 : 0.0);
</code></pre>

<p>Changing it to:</p>

<pre><code>occlusion += (sampleDepth &gt;= sample.z - bias ? 1.0 : 0.0);
</code></pre>

<p>Actually gives us comparision but it is really bold and doesn't include only holes and creases but whole model. Changing bias to small values like 0.001f doesn't help:
<a href="https://i.sstatic.net/mtO9k.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/mtO9k.png" alt="enter image description here"></a></p>

<p><strong>Update #2:</strong>
Directly comparing gives result presented below. However it doesn't seem satisfying enough to use as it seems to present rather edges than creases and holes:</p>

<pre><code>occlusion += (sampleDepth == sample.z ? 1.0 : 0.0);
</code></pre>

<p><a href="https://i.sstatic.net/qsHyi.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/qsHyi.png" alt="enter image description here"></a></p>

CC BY-SA 4.0
2019-04-02T02:11:27.467Z
Next thing I would check is if your samples are being generated as you expect. Try reducing it to 1 sample that is somewhere at the edge of your sampling disc and output the view-space position of that sample. Would be useful to have side-by-side with your original view-space position rendering, they should look similar with the offset version looking slightly shifted in terms of position. How large are the disc sampling vectors compared to the size of your mesh ?
PaulHK
CC BY-SA 4.0
2019-04-02T06:40:08.360Z
I've already made subtle modifications and tests and it turned out that by using around 30 samples and substracting by 10 I am getting best results (https://github.com/komilll/LEngine/blob/master/LEngine/ssaoShader.ps).

However it was only a result of personal tests on given model and might not be general rule. I will try side-by-side comparision.

Also does mesh size vs sampling size matters? Shouldn't there be a universal best sample size that doesn't collide with any mesh size? Could you elaborate on that, please?
DirectX_Programmer
CC BY-SA 4.0
2019-04-02T06:46:37.747Z
Your sampling offsets could be so large that you could miss your model completely so yes, it is related to the mesh size. If my understanding is correct the offsets are added in view coordinate space? I wanted to eliminate the possibility that you're taking samples from somewhere near or beyond the edge of your depth texture.
PaulHK
CC BY-SA 4.0
2019-04-16T17:29:03.553Z
I finally got time to test it. THANK YOU. Everything works fine. I tweaked bias and radius values and after getting pleasable values I copied this values into my code and it works great.
DirectX_Programmer
CC BY-SA 4.0
2019-03-29T19:40:46.097Z
2019-03-30T08:04:05.120Z
2019-03-30T08:04:05.120Z
DirectX_Programmer
<p>I am trying to implement SSAO using DirectX11 but instead I got white screen with few black dots on model. <strong>My suspect is that kernel generation or usage might be wrong</strong>. I've tried <strong>changing order of TBN matrix and sample multiplication order in pixel shader and it doesn't change results</strong> in any way:
<a href="https://i.sstatic.net/otLHt.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/otLHt.png" alt="Final buffer"></a></p>

<p>In fact those "dots" are small models of model itself that appear on it instead of dark creases (Example with sphere model):<a href="https://i.sstatic.net/vEnCe.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/vEnCe.png" alt="Sphere model SSAO"></a></p>

<p>My G-Buffers looks like this (Position, normal, tangent, bitangent):
<a href="https://i.sstatic.net/pLjAp.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/pLjAp.png" alt="Position"></a></p>

<p><a href="https://i.sstatic.net/jJq6E.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/jJq6E.png" alt="Normal"></a></p>

<p><a href="https://i.sstatic.net/EI8iI.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/EI8iI.png" alt="Tangent"></a></p>

<p><a href="https://i.sstatic.net/qwNo7.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/qwNo7.png" alt="Bitangent"></a></p>

<p><strong>myKernelGeneration.cpp</strong></p>

<pre><code>//Create kernel for SSAO
std::uniform_real_distribution&lt;float&gt; randomFloats(0.0f, 1.0f);
std::default_random_engine generator;
XMFLOAT3 tmpSample;
for (int i = 0; i &lt; SSAO_KERNEL_SIZE; i++)
{
    //Generate random vector3 ([-1, 1], [-1, 1], [0, 1])
    tmpSample.x = randomFloats(generator) * 2.0f - 1.0f;
    tmpSample.y = randomFloats(generator) * 2.0f - 1.0f;
    tmpSample.z = randomFloats(generator);

    //Normalize vector3
    XMVECTOR tmpVector;
    tmpVector.m128_f32[0] = tmpSample.x;
    tmpVector.m128_f32[1] = tmpSample.y;
    tmpVector.m128_f32[2] = tmpSample.z;

    tmpVector = XMVector3Normalize(tmpVector);

    tmpSample.x = tmpVector.m128_f32[0];
    tmpSample.y = tmpVector.m128_f32[1];
    tmpSample.z = tmpVector.m128_f32[2];

    //Multiply by random value all coordinates of vector3
    //float randomMultiply = randomFloats(generator);
    //tmpSample.x *= randomMultiply;
    //tmpSample.y *= randomMultiply;
    //tmpSample.z *= randomMultiply;

    //Scale samples so they are more aligned to middle of hemisphere
    float scale = float(i) / 64.0f;
    scale = lerp(0.1f, 1.0f, scale * scale);
    tmpSample.x *= scale;
    tmpSample.y *= scale;
    tmpSample.z *= scale;

    //Pass value to array
    m_ssaoKernel[i] = tmpSample;
}
</code></pre>

<p><strong>myPixelShader.ps</strong></p>

<pre><code>Texture2D textures[3]; //position, normal, noise
SamplerState SampleType;

//////////////
// TYPEDEFS //
//////////////

cbuffer KernelBuffer
{
    float3 g_kernelValue[64];
};

struct PixelInputType
{
    float4 positionSV : SV_POSITION;
    float2 tex : TEXCOORD0;
    float4x4 projection : TEXCOORD1;
};

const float2 noiseScale = float2(1280.0f / 4.0f, 720.0f / 4.0f);
const float radius = 0.5f;
const float bias = 0.025f;
////////////////////////////////////////////////////////////////////////////////
// Pixel Shader
////////////////////////////////////////////////////////////////////////////////
float4 ColorPixelShader(PixelInputType input) : SV_TARGET
{
    float3 position = textures[0].Sample(SampleType, input.tex).xyz;
float3 normal = normalize(textures[1].Sample(SampleType, input.tex).rgb);
float3 randomVector = normalize(textures[2].Sample(SampleType, input.tex * noiseScale).xyz);

float3 tangent = normalize(randomVector - normal * dot(randomVector, normal));
float3 bitangent = cross(normal, tangent);
float3x3 TBN = { tangent, bitangent, normal };

float3 sample = float3(0.0f, 0.0f, 0.0f);
float4 offset = float4(0.0f, 0.0f, 0.0f, 0.0f);
float occlusion = 0.0f;

for (int i = 0; i &lt; 64; i++)
{
    sample = mul(TBN, g_kernelValue[i]);
    sample = position + sample * radius;

    offset = float4(sample, 1.0f);
    offset = mul(input.projection, offset);
    offset.xyz /= offset.w;
    offset.xyz = offset.xyz * 0.5f + 0.5f;

    float sampleDepth = textures[0].Sample(SampleType, offset.xy).z;
    occlusion += (sampleDepth &gt;= sample.z + bias ? 1.0 : 0.0);
}
occlusion = 1.0f - (occlusion / 64.0f);

return float4(occlusion, occlusion, occlusion, 1.0f);
}
</code></pre>

CC BY-SA 4.0
2019-03-28T03:17:51.207Z
First place I would look is at offset.xy used in textures[0].Sample, it looks like its has a high scale factor? Do you use texture wrapping or clamping when sampling the depth buffer, wrapping would explain the tiled small versions of your scene. Also note the values stored in the depth buffer are not in view space, so comparing sampleDepth to sample.z may not work as expected (did I see anothre post from you involving shadow mapping, that also makes the same assumption) , that is my understanding with GLSL, I haven't used HLSL before so I may be wrong.
PaulHK
<directx11><directx><post-processing><ssao>
SSAO - artifacts appearing
DirectX_Programmer
# SSAO - artifacts appearing

(Asked by: username_0 on 2019-03-27)

I am trying to implement SSAO using DirectX11 but instead I got white screen with few black dots on model. **My suspect is that kernel generation or usage might be wrong**. I've tried **changing order of TBN matrix and sample multiplication order in pixel shader and it doesn't change results** in any way:
[![Final buffer](https://i.sstatic.net/otLHt.png)](https://i.sstatic.net/otLHt.png)

In fact those "dots" are small models of model itself that appear on it instead of dark creases (Example with sphere model):[![Sphere model SSAO](https://i.sstatic.net/vEnCe.png)](https://i.sstatic.net/vEnCe.png)

My G-Buffers looks like this (Position, normal, tangent, bitangent):
[![Position](https://i.sstatic.net/pLjAp.png)](https://i.sstatic.net/pLjAp.png)

[![Normal](https://i.sstatic.net/jJq6E.png)](https://i.sstatic.net/jJq6E.png)

[![Tangent](https://i.sstatic.net/EI8iI.png)](https://i.sstatic.net/EI8iI.png)

[![Bitangent](https://i.sstatic.net/qwNo7.png)](https://i.sstatic.net/qwNo7.png)

**myKernelGeneration.cpp**

```
//Create kernel for SSAO
std::uniform_real_distribution<float> randomFloats(0.0f, 1.0f);
std::default_random_engine generator;
XMFLOAT3 tmpSample;
for (int i = 0; i < SSAO_KERNEL_SIZE; i++)
{
    //Generate random vector3 ([-1, 1], [-1, 1], [0, 1])
    tmpSample.x = randomFloats(generator) * 2.0f - 1.0f;
    tmpSample.y = randomFloats(generator) * 2.0f - 1.0f;
    tmpSample.z = randomFloats(generator);

    //Normalize vector3
    XMVECTOR tmpVector;
    tmpVector.m128_f32[0] = tmpSample.x;
    tmpVector.m128_f32[1] = tmpSample.y;
    tmpVector.m128_f32[2] = tmpSample.z;

    tmpVector = XMVector3Normalize(tmpVector);

    tmpSample.x = tmpVector.m128_f32[0];
    tmpSample.y = tmpVector.m128_f32[1];
    tmpSample.z = tmpVector.m128_f32[2];

    //Multiply by random value all coordinates of vector3
    //float randomMultiply = randomFloats(generator);
    //tmpSample.x *= randomMultiply;
    //tmpSample.y *= randomMultiply;
    //tmpSample.z *= randomMultiply;

    //Scale samples so they are more aligned to middle of hemisphere
    float scale = float(i) / 64.0f;
    scale = lerp(0.1f, 1.0f, scale * scale);
    tmpSample.x *= scale;
    tmpSample.y *= scale;
    tmpSample.z *= scale;

    //Pass value to array
    m_ssaoKernel[i] = tmpSample;
}

```

**myPixelShader.ps**

```
Texture2D textures[3]; //position, normal, noise
SamplerState SampleType;

//////////////
// TYPEDEFS //
//////////////

cbuffer KernelBuffer
{
    float3 g_kernelValue[64];
};

struct PixelInputType
{
    float4 positionSV : SV_POSITION;
    float2 tex : TEXCOORD0;
    float4x4 projection : TEXCOORD1;
};

const float2 noiseScale = float2(1280.0f / 4.0f, 720.0f / 4.0f);
const float radius = 0.5f;
const float bias = 0.025f;
////////////////////////////////////////////////////////////////////////////////
// Pixel Shader
////////////////////////////////////////////////////////////////////////////////
float4 ColorPixelShader(PixelInputType input) : SV_TARGET
{
    float3 position = textures[0].Sample(SampleType, input.tex).xyz;
float3 normal = normalize(textures[1].Sample(SampleType, input.tex).rgb);
float3 randomVector = normalize(textures[2].Sample(SampleType, input.tex * noiseScale).xyz);

float3 tangent = normalize(randomVector - normal * dot(randomVector, normal));
float3 bitangent = cross(normal, tangent);
float3x3 TBN = { tangent, bitangent, normal };

float3 sample = float3(0.0f, 0.0f, 0.0f);
float4 offset = float4(0.0f, 0.0f, 0.0f, 0.0f);
float occlusion = 0.0f;

for (int i = 0; i < 64; i++)
{
    sample = mul(TBN, g_kernelValue[i]);
    sample = position + sample * radius;

    offset = float4(sample, 1.0f);
    offset = mul(input.projection, offset);
    offset.xyz /= offset.w;
    offset.xyz = offset.xyz * 0.5f + 0.5f;

    float sampleDepth = textures[0].Sample(SampleType, offset.xy).z;
    occlusion += (sampleDepth >= sample.z + bias ? 1.0 : 0.0);
}
occlusion = 1.0f - (occlusion / 64.0f);

return float4(occlusion, occlusion, occlusion, 1.0f);
}

```

**Tags:** directx11, directx, post-processing, ssao

> **Comments:**
> **username_1**: First place I would look is at offset.xy used in textures[0].Sample, it looks like its has a high scale factor? Do you use texture wrapping or clamping when sampling the depth buffer, wrapping would explain the tiled small versions of your scene. Also note the values stored in the depth buffer are not in view space, so comparing sampleDepth to sample.z may not work as expected (did I see anothre post from you involving shadow mapping, that also makes the same assumption) , that is my understanding with GLSL, I haven't used HLSL before so I may be wrong.  

---

## 1 answers

### Answer by username_0 on 2019-03-29. Score: 0 | Accepted answer

Offset.xy is definatelly off because it just return XY coordinates from screenspace:
[![enter image description here](https://i.sstatic.net/pTk24.png)](https://i.sstatic.net/pTk24.png)

Clamping solved 'small copies of model' on the model itself, thanks!

sampleDepth actually looks like this and indeed is not in viewspace and it may really explain those little models now:
[![enter image description here](https://i.sstatic.net/edGRY.png)](https://i.sstatic.net/edGRY.png)

Using viewspace (*offset = mul(projection, offset) ---> offset = mul(view, offset)*) solves depth problem but final buffer is clear white. No effects are actually visible. What next step do you suggest? If depth comparing is correct now, what might be off?
[![enter image description here](https://i.sstatic.net/cEhHh.png)](https://i.sstatic.net/cEhHh.png)

**UPDATE:**
Comparision is working but in a wrong way. Below line results in just fully white buffer screen:

```
occlusion += (sampleDepth >= sample.z + bias ? 1.0 : 0.0);

```

Changing it to:

```
occlusion += (sampleDepth >= sample.z - bias ? 1.0 : 0.0);

```

Actually gives us comparision but it is really bold and doesn't include only holes and creases but whole model. Changing bias to small values like 0.001f doesn't help:
[![enter image description here](https://i.sstatic.net/mtO9k.png)](https://i.sstatic.net/mtO9k.png)

**Update #2:**
Directly comparing gives result presented below. However it doesn't seem satisfying enough to use as it seems to present rather edges than creases and holes:

```
occlusion += (sampleDepth == sample.z ? 1.0 : 0.0);

```

[![enter image description here](https://i.sstatic.net/qsHyi.png)](https://i.sstatic.net/qsHyi.png)

> **Comments:**
> **username_1**: Next thing I would check is if your samples are being generated as you expect. Try reducing it to 1 sample that is somewhere at the edge of your sampling disc and output the view-space position of that sample. Would be useful to have side-by-side with your original view-space position rendering, they should look similar with the offset version looking slightly shifted in terms of position. How large are the disc sampling vectors compared to the size of your mesh ?  
> **username_0**: I've already made subtle modifications and tests and it turned out that by using around 30 samples and substracting by 10 I am getting best results (https://github.com/komilll/LEngine/blob/master/LEngine/ssaoShader.ps).  However it was only a result of personal tests on given model and might not be general rule. I will try side-by-side comparision.  Also does mesh size vs sampling size matters? Shouldn't there be a universal best sample size that doesn't collide with any mesh size? Could you elaborate on that, please?  
> **username_1**: Your sampling offsets could be so large that you could miss your model completely so yes, it is related to the mesh size. If my understanding is correct the offsets are added in view coordinate space? I wanted to eliminate the possibility that you're taking samples from somewhere near or beyond the edge of your depth texture.  
> **username_0**: I finally got time to test it. THANK YOU. Everything works fine. I tweaked bias and radius values and after getting pleasable values I copied this values into my code and it works great.  

---

