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):
Bình thường ( textures[1]
, nên ở trong không gian xem):
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):
Để 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):
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.0f
khô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ế):
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;
Và 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:
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 ( float2
kết quả hiển thị là màu đỏ và màu xanh lá câ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.5f
thay vì chỉ textures[1].Sample(ObjSamplerState, textureCoordinates)
:
Ngoài ra, tôi đã cố gắng xóa *2.0f - 1.0f
một phần khỏi getNormal(...)
và nó cho tôi một kết quả khác cho thành phần SSAO:
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 - depth
thành depth - 1
đầu ra mới (và lạ, tôi đoán):
Và thành phần SSAO thay đổi thành:
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_FLOAT
cho tôi kết cấu bình thường đẹp hơn:
Ngoài ra, thay đổi độ lệch từ 0.0f
đến 0.2f
và bán kính mẫu từ 10.0f
sang 0.2f
cho tôi:
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 đó?
DXGI_FORMAT_R8G8B8A8_UNORM
. Bạn sẽ cần SNORM
phiê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).
1 - depth
phầ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.