输出其实分两部分。
一部分就是从NI中获取的深度/彩色图像数组,这个你已经懂得了,然后循环把这些像素存储到纹理像素数组中,你可以找到这段循环的代码。
另一部分,就是在Display中,设置OpenGL渲染管线,启用纹理,指定纹理数组地址及启用方式,然后通过在屏幕中Z坐标位0的位置上,绘制一个带纹理的平面。
也就是,真正绘制的地方,是跟NI没关系的,就负责绘制一个数组到指定纹理。而你要在每一帧的绘制之前,从NI中取得数据,填充那个数组。
日期:2011/6/22
回复
激情の小力 18:13:10
好的,那這個原始數據的大小應該是: 640×480個格子的矩陣,每一個格子的值是一個 8bit無符號整數 對嗎?
回复
激情の小力 18:13:42
還是,每一個格子包含 8X3 bit ?因為是RGB
回复
铁幕诱惑 18:13:44
彩色的,应该是24BIT
回复
激情の小力 18:13:53
嗯嗯好的
回复
铁幕诱惑 18:14:56
在NI中,typedef了这个类型 XnRGB24Pixel
回复
激情の小力 18:16:51
好的,
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Setup the OpenGL viewpoint
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
這裡,glPushMatrix(); 指定當前矩陣為有效矩陣,glLoadIdentity();是讓矩陣原點居中
我對這兩個函數的理解是:因為這個的GL輸出用的是雙緩存,
當一副紋理映射輸出時,下一幅紋理在後臺進行處理,然後他們之間交換,以達到smooth的效果。所以每次交換后,都要進行這兩個函數。這麼理解可以嗎?
回复
铁幕诱惑 18:19:26
glMatrixMode用来指定当前投影模式,具体可以参考API,就三种。GL_PROJECTION为正交投影
回复
铁幕诱惑 18:20:11
但是掉用过这句指定投影模式的函数,并不会立即生效,必须LoadIdentity才会让其生效。
回复
激情の小力 18:20:30
原來是這樣~
回复
激情の小力 18:20:42
好的
回复
铁幕诱惑 18:22:16
如果怕这么指定的投影模式对之前绘制的东西产生影响,比如我先用透视投影绘制了很多,突然想用正交投影了,为了不影响之前的渲染内容,那么将之前的矩阵压栈PushMatrix,这样,接下来我们绘制的内容,就是在新矩阵模式中操作了,不影响之前的。
回复
铁幕诱惑 18:22:51
同理,你现在用正交投影模式绘制很多内容,以后还想在后面采用其他模式继续绘制,为了不影响现有的,再操作一次。
回复
铁幕诱惑 18:23:04
glMatrixMode(你要的模式);
glPushMatrix();
glLoadIdentity();
回复
激情の小力 18:24:50
好的,這部份我能理解了,感謝~~,詳細的我自己再去學習調查!
接下來我想問一下這兩個函數,
glOrtho(0, GL_WIN_SIZE_X, GL_WIN_SIZE_Y, 0, -1.0, 1.0);
xnOSMemSet(g_pTexMap, 0, g_nTexMapX*g_nTexMapY*sizeof(XnRGB24Pixel));
glOrtho — multiply the current matrix with an orthographic matrix
我不太明白,爲什麽要讓當前矩陣乘以正交矩陣,而且,是哪個矩陣乘以正交矩陣呢?
回复
铁幕诱惑 18:25:52
矩阵相乘,这在数学上不正好是设置剪裁范围么 -_-#
回复
激情の小力 18:26:38
哦哦,那就是 讓 我設置的窗口與輸入圖像 兩個矩陣相乘,也就是把原始圖像剪裁?
回复
铁幕诱惑 18:26:39
假设一个全部值为1的矩阵,乘以你指定的新矩阵,结果是什么?
回复
铁幕诱惑 18:27:02
这个原始图像,你应该理解为GL内置的渲染矩阵
回复
铁幕诱惑 18:27:51
就假象那是个无限大的矩阵,然后乘以你的矩阵,最后结果就是你所指定的宽、高、远、近
回复
铁幕诱惑 18:27:54
明白?
回复
激情の小力 18:28:50
啊!!我明白了,glOrtho 這個函數就是 我建立的窗口 與 一個無限大矩陣相乘?
回复
铁幕诱惑 18:29:33
也就是说,你在一个三维空间里指定了一个盒子,GL只负责渲染你绘制在盒子之内的东西,绘制在盒子之外,它不会渲染,因为从你的视口看过去,那些是不可见的。
回复
激情の小力 18:32:59
額,我還是不太明白,就這個函數來說,glOrtho()括號里的信息在這個代碼里,其實是我所創建的窗口的信息(雖然我肉眼看這個窗口是二維,但是參數表里,卻有Z方向,-1.0, 1.0),也就是說,可以理解為這個是個盒子?
回复
铁幕诱惑 18:34:04
恩,窗口是窗口,剪裁范围是剪裁范围。
回复
铁幕诱惑 18:34:24
glOrtho是设置剪裁范围,glViewPort才是视口
回复
铁幕诱惑 18:35:02
就比如你这个例子,z near 与 z-far 分别是 1 与 -1
回复
激情の小力 18:35:09
嗯嗯是的,
回复
铁幕诱惑 18:35:38
那么,假如你绘制一个顶点,但是Z参数指定为 -2,那么,这个点将被OpenGL抛弃
回复
激情の小力 18:35:52
可以理解為 640 x 480 的面積,厚 為 2的 盒子嗎?
回复
铁幕诱惑 18:35:59
是的
回复
激情の小力 18:36:05
好的,我想想
回复
铁幕诱惑 18:37:43
就是这个范围,因为我们接下来的目的,是往这个方块的正中间,显示一副640×480的带纹理的方块,也就是Z轴为0的位置上放置一个平面。所以-1 到 1 就足够了
回复
激情の小力 18:37:51
我大概能理解到了,那這個函數就是要剪裁 這個 盒子里的信息。
那請問第二個函數是?
回复
铁幕诱惑 18:37:53
其实你可以指定的更大,但没意义
回复
铁幕诱惑 18:38:19
xnOsMemSet是为了通用性,考虑到跨平台……
回复
激情の小力 18:38:24
好的~!
回复
铁幕诱惑 18:38:34
在windos下,你可以理解这就是memset的另一种写法……
回复
激情の小力 18:38:48
原來是這樣,我能理解,盒子這個概念很好^_^
回复
激情の小力 18:39:27
嗯,好的,那我就先繼續往下看
回复
激情の小力 18:39:47
回复
激情の小力 18:40:52
進行到這裡,
我們有了 const XnUInt8* pImage = g_imageMD.Data(); , pImage存放原始圖像。
而這裡 const XnRGB24Pixel* pImageRow = g_imageMD.RGB24Data(); 這一步是什麽意思呢?
回复
激情の小力 18:41:40
pImageRow 是一個 單位為24bit 的 新矩陣了嗎?
回复
铁幕诱惑 18:45:54
这也不是矩阵……
回复
铁幕诱惑 18:46:09
这个是数组,row不是行么……
回复
铁幕诱惑 18:48:28
还有问题?
回复
激情の小力 18:49:41
嗯是的,可是因為是 行 我就更不理解了= =
嗯抱歉,口誤。。之前用了好久matlab。。
這裡 pImageRow = g_imageMD.RGB24Data(),是否是原始圖像一行的信息呢?
與 pImage = g_imageMD.Data(); 有何區別呢?
回复
激情の小力 18:51:48
我對 XnUInt8* 和 XnRGB24Pixel* 比較不理解,
如果 const XnUInt8* pImage = g_imageMD.Data(); 是存放了一幀圖像。
const XnRGB24Pixel* pImageRow = g_imageMD.RGB24Data(); 存放一行圖像。
行信息怎麼是 3x8bit,而 圖像是 8bit?
回复
铁幕诱惑 18:52:25
在循环之外,指定pImageRow指向原始图像数据第一行。
在for(y=0;…………)这个循环内,每次循环结束 pImageRow+=g_ImageMD.xres(),不就是串到下一行了么……
回复
铁幕诱惑 18:53:08
pImage = g_imageMD.Data(); 至于这句,我怎么没看到 -_-#
回复
铁幕诱惑 18:53:37
我只看到在y循环里,有这句……
回复
激情の小力 18:54:02
const XnUInt8* pImage = g_imageMD.Data(); 這一句在開頭
回复
铁幕诱惑 18:55:53
这个2B作者,它写了却不用,这句你可以删掉了,没有任何意义……
回复
激情の小力 18:56:15
額額。。。這。。是版主寫的= =
回复
铁幕诱惑 18:57:14
那也是没用的
回复
激情の小力 18:57:18
他用了呀,在接下來的循環中有用到。
回复
铁幕诱惑 18:57:25
很明显,逻辑不严谨
回复
铁幕诱惑 18:57:56
你把整个display的代码拷贝进来就行,不用截图
回复
激情の小力 18:57:59
好的
回复
激情の小力 18:58:10
void glutDisplay (void)
{
int tmp=0;
static int flag=0;
g_context.WaitAnyUpdateAll();
g_image.GetMetaData(g_imageMD);
const XnUInt8* pImage = g_imageMD.Data();
unsigned int nImageScale = GL_WIN_SIZE_X / 640;
// Copied from SimpleViewer
// Clear the OpenGL buffers
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Setup the OpenGL viewpoint
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0, GL_WIN_SIZE_X, GL_WIN_SIZE_Y, 0, -1.0, 1.0);//multiply current matrix with
xnOSMemSet(g_pTexMap, 0, g_nTexMapX*g_nTexMapY*sizeof(XnRGB24Pixel));
/
if(flag == 0)
{
printf(“g_nTexMapX*g_nTexMapY*sizeof(XnRGB24Pixel)=%ld/n”,
g_nTexMapX*g_nTexMapY*sizeof(XnRGB24Pixel));
}
//
const XnRGB24Pixel* pImageRow = g_imageMD.RGB24Data();
XnRGB24Pixel* pTexRow = g_pTexMap + g_imageMD.YOffset() * g_nTexMapX;
for (XnUInt y = 0; y < g_imageMD.YRes(); ++y)
{
const XnRGB24Pixel* pImage = pImageRow;
XnRGB24Pixel* pTex = pTexRow + g_imageMD.XOffset();
for (XnUInt x = 0; x < g_imageMD.XRes(); ++x, ++pImage, ++pTex)
{
*pTex = *pImage;
if(x==320||x==319||x==321||y==239||y==240||y==241)
{
*pTex = (const XnRGB24Pixel &)tmp;
}
}
pImageRow += g_imageMD.XRes();
pTexRow += g_nTexMapX;
}
if(flag == 0)
{
printf(“g_imageMD.XRes()=%d/n”,g_imageMD.XRes());
printf(“g_imageMD.YRes()=%d/n”,g_imageMD.YRes());
}
// Create the OpenGL texture map
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, g_nTexMapX, g_nTexMapY, 0, GL_RGB, GL_UNSIGNED_BYTE, g_pTexMap);
// Display the OpenGL texture map
glColor4f(1,1,1,1);
glBegin(GL_QUADS);
int nXRes = 640;
int nYRes = 480;
if(flag == 0)
{
printf(“g_depthMD.FullXRes()=%d/n”,nXRes);
printf(“g_depthMD.FullYRes()=%d/n”,nYRes);
}
//
// upper left
glTexCoord2f(0, 0);
glVertex2f(0, 0);
// upper right
glTexCoord2f((float)nXRes/(float)g_nTexMapX, 0);
// glTexCoord2f(0.6, 0);
glVertex2f(GL_WIN_SIZE_X, 0);
// bottom right
glTexCoord2f((float)nXRes/(float)g_nTexMapX, (float)nYRes/(float)g_nTexMapY);
glVertex2f(GL_WIN_SIZE_X, GL_WIN_SIZE_Y);
// bottom left
glTexCoord2f(0, (float)nYRes/(float)g_nTexMapY);
glVertex2f(0, GL_WIN_SIZE_Y);
glEnd();
// Swap the OpenGL display buffers
glutSwapBuffers();
flag = 1;
}
回复
铁幕诱惑 18:58:47
const XnUInt8* pImage = g_imageMD.Data();
unsigned int nImageScale = GL_WIN_SIZE_X / 640;
回复
铁幕诱惑 18:59:02
这两句,可能刚设计的时候想用,但是对本程序一点意义没有
回复
铁幕诱惑 18:59:05
他没用上
回复
铁幕诱惑 18:59:07
删掉
回复
铁幕诱惑 19:00:00
for (XnUInt y = 0; y < g_imageMD.YRes(); ++y)
{
const XnRGB24Pixel* pImage = pImageRow;
XnRGB24Pixel* pTex = pTexRow + g_imageMD.XOffset();
for (XnUInt x = 0; x < g_imageMD.XRes(); ++x, ++pImage, ++pTex)
{
*pTex = *pImage;
if(x==320||x==319||x==321||y==239||y==240||y==241)
{
*pTex = (const XnRGB24Pixel &)tmp;
}
}
pImageRow += g_imageMD.XRes();
pTexRow += g_nTexMapX;
}
循环内部用的pImage,在Y循环里定义的,不是那个pImage
回复
激情の小力 19:00:20
好的,那一幀圖像所存放的位置 應該是
const XnRGB24Pixel* pImageRow = g_imageMD.RGB24Data();
存放在 pImageRow 里吧?循環里的 pImage只是個變量
回复
激情の小力 19:00:35
嗯嗯,好的
回复
激情の小力 19:01:09
所以 pImageRow 不應該這麼叫,沒有ROW這個字眼
回复
铁幕诱惑 19:01:51
pImageRow,用来指定每行图像的起始地址,随着Y变量每循环一次,递增到下一行。
回复
铁幕诱惑 19:02:34
pImage,用来指定当前行(pImageRow)的第X像素,起始位置在当前行第一个像素,随着X变量循环,递增到下一个像素
回复
铁幕诱惑 19:03:25
for (XnUInt x = 0; x < g_imageMD.XRes(); ++x, ++pImage, ++pTex)
这是pImage递增的方式,采用的是++,也就是一次向后移动sizeof(XnRGB24Pixel)大小,正好是下一个像素
回复
激情の小力 19:04:09
原來是這樣,好的,一下子清楚多了。
回复
铁幕诱惑 19:04:17
pImageRow += g_imageMD.XRes();
这是pImageRow的递增方式,XRes()是640,所以pImageRow每次循环,移动640xsiezof(XnRGB24Pixel),正好是一行
回复
激情の小力 19:05:40
好的!我理清一下
回复
激情の小力 19:09:02
那么說,這個代碼是一行一行從KINECT讀取的,而不是一次讀取一幀圖像存儲下來嘍?
回复
激情の小力 19:11:12
XnRGB24Pixel* pImageRow = g_imageMD.RGB24Data();
因為這裡用的函數,好像是讀取一行
回复
铁幕诱惑 19:11:28
整个签到循环完毕,就将一帧图像的所有数据,保存到纹理数组中了。
回复
铁幕诱惑 19:11:35
当然,作者还干了点别的
回复
激情の小力 19:11:59
嗯,if(x==320||x==319||x==321||y==239||y==240||y==241)
我刪掉了,他是想四分割
回复
铁幕诱惑 19:13:23
XnRGB24Pixel* pImageRow = g_imageMD.RGB24Data();
这句就是让pImageRow指向图像数据的起始位置,只不过是通过后面的逻辑代码,以递增的方式实现了一次Y循环一行的功能
回复
激情の小力 19:14:13
啊,我明白了!!!
回复
激情の小力 19:14:32
太謝謝您了~~
回复
铁幕诱惑 19:15:28
也就是说,如果不考虑kinect的数据偏移问题,一句话就可以完成图像数据拷贝至纹理数组
memcpy(g_pTexMap,g_imageMD.RGB24Data(),640x480x3);
640,480以及3的含义,你应该懂
回复
激情の小力 19:16:13
明白了~~g_imageMD.RGB24Data()返回是一個數組,pImageRow 剛開始指向這個數組(一幀圖像)的首地址,隨後循環中疊加進行運算,循環出來了就是一幀圖像的紋理處理
回复
铁幕诱惑 19:17:08
是的 ^_^
回复
激情の小力 19:17:31
您指的 數據偏移問題 是指?
memcpy(g_pTexMap,g_imageMD.RGB24Data(),640x480x3);
這個從字面意思是很好理解(數組賦值)
回复
激情の小力 19:18:09
是哦,爲什麽不直接將 圖像數組 賦值 給紋理數組呢,還需要一個像素一個像素賦值
回复
铁幕诱惑 19:18:59
便于你进行其他操作
回复
激情の小力 19:20:56
嗯嗯,可以隨著輸入圖像一個像素一個像素的輸入,進行一些圖像處理功能。
如果一口氣將圖像拷貝進紋理數組,然後再進行圖像處理和輸出,會浪費不少時間。
所以這麼做,看似打散了,其實可以壓榨很多時間嘍?
回复
铁幕诱惑 19:21:20
另外,根据配置不同,NI提取出的kinect图像,可能有一段空白,它提取出一幅800×600的图像,而实际上有内容的地方确实640×480,那么一行就是800个像素,可我们在这一行只需要循环640次,另外的160个像素就直接丢弃掉了,如果真是这样,那么memcpy就不对了,不会拷贝完整的图像,而且也拷贝了很多无用的像素。
回复
激情の小力 19:22:14
嗯嗯,這倒是,這麼做比較靈活
回复
激情の小力 19:22:27
也提高了效率
回复
铁幕诱惑 19:22:29
你终于明白了
回复
激情の小力 19:23:02
感謝您的指導,讓我明白了這麼多,太謝謝您了
回复
激情の小力 19:24:13
佔用了您不少時間,謝謝您的無私幫助!
回复
铁幕诱惑 19:24:18
不客气 ^_^
回复
激情の小力 19:25:17
我把聊天記錄存起來,好複習= =
回复
铁幕诱惑 19:26:58
-_-#
回复
铁幕诱惑 19:28:02
你还是自己从头写一个,不要拷贝代码,首先认真的思考下流程,然后又条例的实现它,具体到某个函数不记得了,可以参考这个例子。另外记得写有你自己风格的注释。
回复
铁幕诱惑 19:28:22
这是最能一次成型的学会某种技术
回复
激情の小力 19:28:46
好的,我今晚就重新編寫~!
今天的文章第一次铁幕解答及OPENGL的入门分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/60454.html