在看了《如何优雅地实现一个分屏滤镜》后,就想用Metal
学习着也实现一次,博主是使用GLSL
去实现。Metal
着色器的实现其实跟GLSL
里面的实现一样,没什么差别。
一、静态分屏
静态分屏:每一个屏幕的图像都一样
先上实现效果图:主要是片元着色器里面的代码:
fragment float4 fragmentShader(RasterizerData inVertex [[stage_in]], //stage_in表示这个数据来自光栅化
texture2d textureY [[texture(0)]], // texture 表明是纹理数据
texture2d textureUV [[texture(1)]], // texture 表明是纹理数据
constant XTConvertMatrix *convertMarix [[buffer(0)]] // buffer表示名缓存数据
) {
float row = 2; /// 多少行
float column = 3; /// 多少列
float rowCount = max(row, 1.0); // 确保至少有一行
float columnCount = max(column, 1.0); // 确保至少有一列
float ratio = rowCount / columnCount;
float2 originSize = float2(1.0, 1.0);
float2 newSize = originSize;
if (ratio > 1.0) {
newSize.y = 1.0 / ratio;
} else {
newSize.x = ratio;
}
float2 offset = (originSize - newSize) / 2.0; /// 画面的偏移距离
float2 position = offset + fmod(inVertex.texCoords * min(rowCount, columnCount), newSize); // (5)
/// C 库函数 double fmod(double x, double y) 返回 x 除以 y 的余数。
//取样器
constexpr sampler textureSampler (mag_filter::linear,
min_filter::linear
);
/// 获取yuv数据
float3 yuv = float3(textureY.sample(textureSampler, position).r,
(textureUV.sample(textureSampler, position).rg));
/// yuv转rgb
float3 rgb = convertMarix[0].matrix * (yuv + convertMarix[0].offset);
/// 确保超出不被显示 用黑色
if (position.x > 1.0) {
return float4(0,0,0,1);
}
if (position.y > 1.0) {
return float4(0,0,0,1);
}
return float4(rgb, 1.0);
}
二、动态分屏
动态分屏指的是,每个屏的图像都不一样,每间隔一段时间,会主动捕获一个新的图像。
先上效果图:
上代码:
fragment float4 fragmentShader2(RasterizerData inVertex [[stage_in]], //stage_in表示这个数据来自光栅化
texture2d texture0Y [[texture(0)]], // texture 表明是纹理数据
texture2d texture0UV [[texture(1)]], // texture 表明是纹理数据
texture2d texture1Y [[texture(2)]], // texture 表明是纹理数据
texture2d texture1UV [[texture(3)]], // texture 表明是纹理数据
texture2d texture2Y [[texture(4)]], // texture 表明是纹理数据
texture2d texture2UV [[texture(5)]], // texture 表明是纹理数据
texture2d texture3Y [[texture(6)]], // texture 表明是纹理数据
texture2d texture3UV [[texture(7)]], // texture 表明是纹理数据
texture2d texture4Y [[texture(8)]], // texture 表明是纹理数据
texture2d texture4UV [[texture(9)]], // texture 表明是纹理数据
constant XTConvertMatrix *convertMarix [[buffer(0)]], // buffer表示名缓存数据
constant XTUniform *uniform [[buffer(1)]]
) {
constexpr sampler textureSampler (mag_filter::linear,
min_filter::linear
);
float2 newSize = float2(1.0, 1.0);;
float2 position = modf(inVertex.texCoords * 2, newSize);
texture2d textureY;
texture2d textureUV;
if (inVertex.texCoords.x <= 0.5 && inVertex.texCoords.y <= 0.5) { //左上
if (uniform[0].textureCount > 0) {
textureY = texture1Y;
textureUV = texture1UV;
}else {
textureY = texture0Y;
textureUV = texture0UV;
}
}else if (inVertex.texCoords.x > 0.5 && inVertex.texCoords.y <= 0.5) { //右上
if (uniform[0].textureCount > 1) {
textureY = texture2Y;
textureUV = texture2UV;
}else {
textureY = texture0Y;
textureUV = texture0UV;
}
}else if (inVertex.texCoords.x <= 0.5 && inVertex.texCoords.y > 0.5) { //左下
if (uniform[0].textureCount > 2) {
textureY = texture3Y;
textureUV = texture3UV;
}else {
textureY = texture0Y;
textureUV = texture0UV;
}
}else { // 右下
if (uniform[0].textureCount > 3) {
textureY = texture4Y;
textureUV = texture4UV;
}else {
textureY = texture0Y;
textureUV = texture0UV;
}
}
float3 yuv = float3(textureY.sample(textureSampler, position).r,
(textureUV.sample(textureSampler, position).rg));
float3 rgb = convertMarix[0].matrix * (yuv + convertMarix[0].offset);
if (position.x > 1.0) {
return float4(0,0,0,1);
}
if (position.y > 1.0) {
return float4(0,0,0,1);
}
return float4(rgb, 1.0);
}
在实现动态分屏的时候遇到一个问题:
我全局记录分屏的数据,过了特定时间后,前面的纹理数据会变化。比方就是说当过了4s
,我会记录textureBuffer1
,并且全局记录,然后继续往下跑,textureBuffer1
的数据有发生变化。然后我打印pixelBuffer
地址会重复,发现pixelBuffer
重用了,然后我就记录下pixelBuffer
,完美解决
具体代码放在Metal-11(分屏)里面。
热爱生活,记录生活!