📄 tutorial_35.htm
字号:
DIB比特值位置的指针的指针(呜,真绕口).第五参数设为NULL,我们的DIB已被分配好内存.末了,最后一个参数可忽略(设为NULL).</p>
<p>引自MSDN:SelecObject函数选一个对象进入设备上下文(DC).</p>
<p>现在我们建好一个能直接写的DIB,yeah:)</p>
<p></p></td><td background="Tutorial_35_files/r.png"><img src="Tutorial_35_files/r.png"></td></tr></tbody></table><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img src="Tutorial_35_files/bl.png" height="28" width="28"></td><td width="100%"><img src="Tutorial_35_files/bc.png" height="28" width="100%"></td><td><img src="Tutorial_35_files/br.png" height="28" width="28"></td></tr></tbody></table>
<font color="#aaffaa" size="3">
<pre> bmih.biSize = sizeof (BITMAPINFOHEADER); <font color="#ffffaa">// BitmapInfoHeader的大小</font>
bmih.biPlanes = 1; <font color="#ffffaa">// 位平面</font>
bmih.biBitCount = 24; <font color="#ffffaa">//比特格式(24 Bit, 3 Bytes)</font>
bmih.biWidth = 256; <font color="#ffffaa">// 宽度(256 Pixels)</font>
bmih.biHeight = 256; <font color="#ffffaa">// 高度 (256 Pixels)</font>
bmih.biCompression = BI_RGB; <font color="#ffffaa"> // 申请的模式 = RGB</font>
hBitmap = CreateDIBSection (hdc, (BITMAPINFO*)(&bmih), DIB_RGB_COLORS, (void**)(&data), NULL, NULL);
SelectObject (hdc, hBitmap); <font color="#ffffaa">// 选hBitmap进入设备上下文(hdc)</font>
</pre>
</font>
<table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img src="Tutorial_35_files/tl.png" height="28" width="28"></td><td width="100%"><img src="Tutorial_35_files/tc.png" height="28" width="100%"></td><td><img src="Tutorial_35_files/tr.png" height="28" width="28"></td></tr></tbody></table><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td background="Tutorial_35_files/l.png"><img src="Tutorial_35_files/l.png"></td>
<td valign="top" width="100%">在从AVI中读取帧面前还有几件事要做.接下来使程序做好从AVI文件中解出帧面的准备.用AVIStreamGetFrameOpen(...)函数做这一点.
<p>你
能给这个函数传一个结构体作为第二参数(它会返回一个特定的视频格式).糟糕的是,你能改变的唯一数据是返回的图像的宽度和高度.MSDN也提到能传
AVIGETFRAMEF_BESTDISPLAYFMT为参数来选择一个最佳显示格式.奇怪的是,我的编译器没有定义这玩艺儿.</p>
<p>如果一切顺利,一个GETFRAME对象被返回(用来读帧数据).有问题的话,提示框会出现在屏幕上告诉你有错误!</p>
<p></p></td><td background="Tutorial_35_files/r.png"><img src="Tutorial_35_files/r.png"></td></tr></tbody></table><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img src="Tutorial_35_files/bl.png" height="28" width="28"></td><td width="100%"><img src="Tutorial_35_files/bc.png" height="28" width="100%"></td><td><img src="Tutorial_35_files/br.png" height="28" width="28"></td></tr></tbody></table>
<font color="#aaffaa" size="3">
<pre> pgf=AVIStreamGetFrameOpen(pavi, NULL); <font color="#ffffaa">// 用要求的模式建PGETFRAME</font>
if (pgf==NULL)
{
<font color="#ffffaa">// 解帧出错</font>
MessageBox (HWND_DESKTOP, "不能打开AVI帧", "错误", MB_OK | MB_ICONEXCLAMATION);
}
</pre>
</font>
<table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img src="Tutorial_35_files/tl.png" height="28" width="28"></td><td width="100%"><img src="Tutorial_35_files/tc.png" height="28" width="100%"></td><td><img src="Tutorial_35_files/tr.png" height="28" width="28"></td></tr></tbody></table><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td background="Tutorial_35_files/l.png"><img src="Tutorial_35_files/l.png"></td>
<td valign="top" width="100%">下面的代码把视频宽,高和帧数传给window标题.用函数SetWindowText(...)在window顶部显示标题.以窗口模式运行程序看看以下代码的作用.</td>
<td background="Tutorial_35_files/r.png"><img src="Tutorial_35_files/r.png"></td></tr></tbody></table><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img src="Tutorial_35_files/bl.png" height="28" width="28"></td><td width="100%"><img src="Tutorial_35_files/bc.png" height="28" width="100%"></td><td><img src="Tutorial_35_files/br.png" height="28" width="28"></td></tr></tbody></table>
<font color="#aaffaa" size="3">
<pre> <font color="#ffffaa">// bt标题栏信息(宽 / 高/ 帧数)</font>
wsprintf (title, "NeHe's AVI Player: Width: %d, Height: %d, Frames: %d", width, height, lastframe);
SetWindowText(g_window->hWnd, title); <font color="#ffffaa">// 修改标题栏</font>
}
</pre>
</font>
<table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img src="Tutorial_35_files/tl.png" height="28" width="28"></td><td width="100%"><img src="Tutorial_35_files/tc.png" height="28" width="100%"></td><td><img src="Tutorial_35_files/tr.png" height="28" width="28"></td></tr></tbody></table><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td background="Tutorial_35_files/l.png"><img src="Tutorial_35_files/l.png"></td>
<td valign="top" width="100%">下
面是有趣的东西...从AVI中抓取一帧,把它转为大小和色深可用的图象.lpbi包含一帧的BitmapInfoHeader信息.我们在下面第二行完
成了几件事.先是抓了动画的一帧...我们需要的帧面由这些帧确定.这会让动画走掉这一帧,lpbi会指向这一帧的头信息. <p>下面是有趣的东西...我们要指向图像数据了.要跳过头信息(lpbi->biSize).一件事直到写本文时我才意识到:
也要跳过任何的色彩信息.所以要跳过biClrUsed*sizeof(RGBQUAD)(译者:我想他是说要跳过调色板信息).做完这一切,我们就得到
图像数据的指针了(pdata).</p>
<p>也要把动画的每一帧的大小转为纹理能用的大小,还要把数据转为RGB数据.这用到DrawDibDraw(...).</p>
<p>一个大概的解释.我们能直接写设定的DIB图像.那就是DrawDibDraw(...)所做的.第一参数是DrawDib DC的句柄.第二参数是DC的句柄.接下来用左上角(0,0)和右下角(256,256)构成目标矩形.</p>
<p>lpbi指向刚读的帧的bitmapinfoheader信息.pdata是刚读的帧的图像数据指针.</p>
<p>再把源图象(刚读的帧)的左上角设为(0,0),右下角设为(帧宽,帧高).最后的参数应设为0.</p>
<p>这个方法可把任何大小、色深的图像转为256*256*24bit的图像.</p>
<p></p></td><td background="Tutorial_35_files/r.png"><img src="Tutorial_35_files/r.png"></td></tr></tbody></table><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img src="Tutorial_35_files/bl.png" height="28" width="28"></td><td width="100%"><img src="Tutorial_35_files/bc.png" height="28" width="100%"></td><td><img src="Tutorial_35_files/br.png" height="28" width="28"></td></tr></tbody></table>
<font color="#aaffaa" size="3">
<pre>void GrabAVIFrame(int frame) <font color="#ffffaa">// 从流中抓取一帧</font>
{
LPBITMAPINFOHEADER lpbi; <font color="#ffffaa"> // 存位图的头信息</font>
lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(pgf, frame); <font color="#ffffaa"> // 从AVI流中得到数据</font>
pdata=(char *)lpbi+lpbi->biSize+lpbi->biClrUsed * sizeof(RGBQUAD); <font color="#ffffaa">// 数据指针,由AVIStreamGetFrame返回(跳过头
//信息和色彩信息)</font>
<font color="#ffffaa">// 把数据转为所需格式</font>
DrawDibDraw (hdd, hdc, 0, 0, 256, 256, lpbi, pdata, 0, 0, width, height, 0);
</pre>
</font>
<table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img src="Tutorial_35_files/tl.png" height="28" width="28"></td><td width="100%"><img src="Tutorial_35_files/tc.png" height="28" width="100%"></td><td><img src="Tutorial_35_files/tr.png" height="28" width="28"></td></tr></tbody></table><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td background="Tutorial_35_files/l.png"><img src="Tutorial_35_files/l.png"></td>
<td valign="top" width="100%">我
们得到动画的每帧数据(红蓝数据颠倒的).为解决这个问题,我们的高速代码flipIt(...).记住,data是指向DIB比特值位置的指针的指针变
量.这意味着调用DrawDibDraw后,data指向一个调整过大小(256*256),修改过色深(24bits)的位图数据. <p>原来我通过重建动画的每一帧来更新纹理.我收到几封email建议我用glTexSubImage2D().翻阅了OpenGL红
宝书后,我磕磕绊绊的写出下面注释:"创建纹理的计算消耗比修改纹理要大.在OpenGL1.1版本中,有几条调用能更新全部或部分纹理图像信息.这对某
些应用程序有用,比如实时的抓取视频图像作纹理.对于这些程序,用glTexSubImage2D()根据新视频图像来创建单个纹理以代替旧的纹理数据是
行得通的."</p>
<p>在我个人并没有发现速度明显加快,也许在低端显卡上才会.glTexSubImage2D()的参数是:目标
是一个二维纹理(GL_TEXTURE_2D).细节级别(0),mipmapping用.x(0),y(0)告诉OpenGL开始拷贝的位置(0,0是
纹理的左下角).然后是图像的宽度,我们要拷贝的图像是256像素宽,256像素高.GL_RGB是我们的数据格式.我们在拷贝无符号byte.最
后...图像数据指针----data.非常简单!</p>
<p>Kevin Rogers
另加:我想指出使用glTexSubImage2D()另一个重要原因.不仅因为在许多OpenGL实现中它很快,还因为目标区不必是2的幂.这对视频重
放很方便,因为一帧的维通常不是2的幂(而是像320*200之类的).这样给了你很大机动性,你可以按视频流原本的样子播放,而不是扭曲或剪切每一帧来
适应纹理的维.</p>
<p>重要的是你不能更新一个纹理如果你第一次没有创建他!我们在Initialize()中创建纹理.</p>
<p>还要提到的是...如果你计划在工程里使用多个纹理,务必绑住你要更新的纹理.否则,更新出来的纹理也许不是你想要的!</p>
<p></p></td><td background="Tutorial_35_files/r.png"><img src="Tutorial_35_files/r.png"></td></tr></tbody></table><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img src="Tutorial_35_files/bl.png" height="28" width="28"></td><td width="100%"><img src="Tutorial_35_files/bc.png" height="28" width="100%"></td><td><img src="Tutorial_35_files/br.png" height="28" width="28"></td></tr></tbody></table>
<font color="#aaffaa" size="3">
<pre> flipIt(data); <font color="#ffffaa">// 交换红蓝数据</font>
<font color="#ffffaa">// 更新纹理</font>
glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, 256, 256, GL_RGB, GL_UNSIGNED_BYTE, data);
}
</pre>
</font>
<table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img src="Tutorial_35_files/tl.png" height="28" width="28"></td><td width="100%"><img src="Tutorial_35_files/tc.png" height="28" width="100%"></td><td><img src="Tutorial_35_files/tr.png" height="28" width="28"></td></tr></tbody></table><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td background="Tutorial_35_files/l.png"><img src="Tutorial_35_files/l.png"></td>
<td valign="top" width="100%">接下来的部分当程序退出时调用,我们关掉DrawDib DC,释放占用的资源.然后释放AVI GetFrame资源.最后释放AVI流和文件.</td>
<td background="Tutorial_35_files/r.png"><img src="Tutorial_35_files/r.png"></td></tr></tbody></table><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img src="Tutorial_35_files/bl.png" height="28" width="28"></td><td width="100%"><img src="Tutorial_35_files/bc.png" height="28" width="100%"></td><td><img src="Tutorial_35_files/br.png" height="28" width="28"></td></tr></tbody></table>
<font color="#aaffaa" size="3">
<pre>void CloseAVI(void) <font color="#ffffaa">// 关掉AVI资源</font>
{
DeleteObject(hBitmap); <font color="#ffffaa">//释放设备无关位图信息</font>
DrawDibClose(hdd); <font color="#ffffaa"> // 关掉DrawDib DC</font>
AVIStreamGetFrameClose(pgf); <font color="#ffffaa">// 释放AVI GetFrame资源</font>
AVIStreamRelease(pavi); <font color="#ffffaa">// 释放AVI流</font>
AVIFileExit(); <font color="#ffffaa">// 释放AVI文件</font>
}
</pre>
</font>
<table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img src="Tutorial_35_files/tl.png" height="28" width="28"></td><td width="100%"><img src="Tutorial_35_files/tc.png" height="28" width="100%"></td><td><img src="Tutorial_35_files/tr.png" height="28" width="28"></td></tr></tbody></table><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td background="Tutorial_35_files/l.png"><img src="Tutorial_35_files/l.png"></td>
<td valign="top" width="100%">初始化很简明.设初始的angle为0.再打开DrawDib库(得到一个DC).一切顺利的话,hdd会是新创建的dc的句柄.
<p>以黑色清屏,开启深度测试,等等.</p>
<p>然后建一个新的二次曲面.quadratic是这个新对象的指针.设置光滑的法线,允许纹理坐标的生成.</p>
<p></p></td><td background="Tutorial_35_files/r.png"><img src="Tutorial_35_files/r.png"></td></tr></tbody></table><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img src="Tutorial_35_files/bl.png" height="28" width="28"></td><td width="100%"><img src="Tutorial_35_files/bc.png" height="28" width="100%"></td><td><img src="Tutorial_35_files/br.png" height="28" width="28"></td></tr></tbody></table>
<font color="#aaffaa" size="3">
<pre>BOOL Initialize (GL_Window* window, Keys* keys)
{
g_window = window;
g_keys = keys;
<font color="#ffffaa">// 开始用户的初始</font>
angle = 0.0f; <font color="#ffffaa">// angle为0先</font>
hdd = DrawDibOpen(); <font color="#ffffaa">// 得到Dib的DC</font>
glClearColor (0.0f, 0.0f, 0.0f, 0.5f); <font color="#ffffaa">// 黑色背景</font>
glClearDepth (1.0f); <font color="#ffffaa">// 深度缓冲初始</font>
glDepthFunc (GL_LEQUAL); <font color="#ffffaa"> // 深度测试的类型(小于或等于)</font>
glEnable(GL_DEPTH_TEST); <font color="#ffffaa"> // 开启深度测试</font>
glShadeModel (GL_SMOOTH); <font color="#ffffaa"> // 平滑效果</font>
glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); <font color="#ffffaa">// 透视图计算设为 //最高精度</font>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -