当前位置:硬件测评 > OpenGL中的深度、深度缓存、深度测试并保存为图片

OpenGL中的深度、深度缓存、深度测试并保存为图片

  • 发布:2023-10-10 11:04

-->

1,深度

所谓深度就是openGL坐标系中像素点的Z坐标到相机的距离。相机可以放置在坐标系中的任意位置,所以不能简单地说Z值越大或越小,离相机越近。

2。深度缓冲区

深度缓冲区的原理是将观察平面(近裁剪平面)的深度值(或距离)与窗口中的每个像素相关联。
首先,使用glClear(GL_DEPTH_BUFFER_BIT)将所有像素的深度值设置为最大值(通常是远裁剪平面)。
然后,以任意顺序绘制场景中的所有对象。由硬件或软件执行的图形计算将每个绘图表面转换为窗口上的像素集合,无论其是否被其他对象阻挡。
其次,OpenGL 将计算这些表面与观察平面之间的距离。如果启用了深度缓冲区,则在绘制每个像素之前,OpenGL 会将其深度值与已存储在该像素处的深度值进行比较。如果新的像素深度值小于原像素深度值,则新的像素值将替换原像素深度值;否则,如果新的像素值被遮挡,则其颜色值和深度将被丢弃。

为了启用深度缓冲区,必须先启用它,即glEnable(GL_DEPTH_TEST)。在绘制每个场景之前,需要清除深度缓冲区,即glClear(GL_DEPTH_BUFFER_BIT),然后以任意顺序绘制场景中的对象。

数学基础:

要渲染的相机空间中的深度通常定义为近端和远端之间的 z 值。 Z 坐标与 X 和 Y 坐标相同。经过变换、裁剪和透视分割后,Z 的范围为 -1.0 到 1.0。 DepthRange 映射指定 Z 坐标的变换,这类似于用于将 X 和 Y 映射到窗口坐标的视口变换。透视变换后,得到新的z'值:

?值,有时表示为 w 或 w'。

结果 z' 为 -1 和 1 之间的归一化值,其中近平面位于 -1,远平面位于 1。在此范围之外的对应点位于视图体积之外,不需要被渲染。

为了实现深度缓冲,z'的值是通过在整个屏幕空间中的当前多边形顶点之间插值来计算的。通常这些中间值以定点格式保存在深度缓冲区中。越接近近平面,z'值越密集;距离越远,z' 值越稀疏。距离相机越近,准确度越高。近平面距离相机越近,距离位置就越不准确。近距离 远处物体中人为错误的常见来源是平面离相机太近。

3。深度测试

OpenGL中的深度测试使用深度缓冲算法来消除场景中不可见的面。默认情况下,深度缓存中深度值的范围在0.0到1.0之间。这个范围值可以通过函数传递:
   glDepthRange(nearNormDepth, farNormalDepth);之间。这里nearNormDepth和farNormalDepth可以取0.0到1.0之间的任何值,甚至nearNormDepth>farNormalDepth。这样,通过glDepthRange函数就可以在透视投影的有限观察空间内的任意区域进行深度测试。
        另一个非常有用的函数是:
      glClearDepth (maxDepth); glClearDepth 使用 maxDepth 初始化深度缓冲区,默认情况下,深度缓冲区初始化为 1.0。由于深度测试时不会绘制大于深度缓存初始值的多边形,因此可以使用 glClearDepth 函数来加速深度测试过程。这里需要注意的是,指定深度缓存的初始化值后,应该调用:
  glClear(GL_DEPTH_BUFFER_BIT);完成深度缓存的初始化。
在深度测试中,默认是将需要绘制的新像素的z值与深度缓冲区中相应位置的z值进行比较。如果小于深度缓冲区中的值,则用新像素的颜色值更新帧缓冲区中对应像素的颜色值。这种对比测试的方法可以通过函数
    glDepthFunc(func);
