OpenGL中的深度、深度缓存、深度测试及保存成图片

1、深度

所谓深度,就是在openGL坐标系中,像素点Z坐标距离摄像机的距离。摄像机可能放在坐标系的任何位置,那么,就不能简单的说Z数值越大或越小,就是越靠近摄像机。

2、深度缓冲区

深度缓冲区原理就是把一个距离观察平面(近裁剪面)的深度值(或距离)与窗口中的每个像素相关联。
      首先,使用glClear(GL_DEPTH_BUFFER_BIT),把所有像素的深度值设置为最大值(一般是远裁剪面)。
      然后,在场景中以任意次序绘制所有物体。硬件或者软件所执行的图形计算把每一个绘制表面转换为窗口上一些像素的集合,此时并不考虑是否被其他物体遮挡。
      其次,OpenGL会计算这些表面和观察平面的距离。如果启用了深度缓冲区,在绘制每个像素之前,OpenGL会把它的深度值和已经存储在这个像素的深度值进行比较。新像素深度值<原先像素深度值,则新像素值会取代原先的;反之,新像素值被遮挡,他颜色值和深度将被丢弃。

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

数学基础:

待渲染的照相机空间中的深度经常定义为近距 near 到远距 far 之间的 z 值,Z坐标和X、Y坐标一样。在变换、裁减和透视除法后,Z的范围为-1.0~1.0。DepthRange映射指定Z坐标的变换,这与用于将X和Y映射到窗口坐标的视口变换类似,在透视变换之后,得到新的 z‘ 值:

其中  是照相机空间的值,它有时候也表示为 w 或者 w‘。

结果 z‘ 是在 -1 到 1 之间归一化之后的值,其中近距 near 平面位于 -1 处,远距 far 平面位于 1 处。在这个范围之外的相应点在视图体之外,不需要进行渲染。

为了实现深度缓冲,在整个屏幕空间上的对当前多边形顶点之间进行插值来计算 z‘ 的值,通常这些中间数值在深度缓冲区中用定点数格式保存。距离近距 near 平面越近,z‘ 值越密;距离越远,z‘ 值越稀。这样距离照相机越近精度越高。near 平面距离照相机越近,则远距离位置的精度越低。near 平面距离照相机太近是在远距离物体产生人为误差的一个常见因素。

3、深度测试

OpenGL中的深度测试是采用深度缓存器算法,消除场景中的不可见面。在默认情况下,深度缓存中深度值的范围在0.0到1.0之间,这个范围值可以通过函数:
        glDepthRange (nearNormDepth, farNormalDepth);
       将深度值的范围变为nearNormDepth到farNormalDepth之间。这里nearNormDepth和farNormalDepth可以取0.0到1.0范围内的任意值,甚至可以让nearNormDepth > farNormalDepth。这样,通过glDepthRange函数可以在透视投影有限观察空间中的任意区域进行深度测试。
       另一个非常有用的函数是:
        glClearDepth (maxDepth);
       参数maxDepth可以是0.0到1.0范围内的任意值。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(),可以读取各种缓冲区(深度、颜色,etc)的数值。要将opengl的绘制场景保存成图片,也需要使用这个函数。

一个简单的例子见如下的c程序。按键盘上的“C”键,可以将读取的图像缓冲区数据存储成tmpcolor.txt。

#include "windows.h"

#include <GL/glut.h>

#include <GL/GLAUX.H>

#include <iostream>

using namespace 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, "error \n"); exit(-1); }

 

for(int i=0; i<winWidth * winHeight * 3; i ++) {

if(colorArr[i] == -1) { colorArr[i] = 255; }

}

 

for(int i=0; i<winWidth * winHeight * 3; i ++) {

fprintf(pFile, "%d\n", colorArr[i]);

}

fclose(pFile);

printf("color data saved! \n");

}

 

void init() {

glClearColor(0.5, 0.5, 0.5, 0.0);

glShadeModel(GL_SMOOTH);

}

 

