虚幻4渲染编程(C++篇)【第二卷:PingPongOperation】

我的专栏目录:

YivanLee:专题概述及目录​zhuanlan.zhihu.com图标

简介:

乒乓缓冲其实在各个效果中都非常常用,它主要是为了制作渐变效果。如果是在CPU中,因为数据都在内存里所以做这件事情非常简单,把上次循环计算的结果作为下次计算的输入即可。但是在GPU如果只有一个RT的情况下,一次Draw的结果无法带入到下次Draw。所以需要建两个Buffer,在第一次绘制的时候把BufferA作为RT,BufferB作为输入,Draw完第一次后,第一次的计算结果保存到了BufferA中,然后进行第二次Draw。把BufferB最为RT然后把上次Draw的结果BufferA作为输入然后Draw,以此来制作渐变。

v2-d074c55598c517c6c3f7108e22e4658f_b.gif
v2-d64f3cf6a601774b8b78d59f2d972534.jpghttps://www.zhihu.com/video/1116315779766890496

除了双缓冲,我们还可以制作三缓冲四缓冲来让过渡更加平滑。


为了排除干扰,这里建个最简单的控制台应用程序。

v2-a78437c44776c1e4ca498ebc27cca161_b.jpg

这是最简单的Pingpong代码了。

然后我们来做复杂一点,在CPU上模拟一下GPU的行为(完全不用OpenGL或者DX,就纯C++)。我们实现一个Image类和Draw这个Image的方法

v2-f8d0401b7186108e2c19cb8a68daa8ad_b.jpg
#include "Image.h"
#include <fstream>
#include <iostream>

using namespace std;

void FImage::ClearImage(FLinerColor& ClearColor)
{
	cout << endl << "Begin clear image operation" << endl;

	int nx = Imagedata.size();
	int ny = Imagedata[0].size();

	for (int i = 0; i < nx; i++)
	{
		for (int j = 0; j < ny; j++)
		{
			Imagedata[i][j] = ClearColor.LinerColorToFColor();
		}
	}
}

void FImage::DrawImageData(const vector<FLinerColor>& ColorData)
{
	//       0
	//       ________________\x
	//       |               /
	//       |
	//       |
	//       |
	//       |
	//    Y \/                

	int nx = Imagedata.size();
	int ny = Imagedata[0].size();

	if (ColorData.size() != nx * ny)
	{
		cout << endl << "Draw Color data failed becuse the color data size not equal to image data size" << endl;
	}

	for (int j = 0; j < ny; j++)
	{
		for (int i = 0; i < ny; i++)
		{
			Imagedata[j][i] = ColorData[SizeY * j + i].LinerColorToFColor();
		}
	}
}

bool FImage::SaveImageToDesk(const string& Path)
{
	cout << endl << "begin save image to desk operation" << endl;

	bool bCreateImage = false;

	ofstream fout(Path + ImageName + ".ppm");
	fout << "P3\n" << SizeX << " " << SizeY << "\n255\n";

	for (int j = 0; j < SizeY; j++)
	{
		for (int i = 0; i < SizeX; i++)
		{
			FColor& color = Imagedata[i][j];

			fout << color.R << " " << color.G << " " << color.B << "\n";
		}
	}

	fout.close();

	cout << endl << "Save image successfully" << endl;

	return bCreateImage;
}

Core.h

#pragma once

class FColor
{
public:

	FColor() :
		R(0),
		G(0),
		B(0)
	{}

	FColor(int r, int g, int b) :
		R(r),
		G(g),
		B(b)
	{}

	~FColor() {}

	int R;
	int G;
	int B;
};

class FLinerColor
{

public:

	FLinerColor(float r, float g, float b) :
		R(r),
		G(g),
		B(b)
	{}

	FLinerColor() :
		R(0),
		G(0),
		B(0)
	{}

	~FLinerColor() {}

	FColor LinerColorToFColor() const
	{
		FColor retcol;

		retcol.R = (int)(R * 255);
		retcol.G = (int)(G * 255);
		retcol.B = (int)(B * 255);

		return retcol;
	}

	float R;
	float G;
	float B;
};

class FVector
{
public:

	FVector(float x, float y, float z) :
		X(x),
		Y(y),
		Z(z)
	{}

	FVector() {}
	~FVector() {}

	FVector operator+ (const FVector& Ref)
	{
		FVector ret;
		ret.X = this->X + Ref.X;
		ret.Y = this->Y + Ref.Y;
		ret.Z = this->Z + Ref.Z;

		return ret;
	}

	FVector operator- (const FVector& Ref)
	{
		FVector ret;
		ret.X = this->X - Ref.X;
		ret.Y = this->Y - Ref.Y;
		ret.Z = this->Z - Ref.Z;

		return ret;
	}

