前回のshader試してみたから今度は画像に処理を行ってみたのでshader触ってみたいけどわからないな〜なんて方の助けになれば幸いです。
画像に処理を加える際の数値などに関してこちらの記事を参考にさせていただきました。ありがとうございます!
この記事中ではshaderの処理だけを紹介していますが、全体を確認したい場合はGithubにソースコードを公開していますので実際に動かして確認していただくことができます。
開発環境
- MacbookPro macOS Catalina 10.15.7
- AndroidStudio Flamingo Canary9
- Pixel6a AndroidOS 13
x座標が100px右に進むごとにy座標を+20して階段状に表示してみました
shader内は条件式を極力書かないほうがいいと思いますが、今回は愚直に・・・😇
half4 main(in float2 fragCoord) {
if (fragCoord.x > 1000.0) {
fragCoord.y += 200.0;
} else if (fragCoord.x > 900.0) {
fragCoord.y += 180.0;
} else if (fragCoord.x > 800.0) {
fragCoord.y += 160.0;
} else if (fragCoord.x > 700.0) {
fragCoord.y += 140.0;
} else if (fragCoord.x > 600.0) {
fragCoord.y += 120.0;
} else if (fragCoord.x > 500.0) {
fragCoord.y += 100.0;
} else if (fragCoord.x > 400.0) {
fragCoord.y += 80.0;
} else if (fragCoord.x > 300.0) {
fragCoord.y += 60.0;
} else if (fragCoord.x > 200.0) {
fragCoord.y += 40.0;
} else if (fragCoord.x > 100.0) {
fragCoord.y += 20.0;
}
float4 image = iShader.eval(fragCoord).rgba;
return half4(image.xyz, 1.0);
}
時間経過で画像の切り替えをしてみました
やってることは単純で、shaderに割合を渡してimage1とimage2を混ぜ合わせてる感じです。
alphaでimage1とimage2を切り替えるのとほぼ同じですがshaderの勉強に試してみました😇
half4 main(in float2 fragCoord) {
float4 image1 = iImage1Shader.eval(fragCoord).rgba;
float4 image2 = iImage2Shader.eval(fragCoord).rgba;
float3 color = image1.xyz * (1.0 - iRatio) + image2.xyz * iRatio;
return half4(color.xyz, 1.0);
}
上から30pxずつ画像を切り替えてみました
half4 main(in float2 fragCoord) {
float4 image1 = iImage1Shader.eval(fragCoord).rgba;
float4 image2 = iImage2Shader.eval(fragCoord).rgba;
if (iPosition.y > fragCoord.y) {
image1.xyz = image2.xyz;
} else if (iPosition.x > fragCoord.x && iPosition.y + 30 > fragCoord.y) {
image1.xyz = image2.xyz;
}
return half4(image1.xyz, 1.0);
}
Sliderを使って切り替え具合を自分で確認できるようにしてみました
half4 main(in float2 fragCoord) {
float4 image1 = iImage1Shader.eval(fragCoord).rgba;
float4 image2 = iImage2Shader.eval(fragCoord).rgba;
if (iResolution.x * iRatio.x <= fragCoord.x && iResolution.y * iRatio.y <= fragCoord.y) {
return half4(image1);
} else {
return half4(image2);
}
}
上から順にモノクロ表示、ネガポジ反転、セピア調を試してみました
モノクロ表示
private const val SHADER_UNIFORMS = """
uniform shader iShader;
const float R_LUMINANCE = 0.298912;
const float G_LUMINANCE = 0.586611;
const float B_LUMINANCE = 0.114478;
const float3 monochromeScale = float3(R_LUMINANCE, G_LUMINANCE, B_LUMINANCE);
"""
private const val SHADER_MAIN = """
half4 main(in float2 fragCoord) {
float4 image = iShader.eval(fragCoord).rgba;
float grayColor = dot(image.rgb, monochromeScale);
return half4(float3(grayColor), 1.0);
}
"""
ネガポジ反転
private const val SHADER_MAIN = """
half4 main(in float2 fragCoord) {
float4 image = iShader.eval(fragCoord).rgba;
return half4(1.0 - image.r, 1.0 - image.g, 1.0 - image.b, 1.0);
}
"""
セピア調
private const val SHADER_UNIFORMS = """
uniform shader iShader;
const float R_LUMINANCE = 0.298912;
const float G_LUMINANCE = 0.586611;
const float B_LUMINANCE = 0.114478;
"""
private const val SHADER_MAIN = """
half4 main(in float2 fragCoord) {
float4 image = iShader.eval(fragCoord).rgba;
float v = image.x * R_LUMINANCE + image.y * G_LUMINANCE + image.z * B_LUMINANCE;
image.x = v * 0.9;
image.y = v * 0.7;
image.z = v * 0.4;
return half4(image);
}
"""
モザイク処理を試してみました
private const val SHADER_MAIN = """
half4 main(in float2 fragCoord) {
float2 uv = fragCoord;
uv.x = floor(uv.x * iResolution.x / iMosaicScale) / (iResolution.x / iMosaicScale) + (iMosaicScale / 2.0) / iResolution.x;
uv.y = floor(uv.y * iResolution.y / iMosaicScale) / (iResolution.y / iMosaicScale) + (iMosaicScale / 2.0) / iResolution.y;
float4 image = iShader.eval(uv).rgba;
return half4(image);
}
"""
blurを試してみました
private const val SHADER_UNIFORMS = """
uniform shader iShader;
uniform float2 iResolution;
uniform float iRadius;
"""
private const val SHADER_SUB = """
float rand(float2 co) {
float a = fract(dot(co, float2(2.067390879775102, 12.451168662908249))) - 0.5;
float s = a * (6.182785114200511 + a * a * (-38.026512460676566 + a * a * 53.392573080032137));
float t = fract(s * 43758.5453);
return t;
}
"""
private const val SHADER_MAIN = """
half4 main(in float2 fragCoord) {
float x = (fragCoord.x * iResolution.x) + rand(fragCoord) * iRadius * 2.0 - iRadius;
float y = (fragCoord.y * iResolution.y) + rand(float2(fragCoord.y, fragCoord.x)) * iRadius * 2.0 - iRadius;
float4 image = iShader.eval(float2(x, y) / iResolution).rgba;
return half4(image);
}
"""
渦巻表示を試してみました
private const val SHADER_UNIFORMS = """
uniform shader iShader;
uniform float2 iResolution;
uniform float2 iCenter;
uniform float iRadius;
uniform float iSpiralStrength;
"""
private const val SHADER_MAIN = """
half4 main(in float2 fragCoord) {
float2 pos = (fragCoord * iResolution) - iCenter;
float len = length(pos);
if (len >= iRadius) {
float4 image = iShader.eval(fragCoord).rgba;
return half4(image);
}
float spiral = min(max(1.0 - (len / iRadius), 0.0), 1.0) * iSpiralStrength;
float x = pos.x * cos(spiral) - pos.y * sin(spiral);
float y = pos.y * sin(spiral) - pos.y * cos(spiral);
float2 retPos = (float2(x, y) + iCenter) / iResolution;
float4 image = iShader.eval(retPos).rgba;
return half4(image);
}
"""
あとがき
shaderで画像処理をしていると楽しいのと目を引くことができるので、Android13以上でしかshaderを気軽に使えないのがもったいないな~と思いながら今回色々と作ってみました。