Graphics : Ambient Occlusion / SSAO
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=catllage&logNo=220826203499
Computer Graphics / Photo Gallery: Ambient Occlusion 2 - SSAO (aqrhan.blogspot.com)
cast-unity-plugin/SSAOShader.shader at master · googlecodelabs/cast-unity-plugin · GitHub
d3d12book/Ssao.hlsl at master · d3dcoder/d3d12book · GitHub
dxsandbox/ssao.hlsl at master · jhk2/dxsandbox · GitHub
AO(p,n) = 1/π Σ V(p,ω)n·ωdω
p : position(point)
n : normal direction
포인트에 대해 반원의 디렉션으로 파티클을 생성하여 각각의 물체와의 거리량을 측정하고 이를 가산하여 밝기 값을 출력해 낸다.
SSAO (Screen Space Ambient Occlusion)
AO의 경우 모든 오브젝트에 대해서 모든 디렉션으로 진행이 됨으로 연산량이 극도로 높기 때문에 이를 보완하기 위해 camera로부터 depth buffer의 데이터를 받아오고 이 받아오 depth buffer의 결과물로 부터 AO 값을 계산하여 펴현한다.
이렇게 하면 기존의 AO의 연산법에 비해 상당한 비용 절감이 된다.
sampling을 기존과 비교하여 달라진 부분만 기록하는 방법을 사용할 수 있는데 이렇게 할경우 AO맵이 약간 늦게 따라온다거나 미세한 블러/잔상이 발생할 수 있다.
SSAO 처리 과정
1. 일반적 랜더링
2. Depth buffer를 텍스쳐로 가져오는 방법이 가장 좋으나 그렇지 못할 경우 쉐이더를 통해 패스를 도는 방식으로 가야함 (DX10 에서는 depth buffer을 가져올 수 있다고 함)
3. 저장된 Depth buffer texture을 통해 음영 계산
깊이 값 비교시 랜덤한 위치의 값을 취하기 위해 랜덤텍스쳐를 미리 만든뒤 랜덤 텍스쳐의 값을 이용해 주위의 깊이값을 가져온다.
4. 생성된 음영 값에 블러 적용
블러를 적용하지 않으면 노이즈와 같은 현상이 생긴다.
5. 계산된 음영과 씬 합성
float sampleRadius = 10.0f // sampling radiusfloat distanceScale = 500.0f // Occlusion strengthfloat4x4 Projection;
float3 cornerFrustrum;
struct VS_OUTPUT //VS struct{float4 pos : POSITION;float2 TexCoord : TEXCOORD0;float3 viewDirection : TEXCOORD1;};
VS_OUT VS(float4 Position : POSITION, float2 TexCoord : TEXCOORD0){VS_OUTPUT Out = (VS_OUTPUT)0;
Out.pos = Position;Out.TexCoord = TexCoord;
// 카메라 frustrum의 값.frustrum == A threee dimensional cone that has its point// cut off to a certain portion. Like the thing an// an elephant stands on at the circus, or a lampshade.///////////////////////////////////////////////////////////////////////////////////// float farY = (float)tan(m_pCamera->GetFov()*.5f)*m_pCamera->FarPlane();// float farX = farY * m_pCamera->GetAspectRatio();//// D3DXVECTOR3 vCornerFrustrum;// vCornerFrustrum.x = farX;// vCornerFrustrum.y = farY;// vCornerFrustrum.z = m_pCamera->FarPlane();// m_pEffect->SetValue("cornerFrustrum",&vCornerFrustrum, sizeof(float)*3);
// 카메라에서 보는 방향float3 corner = float3(cornerFrustrum.x * Position.x, -cornerFrustrum.y * Position.y, cornerFrustrum.z);
Out.viewDirection = corner;return Out;}
texture depthTexture;texture randomTexture;
sampler2D depthSampler = sampler_state{Texture = ;ADDRESSU = CLAMP; //Clamp modeADDRESSV = CLAMP;MAGFILTER = LINEAR;MINFILTER = LINEAR;};
sampler2D RandNormal = sampler_state{Texture = ;ADDRESSU = WRAP; // RapModeADDRESSV = WRAP;MAGFILTER = LINEAR;MINFILTER = LINEAR;}
float4 PS(VS_OUTPUT IN) : COLOR0{ //Random vectors for sampling float4 samples[16] = { float4(0.355512, -0.709318, -0.102371, 0.0 ),
float4(0.534186, 0.71511, -0.115167, 0.0 ),
float4(-0.87866, 0.157139, -0.115167, 0.0 ),
float4(0.140679, -0.475516, -0.0639818, 0.0 ),
float4(-0.0796121, 0.158842, -0.677075, 0.0 ),
float4(-0.0759516, -0.101676, -0.483625, 0.0 ),
float4(0.12493, -0.0223423, -0.483625, 0.0 ),
float4(-0.0720074, 0.243395, -0.967251, 0.0 ),
float4(-0.207641, 0.414286, 0.187755, 0.0 ),
float4(-0.277332, -0.371262, 0.187755, 0.0 ),
float4(0.63864, -0.114214, 0.262857, 0.0 ),
float4(-0.184051, 0.622119, 0.262857, 0.0 ),
float4(0.110007, -0.219486, 0.435574, 0.0 ),
float4(0.235085, 0.314707, 0.696918, 0.0 ),
float4(-0.290012, 0.0518654, 0.522688, 0.0 ),
float4(0.0975089, -0.329594, 0.609803, 0.0 ) }; normalize (IN.viewDirection); float depth = tex2D(depthSampler, IN.TexCoord).a; float3 se = depth * IN.viewDirection;
float3 randNormal = tex2D( RandNormal, IN.TexCoord * 200.0 ).rgb;
float finalColor = 0.0f;
for (int i = 0; i < 16; i++) { // Random 값 추출을 위해서 샘플값에 랜덤 텍스쳐에서 가져온 값을 이용 반사 벡터를 취한다 // 반사벡터가 가장 보기 좋음 float3 ray = reflect(samples[i].xyz, randomNormal) * sampleRadius;
float4 sample = float4(se + ray, 1.0f); float4 ss = mul(sample, Projection);
// 0~1 범위의 uv공간으로 변환 float2 sampleTexCoord = 0.5f * ss.xy/ss.w + float2(0.5f, 0.5f); float sampleDepth = tex2D(depthSampler, sampleTexCoord).a;
if(sampleDepth == 1.0) { finalColor ++; //max Intensity } else { // depth > sampleDepth 일 경우 occlusion 적용 float occlusion = distanceScale* max(depth - sampleDepth, 0.0f); finalColor += 1.0f / (1.0f + occlusion * occlusion * 0.1); //Occlusion calculated } }return float4(finalColor/16, finalColor/16, finalColor/16, 1.0f); //16bite}
technique SSAO{pass P0{VertexShader = compile vs_3_0 VS();PixedlShader = compile ps_3_0 PS();}}
fl
댓글
댓글 쓰기