[DirectX 11] IBL (Image Based Lighting)

2022. 1. 23. 22:39·Graphics

Image Based Lighting

 

Diffuse Irradiance

- 직접적인 분석 조명이 아닌 주변 환경을 하나의 큰 광원으로 간주해 조명하는 방식이다.

- 일반적으로 조명 방정식에서 직접 사용할 수 있는 환경맵을 조작해 조명 방정식에서 직접 사용할 수 있도록 수행된다.

- 환경의 조명을 고려할 때 객체가 훨씬 물리적으로 정확해 보이기 때문에 PBR에 IBL이 유용하다.

- 주변 환경으로부터 들어오는 빛의 방향에 따라 약간의 광도가 생겨 요구사항이 생긴다.

  1) 임의의 방향 벡터가 주어진 경우 장면의 밝기를 검색 할 수 있는 방법이 필요하다. 이를 위해 환경이나 장면의 방사 조도를

       표현하는 방법은 큐브맵 형태이다. 이러한 큐브맵을 감안할 때 큐브 맵의 모든 텍셀을 하나의 단일 광원으로 시각화하여 임의의

       방향 벡터로 샘플링 함으로써 그 방향에서 장면의 광도를 검색한다.

  2) 통합을 해결하는 것은 빠르고 실시간이어야 한다. 이를 위해 모든 샘플 방향에 대해 적분 결과를 저장하는 사전 계산된 큐브 맵을

       만들어 두는데, 이 큐브 맵은 복잡한 큐브 맵이 효과적으로 어떤 방향에서든 장면의 방사량을 직접 샘플링 할 수 있도록 보는

       조사 맵을 나타낸다.

 

Environment Map / Irradiance Map

 

float4 IBL_Convolution_PS(PixelIn pin) : SV_TARGET
{
	float3 normal = normalize(pin.PosL);

	float3 irradiance = float3(0.0f, 0.0f, 0.0f);

	float3 up = float3(0.0f, 1.0f, 0.0f);
	float3 right = cross(up, normal);
	up = cross(normal, right);

	float sampleDelta = 0.025f;
	float nrSamples = 0.0f;

	for (float phi = 0.0f; phi < 2.0 * PI; phi += sampleDelta)
	{
		for (float theta = 0.0f; theta < 0.5 * PI; theta += sampleDelta)
		{
			float3 tangentSample = float3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));
			float3 sampleVec = (tangentSample.x * right) + (tangentSample.y * up) + (tangentSample.z * normal);

			irradiance += gSkyCube.Sample(gSamWrapLinear, sampleVec).rgb * cos(theta) * sin(theta);
			nrSamples++;
		}
	}
	
	irradiance = PI * irradiance * (1 / nrSamples);

	return float4(irradiance, 1.0f);
}

- 조사 맵 ( Irradiance Map )을 구하는 Shader Code.

 

Specular Image Based Lighting

 

- 모든 가능한 관찰자의 방향을 포함해 모든 들어오는 빛의 방향에 대한 적분을 풀려고 하는것은 실시간으로 계산하기에는 너무

  비싸다. 그래서 Epic Games에서 고안한 방법으로 Specular 적분을 두 개의 개별 적분으로 나누어 사전 필터링 환경 맵으로

  만들어 두는 것이다.

- 거칠기를 고려해야 하기에 Convolute 각 조도 수준에 대해, 사전 필터링된 환경 맵 밉맵 수준에서 순차적으로 Blur 결과를

   저장한다.

 

재질에 따른 Prefilter Map

float4 IBL_PrefilterMap_PS(PixelIn pin) : SV_TARGET
{
	float3 normalVec = normalize(pin.PosL);
	float3 R = normalVec;
	float3 viewDir = R;
	float roughness2 = gRoughness * gRoughness;
	
	float3 PrefilteredColor = float3(0.0f, 0.0f, 0.0f);
	float totalWeight = 0.0f;
	
	const uint NumSamples = 1024;
	for (uint i = 0; i < NumSamples; i++)
	{
		float2 Xi = Hammersley(i, NumSamples);
		float3 halfwayVec = ImportanceSampleGGX(Xi, roughness2, normalVec);
		float3 lightDir = 2.0f * dot(viewDir, halfwayVec) * halfwayVec - viewDir;
		float NdotL = saturate(dot(normalVec, lightDir));
		
		if (NdotL > 0)
		{
			float D = NormalDistributionGGXTR(normalVec, halfwayVec, roughness2);
			float NdotH = max(dot(normalVec, halfwayVec), 0.0f);
			float HdotV = max(dot(halfwayVec, viewDir), 0.0f);
			float pdf = D * NdotH / (4.0f * HdotV) + 0.0001f;

			float resolution = 512.0f;
			float saTexel = 4.0f * PI / (6.0f * resolution * resolution);
			float saSample = 1.0f / (float(NumSamples) * pdf + 0.0001f);

			float mipLevel = gRoughness == 0.0 ? 0.0 : 0.5 * log2(saSample / saTexel);

			PrefilteredColor += gSkyCube.SampleLevel(gSamWrapLinear, lightDir, 0).rgb * NdotL;
			totalWeight += NdotL;
		}
	}
	PrefilteredColor /= totalWeight;

	return float4(PrefilteredColor, 1.0f);
}

- 재질에 따른 사전 필터링 환경맵 밉맵 ( Prefilter Map) Shader Code.

 

BRDF 2D Look Up Texture ( LUT )

 

- Epic Games는 BRDF 통합 맵이라고 하는 2D Look Up Texture의 다양한 조도 값에 대한 각 정상 및 조명 방향 조합에 대한 사전

  계산된 BRDF의 응답을 저장한다. 2D Look Up Texture는 표면의 Fresnel 응답에 스케일과 바이어스 값을 출력해 분할된

  Specular 적분의 두 번째 부분을 제공한다.

 

BRDF 2D Look Up Texture ( LUT )

float2 IBL_IntegrateBRDF_PS(PixelIn pin) : SV_TARGET
{
    float NdotV = pin.Tex.y;
    float roughness = 1.0f - pin.Tex.x;
    float roughness2 = roughness * roughness;
	
	float3 viewDir;
	viewDir.x = sqrt(1.0f - (NdotV * NdotV)); // sin
	viewDir.y = 0;
	viewDir.z = NdotV; // cos

	float A = 0.0f;
	float B = 0.0f;

	float3 normalVec = float3(0.0f, 0.0f, 1.0f);

	const uint numSamples = 1024;

	for (uint i = 0; i < numSamples; i++)
	{
		float2 Xi = Hammersley(i, numSamples);
		float3 halfwayVec = ImportanceSampleGGX(Xi, roughness2, normalVec);
		float3 lightDir = normalize((2.0f * dot(viewDir, halfwayVec) * halfwayVec) - viewDir);

		float NdotL = saturate(lightDir.z);
		float NdotH = saturate(halfwayVec.z);
		float VdotH = saturate(dot(viewDir, halfwayVec));

		if (NdotL > 0)
		{
			float G = IBLGeometrySmith(normalVec, viewDir, lightDir, roughness2);
			float G_Vis = (G * VdotH) / (NdotH * NdotV);
			float Fc = pow(1.0f - VdotH, 5.0f);

			A += (1.0f - Fc) * G_Vis;
			B += Fc * G_Vis;
		}
	}
	
    return float2(A, B) / numSamples;
}

- BRDF 2D Look Up Texture ( LUT )를 구하는 Shader Code.

 

Image Based Lighting 연산

 

- 위의 세가지 사전 작업이 끝난 후 반사 벡터를 사용해 필터링된 환경 맵을 샘플링해 표면의 간접 반사를 얻은 후 표면 거칠기를

   기준으로 적절한 밉 레벨을 샘플링해 거친 표면에 거울 반사를 흐리게 처리한다.

 

