⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tutorial_35.htm

📁 如果你相信它就好好学学吧,同样这里也只是个入门
💻 HTM
📖 第 1 页 / 共 5 页
字号:
long			lastframe;					<font color="#ffffaa">// 流中最后一帧</font>
int			width;						<font color="#ffffaa">// 视频宽</font>
int			height;						<font color="#ffffaa">// 视频高</font>
char			*pdata;						<font color="#ffffaa">// 纹理数据指针</font>
int			mpf;						<font color="#ffffaa">// 控制每帧显示时间</font>
</font></pre>
 
<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%">在本章中我们用GLU库创建两个二次曲面(球体和圆柱体).quadratic是曲面对象的指针.<br>
      hdd是DrawDib设备上下文的句柄.hdc是设备上下文的句柄.<br>
      hBitmap是设备无关位图的句柄(在后面位图转换时用到).<br>
      data是最后指向转换后位图的图象数据的指针,在后面的代码中会有意义,往下读:)</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>GLUquadricObj *quadratic;						<font color="#ffffaa">// 存储二次曲面对象</font>

HDRAWDIB hdd;							<font color="#ffffaa">// Dib句柄</font>
HBITMAP hBitmap;							<font color="#ffffaa">// 设备无关位图的句柄</font>
HDC hdc = CreateCompatibleDC(0);					<font color="#ffffaa">// 创建一个兼容的设备上下文</font>
unsigned char* data = 0;						<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%">下面使用到汇编语言.那些从来没有用过汇编的不要被吓倒了.他看起来神秘,实际上非常简单!
      <p>在
写本章是我发现了十分奇怪的事.第一次做出来的可以播放,但色彩混乱了.本来是红色的变成蓝色的了,本来是蓝色的变成红色的了.我简直要发狂了!我相信我
的代码某处有问题.看了一边代码还是找不到bug于是又读了MSDN.为什么红色与蓝色互换了!?!MSDN明明说24比特位图是RGB啊!又读了一些东
西,我找到了答案.在WINDOWS图形系统中,RGB数据是倒着存储的(BGR).而在OpenGL中,要用的RGB数据就是RGB的顺序!</p>
      <p>在抱怨了微软之后:)我决定加一条注解!我不因为RGB数据倒过来存放而打算骂微软.只是觉得很奇怪--他叫做RGB实际上在文件中是按BGR存的!</p>
      <p>另:这一点和"little endian"和"big endian"有关.Intel以及Intel兼容产品用little 
        endian--LSB(数据最低位)首先存.OpenGL是产生于Silicon Graphics的机器的,用的是big endian,所以标准的OpenGL要位图格式是big 
        endian格式.这是我的理解.</p>
      <p>棒