void display() {

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();

gluPerspective(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();

}

 

void keyboard(unsigned char key, int x , int y) {

GLint viewPort[4] = {0};

switch(key) {

case ‘c‘:

case ‘C‘:

glGetIntegerv(GL_VIEWPORT, viewPort);

glReadPixels(viewPort[0], viewPort[1], viewPort[2], viewPort[3], GL_RGB, GL_UNSIGNED_BYTE, colorArr);

printf("color data read !\n");

saveColorData(colorArr, (string)"tmpcolor.txt");

default:

break;

}

}

 

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(display);

glutKeyboardFunc(keyboard);

glutMainLoop();

 

delete [] colorArr;

 

return 0;

}

 

tmpcolor.txt 中将每个像素的颜色按R、G、B顺序存放成了一个向量,即(R,G,B,R,G,B,...)。而且,读取缓冲区时的坐标原点是窗口坐标系的坐标原点(图片左下角);因此,可以在matlab中通过调整得到原来的图片:

function test

    a = load(‘tmpcolor.txt‘);

 

    pos = find(a == -1);

    a(pos) = 255;

 

    r = a(1:3:end);

    g = a(2:3:end);

    b = a(3:3:end);

    img = zeros(400,400,3);

    img(:,:,1) = reshape(r,[400,400]);

    img(:,:,2) = reshape(g,[400,400]);

    img(:,:,3) = reshape(b,[400,400]);

    img = uint8(img);

    

    tmpR = zeros(400,400);  tmpR = img(:,:,1);

    tmpG = zeros(400,400);  tmpG = img(:,:,2);

    tmpB = zeros(400,400);  tmpB = img(:,:,3);

    

    for 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);

    end

    

    figure;

    imshow(img);

end

 

    当然,读到数据后,可以直接使用openCV等工具方便地存储图片。一段对应的opencv代码为:

