Efek SSAO aneh (posisi salah / tekstur normal dalam ruang tampilan?)

8

Saya mencoba membuat efek SSAO di mesin gim saya (DirectX 11, C ++), terutama berdasarkan pada tutorial gamedev.net oleh José María Méndez . Sayangnya, itu tidak mencakup masalah menciptakan tekstur (normal, posisi).

Pada pass pertama saya membuat tekstur normal dan kemudian saya juga membaca buffer kedalaman sebagai tekstur (itu mungkin karena saya membuat tampilan sumber daya shader dan melepaskan ikatan buffer kedalaman di pass kedua). Keduanya harus dalam ruang tampilan.

Saya belum menerapkan blur (saya akan melakukannya nanti), tetapi saya memiliki beberapa masalah sekarang. Saya percaya itu karena kesalahan perhitungan tekstur atau metode yang salah dalam mentransformasikan data dari data tersebut , dan bukannya nilai formula atau parameter yang salah .

Tebakan saya tentang apa yang bisa salah:

  • perhitungan tekstur normal (dalam ruang tampilan) - tetapi terlihat ok ,
  • perhitungan tekstur posisi (dalam ruang tampilan) & transformasi kedalaman ke posisi,
  • perhitungan matriks proyeksi terbalik,
  • sesuatu tidak dinormalisasi atau jenuh dan seharusnya,
  • parameter (namun mereka seharusnya tidak memiliki dampak pada output)?

Tekstur saya

Diffuse ( textures[0], tidak benar-benar digunakan di sini, hanya untuk perbandingan): masukkan deskripsi gambar di sini

Normal ( textures[1], harus berada dalam ruang tampilan): masukkan deskripsi gambar di sini

Saya mendapatkannya di pass vertex shader pertama (pixel shaders do just output.normal = input.normal):

output.normal = float4(mul(input.normal, (float3x3)world), 1);
output.normal = float4(mul(output.normal, (float3x3)view), 1);

Tekstur buffer kedalaman ( textures[2], sulit untuk melihat apa pun karena perbedaan nilai yang rendah, tapi saya yakin tidak apa-apa):

masukkan deskripsi gambar di sini

Untuk menampilkannya saya gunakan:

float depth = textures[2].Sample(ObjSamplerState, textureCoordinates).r;
depth = (depth + 1.0f) / 2.0f;
color.rgb = float3(depth, depth, depth);

Setelah koreksi kontras dalam program eksternal (saya tidak menggunakannya shader, tetapi lebih baik untuk mata): masukkan deskripsi gambar di sini

Deskripsi buffer kedalaman:

//create depth stencil texture (depth buffer)
D3D11_TEXTURE2D_DESC descDepth;
ZeroMemory(&descDepth, sizeof(descDepth));
descDepth.Width = settings->getScreenSize().getX();
descDepth.Height = settings->getScreenSize().getY();
descDepth.MipLevels = 1;
descDepth.ArraySize = 1;
descDepth.Format = settings->isDepthBufferUsableAsTexture() ? DXGI_FORMAT_R24G8_TYPELESS : DXGI_FORMAT_D24_UNORM_S8_UINT; //true
descDepth.SampleDesc.Count = settings->getAntiAliasing().getCount(); //1
descDepth.SampleDesc.Quality = settings->getAntiAliasing().getQuality(); //0
descDepth.Usage = D3D11_USAGE_DEFAULT;
descDepth.BindFlags = settings->isDepthBufferUsableAsTexture() ? (D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE) : D3D11_BIND_DEPTH_STENCIL; //true
descDepth.CPUAccessFlags = 0;
descDepth.MiscFlags = 0;

Dan pesawat jauh / dekat (mereka memiliki dampak pada tekstur buffer kedalaman) diatur ke nearPlane = 10.0f( 1.0ftidak berubah terlalu banyak dan objek tidak begitu dekat dengan kamera) dan farPlane = 1000.0f.

Berdasarkan itu saya membuat posisi di ruang tampilan (saya tidak menyimpannya ke tekstur, tetapi jika saya output ke layar terlihat seperti itu): masukkan deskripsi gambar di sini

Setiap piksel di sini dihitung dengan:

float depth = textures[2].Sample(ObjSamplerState, textureCoordinates).r;
float3 screenPos = float3(textureCoordinates.xy* float2(2, -2) - float2(1, -1), 1 - depth);
float4 wpos = mul(float4(screenPos, 1.0f), projectionInverted);
wpos.xyz /= wpos.w;
return wpos.xyz;

Dan projectionInvertedditransfer ke shader dengan:

