2

I am using GPUImage library for Android to apply some effects to the bitmaps. Essentialy, GPUImage accepts bitmap and uses OpenGL ES, rendering 1 x 1 cube into the frame buffer of the bitmap size. User can write custom fragment shader to control the output.

I am trying to write a fragment shader, which rotates the bitmap, given a rotation angle. Here is what I have so far:

varying vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform vec2 inputImageTextureSize;
uniform float angle;

const vec2 HALF = vec2(0.5);

void main()
{
    float aSin = sin(angle);
    float aCos = cos(angle);

    vec2 tc = textureCoordinate;
    tc -= HALF.xy;
    tc *= mat2(aCos, aSin, -aSin, aCos);
    tc += HALF.xy;

    vec3 tex = texture2D(inputImageTexture, tc).rgb;
    gl_FragColor = vec4(tex, 1.0);
}

It works, but obviously distorts the bitmap (as GPUImage viewport is set up to map 1 x 1 cube to actual bitmap size). I cannot figure out how to get rid of distortion. I should apply some extra scaling transformation, based on the angle and the inputImageTextureSize, but I failed to get the correct equation. None of the answers on StackOverflow actually works in my case. Please, help!

Thanks!

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
SadSido
  • 2,511
  • 22
  • 36

2 Answers2

5

You've to take into account the aspect ration of the viewport (window).
If the window has the same aspect ration as the image, then the aspect ration can be calculated by the size of the image:

float aspect = inputImageTextureSize.x / inputImageTextureSize.y;

Scale the u component of the texture coordinate before the rotation by the aspect ration (* aspect) and scale it by the reciprocal aspect ratio (* 1.0/aspect) after the rotation:

tc -= HALF.xy;
tc.x *= aspect;
tc *= mat2(aCos, aSin, -aSin, aCos);
tc.x *= 1.0/aspect;
tc += HALF.xy;

To clarify the behavior, the same can be expressed with matrix operations:

float aSin = sin(angle);
float aCos = cos(angle);

float aspect = u_resolution.x / u_resolution.y;

mat2 rotMat      = mat2(aCos, -aSin, aSin, aCos);
mat2 scaleMat    = mat2(aspect, 0.0, 0.0, 1.0);
mat2 scaleMatInv = mat2(1.0/aspect, 0.0, 0.0, 1.0);

tc -= HALF.xy;
tc = scaleMatInv * rotMat * scaleMat * tc;
tc += HALF.xy;
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • 1
    omg, I didn't pay attention to this line: "tc.x *= aspect", sorry. Scale, rotate, inverse scale, now that makes sense to me. – SadSido Jul 06 '19 at 14:41
  • 1
    finally tried this out and it worked. Thanks, man, you saved my day! – SadSido Jul 06 '19 at 14:54
  • What is the pivot point of rotation here? What do we have to do if we want to change the rotation pivot point? – ZeroOneZeroR Jul 20 '20 at 16:52
  • @ZeroOneZero If you've a question, then please [Ask a public question](https://stackoverflow.com/questions/ask) – Rabbid76 Jul 20 '20 at 17:01
1

As a complement to Rabbid76's answer, in case input and output aspect ratios are different:

float inputAspect = inputSize.x / inputSize.y;
float outputAspect = outputSize.x / outputSize.y;
float aspect = inputAspect / outputAspect;

// adjust aspect
tc.y *= aspect;
tc.y -= aspect * 0.5 - 0.5;

// rotate ...

And here is an example: https://www.shadertoy.com/view/3dXBR7

imbrizi
  • 3,788
  • 1
  • 25
  • 26