Aspect Fit 和 Aspect Fill 内容模式与 OpenGL ES 2.0
Aspect Fit and Aspect Fill content mode with OpenGL ES 2.0
我需要添加两种新的内容模式来使用 OpenGL ES 2.0 显示我的纹理:“Aspect Fit”和“Aspect Fill”。
这是一张解释不同内容模式的图片:
我已经有了“缩放以填充”内容模式,我猜这是默认行为。
这是我的基本纹理顶点着色器代码:
precision highp float;
attribute vec2 aTexCoordinate;
varying vec2 vTexCoordinate;
uniform mat4 uMVPMatrix;
attribute vec4 vPosition;
void main() {
vTexCoordinate = aTexCoordinate;
gl_Position = uMVPMatrix * vPosition;
}
这是我的纹理片段着色器:
precision mediump float;
uniform vec4 vColor;
uniform sampler2D uTexture;
varying vec2 vTexCoordinate;
void main() {
// premultiplied alpha
vec4 texColor = texture2D(uTexture, vTexCoordinate);
// Scale the texture RGB by the vertex color
texColor.rgb *= vColor.rgb;
// Scale the texture RGBA by the vertex alpha to reinstate premultiplication
gl_FragColor = texColor * vColor.a;
//gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
对于“Aspect Fill”模式,我认为我可以使用纹理坐标来裁剪图像。但是对于“Aspect Fit”模式,我并不清楚如何在着色器中做到这一点,甚至更好地使用屏幕截图中的红色背景颜色。
您必须缩放纹理坐标。我建议添加一个制服来缩放纹理:
uniform vec2 textureScale;
void main()
{
vTexCoordinate = textureScale * (aTexCoordinate - 0.5) + 0.5;
// [...]
}
计算纹理的纵横比:
aspect = textrueHeight / textureWidth
必须根据模式设置 textureScale
:
- “填充比例”:
vec2(1.0, 1.0)
。
- 横向“纵横比”:
vec2(1.0, aspect)
。
- 横向“纵横比填充”:
vec2(1.0/aspect, 1.0)
。
- 肖像“长宽比”:
vec2(1.0/aspect, 1.0)
。
- 纵向“纵横比填充”:
vec2(1.0, aspect)
。
您必须将纹理环绕参数(GL_TEXTURE_WRAP_S
、GL_TEXTURE_WRAP_T
)模式设置为GL_CLAMP_TO_EDGE
。参见 glTexParameter
。
或者您可以 discard
片段着色器中的多余片段:
void main() {
if (vTexCoordinate.x < 0.0 || vTexCoordinate.x > 1.0 ||
vTexCoordinate.y < 0.0 || vTexCoordinate.y > 1.0) {
discard;
}
// [...]
}
感谢@Rabbid76 和他的回答 ,我在将这部分添加到他的回答后设法做到了:
float textureAspect = (float)textureSize.width / (float)textureSize.height;
float frameAspect = (float)frameSize.width / (float)frameSize.height;
float scaleX = 1, scaleY = 1;
float textureFrameRatio = textureAspect / frameAspect;
BOOL portraitTexture = textureAspect < 1;
BOOL portraitFrame = frameAspect < 1;
if(contentMode == AspectFill) {
if(portraitFrame)
scaleX = 1.f / textureFrameRatio;
else
scaleY = textureFrameRatio;
}
else if(contentMode == AspectFit) {
if(portraitFrame)
scaleY = textureFrameRatio;
else
scaleX = 1.f / textureFrameRatio;
}
那我就vec2(scaleX, scaleY)
一个金属片段着色器,用于在预期大小内制作纹理方面fill/fit。
fragment float4 fragment_aspect_fitfill(
VertexOut vertexIn [[stage_in]],
texture2d<float, access::sample> sourceTexture [[texture(0)]],
sampler sourceSampler [[sampler(0)]],
constant float2 &size [[ buffer(0) ]],
constant float &contentMode [[ buffer(1) ]])
{
float2 uv = vertexIn.textureCoordinate;
//Calculate Aspect Ration for both Texture and Expected output texture
float textureAspect = (float)sourceTexture.get_width() / (float)sourceTexture.get_height();
float frameAspect = (float)size.x / (float)size.y;
float scaleX = 1, scaleY = 1;
float textureFrameRatio = textureAspect / frameAspect;
bool portraitTexture = textureAspect < 1;
bool portraitFrame = frameAspect < 1;
// Content mode 0 is for aspect Fill, 1 is for Aspect Fit
if(contentMode == 0.0) {
if(portraitFrame)
scaleX = 1.f / textureFrameRatio;
else
scaleY = textureFrameRatio;
} else if(contentMode == 1.0) {
if(portraitFrame)
scaleY = textureFrameRatio;
else
scaleX = 1.f / textureFrameRatio;
}
float2 textureScale = float2(scaleX, scaleY);
float2 vTexCoordinate = textureScale * (uv - 0.5) + 0.5;
return sourceTexture.sample(sourceSampler, vTexCoordinate);
}
** 注意:使用 MetalPetal
可以更好地理解某些结构,例如 VertexOut
等
** 这个片段着色器算法可能并不完美,但它有效并且对我有效。
我需要添加两种新的内容模式来使用 OpenGL ES 2.0 显示我的纹理:“Aspect Fit”和“Aspect Fill”。
这是一张解释不同内容模式的图片:
我已经有了“缩放以填充”内容模式,我猜这是默认行为。
这是我的基本纹理顶点着色器代码:
precision highp float;
attribute vec2 aTexCoordinate;
varying vec2 vTexCoordinate;
uniform mat4 uMVPMatrix;
attribute vec4 vPosition;
void main() {
vTexCoordinate = aTexCoordinate;
gl_Position = uMVPMatrix * vPosition;
}
这是我的纹理片段着色器:
precision mediump float;
uniform vec4 vColor;
uniform sampler2D uTexture;
varying vec2 vTexCoordinate;
void main() {
// premultiplied alpha
vec4 texColor = texture2D(uTexture, vTexCoordinate);
// Scale the texture RGB by the vertex color
texColor.rgb *= vColor.rgb;
// Scale the texture RGBA by the vertex alpha to reinstate premultiplication
gl_FragColor = texColor * vColor.a;
//gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
对于“Aspect Fill”模式,我认为我可以使用纹理坐标来裁剪图像。但是对于“Aspect Fit”模式,我并不清楚如何在着色器中做到这一点,甚至更好地使用屏幕截图中的红色背景颜色。
您必须缩放纹理坐标。我建议添加一个制服来缩放纹理:
uniform vec2 textureScale;
void main()
{
vTexCoordinate = textureScale * (aTexCoordinate - 0.5) + 0.5;
// [...]
}
计算纹理的纵横比:
aspect = textrueHeight / textureWidth
必须根据模式设置 textureScale
:
- “填充比例”:
vec2(1.0, 1.0)
。 - 横向“纵横比”:
vec2(1.0, aspect)
。 - 横向“纵横比填充”:
vec2(1.0/aspect, 1.0)
。 - 肖像“长宽比”:
vec2(1.0/aspect, 1.0)
。 - 纵向“纵横比填充”:
vec2(1.0, aspect)
。
您必须将纹理环绕参数(GL_TEXTURE_WRAP_S
、GL_TEXTURE_WRAP_T
)模式设置为GL_CLAMP_TO_EDGE
。参见 glTexParameter
。
或者您可以 discard
片段着色器中的多余片段:
void main() {
if (vTexCoordinate.x < 0.0 || vTexCoordinate.x > 1.0 ||
vTexCoordinate.y < 0.0 || vTexCoordinate.y > 1.0) {
discard;
}
// [...]
}
感谢@Rabbid76 和他的回答
float textureAspect = (float)textureSize.width / (float)textureSize.height;
float frameAspect = (float)frameSize.width / (float)frameSize.height;
float scaleX = 1, scaleY = 1;
float textureFrameRatio = textureAspect / frameAspect;
BOOL portraitTexture = textureAspect < 1;
BOOL portraitFrame = frameAspect < 1;
if(contentMode == AspectFill) {
if(portraitFrame)
scaleX = 1.f / textureFrameRatio;
else
scaleY = textureFrameRatio;
}
else if(contentMode == AspectFit) {
if(portraitFrame)
scaleY = textureFrameRatio;
else
scaleX = 1.f / textureFrameRatio;
}
那我就vec2(scaleX, scaleY)
一个金属片段着色器,用于在预期大小内制作纹理方面fill/fit。
fragment float4 fragment_aspect_fitfill(
VertexOut vertexIn [[stage_in]],
texture2d<float, access::sample> sourceTexture [[texture(0)]],
sampler sourceSampler [[sampler(0)]],
constant float2 &size [[ buffer(0) ]],
constant float &contentMode [[ buffer(1) ]])
{
float2 uv = vertexIn.textureCoordinate;
//Calculate Aspect Ration for both Texture and Expected output texture
float textureAspect = (float)sourceTexture.get_width() / (float)sourceTexture.get_height();
float frameAspect = (float)size.x / (float)size.y;
float scaleX = 1, scaleY = 1;
float textureFrameRatio = textureAspect / frameAspect;
bool portraitTexture = textureAspect < 1;
bool portraitFrame = frameAspect < 1;
// Content mode 0 is for aspect Fill, 1 is for Aspect Fit
if(contentMode == 0.0) {
if(portraitFrame)
scaleX = 1.f / textureFrameRatio;
else
scaleY = textureFrameRatio;
} else if(contentMode == 1.0) {
if(portraitFrame)
scaleY = textureFrameRatio;
else
scaleX = 1.f / textureFrameRatio;
}
float2 textureScale = float2(scaleX, scaleY);
float2 vTexCoordinate = textureScale * (uv - 0.5) + 0.5;
return sourceTexture.sample(sourceSampler, vTexCoordinate);
}
** 注意:使用 MetalPetal
可以更好地理解某些结构,例如 VertexOut
等
** 这个片段着色器算法可能并不完美,但它有效并且对我有效。