Próbuję zaimplementować BRDF mikrofacet w moim raytracer, ale mam problemy. Wiele artykułów i artykułów, które przeczytałem, definiuje pojęcie geometrii częściowej jako funkcję widoku i pół wektorów: G1 (v, h). Jednak podczas wdrażania tego otrzymałem następujący wynik:
(Dolny rząd jest dielektryczny z chropowatością 1,0 - 0,0, Górny rząd jest metaliczny z chropowatością 1,0 - 0,0)
Na brzegach jest dziwne światło i granica wokół nl == 0. Naprawdę nie wiedziałam, skąd to się bierze. Używam Unity jako odniesienia do sprawdzenia moich renderów, więc sprawdziłem ich źródło modułu cieniującego, aby zobaczyć, czego używają i z tego, co mogę powiedzieć, ich geometria nie jest w ogóle sparametryzowana przez wektor pół! Wypróbowałem więc ten sam kod, ale użyłem makro do normalnej powierzchni zamiast pół wektora i otrzymałem następujący wynik:
Moim niewprawnym oku wydaje się to znacznie bliższe pożądanego rezultatu. Ale mam wrażenie, że to nie jest poprawne? Większość artykułów, które czytam, wykorzystuje wektor połówkowy, ale nie wszystkie. Czy jest powód tej różnicy?
Używam następującego kodu jako terminu geometrycznego:
float RayTracer::GeometryGGX(const Vector3& v, const Vector3& l, const Vector3& n, const Vector3& h, float a)
{
return G1GGX(v, h, a) * G1GGX(l, h, a);
}
float RayTracer::G1GGX(const Vector3& v, const Vector3& h, float a)
{
float NoV = Util::Clamp01(cml::dot(v, h));
float a2 = a * a;
return (2.0f * NoV) / std::max(NoV + sqrt(a2 + (1.0f - a2) * NoV * NoV), 1e-7f);
}
Dla porównania jest to moja normalna funkcja dystrybucji:
float RayTracer::DistributionGGX(const Vector3& n, const Vector3& h, float alpha)
{
float alpha2 = alpha * alpha;
float NoH = Util::Clamp01(cml::dot(n, h));
float denom = (NoH * NoH * (alpha2 - 1.0f)) + 1.0f;
return alpha2 / std::max((float)PI * denom * denom, 1e-7f);
}