2 votes

OpenGL ES - Rotation de texture dans le fragment shader sans distorsion

Je suis en train d'utiliser la bibliothèque GPUImage pour Android pour appliquer certains effets aux bitmaps. Essentiellement, GPUImage accepte les bitmaps et utilise OpenGL ES pour rendre un cube 1 x 1 dans le tampon d'image de la taille du bitmap. L'utilisateur peut écrire un shader de fragment personnalisé pour contrôler la sortie.

Je suis en train d'écrire un shader de fragment, qui fait tourner le bitmap, en lui donnant un angle de rotation. Voici ce que j'ai jusqu'à présent:

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

Cela fonctionne, mais déforme évidemment le bitmap (car le viewport de GPUImage est configuré pour mapper un cube 1 x 1 à la taille réelle du bitmap). Je ne peux pas trouver comment me débarrasser de la distorsion. Je devrais appliquer une transformation de mise à l'échelle supplémentaire, basée sur l'angle et inputImageTextureSize, mais je n'ai pas réussi à trouver la bonne équation. Aucune des réponses sur StackOverflow ne fonctionne réellement dans mon cas. S'il vous plaît, aidez!

Merci!

3voto

Rabbid76 Points 52493

Vous devez tenir compte du rapport d'aspect du viewport (fenêtre).
Si la fenêtre a le même rapport d'aspect que l'image, alors le rapport d'aspect peut être calculé par la taille de l'image:

float aspect = inputImageTextureSize.x / inputImageTextureSize.y;

Mettre à l'échelle la composante u de la coordonnée de texture avant la rotation par le rapport d'aspect (* aspect) et la mettre à l'échelle par le rapport d'aspect réciproque (* 1.0/aspect) après la rotation:

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

Pour clarifier le comportement, la même chose peut être exprimée avec des opérations matricielles:

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;

0voto

imbrizi Points 1260

En complément de la réponse de Rabbid76, au cas où les ratios d'aspect d'entrée et de sortie sont différents :

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

// ajuster l'aspect
tc.y *= aspect;
tc.y -= aspect * 0.5 - 0.5;

// rotation ...

Et voici un exemple : https://www.shadertoy.com/view/3dXBR7

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X