进行修改。参数func的值可以是GL_NEVER(不处理)、GL_ALWAYS(处理全部)、GL_LESS(小于)、GL_LEQUAL(小于等于)、GL_EQUAL(等于)、GL_GEQUAL(大于等于)、 GL_GREATER(大于)或 GL_NOTEQUAL(不等于),其中默认值为 GL_LESS。这些测试可以降低各种应用中深度缓冲区处理的计算成本。

opengl中有一个非常有用的函数:glReadPixels(),可以读取各种缓冲区的值(深度、颜色等)。要将OpenGL绘制场景保存为图片,也需要使用该函数。

以下 c 程序显示了一个简单的示例。按键盘上的“C”键,将读取的图像缓冲区数据存储为tmpcolor.txt。
#include "windows.h"
#包括
#包括
#包括
使用命名空间std;
//
typedef GLbyte* bytePt;
int winWidth = 400;
int winHeight = 400;
int arrLen = winWidth * winHeight * 3;
GLbyte* colorArr = new GLbyte[ arrLen ];
void saveColorData(bytePt& _pt, string& _str) {
FILE* pFile = NULL;
pFile = fopen(_str.c_str(), "wt");
if(!pFile) { fprintf(stderr, "错误\n");退出(-1); }
for(int i=0; i
if(colorArr[i] == -1) { colorArr[i] = 255; }
}
for(int i=0; i
fprintf(pFile, "%d\n", colorArr[i]);
}
fclose(pFile);
printf("颜色数据已保存!\n");
}
 
void init() {
glClearColor(0.5, 0.5, 0.5, 0.0);
glShadeModel(GL_SMOOTH);
}
 
无效显示(){
glClear(GL_COLOR_BUFFER_BIT);
 
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0, 0.0, 100.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
 
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glu透视(45.0, 1.0, 0.1, 500.0);
glMatrixMode(GL_MODELVIEW);
 
glColor3f(1.0, 0.0, 0.0);
glBegin(GL_TRIANGLES);
glVertex3f(0.0, 25.0, 0.0);
glVertex3f(-25.0, -25.0, 0.0);
glVertex3f(25.0, -25.0, 0.0);
glEnd();
 
glFlush();
}
 
无效键盘(无符号字符键,int x,int y){
GLint viewPort[4] = {0};
开关(键){
案例“c”:
案例“C”:
glGetIntegerv(GL_VIEWPORT, viewPort);
glReadPixels(viewPort[0], viewPort[1], viewPort[2], viewPort[3], GL_RGB, GL_UNSIGNED_BYTE, colorArr);
printf("读取颜色数据!\n");
saveColorData(colorArr, (string)"tmpcolor.txt");
默认:
打破;
}
}
 
int main(int argc, char** argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowPosition(200, 200);
glutInitWindowSize(400, 400);
glutCreateWindow(argv[0]);
init();
glutDisplayFunc(显示);
glutKeyboardFunc(键盘);
glutMainLoop();
 
删除[] colorArr;
 
返回0;
}
 
    tmpcolor.txt 中将每个像素的颜色按R、G、B支架放置一个支撑,即(R,G,B,R,G,B,...)。而且,读取缓冲时区的坐标原点是窗口坐标系的坐标原点(图片左下角);,可以在matlab中通过相应调整得到原来的图片:
功能测试
    a = load('tmpcolor.txt');
 
    pos = find(a == -1);
    a(pos) = 255;
 
    r = a(1:3:结束);
    g = a(2:3:结束);
    b = a(3:3:结束);
    img = 零(400,400,3);
    img(:,:,1) = 重塑(r,[400,400]);
    img(:,:,2) = 重塑(g,[400,400]);
    img(:,:,3) = 重塑(b,[400,400]);
    img = uint8(img);
    
    tmpR = 零(400,400); tmpR = img(:,:,1);
    tmpG = 零(400,400); tmpG = img(:,:,2);
    tmpB = 零(400,400); tmpB = img(:,:,3);
    
    对于 i=1:1:400
        img(i,:,1) = tmpR(:,400-i+1);
        img(i,:,2) = tmpG(:,400-i+1);
        img(i,:,3) = tmpB(:,400-i+1);
    结束
    
    图;
    imshow(img);
