それは画像処理やGPUに基づいてリアルタイムフィルタに来るとき、私はあなたが有名なGPUImageを考えるだろうよ、このプロジェクトは、その後の開発、容易に利用可能な基本的な画像処理ツールの利便性の多くを提供します。しかし、私たちのために大きな助けを提供することができ、プロジェクトの構造GPUImageから学びます。
GPUImageプロジェクト構造
GPUImageプロジェクト構造は、Androidのバージョンは、より単純で、次のように構成されており、非常に簡単です:
- スタックフィルタ(コード支持及び設定パラメータシェーダ)
- フィルタグループ(FBO同じサブ画像の複数の処理を使用して)
- (主にオフスクリーンレンダリングに使用される)EGL管理
フィルタのパイルの主値GPUImageが、我々は、主にフレームワークGPUImageなどプラグインフィルタ、等、プラグにプラグインするのは後者の2つの分析:D、我々はまた、Yihuhuhuapiaoをカスタマイズすることができるが独自のフィルタ。
なぜ、オフスクリーンレンダリング
オフスクリーンレンダリングの主な目的は、バックグラウンドでデータを処理している、カメラアプリケーションはSurfaceViewプレビュー、その後、あなたが表示されないようにするために、表示されているカメラデータを配置する必要があれば、それは不可欠SurfaceView収縮が非常に小さい、トラブルにあることを知っていましたそして、資源の無駄。アンドロイド3.0はSurfaceTextureとGLSurfaceViewが、あった後TextureViewは、まだ表示されないカメラデータを扱うが、これらに自由に表示し、レンダリングプロセスを持っています。言い換えれば、TextureView GLSurfaceViewと従順十分には、私たちのすべての要件を完了することはできません。
私たちは、画像を処理GPUを活用したいが、彼はその後、表示されない場合は?
栗のために
、私たちは、インタフェースのCamera360 Liteバージョンを見て:
これらの写真は、あなたが見ることができるフィルタを選択して、後で開いている缶、APK、彼らが運んでいるネットワーキングなし?なぜ、すべて同じ人ですか?しかし、周りを見た後、私は唯一のAPKでそれらを見つけることができます:
異なる色で姉は行きますか?
これは、これらの異なるフィルタ効果、実際には、最初の実行中にAPKがユーザー生成の携帯電話に、あることを示唆しています。それは例えば、多くの利点を有するので、大幅APKは、符号の同じセットが、異なる機能を実行するためにボリュームを使用することができる前記減少、(無料Camera360フォルダデータを表示します)。もちろん、これはオフスクリーンレンダリングの唯一の利点です。
それは、私たちは、環境設定EGLを完了するために、GLSurfaceView、GLSurfaceViewの助けを借りてきて、今GPUImageを見るために、私たちは自分自身で管理する必要があり、GLSurfaceViewを使用していないそれを行う方法ですしていた:
GPUImage参照GLSurfaceViewは、OpenGLの独自の環境設定を行って(のような私は脱出するために、ああ、言いませんでした...
コードの我々の分析の背後にGLSurfaceViewが、それはそれを行う方法のレンダリング画面オフになります(すべての後、環境設定ルーチンどのようなもの)
フィルタフレームバッファグループオブジェクト(FBO)
GPUImageグループフィルタは、これらのフィルタは最高の再利用しているということができます。手段FrameBufferObject(FBO、フレームバッファ)によって、我々は、画像上の異なるフィルタの組み合わせを使用して所望の結果を得ることができます。
別の栗のよう:
私はグレーのフィルタを書いた次のように、あなたは、黒と白に絵を回すことができます。
precision mediump float;
varying vec2 vTextureCoord;
uniform sampler2D sTexture;
void main() {
vec3 centralColor = texture2D(sTexture, vTextureCoord).rgb;
gl_FragColor = vec4(0.299*centralColor.r+0.587*centralColor.g+0.114*centralColor.b);
}
ある日、私は、何の関係もで忙しかった抗カラーフィルタを書きました:
precision mediump float;
varying vec2 vTextureCoord;
uniform sampler2D sTexture;
void main() {
vec4 centralColor = texture2D(sTexture, vTextureCoord);
gl_FragColor = vec4((1.0 - centralColor.rgb), centralColor.w);
}
今、ボスが処理されるように黒と白のビデオストリームに私に尋ね、その後、逆の色。
私にはどのようにこの小さなまれ、その後、私は次のコードを記述するために10分を費やし:
precision mediump float;
varying vec2 vTextureCoord;
uniform sampler2D sTexture;
void main() {
vec4 centralColor = texture2D(sTexture, vTextureCoord);
gl_FragColor =vec4(0.299*centralColor.r+0.587*centralColor.g+0.114*centralColor.b);
gl_FragColor = vec4((1.0 - gl_FragColor.rgb), gl_FragColor.w);
}
各フィルタは非常に複雑になっている場合は、これらの2つのフィルタが、(一つだけの行)は比較的簡単なのですか?その多くの組み合わせであれば?
我々は、すべてのOO大学の先生のアイデアを植え付けるために懸命に働い反映していないで、エレガントではないこと、シェーダを変更するたびので、フィルター内部に二つの機能を書きました。
GPUImageでは、フレームバッファオブジェクト、この問題を解決するために使用され、それが画面に描画される前に、我々はすべて一度に処理され、そして今、我々は、下の結果としてのドローをさせ、その後、それらのフレームバッファに結果を保存することができません入力データが処理されると、そう私のコードは次のようになります。
filterGroup.addFilter(new GrayScaleShaderFilter(context));
filterGroup.addFilter(new InvertColorFilter(context));
あなたはどのように行うに第3の処理ステップを使用している場合は?
そして、新しい方法!それは便利ではないですか?
FBOの作成およびレンダリング処理
まず、我々は店のテクスチャID IDに使用される二つの配列を、必要とFBOの結果をグラフ化。
protected int[] frameBuffers = null;
protected int[] frameBufferTextures = null;
はい、FBO、テクスチャのような、数で表されるように。
if (frameBuffers == null) {
frameBuffers = new int[size-1];
frameBufferTextures = new int[size-1];
for (int i = 0; i < size-1; i++) {
GLES20.glGenFramebuffers(1, frameBuffers, i);
GLES20.glGenTextures(1, frameBufferTextures, i);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, frameBufferTextures[i]);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA,
filters.get(i).surfaceWidth, filters.get(i).surfaceHeight, 0,
GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffers[i]);
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
GLES20.GL_TEXTURE_2D, frameBufferTextures[i], 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
}
}
ここで長いコードが、我々は以前に生成さと質感は非常に似ている(のOpenGL ESベースのない学生がこれを見ることができます)
- オブジェクトGLES20.glGenFramebuffersバッファフレームを生成します
- 以下は、実際にテクスチャを発生する大きなセグメントであると長く、幅の広い私たちの現在との戦略にズームインし、設定するために描き、そして国境の取り扱いを指定します
- ここで重要:我々は、オブジェクトバッファフレームにテクスチャ画像を関連付けるために使用GLES20.glFramebufferTexture2D、これはOpenGLのFBOは、2Dテクスチャを関連付けるために使用される指示、frameBufferTextures [i]とFBOテクスチャに関連付けられています
- サイズ-1であるなぜ我々は最終的に画面ああ上で直接テクスチャに描画するので、それは〜です
描きます
私たちは私たちの描画コードを書き換えることができるようにFBOは、後に生成されました
if (i < size - 1) {
GLES20.glViewport(0, 0, filter.surfaceWidth, filter.surfaceHeight);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffers[i]);
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
filter.onDrawFrame(previousTexture);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
previousTexture = frameBufferTextures[i];
}else{
GLES20.glViewport(0, 0 ,filter.surfaceWidth, filter.surfaceHeight);
filter.onDrawFrame(previousTexture);
}
- FBOを結合glBindFramebufferを描画するために、各使用する前に、我々は結果を引き出すには、画面上に表示されますが、ちょうどとFBOテクスチャオブジェクトバインディングになったことはありませんし、次のフィルタにこのテクスチャを使用します入力として、
- 第一のフィルタを入力し、私たちのカメラやプレイヤー対応するテクスチャです
- 最後に、フィルタは、このように直接のように描かれ、FBOに出力する必要はありません。
コードの完全なセットをフィルタリング
package com.martin.ads.omoshiroilib.filter.base;
import android.opengl.GLES20;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Ads on 2016/11/19.
*/
public class FilterGroup extends AbsFilter {
private static final String TAG = "FilterGroup";
protected int[] frameBuffers = null;
protected int[] frameBufferTextures = null;
protected List<AbsFilter> filters;
protected boolean isRunning;
public FilterGroup() {
super("FilterGroup");
filters=new ArrayList<AbsFilter>();
}
@Override
public void init() {
for (AbsFilter filter : filters) {
filter.init();
}
isRunning=true;
}
@Override
public void onPreDrawElements() {
}
@Override
public void destroy() {
destroyFrameBuffers();
for (AbsFilter filter : filters) {
filter.destroy();
}
isRunning=false;
}
@Override
public void onDrawFrame(int textureId) {
runPreDrawTasks();
if (frameBuffers == null || frameBufferTextures == null) {
return ;
}
int size = filters.size();
int previousTexture = textureId;
for (int i = 0; i < size; i++) {
AbsFilter filter = filters.get(i);
Log.d(TAG, "onDrawFrame: "+i+" / "+size +" "+filter.getClass().getSimpleName()+" "+
filter.surfaceWidth+" "+filter.surfaceHeight);
if (i < size - 1) {
GLES20.glViewport(0, 0, filter.surfaceWidth, filter.surfaceHeight);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffers[i]);
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
filter.onDrawFrame(previousTexture);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
previousTexture = frameBufferTextures[i];
}else{
GLES20.glViewport(0, 0 ,filter.surfaceWidth, filter.surfaceHeight);
filter.onDrawFrame(previousTexture);
}
}
}
@Override
public void onFilterChanged(int surfaceWidth, int surfaceHeight) {
super.onFilterChanged(surfaceWidth, surfaceHeight);
int size = filters.size();
for (int i = 0; i < size; i++) {
filters.get(i).onFilterChanged(surfaceWidth, surfaceHeight);
}
if(frameBuffers != null){
destroyFrameBuffers();
}
if (frameBuffers == null) {
frameBuffers = new int[size-1];
frameBufferTextures = new int[size-1];
for (int i = 0; i < size-1; i++) {
GLES20.glGenFramebuffers(1, frameBuffers, i);
GLES20.glGenTextures(1, frameBufferTextures, i);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, frameBufferTextures[i]);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA,
filters.get(i).surfaceWidth, filters.get(i).surfaceHeight, 0,
GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffers[i]);
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
GLES20.GL_TEXTURE_2D, frameBufferTextures[i], 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
}
}
}
private void destroyFrameBuffers() {
if (frameBufferTextures != null) {
GLES20.glDeleteTextures(frameBufferTextures.length, frameBufferTextures, 0);
frameBufferTextures = null;
}
if (frameBuffers != null) {
GLES20.glDeleteFramebuffers(frameBuffers.length, frameBuffers, 0);
frameBuffers = null;
}
}
public void addFilter(final AbsFilter filter){
if (filter==null) return;
//If one filter is added multiple times,
//it will execute the same times
//BTW: Pay attention to the order of execution
if (!isRunning){
filters.add(filter);
}
else
addPreDrawTask(new Runnable() {
@Override
public void run() {
filter.init();
filters.add(filter);
onFilterChanged(surfaceWidth,surfaceHeight);
}
});
}
public void addFilterList(final List<AbsFilter> filterList){
if (filterList==null) return;
//If one filter is added multiple times,
//it will execute the same times
//BTW: Pay attention to the order of execution
if (!isRunning){
for(AbsFilter filter:filterList){
filters.add(filter);
}
}
else
addPreDrawTask(new Runnable() {
@Override
public void run() {
for(AbsFilter filter:filterList){
filter.init();
filters.add(filter);
}
onFilterChanged(surfaceWidth,surfaceHeight);
}
});
}
}
原作:マーティン、元のリンク:https://blog.csdn.net/Martin20150405/article/details/55520358