[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
  • 공지사항

  • 인기 글

  • 태그

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

  • 최근 글

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

티스토리툴바