极了!所以说这第一个播放器就是一个垃圾!我的解决方法是用一个循环把数据交换过来.这能行,但太慢.我又在纹理生成代码中用GL_BGR_EXT代替了
GL_RGB,速度暴升,色彩显示也对了!问题解决了...原来我是这样想!后来发现一些OpenGL驱动不支持GL_BGR... :(</p>
      <p>与好友Maxwell Sayles讨论后,他推荐我用汇编代码来交换数据.一分钟后,他用icq发来下面的代码!也许不是最优化的,但他很快也很有效!</p>
      <p>动画的每一帧存在一个缓冲里.图象256像素宽,256像素高,每个色彩一字节(一像素3字节).下面的代码会扫描整个缓冲并交换红与蓝的字节.红存在ebx+0,蓝存在ebx+2.我们一次向前走3字节(因为一个像素3字节).不断扫描直到所有数据交换过来.</p>
      <p>你
们有些人不喜欢用汇编代码,所以我想有必要在本章里解释一下.本来计划用GL_BGR_EXT,他管用,但不是所有的显卡都支持!我又用异或交换法,这在
所有机器上都是有效的,但不十分快.用了汇编后速度相当快.考虑到我们在处理实时视频,你需要最快的交换方法.权衡了以上选择,汇编是最好的!如果你有更
好的办法,就用你自己的吧!我并不是告诉你必须如何去做,只是告诉你我的做法.我也会细致的解释代码.如果你要用更好的代码来作替换,你要清楚这些代码是
来干什么的,自己写代码时,要为日后的优化提供方便.</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 flipIt(void* buffer)						<font color="#ffffaa">// 交换红蓝数据(256x256)</font>
{
	void* b = buffer;						<font color="#ffffaa">// 缓冲指针</font>
	__asm							<font color="#ffffaa">// 汇编代码</font>
	{
		mov ecx, 256*256					<font color="#ffffaa">// 设置计数器</font>
		mov ebx, b					<font color="#ffffaa">// ebx存数据指针</font>
		label:						<font color="#ffffaa">// 循环标记</font>
			mov al,[ebx+0]				<font color="#ffffaa">// 把ebx位置的值赋予al</font>
			mov ah,[ebx+2]				<font color="#ffffaa">// 把ebx+2位置的值赋予ah</font>
			mov [ebx+2],al				<font color="#ffffaa">// 把al的值存到ebx+2的位置</font>
			mov [ebx+0],ah				<font color="#ffffaa">// 把ah的值存到ebx+0的位置</font>

			add ebx,3					<font color="#ffffaa">// 向前走3个字节</font>
			dec ecx					<font color="#ffffaa">// 循环计数器减1</font>
			jnz label					<font color="#ffffaa">// ecx非0则跳至label</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文件.szFile是打开文件的名字.title[100]用来修改window标题(显示AVI文件信息).
      <p>首先调用AVIFileInit().他初始化AVI文件库(使东西能用?鹄?).</p>
      <p>打
开AVI文件有很多方法.我采用AVIStreamOpenFromFile(...).他能打开AVI文件中单独一个流(AVI文件可以包含多个流).
它的参数如下:pavi是接收流句柄的缓冲的指针,szFile是打开文件的名字(包括路径).第三参数是打开的流的类型.在这个工程里,我们只对视频流
感兴趣(streamtypeVIDEO).第四参数是0,这表示我们需要第一次读到的视频流(一个AVI文件里会有多个视频流,我们要第一个).
OF_READ表示以只读方式打开文件.最后一个参数是一个类标识句柄的指针.说实话,我也不清楚他是干吗的.我让windows自己设定,于是把
NULL传过去.<br>
      </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 OpenAVI(LPCSTR szFile)						<font color="#ffffaa">// 打开AVI文件szFile</font>
{
	TCHAR	title[100];					<font color="#ffffaa">// 包含修改了的window标题</font>

	AVIFileInit();						<font color="#ffffaa">// 打开AVI文件库</font>

	<font color="#ffffaa">// 打开AVI流</font>
	if (AVIStreamOpenFromFile(&amp;pavi, szFile, streamtypeVIDEO, 0, OF_READ, NULL) !=0)
	{
		<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%">到目前为止,我们假定文件被正确打开,流被正确定位!然后用AVIStreamInfo(...)从AVI文件里抓取一些信息.
      <p>先前我们创建了叫psi的结构体来保存AVI流的信息.下面第一行,我们把AVI信息填入该结构体.从流的宽度(以像素计)到动画的帧速等所有的信息都会存到psi中.那些想要精确控制播放速度的要记住我刚才说的.更多的信息参阅MSDN.</p>
      <p>我们通过右边位置减左边位置算出帧宽.这个结果是以像素记的精确的帧宽.至于高度,可以用底边位置减顶边位置得到.这样得到高度的像素值.</p>
      <p>然后用AVIStreamLength(...)得到AVI文件最后一帧的序号.AVIStreamLength(...)返回动画最后一帧的序号.结果存在lastframe里.</p>
      <p>计
算帧速很简单.每秒帧速(fps)=
psi.dwRate/psi,dwScale.返回的值应该匹配显示帧的速度(你在AVI动画中右击鼠标可以看到).你会问那么这和mpf有什么关系
呢?第一次写这个代码时,我试着用fps来选择动画了正确的帧面.我遇到一个问题...视频放的太快!于是我看了一下视频属性.face2.avi文件有
3.36秒长.帧速是29.974fps.视频动画共有91帧.而3.36*29.974 = 100.71.非常奇怪!!</p>
      <p>所以我采用一些不同的方法.不是计算帧速,我计算每一帧播放所需时间.
AVIStreamSampleToTime()把在动画中的位置转换位你到达该位置所需的时间(毫秒计).所以通过计算到达最后一帧的时间就得到整个动
画的播放时间.再拿这个结果除以动画总帧数(lastframe).这样就给出了每帧的显示时间(毫秒计).结果存在mpf(milliseconds
per
frame)里.你也能通过获取动画中一帧的时间来算每帧的毫秒数,代码为:AVIStreamSampleToTime(pavi,1).两种方法都不
错!非常感谢Albert Chaulk提供思路!</p>
      <p>我说每帧的毫秒数不精确是因为mpf是一个整型值,所以所有的浮点数都会被取整.</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>	AVIStreamInfo(pavi, ψ, sizeof(psi));			<font color="#ffffaa">// 把流信息读进psi</font>
	width=psi.rcFrame.right-psi.rcFrame.left;			<font color="#ffffaa">// 宽度为右边减左边</font>
	height=psi.rcFrame.bottom-psi.rcFrame.top;			<font color="#ffffaa">// 高为底边减顶边</font>

	lastframe=AVIStreamLength(pavi);				<font color="#ffffaa">// 最后一帧的序号</font>

	mpf=AVIStreamSampleToTime(pavi,lastframe)/lastframe;		<font color="#ffffaa">// mpf的不精确值</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%">因为OpenGL需要纹理数据是2的幂,而大多视频是160*120,320*240等等,所以需要一种把视频格式重调整为能用作纹理的格式.我们可利用Windows 
      Dib函数去做.
      <p>首先要做的是描述我们想要的图像的类型.于是我们要以所需参数填好bmih这个BitmapInfoHeader结构.<br>
        首先设定该结构体的大小.再把位平面数设为1.3字节的数据有24比特(RGB).要使图像位256像素宽,256像素高,最后要让数据返回为UNCOMPRESSED(非压缩)的RGB数据(BI_RGB). 
      </p>
      <p>CreateDIBSection
创建一个可直接写的设备无关位图(dib).如果一切顺利,hBitmap会指向该dib的比特值.hdc是设备上下文(DC)的句柄第二参数是
BitmapInfo结构体的指针.该结构体包含了上述dib文件的信息.第三参数(DIB_RGB_COLORS)设定数据是RGB值.data是指向

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -