Hiệu ứng SSAO lạ (vị trí sai / kết cấu bình thường trong không gian xem?)


8

Tôi cố gắng tạo hiệu ứng SSAO trong công cụ trò chơi của mình (DirectX 11, C ++), chủ yếu dựa trên hướng dẫn gamedev.net của Jose María Méndez . Thật không may, nó không bao gồm vấn đề tạo kết cấu (quy tắc, vị trí).

Trong lần đầu tiên tôi tạo kết cấu bình thường và sau đó tôi cũng đọc bộ đệm độ sâu dưới dạng kết cấu (có thể vì tôi tạo chế độ xem tài nguyên đổ bóng và hủy liên kết bộ đệm độ sâu trong lần chuyển thứ hai). Cả hai nên ở trong không gian xem.

Tôi chưa thực hiện mờ (tôi sẽ làm điều đó sau), nhưng tôi có một số vấn đề ngay bây giờ. Tôi tin rằng đó là do tính toán sai về kết cấu hoặc phương pháp chuyển đổi dữ liệu sai từ chúng , thay vào đó là các giá trị của công thức hoặc tham số không chính xác .

Tôi đoán những gì có thể đã đi sai:

  • tính toán kết cấu bình thường (trong không gian xem) - nhưng có vẻ ổn ,
  • tính toán kết cấu vị trí (trong không gian xem) & độ sâu để chuyển đổi vị trí,
  • phép tính ma trận chiếu ngược,
  • một cái gì đó không được chuẩn hóa hoặc bão hòa và nó nên được,
  • tham số (tuy nhiên chúng không nên có tác động như vậy đến đầu ra)?

Hoạ tiết của tôi

Diffuse ( textures[0], không thực sự được sử dụng ở đây, chỉ để so sánh): nhập mô tả hình ảnh ở đây

Bình thường ( textures[1], nên ở trong không gian xem): nhập mô tả hình ảnh ở đây

Tôi nhận được chúng trong shader đỉnh đầu tiên (pixel shader làm chỉ output.normal = input.normal):

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

Kết cấu bộ đệm sâu ( textures[2], thật khó để nhìn thấy bất cứ điều gì vì sự khác biệt thấp về giá trị, nhưng tôi tin rằng nó ổn):

nhập mô tả hình ảnh ở đây

Để hiển thị nó tôi sử dụng:

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

Sau khi hiệu chỉnh độ tương phản trong chương trình bên ngoài (Tôi không sử dụng nó shader, nhưng nó tốt hơn cho mắt): nhập mô tả hình ảnh ở đây

Mô tả bộ đệm sâu:

//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;

Và mặt phẳng xa / gần (chúng có tác động đến kết cấu bộ đệm độ sâu) được đặt thành nearPlane = 10.0f( 1.0fkhông thay đổi quá nhiều và các đối tượng không quá gần camera) và farPlane = 1000.0f.

Dựa vào đó, tôi tạo vị trí trong không gian xem (tôi không lưu nó vào kết cấu, nhưng nếu tôi xuất nó ra màn hình thì nó trông như thế): nhập mô tả hình ảnh ở đây

Mỗi pixel ở đây được tính bằng:

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;

projectionInvertedđược chuyển sang shader với:

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);

Tôi tin rằng camera->getProjection()trả về ma trận tốt, vì tôi sử dụng cùng một ma trận để xây dựng các viewProjectionMatrixđối tượng ổn (tôi thấy các đỉnh bên phải ở đúng vị trí). Có lẽ một cái gì đó với chuyển vị?

Ngẫu nhiên ( textures[3], bình thường) - đó là kết cấu từ hướng dẫn, chỉ ở .tgađịnh dạng: nhập mô tả hình ảnh ở đây

Kết cấu ngẫu nhiên không có gạch (nó có thể lặp lại, khi bạn đặt một kết cấu bên cạnh một kết cấu khác). Nếu tôi kết xuất nó với getRandom(uv)toàn bộ màn hình tôi nhận được ( float2kết quả hiển thị là màu đỏ và màu xanh lá cây): nhập mô tả hình ảnh ở đây

Và kết quả: nhập mô tả hình ảnh ở đây

