1 つの白いピクセルを描画し、それを各フレームで 1 ピクセルの方向に移動すると、各フレームで画面ピクセルの R/G/B 値が 1 つ少なくなり (範囲 0 ~ 255)、したがって 255 フレーム後に白いピクセルは完全に黒くなります。したがって、白いピクセルを移動すると、グラデーションの軌跡が白から黒に均等に移動し、前のピクセルの色と比較して 1 色の値が異なります。
これを行う方法を説明する前に、あなたが求めている視覚効果はひどい視覚効果であり、使用すべきではないことをお伝えしたいと思います。RGB の各色から値を差し引くと、同じ色の暗いバージョンではなく、別の色が生成されます。RGB カラー (255,128,0) から 1 を 128 回引くと (128, 0, 0) になります。1色目はブラウン、2色目はダークレッドです。これらは同じではありません。
さて、あなたはこれをあまりよく説明していないので、いくつか推測する必要があります. あなたがレンダリングしているものには「オブジェクト」がないと仮定しています。状態はありません。あなたは単に任意の場所に何かを描いているだけで、何をどこに描いたか覚えていないし、何をどこに描いたかを覚えたくもない.
必要なことを行うには、2 つのオフスクリーン バッファーが必要です。これらにはFBOと画面サイズのテクスチャを使用することをお勧めします。基本的なアルゴリズムは単純です。書き込む色から「1 を引く」ブレンド モードを使用して、前のフレームの画像を現在の画像にレンダリングします。次に、必要な新しいものを現在の画像にレンダリングします。次に、その画像を表示します。その後、前の画像と現在の画像を入れ替えて、最初からやり直します。
注: 次のコードは、OpenGL 3.3 の機能を前提としています。
初期化
最初に、初期化中 (OpenGL の初期化後) に、画面サイズのテクスチャを作成する必要があります。また、画面サイズの深度バッファーが 2 つ必要です。
GLuint screenTextures[2];
GLuint screenDepthbuffers[2];
GLuint fbos[2]; //Put these definitions somewhere useful.
glGenTextures(2, screenTextures);
glGenRenderbuffers(2, screenDepthbuffers);
glGenFramebuffers(2, fbos);
for(int i = 0; i < 2; ++i)
{
glBindTexture(GL_TEXTURE_2D, screenTextures[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, SCREEN_WIDTH, SCREEN_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
glBindRenderbuffer(GL_RENDERBUFFER, screenDepthBuffers[i]);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, SCREEN_WIDTH, SCREEN_HEIGHT);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[i]);
glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, screenTextures[i], 0);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, screenDepthBuffers[i]);
if(glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
//Error out here.
}
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}
前のフレームの描画
次のステップでは、前のフレームの画像を現在の画像に描画します。
これを行うには、以前と現在の FBO の概念が必要です。currIndex
これは、との 2 つの変数を持つことによって行われますprevIndex
。これらの値は、テクスチャ、レンダ バッファ、および FBO の GLuint 配列へのインデックスです。次のように初期化する必要があります (フレームごとではなく、初期化中に)。
currIndex = 0;
prevIndex = 1;
描画ルーチンの最初のステップは、前のフレームから 1 を引いて描画することです (ここでも、リアル ブレンドを使用することを強くお勧めします)。
これは完全なコードではありません。あなたが記入することを期待している疑似コードがあります。
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[currIndex]);
glClearColor(...);
glClearDepth(...);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0 + 0);
glBindTexture(GL_TEXTURE_2D, screenTextures[prevIndex]);
glUseProgram(BlenderProgramObject); //The shader will be talked about later.
RenderFullscreenQuadWithTexture();
glUseProgram(0);
glBindTexture(GL_TEXTURE_2D, 0);
このRenderFullscreenQuadWithTexture
関数は、現在バインドされているテクスチャを使用して、画面のサイズのクワッドをレンダリングします。プログラム オブジェクトBlenderProgramObject
は、ブレンド操作を行う GLSL シェーダーです。テクスチャから取得し、ブレンドを行います。繰り返しますが、シェーダーなどの設定方法を知っていることを前提としています。
フラグメント シェーダーには、次のようなメイン関数があります。
shaderOutput = texture(prevImage, texCoord) - (1.0/255.0);
繰り返しますが、私はこれを強くお勧めします:
shaderOutput = texture(prevImage, texCoord) * (0.05);
シェーダーの使い方がわからない場合は、学習する必要があります。しかし、そうしたくない場合は、glTexEnv関数を使用して同じ効果を得ることができます。それらが何であるかわからない場合は、シェーダーを学ぶことをお勧めします。長期的にははるかに簡単です。
通常どおりに描画する
これで、通常どおりすべてをレンダリングするだけです。FBO のバインドを解除しないでください。まだレンダリングしたいのです。
レンダリング イメージを画面に表示する
通常、レンダリングの結果を表示するには、swapbuffer 呼び出しを使用します。しかし、FBO にレンダリングしたので、それはできません。代わりに、何か違うことをしなければなりません。イメージをバックバッファーにブリットしてから、バッファーをスワップする必要があります。
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos[currIndex]);
glBlitFramebuffer(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, SCREEN_WDITH, SCREEN_HEIGHT, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
//Do OpenGL swap buffers as normal
画像の切り替え
ここで、もう 1 つ行う必要があります。使用している画像を切り替えます。前のイメージが現在のイメージになり、その逆も同様です。
std::swap(currIndex, prevIndex);
これで完了です。