2

I have a Sphere structure that looks like this

struct Sphere {
    vec3 _center;
    float _radius;
};

How do I apply a 4x4 transformation matrix to that sphere? The matrix may contain a scale factor, a rotation (which will obviously will not affect the sphere) and a translation.

The current approach I'm using contains three length() methods (that have sqrt() in them) which are pretty slow.

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);
}

I wonder if anyone knows of a more efficient way to do this?

legends2k
  • 31,634
  • 25
  • 118
  • 222
McLovin
  • 3,295
  • 7
  • 32
  • 67
  • "I wonder if anyone knows of a more efficient way to do this?" http://codereview.stackexchange.com/ will help – gabbar0x Dec 11 '15 at 09:46
  • What do you mean by "non-uniform"? Why is transforming the radius with the largest scale isn't right? – McLovin Dec 11 '15 at 09:59
  • See the answer I've given. – legends2k Dec 11 '15 at 10:12
  • Why not just store the sphere as a tranformation matrix (assume centre = 0,0,0, make original transform out of translation + scale by radius), and multiply by the new tranformation matrix in applyTranformation? – Zepee Dec 11 '15 at 10:31

1 Answers1

4

This is a question about efficiency and specifically to avoid doing the square root. One idea would be to defer doing the square root until the last moment. Since length and length squared are increasing functions starting at 0, comparing length squared is the same as comparing length. So you could avoid the three calls to length and make it one.

#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);
}

Aside: A non-uniform scale means the scaling ratios along the different axes aren't the same. E.g. S1, 2, 4 is non-uniform while S2, 2, 2 is uniform. See this intuitive primer on transformations to understand them better; it has animations to demonstrate such differences.

Can the scale be non-uniform too? From the code it looks like it could. Transforming the radius with the largest scale isn't right. If you'd a non-uniform scale, the sphere would actually become an ellipsoid and hence just scaling the radius isn't correct. You'd have to transform the sphere into an ellipsoid with semi-principle axes of differing lengths.

legends2k
  • 31,634
  • 25
  • 118
  • 222
  • The scale can indeed be non-uniform. The thing is I don't want to have an ellipsoid, but rather have a bit inaccurate sphere instead. Thanks for the solution by the way! – McLovin Dec 11 '15 at 11:14