Nó trông giống như "một số bóng mờ" nhưng không có gì giống như thành phần SSAO (dù sao nó vẫn chưa bị mờ hoặc trộn lẫn với ánh sáng / khuếch tán). Tôi đoán nó giống với bóng râm hơn sau đó với SSAO thực tế (không có sắc thái xung quanh "góc sâu", v.v.)

Trình tạo bóng SSAO (đường chuyền thứ hai):

//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);
}

Tôi cung cấp các tham số đó (Tôi đã cố gắng chơi với chúng, chúng thay đổi chất lượng kết quả, v.v. nhưng không phải là hiệu ứng tổng thể):

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)

Lưu ý rằng tôi đã nhận xét ao /= (float)iterations*4.0; ao /= (float)parameters[1]/*sampleRad*/;chỉ vì cách đó tôi có thể thấy bất cứ điều gì (trong trường hợp khác, sự khác biệt trong âm thành phần SSAO là quá nhỏ - nhưng đó có thể là một vấn đề với các tham số sai).

chỉnh sửa số 1

Theo @AndonM.Colemanđề xuất, tôi xuất kết cấu bình thường ra màn hình textures[1].Sample(ObjSamplerState, textureCoordinates) *0.5f + 0.5fthay vì chỉ textures[1].Sample(ObjSamplerState, textureCoordinates):

nhập mô tả hình ảnh ở đây

Ngoài ra, tôi đã cố gắng xóa *2.0f - 1.0fmột phần khỏi getNormal(...)và nó cho tôi một kết quả khác cho thành phần SSAO:

nhập mô tả hình ảnh ở đây

Nó vẫn sai, tôi không biết nó gần hơn hay xa hơn từ "tốt". Có lẽ, theo @AndonM.Coleman(nếu tôi hiểu anh ta) nó có liên quan gì đến các định dạng của bộ đệm? Định dạng của tôi là:

#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

Tôi thực sự không muốn sử dụng các định dạng chậm hơn nếu không cần thiết.

chỉnh sửa số 2

Thay đổi kết cấu vị trí 1 - depththành depth - 1đầu ra mới (và lạ, tôi đoán):

nhập mô tả hình ảnh ở đây

Và thành phần SSAO thay đổi thành:

nhập mô tả hình ảnh ở đây

Lưu ý rằng tôi vẫn có bản gốc ao /= (float)iterations*4.0; ao /= (float)parameters[1]/*sampleRad*/nhận xét để xem bất cứ điều gì.

chỉnh sửa số 3

Thay đổi định dạng bộ đệm cho kết cấu (và chỉ cho nó) từ DXGI_FORMAT_R8G8B8A8_UNORMđể DXGI_FORMAT_R32G32B32A32_FLOATcho tôi kết cấu bình thường đẹp hơn:

nhập mô tả hình ảnh ở đây

Ngoài ra, thay đổi độ lệch từ 0.0fđến 0.2fvà bán kính mẫu từ 10.0fsang 0.2fcho tôi:

nhập mô tả hình ảnh ở đây

Có vẻ tốt hơn phải không? Tuy nhiên, tôi có bóng xung quanh các vật thể bên trái và đảo ngược nó ở bên phải. Điều gì có thể gây ra điều đó?


Một cái gì đó cảm thấy lạ đối với tôi trong getRandom (), khi bạn lấy mẫu một kết cấu không lặp lại, bạn sẽ sử dụng tọa độ UV [0-1]. Cho rằng bạn sử dụng float2 (1980,1050), ngay cả khi bạn nhân / chia mà tôi không thấy cách bạn sẽ lấy lại giá trị [0-1] với các công thức như vậy ... Hoặc kết cấu nhiễu của bạn được đặt ở chế độ lặp lại ?
VB_overflow

1
Câu hỏi ngu ngốc: mục tiêu kết xuất của bạn có phải là dấu phẩy động không? Ảnh chụp màn hình bạn đã hiển thị với không gian tọa độ trong đó có khoảng trắng lớn trong đó cả ba tọa độ là 0 hoặc âm. Tất cả đều được kẹp thành 0 bằng cách sử dụng các mục tiêu kết xuất điểm cố định (UNORM) truyền thống và bạn chắc chắn sẽ không nhận được SSAO chính xác khi bạn không thể lấy mẫu vị trí / bình thường một cách chính xác. Về mặt kỹ thuật, bạn không cần các mục tiêu kết xuất dấu phẩy động nếu bạn quan tâm đến hiệu suất - bạn có thể sử dụng SNORM hoặc chỉnh lại / bù màu thủ công.
Andon M. Coleman

