GAMES202->assignment4 Kulla-Conty BRDF
项目地址:here
PBR
初始状态
float DistributionGGX(vec3 N, vec3 H, float roughness)
{
// TODO: To calculate GGX NDF here
return 1.0;
}
float GeometrySchlickGGX(float NdotV, float roughness)
{
// TODO: To calculate Smith G1 here
return 1.0;
}
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
{
// TODO: To calculate Smith G here
return 1.0;
}
vec3 fresnelSchlick(vec3 F0, vec3 V, vec3 H)
{
// TODO: To calculate Schlick F here
return vec3(1.0);
}
补充公式
float DistributionGGX(vec3 N, vec3 H, float roughness)
{
float a = roughness*roughness;
float a2 = a*a;
float NdotH = max(dot(N, H), 0.0);
float NdotH2 = NdotH*NdotH;
float nom = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;
return nom / max(denom, 0.0001);
}
float GeometrySchlickGGX(float NdotV, float roughness) {
float a = roughness;
float k = (a * a) / 2.0;
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
{
// TODO: To calculate Smith G here
float NdotL = max(dot(N,L),0.0);
float NdotV = max(dot(N,V),0.0);
return GeometrySchlickGGX(NdotL, roughness)*GeometrySchlickGGX(NdotV, roughness);
}
float Pow5(float x)
{
return x*x*x*x*x;
}
vec3 fresnelSchlick(vec3 F0, vec3 V, vec3 H)
{
// TODO: To calculate Schlick F here
float cosA = max(dot(V,H),0.0);
float t = Pow5(1.0 - cosA);
return F0 + (vec3(1.0)-F0) * t;
}
roughness=0.35
roughness=0.55
roughness=0.95
Kulla-Conty
预计算E(μ)
蒙特卡洛方法
Vec3f IntegrateBRDF(Vec3f V, float roughness, float NdotV) {
float A = 0.0;
float B = 0.0;
float C = 0.0;
const int sample_count = 1024;
Vec3f N = Vec3f(0.0, 0.0, 1.0);
float R0 = 1.0f;
samplePoints sampleList = squareToCosineHemisphere(sample_count);
for (int i = 0; i < sample_count; i++) {
// TODO: To calculate (fr * ni) / p_o here
Vec3f L = normalize(sampleList.directions[i]);
Vec3f H = normalize(V + L);
float cosA = std::max(0.0f,dot(V,H));
float NdotL = std::max(dot(N, L), 0.0f);
float F = R0 + (1.0f-R0)*pow(1- cosA,5.0f);
float G = GeometrySmith(roughness, NdotV, NdotL);
float D = DistributionGGX(N,H,roughness);
float numerator = D * G * F;
float denominator = 4.0f * NdotV * NdotL;
float Fmicro = numerator / std::max(denominator, 1e-7f);
float pdf = sampleList.PDFs[i];
A += Fmicro * NdotL / pdf;
}
B = C = A;
return {A / sample_count, B / sample_count, C / sample_count};
}
重要性采样
Vec3f ImportanceSampleGGX(Vec2f Xi, Vec3f N, float roughness) {
float a = roughness * roughness;
//TODO: in spherical space - Bonus 1
float Phi = 2 * PI * Xi.x;
//TODO: from spherical space to cartesian space - Bonus 1
float CosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y));
float SinTheta = sqrt(1.0 - CosTheta * CosTheta);
Vec3f H;
H.x = SinTheta * cos(Phi);
H.y = SinTheta * sin(Phi);
H.z = CosTheta;
//TODO: tangent coordinates - Bonus 1
Vec3f UpVector = abs(N.z) < 0.999 ? Vec3f(0.0f, 0.0f, 1.0f) : Vec3f(1.0f, 0.0f, 0.0f);
Vec3f TangentX = normalize(cross(UpVector, N));
Vec3f TangentY = cross(N, TangentX);
//TODO: transform H to tangent space - Bonus 1
Vec3f result = TangentX * H.x + TangentY * H.y + N * H.z;
return normalize(result);
}
float GeometrySchlickGGX(float NdotV, float roughness) {
// TODO: To calculate Schlick G1 here - Bonus 1
float a = roughness;
float k = (a * a) / 2.0f;
float nom = NdotV;
float denom = NdotV * (1.0f - k) + k;
return nom / denom;
}
Vec3f IntegrateBRDF(Vec3f V, float roughness) {
float A = 0.0;
float B = 0.0;
float C = 0.0;
float R0 = 1.0f;
const int sample_count = 1024;
Vec3f N = Vec3f(0.0, 0.0, 1.0);
for (int i = 0; i < sample_count; i++) {
Vec2f Xi = Hammersley(i, sample_count);
Vec3f H = ImportanceSampleGGX(Xi, N, roughness);
Vec3f L = normalize(H * 2.0f * dot(V, H) - V);
float NoL = std::max(L.z, 0.0f);
float NoH = std::max(H.z, 0.0f);
float VoH = std::max(dot(V, H), 0.0f);
float NoV = std::max(dot(N, V), 0.0f);
float cosA = VoH;
float F = R0 + (1.0f - R0)*pow(1 - cosA, 5.0f);
// TODO: To calculate (fr * ni) / p_o here - Bonus 1
A += F * VoH * GeometrySmith(roughness,NoV, NoL) / (NoV*NoH);
}
B = C = A;
return { A / sample_count, B / sample_count, C / sample_count };
}
1-Eμ检验
Vec3f IntegrateBRDF(Vec3f V, float roughness) {
float A = 0.0;
float B = 0.0;
float C = 0.0;
float R0 = 1.0f;
const int sample_count = 1024;
Vec3f N = Vec3f(0.0, 0.0, 1.0);
for (int i = 0; i < sample_count; i++) {
//... some code
// TODO: To calculate (fr * ni) / p_o here - Bonus 1
A += F * VoH * GeometrySmith(roughness,NoV, NoL) / (NoV*NoH);
}
B = C = A = 1 - A;
return { A / sample_count, B / sample_count, C / sample_count };
}
预计算Eavg
蒙特卡洛方法
Vec3f IntegrateEmu(Vec3f V, float roughness, float NdotV, Vec3f Ei) {
Vec3f Eavg = Vec3f(0.0f);
const int sample_count = 1024;
Vec3f N = Vec3f(0.0, 0.0, 1.0);
samplePoints sampleList = squareToCosineHemisphere(sample_count);
for (int i = 0; i < sample_count; i++) {
Vec3f L = sampleList.directions[i];
Vec3f H = normalize(V + L);
float NoL = std::max(L.z, 0.0f);
float NoH = std::max(H.z, 0.0f);
float VoH = std::max(dot(V, H), 0.0f);
float NoV = std::max(dot(N, V), 0.0f);
// TODO: To calculate Eavg here
Eavg += Ei * NoV*2.0f;
}
return Eavg / sample_count;
}
重要性采样
实时计算
vec3 MultiScatterBRDF(float NdotL, float NdotV)
{
vec3 albedo = pow(texture2D(uAlbedoMap, vTextureCoord).rgb, vec3(2.2));
vec3 E_o = texture2D(uBRDFLut, vec2(NdotL, uRoughness)).xyz;
vec3 E_i = texture2D(uBRDFLut, vec2(NdotV, uRoughness)).xyz;
vec3 E_avg = texture2D(uEavgLut, vec2(0, uRoughness)).xyz;
// copper
vec3 edgetint = vec3(0.827, 0.792, 0.678);
vec3 F_avg = AverageFresnel(albedo, edgetint);
// TODO: To calculate fms and missing energy here
vec3 fms = (vec3(1.0)-E_o)*(vec3(1.0)-E_i)/(PI*(vec3(1.0)-E_avg));
vec3 F_add = F_avg*E_avg/(vec3(1.0)-F_avg*(vec3(1.0)-E_avg));
return F_add*fms;
}