片段着色器中的OpenGL点精灵旋转

Posted

技术标签:

【中文标题】片段着色器中的OpenGL点精灵旋转【英文标题】:OpenGL point sprites rotation in fragment shader 【发布时间】:2012-01-30 16:04:42 【问题描述】:

我正在关注this tutorial 以了解有关 OpenGL 的更多信息,尤其是点精灵。但我被困在页面末尾的一个练习中:

尝试通过更改片段着色器将点精灵旋转 45 度。

在这一章中没有关于这类事情的提示,在之前的章节中也没有。而且我没有找到任何有关如何执行此操作的文档。这些是我的顶点和片段着色器:

顶点着色器

#version 140

attribute vec2 coord2d;

varying vec4 f_color;

uniform float offset_x;
uniform float scale_x;
uniform float point_size;

void main(void) 
    gl_Position = vec4((coord2d.x + offset_x) * scale_x, coord2d.y, 0.0, 1.0);
    f_color = vec4(coord2d.xy / 2.0 + 0.5, 1.0, 1.0);
    gl_PointSize = point_size;

片段着色器

#version 140

varying vec4 f_color;

uniform sampler2D texture;

void main(void) 
    gl_FragColor = texture2D(texture, gl_PointCoord) * f_color;

我想过在 FS 中使用 2x2 矩阵来旋转 gl_PointCoord,但我不知道如何填充矩阵来完成它。我是不是应该直接把它作为制服传给FS?

【问题讨论】:

【参考方案1】:

传统的方法是将矩阵传递给着色器,无论是顶点还是片段。如果您不知道如何填写旋转矩阵,Google 和 Wikipedia 可以提供帮助。

主要是您将遇到一个简单的事实,即 2D 旋转是不够的。 gl_PointCoord 来自 [0, 1]。纯旋转矩阵围绕原点旋转,原点位于点坐标空间的左下角。所以你需要的不仅仅是一个纯旋转矩阵。

您需要一个 3x3 矩阵,该矩阵具有部分旋转和部分平移。该矩阵应按如下方式生成(使用GLM 进行数学运算):

glm::mat4 currMat(1.0f);
currMat = glm::translate(currMat, glm::vec3(0.5f, 0.5f, 0.0f));
currMat = glm::rotate(currMat, angle, glm::vec3(0.0f, 0.0f, 1.0f));
currMat = glm::translate(currMat, glm::vec3(-0.5f, -0.5f, 0.0f));

然后您将currMat 作为 4x4 矩阵传递给着色器。您的着色器会这样做:

vec2 texCoord = (rotMatrix * vec4(gl_PointCoord, 0, 1)).xy
gl_FragColor = texture2D(texture, texCoord) * f_color;

我将把它作为练习留给您,以了解如何将翻译从第四列移动到第三列,以及如何将其作为 3x3 矩阵传递。当然,在这种情况下,您将为矩阵乘法执行vec3(gl_PointCoord, 1)

【讨论】:

【参考方案2】:

我也遇到了同样的问题,但我找到了一个教程,它解释了如何在同一个片段着色器中执行 2d 纹理旋转,只需要传递旋转值 (vRotation)。

#version 130

uniform sampler2D tex;
varying float vRotation;
void main(void)


    float mid = 0.5;
    vec2 rotated = vec2(cos(vRotation) * (gl_PointCoord.x - mid) + sin(vRotation) * (gl_PointCoord.y - mid) + mid,
                        cos(vRotation) * (gl_PointCoord.y - mid) - sin(vRotation) * (gl_PointCoord.x - mid) + mid);

    vec4 rotatedTexture=texture2D(tex, rotated);
    gl_FragColor =  gl_Color * rotatedTexture;

也许这种方法很慢,但只是为了证明/表明您可以在片段着色器中执行纹理 2D 旋转而不是传递矩阵。

注意:vRotation 应该是弧度。

干杯,

【讨论】:

【参考方案3】:

你是对的 - 一个 2x2 旋转矩阵会做你想要的。

此页面:http://www.cg.info.hiroshima-cu.ac.jp/~miyazaki/knowledge/teche31.html 显示如何计算元素。请注意,您将旋转纹理坐标,而不是顶点位置 - 结果可能不是您所期望的 - 例如,它将围绕 0,0 纹理坐标旋转。

您可能还需要将 point_size 乘以 2 并将 gl_PointCoord 缩小 2 以确保整个纹理在旋转时适合点精灵。但这样做是第二次改变。请注意,纹理坐标的直线比例会将它们移向纹理坐标原点,而不是精灵的中间。

如果您使用更高维度的矩阵 (3x3),那么您将能够将偏移、缩放和旋转组合到一个操作中。

【讨论】:

谢谢,但正如你所说,结果不是我所期望的,因为我绕错了点。

以上是关于片段着色器中的OpenGL点精灵旋转的主要内容,如果未能解决你的问题,请参考以下文章

片段着色器中未使用纹理数据 - OpenGL

片段着色器中的球面映射

openGL之API学习(一六七)默认着色器 顶点属性索引 别名索引

在GLSL ES中的片段着色器上旋转纹理

在片段着色器中绘制别名像素完美线?

跨多个着色器的 OpenGL 统一