DirectX::XMFLOAT4X4 projection = camera->getProjection();
DirectX::XMMATRIX camProjection = XMLoadFloat4x4(&projection);
camProjection = XMMatrixTranspose(camProjection);
DirectX::XMVECTOR det; DirectX::XMMATRIX projectionInverted = XMMatrixInverse(&det, camProjection);
projectionInverted = XMMatrixTranspose(projectionInverted);
cbPerObj.projectionInverted = projectionInverted;
....
context->UpdateSubresource(constantBuffer, 0, NULL, &cbPerObj, 0, 0);
context->VSSetConstantBuffers(0, 1, &constantBuffer);
context->PSSetConstantBuffers(0, 1, &constantBuffer);

Saya percaya bahwa camera->getProjection()mengembalikan matriks yang baik, karena saya menggunakan yang sama untuk membangun viewProjectionMatrixobjek yang ok (saya melihat simpul yang tepat di tempat yang tepat). Mungkin sesuatu dengan transpos?

Acak ( textures[3], normal) - tekstur dari tutorial, hanya dalam .tgaformat: masukkan deskripsi gambar di sini

Tekstur acak adalah ubin-kurang (itu berulang, ketika Anda meletakkan satu tekstur di sebelah yang lain). Jika saya merendernya getRandom(uv)untuk seluruh layar yang saya dapatkan ( float2hasilnya ditampilkan sebagai merah dan hijau): masukkan deskripsi gambar di sini

Dan hasilnya: masukkan deskripsi gambar di sini

Sepertinya "beberapa bayangan" tetapi tidak seperti komponen SSAO (belum buram atau dicampur dengan cahaya / difus pula). Saya kira itu lebih mirip dengan phong shading kemudian ke SSAO yang sebenarnya (tidak ada nuansa "sudut dalam" dll.)

Shader SSAO (lulus kedua):

//based on: http://www.gamedev.net/page/resources/_/technical/graphics-programming-and-theory/a-simple-and-practical-approach-to-ssao-r2753

Texture2D textures[4]; //color, normal (in view space), position (depth), random
SamplerState ObjSamplerState;

cbuffer cbPerObject : register(b0) {
    float4 notImportant;
    float4 notImportant2;
    float2 notImportant3;
    float4x4 projectionInverted;
};

cbuffer cbPerShader : register(b1) {
    float4 parameters; //randomSize, sampleRad, intensity, scale
    float4 parameters2; //bias
};


struct VS_OUTPUT {
    float4 Pos : SV_POSITION;
    float2 textureCoordinates : TEXCOORD;
};

float3 getPosition(float2 textureCoordinates) {
    float depth = textures[2].Sample(ObjSamplerState, textureCoordinates).r;
    float3 screenPos = float3(textureCoordinates.xy* float2(2, -2) - float2(1, -1), 1 - depth);
    float4 wpos = mul(float4(screenPos, 1.0f), projectionInverted);
    wpos.xyz /= wpos.w;
    return wpos.xyz;
}

float3 getNormal(in float2 textureCoordinates) {
    return normalize(textures[1].Sample(ObjSamplerState, textureCoordinates).xyz * 2.0f - 1.0f);
}

float2 getRandom(in float2 textureCoordinates) {
    return normalize(textures[3].Sample(ObjSamplerState, float2(1980,1050)/*screenSize*/ * textureCoordinates / parameters[0]/*randomSize*/).xy * 2.0f - 1.0f);
}

float doAmbientOcclusion(in float2 tcoord, in float2 textureCoordinates, in float3 p, in float3 cnorm) {
    float3 diff = getPosition(tcoord + textureCoordinates) - p;
    const float3 v = normalize(diff);
    const float d = length(diff)*parameters[3]/*scale*/;
    return max(0.0, dot(cnorm, v) - 0.2f/*bias*/)*(1.0 / (1.0 + d))*parameters[2]/*intensity*/;
}

float4 main(VS_OUTPUT input) : SV_TARGET0{

    float2 textureCoordinates = input.textureCoordinates;

    //SSAO

    const float2 vec[4] = { float2(1,0),float2(-1,0), float2(0,1),float2(0,-1) };

    float3 p = getPosition(textureCoordinates);
    float3 n = getNormal(textureCoordinates);
    float2 rand = getRandom(textureCoordinates);

    float ao = 0.0f;
    float rad = parameters[1]/*sampleRad*/ / p.z;

    //**SSAO Calculation**//
    int iterations = 4;
    for (int j = 0; j < iterations; ++j) {
        float2 coord1 = reflect(vec[j], rand)*rad;
        float2 coord2 = float2(coord1.x*0.707 - coord1.y*0.707, coord1.x*0.707 + coord1.y*0.707);

        ao += doAmbientOcclusion(textureCoordinates, coord1*0.25, p, n);
        ao += doAmbientOcclusion(textureCoordinates, coord2*0.5, p, n);
        ao += doAmbientOcclusion(textureCoordinates, coord1*0.75, p, n);
        ao += doAmbientOcclusion(textureCoordinates, coord2, p, n);
    }
    //ao /= (float)iterations*4.0;
    //ao /= (float)parameters[1]/*sampleRad*/;
    ao = 1 - (ao * parameters[2]/*intensity*/);

    //ao = saturate(ao);
    //**END**//

    //Do stuff here with your occlusion value ??ao??: modulate ambient lighting, write it to a buffer for later //use, etc.

    //SSAO end

    color = ao; //let's just output the SSAO component for now
    return float4(color.rgb, 1.0f);
}