1
Không, bạn không thể lưu trữ giá trị âm trong DXGI_FORMAT_R8G8B8A8_UNORM. Bạn sẽ cần SNORMphiên bản đó, hoặc một nửa không gian vectơ của bạn sẽ được kẹp thành 0 . Bây giờ tôi thực sự thấy rằng điều này đã được xử lý bằng cách nhân 2 và trừ âm 1 trong cả mã thông thường và mã vị trí. Tuy nhiên, tôi thực sự khuyên bạn nên trước khi xuất các màu này ra màn hình để trực quan hóa, bạn hãy tạo thói quen thực hiện * 0.5 + 0.5(để bạn có thể thấy các phần tiêu cực của không gian tọa độ của mình). Đối với 0.5 + 1.0điều đó sẽ không hoạt động, màu sắc của bạn sẽ đi 0,5 đến 1,5 (được kẹp thành 1).
Andon M. Coleman

1
Đó là tất cả tốt, thực sự. Mã hoàn toàn chăm sóc điều này cho bạn ngay bây giờ. Các normals được lưu trữ trong khoảng 0.0 - 1.0 trong kết cấu, nhưng thay đổi tỷ lệ -1.0 - 1.0 sau khi lấy mẫu; vị trí của bạn cũng được. Tuy nhiên, 1 - depthphần có vẻ kỳ lạ với tôi. Tôi nghĩ rằng nó nên được depth - 1, nếu không phạm vi độ sâu của bạn bị đảo ngược.
Andon M. Coleman

1
Tôi hoàn toàn không phải là chuyên gia, nhưng ... kết cấu bình thường của bạn không chứa bất kỳ màu xanh nào? AFAIUnderstand, mọi pixel của kết cấu bình thường phải thỏa mãn phương trình này: r ^ 2 + g ^ 2 + b ^ 2 = 1 (tức là: chiều dài 1). Tôi đã kiểm tra hình ảnh của bạn và nó chứa các pixel đen thuần túy.
Martijn Courteaux

Câu trả lời:


3

Theo hiểu biết của tôi, đầu ra vị trí của bạn là sai trong cả hai trường hợp. Nếu nên như thế này:nhập mô tả hình ảnh ở đây

Hoặc này: nhập mô tả hình ảnh ở đây

Tùy thuộc vào việc bạn đang sử dụng hệ thống tọa độ tay trái hay tay phải. Bạn nên tạo một bộ đệm vị trí và bỏ qua việc cố gắng tính toán nó từ độ sâu cho đến khi bạn biết cái gì hoạt động và cái gì không. Tạo một mục tiêu kết xuất mới trong thẻ GBuffer và chỉ xuất ra vị trí như thế này:

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

Ảnh chụp màn hình cuối cùng của bạn trông giống như một vấn đề bình thường, bạn có chắc chắn các thông số trong không gian xem không? Nếu không, tường bên phải sẽ tối ngay cả khi bạn xoay camera 180 độ và tường ở bên trái. Nó hay nó vẫn còn tối ở phía bên phải?

Nếu di chuyển chế độ xem chỉ làm cho các phần tối xuất hiện và biến mất, rất có thể đó là sự cố chiếu. Như thế này: (đó là cùng một góc của một căn phòng) nhập mô tả hình ảnh ở đây

Hãy thử chuyển ma trận chiếu của bạn từ LH sang RH hoặc ngược lại, bạn có thể đã nhầm lẫn nó.

Bên cạnh đó, đảm bảo bạn chỉ giải nén các quy tắc với "* 2.0f - 1.0f" nếu bạn cũng lưu trữ chúng với "* 0.5f + 1.0f". Bạn đã bao quát điều đó trong các bình luận nhưng đáng để kiểm tra thêm một lần nữa.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.