结束
 
    当然,读到数据后,可以直接使用openCV等工具方便存储图片。一段对应的opencv代码为:
void saveColorData2img(bytePt& _pt, string& _str) {
cv::Mat img;
矢量imgPlanes;
img.create(winHeight, winWidth, CV_8UC3);
cv::split(img, imgPlanes);
 
for(int i = 0; i < winHeight; i ++) {
UCHAR*plane0Ptr = imgPlanes[0].ptr(i);
UCHAR*plane1Ptr = imgPlanes[1].ptr(i);
UCHAR*plane2Ptr = imgPlanes[2].ptr(i);
for(int j = 0; j < winWidth; j ++) {
int k = 3 * (i * winWidth + j);
plane2Ptr[j] = _pt[k];
plane1Ptr[j] = _pt[k+1];
plane0Ptr[j] = _pt[k+2];
}
}
cv::合并(imgPlanes, img);
cv::翻转(img, img ,0); //!!!
cv::imwrite(_str.c_str(), img);
 
printf("opencv保存opengl img完成!\n");
}
 
   需要注意的是,如果想要把参数集成在MFC中的openGL场景转存成图片,因为MFC中的像素格式只支持RGBA和颜色索引,所以glReadPixels中需要使用GL_RGBA作为对应。写了一个 C++ 类,供参考:
glGrabber 类 {
公众:
glGrabber();
~glGrabber();
 
void glGrab();
void saveColorData2Img(string& _str);
私人:
GLbyte* colorArr;
GLint viewPort[4];
int winWidth;
int winHeight;
};
 
//
glGrabber::glGrabber() {
colorArr = NULL;
}
 
//
glGrabber::~glGrabber() {
if(colorArr!=NULL) { 删除 [] colorArr;颜色Arr = NULL; }
}
 
//
void glGrabber::glGrab() {
glGetIntegerv(GL_VIEWPORT, viewPort);
if(colorArr != NULL) { 删除 [] colorArr;颜色Arr = NULL; }
winWidth = viewPort[2];
winHeight = viewPort[3];
 
colorArr = new GLbyte[ winWidth * winHeight * 4 ]; // MFC的像素格式只支持RGBA
 
glReadPixels(viewPort[0], viewPort[1], viewPort[2], viewPort[3], GL_RGBA, GL_UNSIGNED_BYTE, colorArr); // RGBA
 
printf("x: %d, y: %d, 窗口宽度: %d, 窗口高度: %d \n", viewPort[0], viewPort[1], viewPort[2], viewPort[3] );
printf("读取颜色数据!\n");
}
 
//
void glGrabber::saveColorData2Img(string& _str) {
cv::Mat img;
矢量imgPlanes;
img.create(winHeight, winWidth, CV_8UC3);
cv::split(img, imgPlanes);
 
for(int i = 0; i < winHeight; i ++) {
UCHAR*plane0Ptr = imgPlanes[0].ptr(i);
UCHAR*plane1Ptr = imgPlanes[1].ptr(i);
UCHAR*plane2Ptr = imgPlanes[2].ptr(i);
for(int j = 0; j < winWidth; j ++) {
int k = 4 * (i * winWidth + j); // RGBA
plane2Ptr[j] = colorArr[k];
plane1Ptr[j] = colorArr[k+1];
plane0Ptr[j] = colorArr[k+2];
}
}
 
cv::合并(imgPlanes, img);
cv::翻转(img, img ,0); //!!!
cv::namedWindow("openglGrab");
cv::imshow("openglGrab", img);
cv::waitKey();
 
//cv::imwrite(_str.c_str(), img);
 
printf("opencv保存opengl img完成!\n");
} -->

相关文章

热门推荐