В Unity у меня есть следующий метод, который берет камеру и создает прозрачное изображение (которое уже хорошо работает). RenderTexture rt
строится и создается вне этого метода.
Texture2D reallyTakeShot() {
camera.backgroundColor = Color.white;
Texture2D texWhite = new Texture2D(width, height, TextureFormat.RGB24, mipmap: false);
DoCameraRender(camera, shader);
RenderTexture.active = rt;
texWhite.ReadPixels(new Rect(0f, 0f, width, height), 0, 0);
texWhite.Apply();
camera.backgroundColor = Color.black;
DoCameraRender(camera, shader);
Texture2D texBlack = new Texture2D(width, height, TextureFormat.RGB24, mipmap: false);
texBlack.ReadPixels(new Rect(0f, 0f, width, height), 0, 0);
texBlack.Apply();
Texture2D transparentTexture = new Texture2D(width, height, TextureFormat.ARGB32, mipmap: false);
for (int y = 0; y < transparentTexture.height; ++y)
{
for (int x = 0; x < transparentTexture.width; ++x)
{
float alpha = 1.0f - (texWhite.GetPixel(x, y).r - texBlack.GetPixel(x, y).r);
Color color;
if ((int)(alpha * 100) == 0)
{
color = Color.clear;
}
else
{
color = texBlack.GetPixel(x, y) / alpha;
color.a = alpha;
}
transparentTexture.SetPixel(x, y, color);
}
}
return transparentTexture;
То, что я пытаюсь сделать, это сделать серию снимков, которые на самом деле не должны быть на 100% гладкими, но мне не очень везет. Из 30 снимков большая часть выглядит одинаково.
Самым последним, что я сделал, был цикл, который вызывает
У которого есть следующий метод (который выводит 0 мс для захвата, поэтому я предполагаю, что он быстрый)
private IEnumerator TakeShotNoWrite()
{
shooting = CaptureStates.Capturing;
Stopwatch sw = new Stopwatch();
bool useJpeg = ModPrefs.GetBool("HomeShot", "use_jpeg");
int outputWidth = ModPrefs.GetInt("HomeShot", "output_width");
Texture2D tex = TakeShotToTexture2(!useJpeg);
caps.Add(tex);
shooting = CaptureStates.NormalCapture;
Console.WriteLine("capture took " + sw.Elapsed.TotalMilliseconds + "ms");
yield return new WaitForFixedUpdate();
// tried these too
// yield return 0;
// yield return WaitForNextFrame();
}
Я активирую камеру FixedUpdate()
(это единственный раз, когда я могу заставить объекты выглядеть правильно LateUpdate()
не работает)
private void FixedUpdate()
{
if (shooting == CaptureStates.Capturing)
{
return;
}
if (shooting != CaptureStates.NotCapturing)
{
// Extra StopWatch logic so I can increase the minimum time
// before next screen shot I set it to 30ms.
StartCoroutine(TakeShotNoWrite()); // the render to texture code is above.
// when stopwatch reaches 15 seconds start writing the files
}
}
Пожалуйста, опубликуйте свой ответ как «Ответ» и примите его, чтобы, если у кого-то еще возникла такая же проблема, он мог ее найти.
Это моя реализация. Я могу сделать 200 снимков за 10 секунд (около 20 кадров в секунду), сохраненных в формате 1080p. Но это уменьшается до 60 кадров за 10 секунд в 4K. Я думаю, что это двойной рендеринг, но это удовлетворяет то, что мне нужно на данный момент.
public class BurstShooter : MonoBehaviour
{
private Camera camera;
private Texture2D[] blacks;
private Texture2D[] whites;
/**
* Number of captures
*/
private int caps;
private int width;
private int height;
private int burstSeconds;
private int burstInterval;
private Stopwatch burstWatch = new Stopwatch();
private Color backgroundColor;
private RenderTexture rt;
private double lastBurstMillis;
private void Start()
{
}
public void Shoot()
{
if (!burstWatch.IsRunning)
{
StartCoroutine(TakeBurstShotCoroutine());
}
}
private IEnumerator<YieldInstruction> TakeBurstShotCoroutine()
{
StartTakeBurstShot();
while (burstWatch.Elapsed.TotalSeconds < burstSeconds && caps < blacks.Length)
{
yield return new WaitForEndOfFrame();
if (burstWatch.Elapsed.TotalMilliseconds - lastBurstMillis > burstInterval)
{
lastBurstMillis = burstWatch.Elapsed.TotalMilliseconds;
TakeBurstShot();
}
}
burstWatch.Stop();
RenderTexture.ReleaseTemporary(rt);
yield return new WaitForEndOfFrame();
// Flush Caps takes the Texture2Ds and
// converts them to PNG. Not going to
// bother with this as it is outside
//the scope of the question.
FlushCaps();
}
private void StartTakeBurstShot()
{
// these can be larger than the screen
width = 1920;
height = 1080;
rt = getRenderTexture();
SelectCamera();
backgroundColor = camera.backgroundColor;
burstInterval = 15;
burstSeconds = 15;
// assume 60 frames per second max
blacks = new Texture2D[60 * burstSeconds];
whites = new Texture2D[60 * burstSeconds];
caps = 0;
burstWatch.Reset();
burstWatch.Start();
lastBurstMillis = -burstInterval;
}
private void TakeBurstShot()
{
Stopwatch sw = new Stopwatch();
sw.Reset();
sw.Start();
try
{
camera.targetTexture = rt;
RenderTexture.active = rt;
Texture2D texWhite = new Texture2D(width, height, TextureFormat.RGB24, mipmap: false);
texWhite.filterMode = FilterMode.Point;
texWhite.wrapMode = TextureWrapMode.Clamp;
Texture2D texBlack = new Texture2D(width, height, TextureFormat.RGB24, mipmap: false);
texBlack.filterMode = FilterMode.Point;
texBlack.wrapMode = TextureWrapMode.Clamp;
camera.backgroundColor = Color.white;
camera.Render();
texWhite.ReadPixels(new Rect(0f, 0f, width, height), 0, 0);
texWhite.Apply();
camera.backgroundColor = Color.black;
camera.Render();
texBlack.ReadPixels(new Rect(0f, 0f, width, height), 0, 0);
texBlack.Apply();
blacks[caps] = texBlack;
whites[caps] = texWhite;
++caps;
}
finally
{
camera.backgroundColor = backgroundColor;
RenderTexture.active = null;
camera.targetTexture = null;
}
}
}
Я решил эту проблему, переместив операцию прозрачности за пределы камеры и используя сопрограммы и yieldWaitForEndFrame.