3D场景画面放大输出的方法[OpenGL]
需求描述: 普通的OpenGL输出BMP图像的分辨率就是窗口尺寸(w*h),现在要求将窗口场景放大n倍输出分辨率为((w*n)*(h*n))的BMP图像,并且BMP画面内容 要与窗口场景一致。
需求分析: 首先相机位置不能移动,因为对透视投影来说,同一个物体从不同位置看到的结果是不同的,这样渲染出的场景是无法拼接到一起的。
功能实现: 思路一:改变ViewPort的大小为原来的n*n倍,起始位置x,y变化,从而得到分块的场景。限制条件就是Viewport的最大值MAX_VIEWPORT跟显卡硬件相关[4096],不能无限放大输出.
思路二:改变投影参数,将小块场景投射到整个窗口渲染输出,然后拼接在一起从而实现大分辨率输出。
正交投影下实现此功能很简单,只需要分别设置投影的左右上下面的值,分块渲染并得到场景数据(glReadPixels),然后将分块数据合成一个整个的大的BMP输出。
透视投影比较复杂,如果当前的投影模式用的是对称投影gluperspective,那么首先要将其转为对应的非对称投影gluFrustum, 根据得到的left,right,top,bottom值分割渲染得到场景数据, 然后将分块数据合成一个整个的大的BMP输出。
代码实现:
//// Get the requested extents. 相当于glufrustum的left , right ,top ,bottom, near, far
int inExtent[4]; //这里似乎不应该是int值
inExtent[0] = 0;
inExtent[1] = w*Magnification;
inExtent[2] = 0;
inExtent[3] = h*Magnification;
int inWindowExtent[4];
// convert the request into windows
inWindowExtent[0] = inExtent[0]/w;
inWindowExtent[1] = inExtent[1]/w;
inWindowExtent[2] = inExtent[2]/h;
inWindowExtent[3] = inExtent[3]/h;
int LineLength, TotalLength;
int nExtentWidth = abs(inExtent[1]-inExtent[0]);
int nExtentHeight = abs(inExtent[3]-inExtent[2]);
LineLength = nExtentWidth * BytesPerPixel; // 每行数据长度大致为图象宽度乘以
while( LineLength % 4 != 0 ) // 修正LineLength使其为4的倍数
++LineLength;
// 每像素的字节数
TotalLength = LineLength * abs(inExtent[3]-inExtent[2]); // 数据总长 = 每行长度 * 图象高度
pData = new unsigned char[TotalLength]; //建立一个大的数据buffer用于写入
int x,y;
// render each of the tiles required to fill this request
int row = 0;
double cx,cy;
int offset = 0;
CString strFilePath;
for (y = inWindowExtent[2]; y < inWindowExtent[3]; y++)
{
int col = 0;
for (x = inWindowExtent[0]; x < inWindowExtent[1]; x++)
{
cx = x*2 - Magnification*(1-windowCenter[0]) + 1;
cy = y*2 - Magnification*(1-windowCenter[1]) + 1;
pCamera->SetWindowCenter(cx,cy);
pMapView->Render();
// add
//pView->Render();
// 读到的是pMainView的图像
glReadPixels(0,0,w,h,GL_BGR_EXT,GL_UNSIGNED_BYTE,pPixelData);
//BSTR filename = (col > 0) ? _bstr_t("f:\\test1.bmp") : _bstr_t("f:\\test2.bmp");
//if (!WriteBmpFile(w,h,pPixelData,filename))
// return false;
for (int i = 0; i < h; i++)
{
offset = row*h*LineLength + col*w*BytesPerPixel + i*LineLength;
memcpy((unsigned char*)pData+offset,(unsigned char*)pPixelData+(i*nBlockLineLength),nBlockLineValidDataLength);
}
col++;
}
row++;
}
// 输出为BMP文件
if (!WriteBmpFile(nExtentWidth,nExtentHeight,pData,str))
return S_FALSE;
bool CSaveImageTool::WriteBmpFile(int w, int h, unsigned char *pdata, BSTR filename)//char *pBmpFileName)
{
unsigned char header[COLORTABLE] = {
0x42, 0x4d, 0, 0, 0, 0, 0, 0, 0, 0,
54, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 24, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0
};
long file_size = (long)w * (long)h * 3 + 54;
header[2] = (unsigned char)(file_size &0x000000ff);
header[3] = (file_size >> 8) & 0x000000ff;
header[4] = (file_size >> 16) & 0x000000ff;
header[5] = (file_size >> 24) & 0x000000ff;
long width = w;
header[18] = width & 0x000000ff;
header[19] = (width >> 8) &0x000000ff;
header[20] = (width >> 16) &0x000000ff;
header[21] = (width >> 24) &0x000000ff;
long height = h;
header[22] = height &0x000000ff;
header[23] = (height >> 8) &0x000000ff;
header[24] = (height >> 16) &0x000000ff;
header[25] = (height >> 24) &0x000000ff;
FILE *pWritingFile = NULL;
//pWritingFile = fopen(pBmpFileName, "wb");
_bstr_t bFileName = filename;
char* pBmpFileName = bFileName;
pWritingFile = fopen(pBmpFileName, "wb");
if( pWritingFile == NULL )
return false;
fwrite(header, sizeof(unsigned char), 54, pWritingFile);
int BytesPerPixel = 3;
int LineLength, TotalLength;
LineLength = w * BytesPerPixel; // 每行数据长度大致为图象宽度乘以
// 每像素的字节数
while( LineLength % 4 != 0 ) // 修正LineLength使其为4的倍数
++LineLength;
TotalLength = LineLength * h; // 数据总长 = 每行长度 * 图象高度
fwrite(pdata, sizeof(unsigned char), (size_t)(long)TotalLength, pWritingFile);
// 释放内存和关闭文件
fclose(pWritingFile);
return true;
}