void saveColorData2img(bytePt& _pt, string& _str) {

cv::Mat img;

vector<cv::Mat> imgPlanes;

img.create(winHeight, winWidth, CV_8UC3);

cv::split(img, imgPlanes);

 

for(int i = 0; i < winHeight; i ++) {

UCHAR* plane0Ptr = imgPlanes[0].ptr<UCHAR>(i);

UCHAR* plane1Ptr = imgPlanes[1].ptr<UCHAR>(i);

UCHAR* plane2Ptr = imgPlanes[2].ptr<UCHAR>(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::merge(imgPlanes, img);

cv::flip(img, img ,0); // !!!

cv::imwrite(_str.c_str(), img);

 

printf("opencv save opengl img done! \n");

}

 

    需要注意的是,如果想要把集成在MFC中的openGL场景转存成图片,因为MFC中的像素格式只支持RGBA和颜色索引,所以 glReadPixels 中需要使用 GL_RGBA 作为参数。对应写了一个C++类,可供参考:

class glGrabber {

public:

glGrabber();

~glGrabber();

 

void glGrab();

void saveColorData2Img(string& _str);

private:

GLbyte* colorArr;

GLint viewPort[4];

int winWidth;

int winHeight;

};

 

//

glGrabber::glGrabber() {

colorArr = NULL;

}

 

//

glGrabber::~glGrabber() {

if(colorArr!=NULL) { delete [] colorArr; colorArr = NULL; }

}

 

//

void glGrabber::glGrab() {

glGetIntegerv(GL_VIEWPORT, viewPort);

if(colorArr != NULL) { delete [] colorArr; colorArr = 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, window width: %d, window height: %d \n", viewPort[0], viewPort[1], viewPort[2], viewPort[3]);

printf("color data read! \n");

}

 

//

void glGrabber::saveColorData2Img(string& _str) {

cv::Mat img;

vector<cv::Mat> imgPlanes;

img.create(winHeight, winWidth, CV_8UC3);

cv::split(img, imgPlanes);

 

for(int i = 0; i < winHeight; i ++) {

UCHAR* plane0Ptr = imgPlanes[0].ptr<UCHAR>(i);

UCHAR* plane1Ptr = imgPlanes[1].ptr<UCHAR>(i);

UCHAR* plane2Ptr = imgPlanes[2].ptr<UCHAR>(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::merge(imgPlanes, img);

cv::flip(img, img ,0); // !!!

cv::namedWindow("openglGrab");

cv::imshow("openglGrab", img);

cv::waitKey();

 

//cv::imwrite(_str.c_str(), img);

 

printf("opencv save opengl img done! \n");

}

时间: 2024-12-22 18:41:04

OpenGL中的深度、深度缓存、深度测试及保存成图片的相关文章

如何把Excel中的单元格等对象保存成图片

对于Excel中的很多对象,比如单元格(Cell),图形(shape),图表(chart)等等,有时需要将它们保存成一张图片.就像截图一样. 最近做一个Excel相关的项目,项目中遇到一个很变态的需求, 需要对Excel中的一些对象进行拍图,比如,对一个单元格设置一些颜色之后拍图,或者对一个图表,报表拍成图片.经过比较曲折的经历,终于还是完成了.拿出来分享一下. 要做Excel,首先当然是查看Excel的com对象模型.地址在这里: http://msdn.microsoft.com/en-us

OpenGL 绘制效果保存成图片

分享一下我老师大神的人工智能教程吧.零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!http://www.captainbed.net opengl中有一个非常有用的函数:glReadPixels(),可以读取各种缓冲区(深度.颜色,etc)的数值.要将opengl的绘制场景保存成图片,也需要使用这个函数. 一个简单的例子见如下的c程序.按键盘上的“C”键,可以将读取的图像缓冲区数据存储成tmpcolor.txt. #include "windows.h"

什么是OpenGL中的深度、深度缓存、深度测试?

原文来自http://blog.csdn.net/xiaoquanhuang/article/details/6613705 1)直观理解 深度其实就是该象素点在3d世界中距离摄象机的距离,深度缓存中存储着每个象素点(绘制在屏幕上的)的深度值!深度测试决定了是否绘制较远的象素点(或较近的象素点),通常选用较近的,而较远优先能实现透视的效果!!! 2)Z值(深度值).Z buffer(深度缓存) 下面先讲讲Z坐标.Z坐标和X.Y坐标一样.在变换.裁减和透视除法后,Z的范围为-1.0~1.0.Dep

Canvas图保存成图片或pdf

Canvas画好的图片虽然可以通过toDataURL()转成二进制流的字符串格式,图片稍大一点就无法发送了,当然如果需求简单的话,可以在页面上加一个image元素,将转成的流直接赋给image的src就可以显示图片了. 但是大部分的时候我们还希望弹出保存框,保存图片到我们自己想要的路径下,或者添加一些统计和分析的信息到pdf中一起保存成一个pdf文件,这就需要在后台处理了,两种方式:后台新建一个Web Browser加载当前的页面,然后将获得到的图片流的信息再发送到前台弹出保存的对话框,后台发送

MindMapper导图如何保存成图片

我们可以将MindMapper思维导图保存为多种格式的文件,具有对多样性,从文档到图片这些我们常用的格式都能够自由转换.下面我们就一起去看看如何如何将MindMapper导图保存为图片. 当我们在MindMapper思维导图中绘制好导图,像要将之保存为图片时,点击[文件]中的保存/发送,然后跳转至文件格式板块中的更改文件格式. 我们可以看到都多种保存为图片的形式,我们一般常用第一项保存为图片文件形式. 点击后会跳出保存文件框,选择保存MindMapper文件的位置,选择好后点击保存,这时系统会跳

android脚步---将layout和其输出一起保存成图片

public void convertViewToBitmap(View view) { //View view = getLayoutInflater().inflate(R.layout.test_layout, null); view.setDrawingCacheEnabled(true); view.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0

InputStream流保存成图片文件

public void saveBit(InputStream inStream) throws IOException{ ByteArrayOutputStream outStream = new ByteArrayOutputStream(); //创建一个Buffer字符串 byte[] buffer = new byte[1024]; //每次读取的字符串长度,如果为-1,代表全部读取完毕 int len = 0; //使用一个输入流从buffer里把数据读取出来 while( (len

php 加载字体 并保存成图片

[php] view plaincopy // Set the content-type header("Content-type: image/png"); // Create the image $im = imagecreatetruecolor(400, 100); // Create some colors $white = imagecolorallocate($im, 255, 255, 255); $grey = imagecolorallocate($im, 128,

C# 整个网页保存成图片

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Drawing.Imaging; using System.Runtime.InteropServices; using System.Secu