(下排是电介质,粗糙度为1.0-0.0,上排是金属,粗糙度为1.0-0.0)
在边缘周围有一个怪异的高光,在nl == 0处有一个截止点。我使用Unity作为参考来检查我的渲染,因此我检查了它们的着色器源,以查看它们的用途,并从中可以看出它们的几何项根本没有被半向量参数化!因此,我尝试了相同的代码,但使用了宏观表面法线而不是半向量,并得到以下结果:
对我而言,未经训练的眼睛似乎更接近于预期的结果。但是我有感觉这是不正确的?我阅读的大多数文章都使用半向量,但不是全部。
我有以下原因吗?
我将以下代码用作我的几何术语:
正态分布函数:
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);
}
#1 楼
TL; DR:您的$ G1 $公式错误。为了避免混淆,我假设是BRDF的各向同性版本,Smith微面模型(与V腔模型相对)和GGX微面分布。
根据Heitz 2014,掩蔽/阴影术语$ G1 $是
$$
\ chi ^ {+} \ left(\ omega_ {v} \ cdot \ omega_ {m} \ right)
\ frac {2} {1+ \ sqrt {1+ \ alpha_ {o} ^ {2} \ tan ^ {2} \ theta_ {v}}}
$$
,根据Walter 2007,公式为
$$
\ chi ^ {+} \ left(\ frac {\ omega_ {v } \ cdot \ omega_ {g}} {\ omega_ {v} \ cdot \ omega_ {m}} \ right)\ frac {2} {1+ \ sqrt {1+ \ alpha ^ {2} \ tan ^ {2 } \ theta_ {v}}}
$$
其中$ \ omega_ {m} $是微面法线方向(半矢量),$ \ omega_ {g} $是主要(几何)法线方向(法线),$ \ omega_ {v} $是传入或传出方向,$ \ alpha $是各向同性粗糙度参数,而$ \ chi ^ {+} \ left(a \ right)$是正特征函数或Heaviside阶跃函数(如果$ a> 0 $等于1,否则等于0)。
您会注意到,如果几何构型被禁止,则半矢量$ \ omega_ {m} $仅用于确保$ G1 $为零。更准确地说,它可以确保从宏观表面的$ omega_ {v} $方向上看不到微观表面的背面,反之亦然(后一种情况仅在还支持折射时才有意义)。如果调用代码保证了这一点,那么您显然可以忽略此参数。另一方面,这可能就是他们在Unity中这样做的原因。
另一方面,您的实现使用半向量来计算$ \ omega_ {v} $方向相对于microfacet,这会导致计算除所提供公式之外的其他内容。
如果有帮助,那么这是我对$ G1 $因子的实现:
float SmithMaskingFunctionGgx(
const Vec3f &aDir, // the direction to compute masking for (either incoming or outgoing)
const Vec3f &aMicrofacetNormal,
const float aRoughnessAlpha)
{
PG3_ASSERT_VEC3F_NORMALIZED(aDir);
PG3_ASSERT_VEC3F_NORMALIZED(aMicrofacetNormal);
PG3_ASSERT_FLOAT_NONNEGATIVE(aRoughnessAlpha);
if (aMicrofacetNormal.z <= 0)
return 0.0f;
const float cosThetaVM = Dot(aDir, aMicrofacetNormal);
if ((aDir.z * cosThetaVM) < 0.0f)
return 0.0f; // up direction is below microfacet or vice versa
const float roughnessAlphaSqr = aRoughnessAlpha * aRoughnessAlpha;
const float tanThetaSqr = Geom::TanThetaSqr(aDir);
const float root = std::sqrt(1.0f + roughnessAlphaSqr * tanThetaSqr);
const float result = 2.0f / (1.0f + root);
PG3_ASSERT_FLOAT_IN_RANGE(result, 0.0f, 1.0f);
return result;
}
评论
$ \ begingroup $
感谢您的答复。我已经实现了您提供的公式,并且得到的结果与我自己的公式相同(使用宏观曲面法线时)。因此,似乎这只是一种不同的形式(我来自:graphicrants.blogspot.nl/2013/08/specular-brdf-reference.html)我对半向量感到困惑,因为SIGGRAPH 2015 PBS数学课程专门说明了几何功能取决于视图,光和半向量。所以这是幻灯片中的错误?
$ \ endgroup $
–欧文
16年5月29日在8:26
$ \ begingroup $
@Erwin,现在您还提供了公式本身,因此更加清楚。下次在开始时做对它有帮助。是的,两个版本(mine和您的版本)都是等效的,但是它们都不使用中途向量来计算正弦或切线函数。它使用$ n \ cdot v $而不是您在实现中使用的$ h \ cdot v $-这似乎是错误的。我怀疑您在新实现中也犯了同样的错误。
$ \ endgroup $
– ivokabel
16年5月29日在10:01
$ \ begingroup $
我在新实现中确实使用了N点V,这与我发布的第二张图片的结果相同。但是我仍然不清楚为什么PBS课程幻灯片会指出应该使用中途向量(请参阅:blog.selfshadow.com/publications/s2015-shading-course/hoffman/…,幻灯片88)。
$ \ endgroup $
–欧文
16年5月30日在15:24
$ \ begingroup $
我是否正确理解使用$ h \ cdot v $而不是$ n \ cdot v $是问题所在?关于在$ G_1 $中使用中途向量:实际上,我在发布的两个版本中都使用了中途向量(构造LaTeX公式时我犯了一个错误,并在第一个中写入了几何法线,我会尽快修复),但要点是,中途向量不用于计算余弦值(即不使用$ h \ cdot v $)。
$ \ endgroup $
– ivokabel
16年5月30日在21:28
$ \ begingroup $
是的,这就是问题所在。但是我的主要问题是:半向量用于什么,因为它出现在函数定义中。据我所知,它仅用于检查H点V是否为正。感谢您抽出宝贵的时间写答案。
$ \ endgroup $
–欧文
16年6月6日在8:00