OpenGL ES - 在片段着色器中旋转纹理而不会失真
OpenGL ES - Rotating texture in fragment shader without distortion
我正在使用 Android 的 GPUImage 库来对位图应用一些效果。本质上,GPUImage 接受位图并使用 OpenGL ES,将 1 x 1 立方体渲染到位图大小的帧缓冲区中。用户可以编写自定义片段着色器来控制输出。
我正在尝试编写片段着色器,在给定旋转角度的情况下旋转位图。这是我目前所拥有的:
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);
}
它有效,但明显扭曲了位图(因为 GPUImage 视口设置为将 1 x 1 立方体映射到实际位图大小)。我不知道如何摆脱失真。我应该根据角度和 inputImageTextureSize 应用一些额外的缩放变换,但我没有得到正确的方程。 None Whosebug 上的答案实际上适用于我的情况。请帮忙!
谢谢!
您必须考虑视口的宽高比 (window)。
如果window和图片有相同的纵横比,那么纵横比可以通过图片的大小来计算:
float aspect = inputImageTextureSize.x / inputImageTextureSize.y;
旋转前按纵横比(* aspect
)缩放纹理坐标的u分量,旋转后按倒数纵横比(* 1.0/aspect
)缩放:
tc -= HALF.xy;
tc.x *= aspect;
tc *= mat2(aCos, aSin, -aSin, aCos);
tc.x *= 1.0/aspect;
tc += HALF.xy;
为了阐明行为,同样可以用矩阵运算来表示:
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 答案的补充,如果输入和输出纵横比不同:
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 ...
我正在使用 Android 的 GPUImage 库来对位图应用一些效果。本质上,GPUImage 接受位图并使用 OpenGL ES,将 1 x 1 立方体渲染到位图大小的帧缓冲区中。用户可以编写自定义片段着色器来控制输出。
我正在尝试编写片段着色器,在给定旋转角度的情况下旋转位图。这是我目前所拥有的:
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);
}
它有效,但明显扭曲了位图(因为 GPUImage 视口设置为将 1 x 1 立方体映射到实际位图大小)。我不知道如何摆脱失真。我应该根据角度和 inputImageTextureSize 应用一些额外的缩放变换,但我没有得到正确的方程。 None Whosebug 上的答案实际上适用于我的情况。请帮忙!
谢谢!
您必须考虑视口的宽高比 (window)。
如果window和图片有相同的纵横比,那么纵横比可以通过图片的大小来计算:
float aspect = inputImageTextureSize.x / inputImageTextureSize.y;
旋转前按纵横比(* aspect
)缩放纹理坐标的u分量,旋转后按倒数纵横比(* 1.0/aspect
)缩放:
tc -= HALF.xy;
tc.x *= aspect;
tc *= mat2(aCos, aSin, -aSin, aCos);
tc.x *= 1.0/aspect;
tc += HALF.xy;
为了阐明行为,同样可以用矩阵运算来表示:
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 答案的补充,如果输入和输出纵横比不同:
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 ...