#define MAX_REF_LOD 4.0

float3 FresnelSchlickRoughness(float NdotV, float3 F0, float roughness)
{
    float roughnessPercent = 1.0f - roughness;
    return F0 + (max(float3(roughnessPercent, roughnessPercent, roughnessPercent), F0) - F0) * pow(1.0 - NdotV, 5.0f);
}

float3 IBL_EnvironmentLight(in float3 V, in float3 N, in float3 irradiance, in float3 prefilterColor, in float2 brdf, in float3 albedo, in float ao, in float roughness, in float metallic, in float shadow)
{
    float3 F0 = lerp(F_ZERO, albedo, metallic);
    
    float3 kS = FresnelSchlickRoughness(max(dot(N, V), 0.0f), F0, roughness);
    float3 kD = float3(1.0f, 1.0f, 1.0f) - kS;
    kD *= 1.0 - metallic;
    
    float3 diffuse = albedo * irradiance * kD;
    
    float3 specular = prefilterColor * (kS * brdf.x + brdf.y);
    
    return (diffuse + specular) * ao;
}

float4 Light_IBL_PS(PixelIn pin) : SV_TARGET
{

    ...
    
    float3 irradiance = gIBLIrradiance.Sample(gSamWrapLinear, normal).rgb;
    float3 prefilteredColor = gIBLPrefilter.SampleLevel(gSamWrapLinear, reflect(-ViewDirection, normal), roughness * MAX_REF_LOD).rgb;
    float2 brdf = gBRDFlut.Sample(gSamWrapLinear, float2(max(dot(normal, ViewDirection), 0.0f), roughness)).rg;
    
    litColor += IBL_EnvironmentLight(ViewDirection, normal, irradiance, prefilteredColor, brdf, 
                                        albedo, ao, roughness, metallic, shadows);
    
    ...

    return float4(litColor, 1.0f);
}

- 사전 작업이 끝난 환경맵을 기준으로 Image Based Lighting Shader Code.

 

 

적용 영상

Image Based Lighting 적용 영상

 

저작자표시 (새창열림)

'Graphics' 카테고리의 다른 글

[DirectX 11] Lambert / Half Lambert  (0) 2022.02.07
[DirectX 11] Tone Mapping  (0) 2022.02.03
[DirectX 11] PBR (Physically Based Rendering)  (0) 2022.01.21
[DirectX 11] Deferred Rendering FXAA  (0) 2022.01.16
[DirectX 11] Order Independent Transparency  (0) 2022.01.07
'Graphics' 카테고리의 다른 글
  • [DirectX 11] Lambert / Half Lambert
  • [DirectX 11] Tone Mapping
  • [DirectX 11] PBR (Physically Based Rendering)
  • [DirectX 11] Deferred Rendering FXAA
KyuHwang
KyuHwang
  • KyuHwang
    DirectX Engine
    KyuHwang
  • 전체
    오늘
    어제
    • 분류 전체보기 (50)
      • C++ (4)
      • CS (0)
      • Graphics (32)
      • DLL (2)
      • Shader (7)
      • Project (4)
      • ETC (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • YouTube
  • 공지사항

  • 인기 글

  • 태그

    DirectX 2D
    Project
    animation retargeting
    bc format
    animation constraint
    hlsl
    Define Macro
    Explicit Linking
    C ++
    nvtt
    Shader Macro
    Hash Code
    Alpha Blending
    dll
    RunTime Compile Shader
    std::enable_if
    std::is_base_of
    Implicit Linking
    Shader Resource 관리
    Return Type Operator
    Shader Reflection
    DLL Interface
    rigging chain
    Define Function
    shader
    Directx11
    Win API
    mobile format
    texture block compression
    Order Independent Transparency
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
KyuHwang
[DirectX 11] IBL (Image Based Lighting)
상단으로

티스토리툴바