	FVector operator* (const FVector& Ref)
	{
		FVector ret;
		ret.X = this->X * Ref.X;
		ret.Y = this->Y * Ref.Y;
		ret.Z = this->Z * Ref.Z;

		return ret;
	}

	FVector operator/ (const FVector& Ref)
	{
		FVector ret;
		ret.X = this->X / Ref.X;
		ret.Y = this->Y / Ref.Y;
		ret.Z = this->Z / Ref.Z;

		return ret;
	}

	void operator= (const FVector& Ref)
	{
		this->X = Ref.X;
		this->Y = Ref.Y;
		this->X = Ref.Z;
	}

	float X;
	float Y;
	float Z;
};

class FVector2D
{
public:

	FVector2D(float x, float y) :
		X(x),
		Y(y)
	{}

	FVector2D() {}
	~FVector2D() {}


	FVector2D operator+ (const FVector2D& Ref)
	{
		FVector2D ret;
		ret.X = this->X + Ref.X;
		ret.Y = this->Y + Ref.Y;

		return ret;
	}

	FVector2D operator- (const FVector2D& Ref)
	{
		FVector2D ret;
		ret.X = this->X - Ref.X;
		ret.Y = this->Y - Ref.Y;

		return ret;
	}

	FVector2D operator* (const FVector2D& Ref)
	{
		FVector2D ret;
		ret.X = this->X * Ref.X;
		ret.Y = this->Y * Ref.Y;

		return ret;
	}

	FVector2D operator/ (const FVector2D& Ref)
	{
		FVector2D ret;
		ret.X = this->X / Ref.X;
		ret.Y = this->Y / Ref.Y;

		return ret;
	}

	void operator= (const FVector2D& Ref)
	{
		this->X = Ref.X;
		this->Y = Ref.Y;
	}

	float X;
	float Y;
};

好了有了个简单的图片渲染器下面就开始在C++中模拟GPU的行为吧。首先我们在Main函数中敲入如下代码:

v2-33a23b23c09a081adc6e08af3f920a80_b.jpg

执行后拿PhotoShop打开桌面生成的文件,我们得到了一张图片。

v2-a1f50dca37325ca5c99c0f4826f4dbb4_b.jpg

下面的代码我声明了两个buffer,一个ImageA一个ImageB,每次循环把一个作为Input一个作为Output,每次循环的操作也非常简单就是让图片亮度下降为原来的百分之八十。

#include <iostream>
#include <ctime>
#include <random>

#include "Core.h"
#include "Image.h"

using namespace std;

int main()
{
	FLinerColor ClearColor = FLinerColor(1, 1, 1);

	FImage* OutputImage = new FImage(64, 64, "OutputImage");
	FImage* BufferA = new FImage(64, 64, "ImageA");
	FImage* BufferB = new FImage(64, 64, "ImageB");

	BufferA->ClearImage(ClearColor);
	BufferB->ClearImage(ClearColor);
	vector<FImage*>ImagePingpongBuffer;
	ImagePingpongBuffer.push_back(BufferA);
	ImagePingpongBuffer.push_back(BufferB);

	int FrameIndex = 0;
	while (1)
	{
		int BufferIndexA = FrameIndex % 2;
		int BufferIndexB = (FrameIndex + 1) % 2;

		vector<vector<FColor>>& ImageTempData_Input = ImagePingpongBuffer[BufferIndexA]->Imagedata;
		vector<vector<FColor>>& ImageTempData_Output = ImagePingpongBuffer[BufferIndexB]->Imagedata;

		for (int i = 0; i < 64; i++)
		{
			for (int j = 0; j < 64; j++)
			{
				FColor& InputColor = ImageTempData_Input[i][j];
				FColor& OutputColor = ImageTempData_Output[i][j];

				OutputColor.R = InputColor.R * 0.8;
				OutputColor.G = InputColor.G * 0.8;
				OutputColor.B = InputColor.B * 0.8;
			}
		}

		OutputImage->Imagedata = ImageTempData_Output;
		OutputImage->ImageName += "1";
		OutputImage->SaveImageToDesk("C:/Users/yivanli/Desktop/");

		if (FrameIndex >= 10)
			break;
		FrameIndex++;
	}
	system("Pause");
	return 0;
}

我们在桌面可以得到一系列图片

v2-d978751fdd5465ec40a1ec651d1f23e5_b.jpg

在PS中打开把它们拼到一起可以看到

v2-bfbf71844b53f0635c57c86b5a84c884_b.jpg

每一格都是我们乒乓Buffer的输出结果。

至此我们使用纯C++完成了一个最简单的乒乓缓冲。


源代码链接:文件分享


Enjoy it

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cpongo11

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值