Hay*_*ach 8 c++ opengl glsl quaternions normals
受crytek关于使用四元数在四元数中为较小顶点存储切线空间的介绍的启发,我得出了一个合乎逻辑的结论:如果可以使用四元数来存储切线空间,那么您还可以在顶点之间触发四元数并使用它们直接旋转法线.这将消除重新正交化切线空间矢量或重建其中一个的需要,并且它将切出每个片段的矩阵 - 矢量乘法,用单个四元数向量乘法替换它.
我试图在我的OpenGL应用程序中使用我自制的四元数类实现它,我遇到了一些问题.我知道我的四元数可以用矩阵构造,将四元数乘以向量,得到与矩阵乘以向量相同的结果 - 我已经在cpu方面成功完成了.然而,一旦我开始在GLSL中使用它们,一切都会变得混乱.
值得注意的是,事实上我可以辨别出法线贴图的模式,所以我觉得我走在了正确的轨道上.不幸的是,似乎我的颜色变得混乱.
这是我在glsl中使用的四元数数学:
vec4 multQuat(vec4 q1, vec4 q2)
{
return vec4(
(q1.w * q2.y) + (q1.y * q2.w) + (q1.x * q2.z) - (q1.z * q2.x),
(q1.w * q2.z) + (q1.z * q2.w) + (q1.y * q2.x) - (q1.x * q2.y),
(q1.w * q2.w) - (q1.x * q2.x) - (q1.y * q2.y) - (q1.z * q2.z),
(q1.w * q2.x) + (q1.x * q2.w) + (q1.z * q2.y) - (q1.y * q2.z)
);
}
vec3 rotateVector(vec4 quat, vec3 vec)
{
return vec + 2.0 * cross(quat.xyz, cross(quat.xyz, vec) + (quat.w * vec));
}
Run Code Online (Sandbox Code Playgroud)
这是它从顶点着色器传递的方式:
vQtangent = multQuat(inQtangent, quatView);
Run Code Online (Sandbox Code Playgroud)
其中quatView是由视图矩阵构成的四元数.这可能是我的问题,因为生成此四元数的代码假定矩阵是正交的.
最后,我们计算片段着色器中的凸起法线:
vec3 calcBumpedNormal(void)
{
vec4 qtangent = normalize(vQtangent);
vec3 normal = texture2D(texNormal, vTexCoord).xyz;
normal = (normal * 2) - 1;
return normalize(rotateVector(qtangent, normal));
};
Run Code Online (Sandbox Code Playgroud)
这是我如何计算3 vec3的四元数(如何从tbn向量得到四元数):
inline static quat fromMat3(const vec3& col0, const vec3& col1, const vec3& col2)
{
/* warning - this only works when the matrix is orthogonal and special orthogonal */
float w = sqrtf(1.0f + col0.x + col1.y + col2.z) / 2.0f;
return quat(
(col1.z - col2.y) / (4.0f * w),
(col2.x - col0.z) / (4.0f * w),
(col0.y - col1.x) / (4.0f * w),
w);
}
Run Code Online (Sandbox Code Playgroud)
这是我如何从mat4中计算四元数(我如何从视图matix获取quatView):
inline static quat fromMat4(const mat4& mat)
{
/* warning - this only works when the matrix is orthogonal and special orthogonal */
float w = sqrtf(1.0f + mat.m[0][0] + mat.m[1][1] + mat.m[2][2]) / 2.0f;
return quat(
(mat.m[1][2] - mat.m[2][1]) / (4.0f * w),
(mat.m[2][0] - mat.m[0][2]) / (4.0f * w),
(mat.m[0][1] - mat.m[1][0]) / (4.0f * w),
w);
}
Run Code Online (Sandbox Code Playgroud)
我知道它既不适用于非正交矩阵.
但是,只有法线的x和y存储在普通缓冲区中,我使用sqrt技巧在光通道片段着色器中重建z.因为这些法线意味着在视图空间中,所以z分量始终是正的.
不幸的是,我的结果不正确,我不知道在哪里看.我可以辨别法线贴图的格局,所以东西必须是正确的.
如果有人让我知道我的问题可能在哪里,或者他们自己有这方面的经验,我们非常感谢任何建议.
如果您仅在顶点着色器中使用每顶点四元数(通过将光线和相机矢量转换为切向空间),您的代码是否可以正常工作?如果它仅在您尝试在像素着色器中旋转法线时才中断,那么您的问题是四元数插值(如果不是,那么我只是浪费了 20 分钟)。
四元数与所选旋手性的正交矩阵不存在 1:1 关系(我假设您的旋手性很好,但您应该验证这一点)。如果将每个四元数分量乘以-1,您将得到相同的变换。
现在,您fromMat3总是会产生一个具有正分量的四元数W。(0.99,0,0,0.1)想象一下插值如何沿着和 之间的边进行(-0.99,0,0,0.1)。该X组件将沿着其轴一路移动,从而给您带来各种着色问题。
您必须确保任何四元数插值(QI)发生在属于同一半球的四元数之间,即dot(q1,q2) > 0。很容易看出对于我提到的示例四元数,此检查如何失败,以及如果将第二个四元数乘以 ,它如何正常工作-1。
棘手的部分是,确保 QI 正确性可能需要分割边并添加新顶点,因此最好在导出器端完成,而不是在模型加载期间完成。查看KRI 网格导出器代码以供参考。
出于实际原因我不建议你去那里,除非你非常执着。相反,您可以愉快地在顶点着色器中使用四元数。如果你有一本 GPU Pro 3 书,你可以在那里找到我关于四元数的文章,详细解释了同样的问题(和解决方案)。