居中 fill/fit Shader Metal 中的 2D 纹理
Centered fill/fit 2D texture in Shader Metal
在视图中调整 fit/fill 纹理时,我需要将其置于 2D 纹理的中心,但我无法配置 uv 坐标。
Original image
When adjust fill, show the first part of the image not the center:
Fill image
when adjust fit, not get the correct center:
Fit image
float2 adjustPos(float2 size,
float2 uv) {
uv.x /= size.x;
uv.y /= size.y;
uv.y = 1.0f - uv.y;
return uv;
}
float2 scaleTexture(texture2d<float, access::sample> tex2d,
float2 size,
float2 uv,
int mode) {
int width = tex2d.get_width();
int height = tex2d.get_height();
float widthRatio = size.x/width;
float heightRatio = size.y/height;
float2 pos;
if (mode == 0) { // Aspect Fit
int2 newSize = int2(width*widthRatio, height*widthRatio);
pos = adjustPos(float2(newSize), uv);
float y = (uv.y/size.y) / 2.0;
y = y-pos.y;
y = 1.0f-y;
pos.y = y;
} else if (mode == 1) { // Aspect Fill
int2 newSize = int2(width*heightRatio, height*heightRatio);
pos = adjustPos(float2(newSize), uv);
if (newSize.x != size.x) {
pos.x = 0.5f + ((pos.x - 0.5f) * (1.0f - (heightRatio/100)));
}
} else {
float scale = min(widthRatio, heightRatio);
int2 newSize = int2(width*scale, height*scale);
pos = adjustPos(float2(newSize), uv);
}
return pos;
}
您可以使用此着色器并根据您的要求对其进行自定义。此解决方案可让您在给定尺寸内进行纹理填充。
以下金属着色器采用纹理、输出大小、预期内容模式和 returns 给定大小的 fit/fill 图像。
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);
}
*提示:本MSL使用了MetalPetal
中的struct
部分。
在视图中调整 fit/fill 纹理时,我需要将其置于 2D 纹理的中心,但我无法配置 uv 坐标。
Original image
When adjust fill, show the first part of the image not the center:
Fill image
when adjust fit, not get the correct center:
Fit image
float2 adjustPos(float2 size,
float2 uv) {
uv.x /= size.x;
uv.y /= size.y;
uv.y = 1.0f - uv.y;
return uv;
}
float2 scaleTexture(texture2d<float, access::sample> tex2d,
float2 size,
float2 uv,
int mode) {
int width = tex2d.get_width();
int height = tex2d.get_height();
float widthRatio = size.x/width;
float heightRatio = size.y/height;
float2 pos;
if (mode == 0) { // Aspect Fit
int2 newSize = int2(width*widthRatio, height*widthRatio);
pos = adjustPos(float2(newSize), uv);
float y = (uv.y/size.y) / 2.0;
y = y-pos.y;
y = 1.0f-y;
pos.y = y;
} else if (mode == 1) { // Aspect Fill
int2 newSize = int2(width*heightRatio, height*heightRatio);
pos = adjustPos(float2(newSize), uv);
if (newSize.x != size.x) {
pos.x = 0.5f + ((pos.x - 0.5f) * (1.0f - (heightRatio/100)));
}
} else {
float scale = min(widthRatio, heightRatio);
int2 newSize = int2(width*scale, height*scale);
pos = adjustPos(float2(newSize), uv);
}
return pos;
}
您可以使用此着色器并根据您的要求对其进行自定义。此解决方案可让您在给定尺寸内进行纹理填充。
以下金属着色器采用纹理、输出大小、预期内容模式和 returns 给定大小的 fit/fill 图像。
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);
}
*提示:本MSL使用了MetalPetal
中的struct
部分。