首页 > 解决方案 > 将 vec3 乘以模型矩阵的问题(缩放问题)

问题描述

在我的 3D 应用程序中,我有一个 Object3D 类,它在创建时构造为边界框,这个边界框是一个简单的长方体,带有一个最小值和一个最大值:简单的长方体边界框

但是,当我翻译我的 Object3D 时,我想使用我的对象的模型矩阵来更新我的边界框:

     BoundingBox& TransformBy(const glm::mat4& modelMatrice){
        //extract position of my model matrix
        glm::vec3 pos( modelMatrice[3] );
        
        //extract scale of my model matrix
        glm::vec3 scale( glm::length(glm::vec3(modelMatrice[0])), glm::length(glm::vec3(modelMatrice[1])) , glm::length(glm::vec3(modelMatrice[2])));
        
        //Creating vec4 min/max from vec3
        glm::vec4 min_(min.x, min.y, min.z, 1.0f);
        glm::vec4 max_(max.x, max.y, max.z, 1.0f);
        
        //Inverting the matrice to multiply my vec4 with it in order to rotate my min max
        glm::mat4 inverse = glm::inverse(modelMatrice);
        min_ = min_ * inverse;
        max_ = max_ * inverse;
        
        //Scale my min_ max_
        min_ *= glm::vec4(scale,1.0f);
        max_ *= glm::vec4(scale,1.0f);
        
        //adding model matrice translation to min_ and max_
        min_ += glm::vec4(pos,0.0f);
        max_ += glm::vec4(pos,0.0f);
    
        //Redefining max and min
        max = glm::vec3( (min_.x > max_.x)? min_.x:max_.x, (min_.y > max_.y)? min_.y: max_.y, (min_.z > max_.z)? min_.z : max_.z);
        min = glm::vec3( (min_.x < max_.x)? min_.x:max_.x, (min_.y < max_.y)? min_.y: max_.y, (min_.z < max_.z)? min_.z : max_.z);
        return *this;
    }

这是我如何调用我的 TransformBy 函数:

box.TransformBy(transform.GetModelMatrix());

出于某种原因,它使我的最小和最大点正确旋转,它也翻译它,但我的缩放没有正确应用,使我的边界框比我的 3D 对象具有更大的比例。

为什么我的缩放不能正常工作?也许有一种不那么复杂的方式来做我想做的事?

标签: c++openglglm-math

解决方案


如果我正确理解结果的GetModelMatrix()外观,即它只是一个普通的 4x4 变换矩阵,那么我想我可以猜出问题所在。我发现用一个例子来形象化/解释是最容易的,所以希望没关系。

假设您的结果GetModelMatrix()如下,一个没有旋转分量的简单变换矩阵,只是 [5 6 7] 的平移。

| 1 0 0 5 |
| 0 1 0 6 |
| 0 0 1 7 |
| 0 0 0 1 |

传入 as 的反函数modelMatrice就是:

| 1 0 0 -5 |
| 0 1 0 -6 |
| 0 0 1 -7 |
| 0 0 0  1 |

调用后glm::vec4 min_(min.x,min.y,min.z,1.0f);,让我们min_用向量来表示[x y z 1]。然后min_ * modelMatrice看起来像:

            | 1 0 0 -5 |   
            | 0 1 0 -6 |
[x y z 1] * | 0 0 1 -7 | = [x y z (-5x-6y-7z+1)]
            | 0 0 0  1 |   

也就是说,x、y 和 z 分量不会改变,因为|0 0 0 1|在乘法中应用到它们的是行,而不是包含平移分量的列。

请问,您是否有特定的原因来(a)反转GetModelMatrix()和(b)将矩阵乘以向量而不是反之亦然?我猜想也许您的代码应该如下所示:

BoundingBox& TransformBy(const glm::mat4 modelMatrice){
        ...
        min_ = modelMatrice * min_;
        max_ = modelMatrice * max_;
        ...
        return *this;
    } 

调用box.TransformBy(transform.GetModelMatrix());

更新:

关于您的新代码,我质疑是否需要反转变换矩阵以进行旋转,需要将缩放/平移/旋转分成三个单独的步骤等。感觉就像,除非其他地方出现错误,该过程应该像获取对象的转换向量一样简单,将 min_ 和 max_ 乘以它,然后测试您当前在函数结束时是否交换了任何最小/最大组件:

     BoundingBox& TransformBy(const glm::mat4& modelMatrice){
        min_ = modelMatrice * min_;
        max_ = modelMatrice * max_;
        max = glm::vec3( (min_.x > max_.x)? min_.x:max_.x, (min_.y > max_.y)? min_.y: max_.y, (min_.z > max_.z)? min_.z : max_.z);
        min = glm::vec3( (min_.x < max_.x)? min_.x:max_.x, (min_.y < max_.y)? min_.y: max_.y, (min_.z < max_.z)? min_.z : max_.z);
        return *this;
    }

如果上面的五行功能不起作用,请告诉我它是如何失败的;如果它不起作用,我觉得问题可能出在代码的其他地方。

但是,要回答您更新的问题,为什么缩放不起作用:当您反转变换矩阵时,您也在反转缩放。因此,当您乘以 时,您不仅在旋转inverse:您还在应用倒置比例。虽然您可以通过再次乘以比例来解决此问题,但更简单的解决方案似乎是试图让上述五行解决方案发挥作用。


推荐阅读