Saya memberikan parameter tersebut (saya mencoba untuk bermain dengan mereka, mereka mengubah kualitas hasil, dll. Tapi bukan efek keseluruhan):

getShader()->setParameter(0, 64.0f); //randomSize
getShader()->setParameter(1, 10.0f); //sampleRad
getShader()->setParameter(2, 1.0f); //intensity
getShader()->setParameter(3, 0.5f); //scale
getShader()->setParameter(4, 0.0f); //bias (also 0.2f sometimes)

Perhatikan bahwa saya berkomentar ao /= (float)iterations*4.0; ao /= (float)parameters[1]/*sampleRad*/;hanya karena cara itu saya bisa melihat apa pun (dalam hal lain perbedaan dalam nada komponen SSAO terlalu kecil - tetapi itu bisa menjadi masalah dengan parameter yang salah).

edit # 1

Seperti yang @AndonM.Colemandisarankan, saya menampilkan tekstur normal ke layar dengan textures[1].Sample(ObjSamplerState, textureCoordinates) *0.5f + 0.5fbukan hanya textures[1].Sample(ObjSamplerState, textureCoordinates):

masukkan deskripsi gambar di sini

Juga, saya mencoba untuk menghapus *2.0f - 1.0fbagian dari getNormal(...)dan itu memberi saya hasil lain untuk komponen SSAO:

masukkan deskripsi gambar di sini

Itu masih salah, saya tidak tahu apakah itu lebih dekat atau lebih jauh dari "baik". Mungkin, menurut @AndonM.Coleman(jika saya memahaminya) itu ada hubungannya dengan format buffer? Format saya adalah:

#define BUFFER_FORMAT_SWAP_CHAIN DXGI_FORMAT_R8G8B8A8_UNORM

//depth buffer
//I don't use it: #define BUFFER_FORMAT_DEPTH DXGI_FORMAT_D24_UNORM_S8_UINT
#define BUFFER_FORMAT_DEPTH_USABLE_AS_TEXTURE DXGI_FORMAT_R24G8_TYPELESS
//I don't use it: #define BUFFER_FORMAT_DEPTH_VIEW DXGI_FORMAT_D24_UNORM_S8_UINT
#define BUFFER_FORMAT_DEPTH_VIEW_AS_TEXTURE DXGI_FORMAT_D24_UNORM_S8_UINT
#define BUFFER_FORMAT_DEPTH_SHADER_RESOURCE_VIEW DXGI_FORMAT_R24_UNORM_X8_TYPELESS

#define BUFFER_FORMAT_TEXTURE DXGI_FORMAT_R8G8B8A8_UNORM

Saya benar-benar tidak ingin menggunakan format yang lebih lambat jika tidak perlu.

edit # 2

Mengubah 1 - depthuntuk depth - 1menampilkan tekstur posisi baru (dan aneh, saya kira):

masukkan deskripsi gambar di sini

Dan komponen SSAO berubah menjadi:

masukkan deskripsi gambar di sini

Perhatikan bahwa saya masih memiliki ao /= (float)iterations*4.0; ao /= (float)parameters[1]/*sampleRad*/komentar asli untuk melihat apa pun.

edit # 3

Mengubah format buffer untuk tekstur (dan hanya untuk itu) dari DXGI_FORMAT_R8G8B8A8_UNORMuntuk DXGI_FORMAT_R32G32B32A32_FLOATmemberi saya tekstur normal yang lebih baik:

masukkan deskripsi gambar di sini

Juga, mengubah bias dari 0.0fke 0.2fdan sampel radius dari 10.0fke 0.2fmemberi saya:

masukkan deskripsi gambar di sini

Terlihat lebih baik, bukan? Namun, saya memiliki bayangan di sekitar objek di sebelah kiri dan inversinya di sebelah kanan. Apa yang menyebabkannya?

