使用缩放+旋转矩阵在OpenGL中的两个点之间绘制一个矩形

时间:2016-01-11 10:38:06

标签: c++ opengl geometry

我有一个矩形网格,我在一个flyweight类型的对象中共享,其他类用它来绘制自己。绘图代码如下:

void Draw(const glm::ivec2 position, const glm::ivec2 size)
{
    /* do some binding ...*/

    glm::vec2 s(m_screenDimensions.x,m_screenDimensions.y);
    glm::vec2 p(position.x,position.y);
    glm::vec2 d(size.x,size.y);
    glm::mat4 translationMatrix = glm::translate(glm::mat4(1.0f),glm::vec3(-1.0f+p.x/s.x*2.0f+(d.x/s.x),1.0f-p.y/s.y*2.0f-(d.y/s.y),0.0f));
    glm::mat4 scaleMatrix = glm::scale(glm::mat4(1.0f),glm::vec3(d.x/s.x,d.y/s.y,1.0f));
    m_program.SetUniform(m_modelMatrixLocation,translationMatrix * scaleMatrix * glm::mat4(1.0f));

    /* dispatch draw calls ... */
}

这可以轻松地在所需的位置和大小绘制一个矩形。但是,我现在想使用相同的矩形flyweight绘制线条。所以基本上:

void Draw(const glm::ivec2 position1, const glm::ivec2 position2, const unsigned int width)
{
    /* ... */
}

我遇到的问题是计算平移,缩放和旋转矩阵。

我知道我可以通过以下方式计算矩形点:

  1. 在两个位置之间获取向量
  2. 获取垂直向量
  3. 将四个中的每一个放置在沿着这个垂直向量移动的位置
  4. 但我不明白如何计算所需的旋转矩阵。显然,因为它被表示为一条线(两点和一个宽度)而不是一个矩形(位置和大小),我计算平移矩阵和比例矩阵的方式需要不同。

    例如,如果我像现在一样计算平移和比例矩阵(通过从提供的位置和宽度推导出尺寸和位置点);如何计算旋转矩阵?

    EDIT1

    我按照建议尝试过,但屏幕上没有任何内容:

    void RectColoured::Draw(const glm::ivec2 position1, const glm::ivec2 position2, const int width) const
    {
        m_program.Bind();
        m_program.SetUniform(m_colorUniformLocation,m_color);
        m_program.SetUniform(m_useTextureUniformLocation,false);
    
     /*   glm::vec2 s(m_screenDimensions.x,m_screenDimensions.y);
        glm::vec2 p(position1.x,position1.y);
        glm::vec2 d(position2.x-position1.x+10,position2.y-position1.y+10);
        glm::mat4 translationMatrix = glm::translate(glm::mat4(1.0f),glm::vec3(-1.0f+p.x/s.x*2.0f+(d.x/s.x),1.0f-p.y/s.y*2.0f-(d.y/s.y),0.0f));
        glm::mat4 scaleMatrix = glm::scale(glm::mat4(1.0f),glm::vec3(d.x/s.x,d.y/s.y,1.0f));
        m_program.SetUniform(m_modelMatrixLocation,translationMatrix * scaleMatrix * glm::mat4(1.0f));
    */
        glm::vec2 s(m_screenDimensions.x,m_screenDimensions.y);
        glm::vec2 d = position2-position1;
        glm::vec2 n = glm::normalize(d);
        auto p1 = position1;
        float w = width;
        float m[] = {
                2*d.x/s.x,  2*n.x/s.x*w,    0,  2*(p1.x-0.5*n.x)/s.x-1.0,
                2*d.y/s.y,  2*n.y/s.y*w,    0,  2*(p1.y-0.5*n.y)/s.y-1.0,
                0,          0,              0,  0,
                0,          0,              0,  1
        };
        glm::mat4 matrix = glm::make_mat4(m);
        m_program.SetUniform(m_modelMatrixLocation,matrix);
    
        m_quad.BindAndDraw();
    }
    

    编辑2 不太相似:

    void RectColoured::Draw(const glm::ivec2 position1, const glm::ivec2 position2, const int width) const
    {
        m_program.Bind();
        m_program.SetUniform(m_colorUniformLocation,m_color);
        m_program.SetUniform(m_useTextureUniformLocation,false);
    
        glm::vec2 s = m_screenDimensions;
        glm::vec2 d = position2-position1;
        glm::vec2 n = glm::normalize(glm::vec2(d.y,-d.x));
        auto p1 = position1;
        float w = width;
        float m[] = {
            2.0f*d.x/s.x,                       -2.0f*d.y/s.y,                      0.0f, 0.0f,
            2.0f*n.x/s.x*w,                     -2.0f*n.y/s.y*w,                    0.0f, 0.0f,
            0.0f,                               0.0f,                               0.0f, 0.0f,
            2.0f*(p1.x-0.5f*n.x*w)/s.x-1.0f,    -2.0f*(p1.y-0.5f*n.y*w)/s.y+1.0f,   0.0f, 1.0f
        };
        glm::mat4 matrix = glm::make_mat4(m);
        m_program.SetUniform(m_modelMatrixLocation,matrix);
    
        m_quad.BindAndDraw();
    }
    

    给出了这个结果:

    enter image description here

    两个矩形“节点”的左上角是我想要绘制的线的两个点。我可以看到它的长度应该是沿着方向的两倍。

1 个答案:

答案 0 :(得分:1)

我假设您想要用矩形表示线,使得p1和p2位于矩形的两个相对边的中间。此外,我假设您的绘图命令使用左上角(0,0)的1 x 1平方的顶点。

然后,像素坐标中屏幕上的位置将是

p1 + d * x + n * w * (y - 0.5)

d =(p2-p1),n =标准化(-d.y,d.x),宽度为w。然后转换到opengl -1到1范围,我们得到

vec2(-1.0, -1.0) + 2/s * (p1 + d * x + n  * w * (y - 0.5))

用于屏幕尺寸。这导致以下转换矩阵

2 * d.x / s.x | 2 * n.x / s.x * w | 0.0 | 2 * (p1.x - 0.5 * n.x * w) / s.x - 1.0
-----------------------------------------------------------------
-2 * d.y / s.y | -2 * n.y / s.y * w | 0.0 | -2 * (p1.y - 0.5 * n.y * w) / s.y + 1.0
-----------------------------------------------------------------
0.0       | 0.0                   | 0.0 | 0.0
-----------------------------------------------------------------
0.0       | 0.0                   | 0.0 | 1.0

(或转置取决于你想要左乘还是右乘)。该矩阵已经可以用于示例中的统一。

要将其拆分为平移/旋转/缩放矩阵,您实际上需要2个缩放矩阵,因为非均匀缩放通常不会与旋转通信。我们可以分为以下4个步骤:

scale by (length(p2 - p1), width)
rotation by rotation matrix with columns (normalize(d), n)
scale by (2/s.x, 2/s.y)
translate by (see last column in the big matrix)

修改

矩阵包含两个错误:

  • 我在翻译部分错过了乘以w
  • 第二行需要乘以-1以确保(0,0)是左上角,而不是左下角。

两者都已在大矩阵中修复。

至于你的问题,我现在可以想到的两件事是:

  • 默认情况下,OpenGL矩阵是列主要的,并且数组中的矩阵是行主要的。您可能需要转置(即在行和列之间切换)矩阵。
  • 使用背面剔除,变换可能是从正方形中产生顺时针三角形。要解决此问题,请尝试将法线从标准化(-dy,dx)更改为标准化(dy,-dx),即镜像它(或者更改矩阵中所有n出现的符号等)。