将矩阵变换应用于球体

Apply matrix transformation to a sphere

我有一个如下所示的 Sphere 结构

struct Sphere {
    vec3 _center;
    float _radius;
};

如何将 4x4 变换矩阵应用于该球体?矩阵可能包含比例因子、旋转(显然不会影响球体)和平移。

我目前使用的方法包含三个 length() 方法(其中包含 sqrt()),它们非常慢。

glm::vec3 extractTranslation(const glm::mat4 &m)
{
    glm::vec3 translation;  
    // Extract the translation

    translation.x = m[3][0];
    translation.y = m[3][1];
    translation.z = m[3][2];

    return translation;
}

glm::vec3 extractScale(const glm::mat4 &m) //should work only if matrix is calculated as M = T * R * S
{
    glm::vec3 scale;

    scale.x = glm::length( glm::vec3(m[0][0], m[0][1], m[0][2]) );
    scale.y = glm::length( glm::vec3(m[1][0], m[1][1], m[1][2]) );
    scale.z = glm::length( glm::vec3(m[2][0], m[2][1], m[2][2]) );

    return scale;
}

float extractLargestScale(const glm::mat4 &m)
{
    glm::vec3 scale = extractScale(m);

    return glm::max(scale.x, glm::max(scale.y, scale.z));
}

void Sphere::applyTransformation(const glm::mat4 &transformation)
{
    glm::vec4 center = transformation * glm::vec4(_center, 1.0f);
    float largestScale = extractLargestScale(transformation);

    set(glm::vec3(center)/* / center.w */, _radius * largestScale);
}

不知道有没有人知道更有效的方法?

这是一个关于效率的问题,特别是为了避免求平方根。一个想法是推迟到最后一刻才计算平方根。由于长度和长度平方是从 0 开始的递增函数,因此比较长度平方与比较长度相同。因此,您可以避免对 length 的三个调用并将其设为一个。

#include <glm/gtx/norm.hpp>
#include <algorithm>

glm::vec3 extractScale(const glm::mat4 &m)
{
    // length2 returns length squared i.e. v·v
    // no square root involved
    return glm::vec3(glm::length2( glm::vec3(m[0]) ),
                     glm::length2( glm::vec3(m[1]) ),
                     glm::length2( glm::vec3(m[2]) ));
}

void Sphere::applyTransformation(const glm::mat4 &transformation)
{
    glm::vec4 center = transformation * glm::vec4(_center, 1.0f);
    glm::vec3 scalesSq = extractScale(transformation);
    float const maxScaleSq = std::max_element(&scalesSq[0], &scalesSq[0] + scalesSq.length());  // length gives the dimension here i.e. 3
    // one sqrt when you know the largest of the three
    float const largestScale = std::sqrt(maxScaleSq);

    set(glm::vec3(center), _radius * largestScale);
}

旁白: 非均匀比例意味着沿不同轴的比例不相同。例如。 S1, 2, 4 是非均匀的,而 S2, 2, 2 是均匀的。请参阅 this intuitive primer on transformations 以更好地理解它们;它有动画来展示这些差异。

比例也可以不均匀吗?从代码看起来它可以。用最大比例变换半径是不对的。如果你有一个不均匀的比例,球体实际上会变成一个椭圆体,因此只缩放半径是不正确的。您必须将球体转换为具有不同长度的半原理轴的 ellipsoid