PolGraphic
sumber
Sesuatu terasa aneh bagi saya di getRandom (), ketika Anda mencicipi tekstur yang tidak diulang, Anda akan menggunakan [0-1] koordinat UV. Mengingat bahwa Anda menggunakan float2 (1980,1050), bahkan jika Anda mengalikan / membagi bahwa saya tidak melihat bagaimana Anda akan mendapatkan kembali nilai [0-1] dengan formula seperti itu ... Atau tekstur suara Anda diatur dalam mode ulang ?
VB_overflow
1
Pertanyaan bodoh: apakah target rendering Anda mengambang? Tangkapan layar yang Anda tunjukkan dengan ruang koordinat di dalamnya memiliki ruang hitam besar tempat ketiga koordinat bernilai 0 atau negatif. Semuanya dijepit ke 0 menggunakan target render titik tetap tradisional (UNORM) dan Anda pasti tidak akan mendapatkan SSAO yang akurat ketika Anda tidak dapat mengambil sampel posisi / normal dengan benar. Anda tidak secara teknis membutuhkan target render floating-point jika Anda khawatir dengan kinerja - Anda dapat menggunakan SNORM atau mengubah skala / mengimbangi warna secara manual.
Andon M. Coleman
1
Tidak, Anda tidak dapat menyimpan nilai negatif di DXGI_FORMAT_R8G8B8A8_UNORM. Anda membutuhkan SNORMversi itu, atau setengah dari ruang vektor Anda akan dijepit ke 0 . Saya benar-benar melihat sekarang bahwa ini sudah diatasi dengan mengalikan 2 dan mengurangi 1 negatif baik dalam kode normal dan posisi. Namun, saya sangat menyarankan bahwa sebelum mengeluarkan warna-warna ini ke layar untuk visualisasi, Anda membuat kebiasaan * 0.5 + 0.5(sehingga Anda dapat melihat bagian negatif dari ruang koordinat Anda). Sedangkan untuk 0.5 + 1.0itu tidak akan berhasil, warna Anda akan menjadi 0,5 hingga 1,5 (dijepit ke 1).
Andon M. Coleman
1
Sebenarnya itu semua bagus. Kode sepenuhnya menangani ini untuk Anda sekarang. Normalnya disimpan dalam kisaran 0,0 - 1,0 dalam tekstur, tetapi diubah menjadi -1,0 - 1,0 setelah sampel; posisi Anda juga. Namun, 1 - depthbagian itu terlihat aneh bagi saya. Saya pikir itu harus depth - 1, jika rentang kedalaman Anda terbalik.
Andon M. Coleman
1
Saya bukan ahli sama sekali, tapi ... tekstur normal Anda tidak mengandung warna biru? AFAIUpahami, setiap piksel dari tekstur normal harus memenuhi persamaan ini: r ^ 2 + g ^ 2 + b ^ 2 = 1 (yaitu: panjang 1). Saya menguji gambar Anda, dan itu mengandung piksel hitam murni.
Martijn Courteaux

Jawaban:

3

Sepengetahuan saya, output posisi Anda salah dalam kedua kasus. Jika harus terlihat seperti ini:masukkan deskripsi gambar di sini

Atau ini: masukkan deskripsi gambar di sini

Tergantung pada apakah Anda menggunakan sistem koordinat Tangan kiri atau Kanan. Anda harus membuat buffer posisi dan lewati mencoba menghitungnya dari kedalaman sampai Anda tahu apa yang berfungsi dan apa yang tidak. Buat target penargetan baru di pas GBuffer dan output saja posisi seperti ini:

output.Target3 = float4(input.PosV.z, input.PosV.z, input.PosV.z, 1.0f);

Tangkapan layar terakhir Anda terlihat seperti masalah normal, apakah Anda yakin normalnya ada di ruang tampilan? Jika tidak, dinding ke kanan harus tetap gelap walaupun Anda memutar kamera 180 derajat dan dinding di sebelah kiri. Apakah masih atau masih gelap di sisi kanan?

Jika memindahkan tampilan hanya sedikit menyebabkan bagian gelap muncul dan menghilang, kemungkinan besar itu adalah masalah proyeksi. Seperti ini: (ini sudut ruangan yang sama) masukkan deskripsi gambar di sini

Coba alihkan matriks proyeksi Anda dari LH ke RH atau sebaliknya, Anda mungkin mencampuradukkannya.

Selain itu pastikan Anda hanya mendekompresi normals dengan "* 2.0f - 1.0f" jika Anda juga menyimpannya dengan "* 0.5f + 1.0f". Anda membahasnya di komentar tetapi perlu memeriksa sekali lagi.

Stefan Agartsson
sumber