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");
}

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

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

    对于Excel中的很多对象,比如单元格(Cell),图形(shape),图表(chart)等等,有时需要将它们保存成一张图片.就像截图一样. 最近做一个Excel相关的项目,项目中遇到一个很变态的需求 ...

  2. BitmapUtil【缩放bitmap以及将bitmap保存成图片到SD卡中】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 用于缩放bitmap以及将bitmap保存成图片到SD卡中 效果图 代码分析 bitmapZoomByHeight(Bitmap s ...

  3. javacpp-opencv图像处理之1:实时视频添加文字水印并截取视频图像保存成图片,实现文字水印的字体、位置、大小、粗度、翻转、平滑等操作

    欢迎大家积极开心的加入讨论群 群号:371249677 (点击这里进群) javaCV图像处理系列: javaCV图像处理之1:实时视频添加文字水印并截取视频图像保存成图片,实现文字水印的字体.位置. ...

  4. 修改css的(屏蔽)overflow: hidden;实现浏览器能把网页全图保存成图片

    摘要: 1.项目需要,需要对网页内容“下载”保存成全图片 2.QQ浏览器等主流浏览器都支持这种下载保存功能 3.项目需要场景:编写好的项目维护文档,放在服务器上.如果是txt不能带图片可视化,如果wo ...

  5. Java 将 PPT 形状(表格、文本框、心形、图表等)保存成图片

    MS PowerPoint中的表格.文本框.心形.图表.图片等均可以称为形状,将这些形状保存成图片,便可分类储存,方便日后查找,再利用. 本文将介绍如何使用 Spire.Presentation fo ...

  6. c# 将byte数组保存成图片

    将byte数组保存成图片: 方式一:System.IO.File.WriteAllBytes(@"c:\test.jpg", bytes); 方式二:MemoryStream ms ...

  7. 如何实现批量截取整个网页完整长截图,批量将网页保存成图片web2pic/webshot/screencapture/html2picture

    如何实现批量截取整个网页完整长截图,批量将网页保存成图片web2pic/webshot/screencapture [困扰?疑问?]: 您是否正受到:如何将网页保存为图片的困扰?网页很高很长截图截不全 ...

  8. Unity3D中调用外接摄像头,并保存为图片文件

    http://bbs.9ria.com/thread-170539-1-1.html 项目要求调用摄像头,并且把图像保存下来,上传到服务器. 这里有几个难点,调用摄像头是很简单的,unity已经提供好 ...

  9. js中保存成图片并下载

    1.保存canvas中绘制的内容为图片 HTML代码: <canvas id="canvas" width="400" height="400& ...

随机推荐

  1. Scala学习笔记(二)表达式和函数

    笔记的整理主要针对Scala对比Java的新特性:   1.if表达式 if表达式是有结果返回的. val a= if (5>2) "你好" else 1 a的值为if表达式 ...

  2. 利用系统镜像文件安装.Net框架的方式

    最近重装系统之后,在安装部分程序时需要.NET3.5框架,在线安装时间较长,网上搜到了一个很好的解决办法.利用windows系统镜像.首先将镜像加载到驱动中比如L,然后在cmd中输入 dism.exe ...

  3. A Tour of Go If with a short statement

    Like for, the if statement can start with a short statement to execute before the condition. Variabl ...

  4. 说一说window.parent

    <iframe>标签是很常用的,嵌在页面之中,可以做独立的加载和刷新.比如说,页面分左右或者上下结构,一般左侧和上侧是导航部分,右侧和下侧是目标页面的展示部分,只需要设置导航链接的targ ...

  5. WIN10环境下搭建与连接VPN服务器

    搭建VPN服务器 0.前言 1.WIN+E打开此电脑 2.勾选始终显示菜单,并应用,确定 3.新建传入连接,可以添加用户,也可以直接选取已有用户 连接VPN服务器 1.打开vpn设置 2.添加vpn连 ...

  6. 【腾讯Bugly干货分享】手游热更新方案xLua开源:Unity3D下Lua编程解决方案

    本文来自于腾讯Bugly公众号(weixinBugly),未经作者同意,请勿转载,原文地址:http://mp.weixin.qq.com/s/2bY7A6ihK9IMcA0bOFyB-Q 导语 xL ...

  7. IOI1998 hdu1828 poj1177 Picture

    写了一发扫描线竟然狂WA不止,hdu死活过不了,poj和当时IOI的数据(还花了我1dsdn积分..)都过了. 然后看到谋篇blog里有评论,把数据拿下来发现WA了. 数据是 20 0 1 11 0 ...

  8. Java- Jdbc学习

    java jdbc test jsbc: package cn.honji.sqlserver; import java.sql.Connection; import java.sql.ResultS ...

  9. DoctorNote医生处方笔记开发记录

    1.开发背景 一个开诊所的中医朋友,希望我能给他开发一个记录病人姓名和处方的Android手机app,以便查询病人每次就诊信息,比如上一次的处方,以前他要找个病人上一次的就诊处方,几乎要翻遍一叠厚厚的 ...

  10. Splay Tree的删除操作

    Splay Tree的插入操作,搜索操作,和删除操作都实现了,那么就能够使用来解题了. 指针的删除操作的处理还是那么难的,非常多坎须要避开. 同一个坎还是坑了我好多次,就是指针传递的问题,什么时候须要 ...