我的专栏目录:
YivanLee:专题概述及目录![图标](https://i-blog.csdnimg.cn/blog_migrate/b09af8aa7673f507dda5232c8fed91cd.png)
简介:
乒乓缓冲其实在各个效果中都非常常用,它主要是为了制作渐变效果。如果是在CPU中,因为数据都在内存里所以做这件事情非常简单,把上次循环计算的结果作为下次计算的输入即可。但是在GPU如果只有一个RT的情况下,一次Draw的结果无法带入到下次Draw。所以需要建两个Buffer,在第一次绘制的时候把BufferA作为RT,BufferB作为输入,Draw完第一次后,第一次的计算结果保存到了BufferA中,然后进行第二次Draw。把BufferB最为RT然后把上次Draw的结果BufferA作为输入然后Draw,以此来制作渐变。
![v2-d074c55598c517c6c3f7108e22e4658f_b.gif](https://i-blog.csdnimg.cn/blog_migrate/c803e44f8cf48c80aa3b81ace596ab7e.gif)
![v2-d64f3cf6a601774b8b78d59f2d972534.jpg](https://i-blog.csdnimg.cn/blog_migrate/00336a99982af3a6601d712f2024f80f.jpeg)
除了双缓冲,我们还可以制作三缓冲四缓冲来让过渡更加平滑。
为了排除干扰,这里建个最简单的控制台应用程序。
![v2-a78437c44776c1e4ca498ebc27cca161_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/78bf7b87cf28e90c9ed5e55d8ee40eda.jpeg)
这是最简单的Pingpong代码了。
然后我们来做复杂一点,在CPU上模拟一下GPU的行为(完全不用OpenGL或者DX,就纯C++)。我们实现一个Image类和Draw这个Image的方法
![v2-f8d0401b7186108e2c19cb8a68daa8ad_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/20bb814e59f677914e754b4315c004cd.jpeg)
#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](https://i-blog.csdnimg.cn/blog_migrate/35605686663978b708b2a66d565585a9.jpeg)
执行后拿PhotoShop打开桌面生成的文件,我们得到了一张图片。
![v2-a1f50dca37325ca5c99c0f4826f4dbb4_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/c85426e7212e9f5eed17812a68d02c54.jpeg)
下面的代码我声明了两个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](https://i-blog.csdnimg.cn/blog_migrate/2bb988e4feb8cf76e41718e295788e88.jpeg)
在PS中打开把它们拼到一起可以看到
![v2-bfbf71844b53f0635c57c86b5a84c884_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/77d784e8463596121eb26be1dffe063b.jpeg)
每一格都是我们乒乓Buffer的输出结果。
至此我们使用纯C++完成了一个最简单的乒乓缓冲。
源代码链接:文件分享
Enjoy it