FWGS / xash3d

DEPRECATED in favor of https://github.com/FWGS/xash3d-fwgs. Only bugfixes are accepted.
https://xash.su
GNU General Public License v3.0
551 stars 107 forks source link

Software Based Strobing Implementation (Black Frame Insertion) #343

Closed fuzun closed 6 years ago

fuzun commented 6 years ago

New Version: #353

Simple strobe support with black frame insertion (replacement) technique.

Purpose Provides motion clarity by eliminating motion blur on high frequency monitors.

How Replaces certain amount of consequent frames with black frames. This makes a scanning effect like CRT monitors.

Usage Set r_strobe cvar to the black frame insertion interval (black frame count) you want (ideal values for 144hz monitors are probably -2, -1 = 1, 2, 3) . Frame display interval can be changed with gl_swapInterval cvar. Use cl_showfps 1 to see the actual / effective fps (displayed non-black frame count in 1 second).

Note that V-Sync must be active to enable this! (gl_swapInterval != 0)

Issues

Web Demo: https://www.testufo.com/blackframes#count=2&bonusufo=0&equalizer=1&background=000000

Not tested on Android yet!

fuzun commented 6 years ago

This was supposed to get updated to support phase swaps and bunch of other things along actually.

Discussion: https://forums.blurbusters.com/viewtopic.php?f=7&t=3815&p=30401

New prototype:

void R_Strobe(void)
{
   ...

   if ((SwapPhaseInfo.fCounter % 2) == 0) //Starts with +
   {
      ++SwapPhaseInfo.pCounter;
      SwapPhaseInfo.isPositive = true;
   }
   else
   {
      ++SwapPhaseInfo.nCounter;
      SwapPhaseInfo.isPositive = false;
   }

   if (swapInterval < 0)
      swapInterval = abs(swapInterval);

   if ((swapInterval != 0) && (getInterval % 2 != 0)) //Swapping is not enabled for even intervals. Because it makes it like r_strobe -interval . It is not needed anyway since even intervals do not cause burn in problem.
   {
      currentTime = Sys_DoubleTime();
      delta = currentTime - recentTime;
      if ((delta >= (double)(swapInterval)) && (delta < (double)(2 * swapInterval)))
      {
         SwapPhaseInfo.isInverted = true;
      }
      else if (delta < (double)(swapInterval))
      {
         SwapPhaseInfo.isInverted = false;
      }
      else
      {
         recentTime = currentTime;
      }
   }

   if (SwapPhaseInfo.isPositive == true && SwapPhaseInfo.isInverted == false)
   {
      if (abs(getInterval) % 2 == 0)
         SwapPhaseInfo.isNormal = (((SwapPhaseInfo.pCounter - 1) % (abs(getInterval) + 1)) == 0) ? true : false; //even
      else
         SwapPhaseInfo.isNormal = (((SwapPhaseInfo.pCounter -1) % ((abs(getInterval) + 1) / 2)) == 0) ? true : false; //odd

      if (abs(getInterval) == 1)
         SwapPhaseInfo.isNormal = true;
   }
   else if (SwapPhaseInfo.isPositive == true && SwapPhaseInfo.isInverted == true)
   {
      if ((abs(getInterval) % 2) == 0)
         SwapPhaseInfo.isNormal = (((SwapPhaseInfo.pCounter - 1) % (abs(getInterval) + 1)) == (abs(getInterval) / 2)) ? true : false; //even
      else
         SwapPhaseInfo.isNormal = false;
      if (abs(getInterval) == 1)
         SwapPhaseInfo.isNormal = false;
   }
   else if (SwapPhaseInfo.isPositive == false && SwapPhaseInfo.isInverted == false)
   {
      if ((abs(getInterval) % 2) == 0)
         SwapPhaseInfo.isNormal = (((SwapPhaseInfo.nCounter - 1) % (abs(getInterval) + 1)) == (abs(getInterval) / 2)) ? true : false; //even
      else
         SwapPhaseInfo.isNormal = false;
      if (abs(getInterval) == 1)
         SwapPhaseInfo.isNormal = false;
   }
   else if (SwapPhaseInfo.isPositive == false && SwapPhaseInfo.isInverted == true)
   {
      if (abs(getInterval) % 2 == 0)
         SwapPhaseInfo.isNormal = (((SwapPhaseInfo.nCounter - 1) % (abs(getInterval) + 1)) == 0) ? true : false; //even
      else
         SwapPhaseInfo.isNormal = (((SwapPhaseInfo.nCounter - 1) % ((abs(getInterval) + 1) / 2)) == 0) ? true : false; //odd

      if (abs(getInterval) == 1)
         SwapPhaseInfo.isNormal = true;
   }

   if (getInterval < 0)
      SwapPhaseInfo.isNormal = !SwapPhaseInfo.isNormal;

   if (SwapPhaseInfo.isNormal) //Show normal
   {
      R_Set2DMode(false);
      if (SwapPhaseInfo.isPositive)
         ++SwapPhaseInfo.pNCounter;
      else
         ++SwapPhaseInfo.nNCounter;
   }
   else //Show black frame
   {
      gl_GenerateBlackFrame();
      if (SwapPhaseInfo.isPositive)
         ++SwapPhaseInfo.pBCounter;
      else
         ++SwapPhaseInfo.nBCounter;
   }
   ++SwapPhaseInfo.fCounter;
}

Maybe revert and re-open the pull request? Or leave this as is and I will open a new pull request soon?

a1batross commented 6 years ago

Just open new PR based on formatting fixes from master. I did it as an example for you. Please, follow them. :)

a1batross commented 6 years ago

Everything else is good, and I can't wait for further development of such cool feature for engine.

fuzun commented 6 years ago

Yeah the idea is pretty cool actually. As far as I know it will be the first 3D engine that has built-in software based strobing. I want to know if you would like to see strobe settings in the video configuration setting. I can work on that as well.

a1batross commented 6 years ago

@fuzun yes. You can try to add them in menu. Just send PR to mainui_cpp repository.

fuzun commented 6 years ago

https://github.com/fuzun/xash3d/commits/strobe-support

a1batross commented 6 years ago

@fuzun Sorry for late answer, I was busy these two days. Great work!

I will completely review and test it, with next PR.

There is also some problems with strobing on Mesa drivers. They are pretty bad with VSync and tear-free, but I think it's not your trouble. Meanwhile it works on my Android device perfectly, although my RN4X have 60Hz screen.