Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OpenGL point sprites rotation in fragment shader

Tags:

c++

opengl

glsl

I'm following this tutorial to learn something more about OpenGL and in particular point sprites. But I'm stuck on one of the exercises at the end of the page:

Try to rotate the point sprites 45 degrees by changing the fragment shader.

There are no hints about this sort of thing in the chapter, nor in the previous ones. And I didn't find any documentation on how to do it. These are my vertex and fragment shaders:

Vertex Shader

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

Fragment Shader

#version 140

varying vec4 f_color;

uniform sampler2D texture;

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

I thought about using a 2x2 matrix in the FS to rotate the gl_PointCoord, but I have no idea how to fill the matrix to accomplish it. Should I pass it directly to the FS as a uniform?

like image 577
Pietro Lorefice Avatar asked Jan 30 '12 16:01

Pietro Lorefice


3 Answers

The traditional method is to pass a matrix to the shader, whether vertex or fragment. If you don't know how to fill in a rotation matrix, Google and Wikipedia can help.

The main thing is that you're going to run into is the simple fact that a 2D rotation is not enough. gl_PointCoord goes from [0, 1]. A pure rotation matrix rotates around the origin, which is the bottom-left in point-coord space. So you need more than a pure rotation matrix.

You need a 3x3 matrix, which has part rotation and part translation. This matrix should be generated as follows (using GLM for math stuff):

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

You then pass currMat to the shader as a 4x4 matrix. Your shader does this:

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

I'll leave it as an exercise for you as to how to move the translation from the fourth column into the third, and how to pass it as a 3x3 matrix. Of course, in that case, you'll do vec3(gl_PointCoord, 1) for the matrix multiply.

like image 62
Nicol Bolas Avatar answered Oct 05 '22 23:10

Nicol Bolas


I was stuck in the same problem too, but I found a tutorial that explain how to perform a 2d texture rotation in the same fragment shader with only with passing the rotate value (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;
}

Maybe this method is slow but is only to prove/show that you have an alternative to perform a texture 2D rotation inside fragment shader instead of passing a Matrix.

Note: vRotation should be in Radians.

Cheers,

like image 21
Jordi Espada Avatar answered Oct 06 '22 01:10

Jordi Espada


You're right - a 2x2 rotation matrix will do what you want.

This page: http://www.cg.info.hiroshima-cu.ac.jp/~miyazaki/knowledge/teche31.html shows how to compute the elements. Note that you will be rotating the texture coordinates, not the vertex positions - the result will probably not be what you're expecting - it will rotate around the 0,0 texture coordinate, for example.

You may alse need to multiply the point_size by 2 and shrink the gl_PointCoord by 2 to ensure the whole texture fits into the point sprite when it's rotated. But do that as a second change. Note that a straight scale of texture coordinates move them towards the texture coordinate origin, not the middle of the sprite.

If you use a higher dimension matrix (3x3) then you will be able to combine the offset, scale and rotation into one operation.

like image 30
Tom Whittock Avatar answered Oct 06 '22 01:10

Tom Whittock