5

フェードトゥブラック効果を実現しようとしていますが、その方法がわかりません。私はいくつかのことを試しましたが、openglの仕組みが原因で失敗しました

それがどのように機能するかを説明します:

1 つの白いピクセルを描画し、それを各フレームで 1 ピクセルの方向に移動すると、各フレームで画面ピクセルの R/G/B 値が 1 つ少なくなり (範囲は 0 ~ 255)、したがって 255 フレーム後に白いピクセルは完全に黒くなります。したがって、白いピクセルを移動すると、グラデーションの軌跡が白から黒に均等に移動し、前のピクセルの色と比較して 1 色の値が異なります。

編集:これを行うシェーダー以外の方法を知りたいのですが、それが不可能な場合は、シェーダーの方法も受け入れることができます。

編集 2:このあたりで混乱が生じているので、シーン全体に黒い透明なクワッドを描画することで、この種の効果を既に実行できることを伝えたいと思います。しかし、これは機能したいので機能しません。ピクセルが取得できる暗さには制限があるため、次の理由により、ピクセルの一部が常に「表示」されたままになります (ゼロの色値を超える)。1*0.9 = 0.9 -> 再び 1 に丸められるなど「これはトレイルを短くすることによるものですが、トレイルの長さを可能な限り調整できるようにしたいので、バイリニア(正しい言葉であれば)補間の代わりに線形が必要です(したがって、常に各rから-1を減らします。パーセント値を使用する代わりに、0 ~ 255 スケールの g,b 値)。

Edit3:まだいくつかの混乱が残っているので、はっきりさせておきます: glClear() から GL_COLOR_BUFFER_BIT を無効にすることによって行われる効果を改善したいのですが、画面上のピクセルを永遠に見たくないので、時間内にそれらを暗くしたいです、各ピクセルの色の値を 1 (0 ~ 255 スケール) 減らすシーンの上にクワッドを描画します。

Edit4:簡単にします。これにはOpenGLメソッドが必要です。効果は、電力、メモリ、または帯域幅をできるだけ少なくする必要があります。この効果は、画面のピクセルをクリアしなくても機能するはずなので、シーンに透明なクワッドを描画すると、以前に描画されたピクセルが暗くなります。しかし、上記で何度か説明したように、うまく機能しません。大きなNOは次のとおりです。1)画面からピクセルを読み取り、forループで1つずつ変更してからアップロードし直します。2) トレイル効果をエミュレートするために、異なる暗さでオブジェクトを X 回レンダリングします。3) ピクセルを黒くしないため、色の値を乗算することはできません。ピクセルは特定の明るさで画面に永久に表示されます (上記の説明を参照)。

4

5 に答える 5

9

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);

これで完了です。

于 2011-08-22T00:11:09.533 に答える
6

You may want to render a black rectangle with alpha going from 1.0 to 0.0 using glBlendFunc (GL_ONE, GL_SRC_ALPHA).

Edit in response to your comment (reply doesn't fit in a comment):

You cannot fade single pixels depending on their age with a simple fade-to-black operation. Usually a render target does not "remember" what has drawn to it in previous frames. I could think of a way to do this by alternatingly rendering to one of a pair of FBOs and using their alpha channel for it, but you needed a shader there. So what you would do is first render the FBO containing the pixels at their previous positions, decreasing their alpha value by one, dropping them when alpha == 0, otherwise darkening them whenever their alpha decreases, then render the pixels at their current positions with alpha == 255.

If you only have moving pixels:

render FBO 2 to FBO 1, darkening each pixel in it by a scale (skip during first pass)
render moving pixels to FBO 1
render FBO 1 to FBO 2 (FBO 2 is the "age" buffer)
render FBO 2 to screen

If you want to modify some scene (i.e. have a scene and moving pixels in it):

set glBlendFunc (GL_ONE, GL_ZERO)
render FBO 2 to FBO 1, reducing each alpha > 0.0 in it by a scale (skip during first pass)
render moving pixels to FBO 1
render FBO 1 to FBO 2 (FBO 2 is the "age" buffer)
render the scene to screen
set glBlendFunc (GL_ONE, GL_SRC_ALPHA)
render FBO 2 to screen

Actually the scale should be (float) / 255.0 / 255.0 to make the components equally fade away (and not one that started at a lower value become zero before the others do).

If you only have a few moving pixels, you could re-render the pixel at all previous positions up to 255 "ticks" back.

Since you need to re-render each of the pixels anyway, just render each one with the proper color gradient: Darker, the older the pixel is. If you have a real lot of pixels, the dual FBO approach might work.

I am writing ticks, and not frames, because frames can take a varying amount of time depending on renderer and hardware, but you probably want to have the pixel trail fade away within a constant time. That means you need to dim each pixel only after so-and-so many milliseconds, keeping their color for the frames in between.

于 2011-08-21T12:37:52.933 に答える
2

readpixelsこれを行うシェーダー以外の方法の 1 つは、特に黒へのフェードだけが画面に表示される場合は、iircを介して画面のコンテンツを取得し、それらをテクスチャにポップし、画面に長方形を配置することです。そのテクスチャを使用すると、長方形の色を黒に調整して、達成したい効果を得ることができます。

于 2011-08-21T13:45:27.683 に答える
1

このような状況なので、純粋な OpenGL を使用できません。プロジェクトに余裕があるかどうか (別のウィンドウ API を使用している場合はそうでない可能性があります)、または追加された複雑さがそれだけの価値があるかどうかはわかりませんが、OpenGL で動作する SDL のような 2D ライブラリを追加すると、合理的な方法でディスプレイ表面のピクセルを直接操作するだけでなく、一般的なピクセルのみを操作することもできますが、これは OpenGL では一般的に容易ではありません。

次に、OpenGL がジオメトリをレンダリングする前にディスプレイ サーフェスのピクセルを実行し、各 RGB コンポーネントから 1 を引くだけです。

OpenGLで追加のライブラリを使用することがオプションである場合、それはとにかく私が見ることができる最も簡単な解決策です.

于 2011-08-24T05:14:55.177 に答える
1

それはドライバーです。Windows 自体は OpenGL をサポートしていないか、バージョンが 1.5 しかサポートしていません。すべての新しいバージョンには、ATI または NVIDIA、Intel などのドライバーが付属しています。別のカードを使用していますか? どのバージョンの OpenGL を効果的に使用していますか?

于 2011-07-25T00:11:40.833 に答える