﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-教父的告白-随笔分类-HGE</title><link>http://www.cppblog.com/keigoliye/category/12618.html</link><description>一切都是纸老虎</description><language>zh-cn</language><lastBuildDate>Wed, 23 Dec 2009 08:54:06 GMT</lastBuildDate><pubDate>Wed, 23 Dec 2009 08:54:06 GMT</pubDate><ttl>60</ttl><item><title>HGE 系列教材 --- 输入、声音和渲染</title><link>http://www.cppblog.com/keigoliye/archive/2009/12/23/103812.html</link><dc:creator>暗夜教父</dc:creator><author>暗夜教父</author><pubDate>Wed, 23 Dec 2009 08:31:00 GMT</pubDate><guid>http://www.cppblog.com/keigoliye/archive/2009/12/23/103812.html</guid><wfw:comment>http://www.cppblog.com/keigoliye/comments/103812.html</wfw:comment><comments>http://www.cppblog.com/keigoliye/archive/2009/12/23/103812.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/keigoliye/comments/commentRss/103812.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/keigoliye/services/trackbacks/103812.html</trackback:ping><description><![CDATA[<p>建议读者对应 HGE 的官方的例子：Tutorial 02 - Using input, sound and rendering
来阅读本文</p>
<p><strong>渲染：</strong></p>
<p>在 HGE 中，四边形是一种图元，对应了结构体 hgeQuad，另外还有三角形图元，对应
hgeTriple，为了渲染，我们现在需要使用 hgeQuad 结构体，这个结构体如下：</p>
<p>struct hgeQuad<br>
{<br>
&nbsp; hgeVertex&nbsp; v[4];&nbsp;&nbsp; // 顶点描述了这个四边形<br>
&nbsp; HTEXTURE&nbsp;&nbsp; tex;&nbsp;&nbsp; // 纹理的句柄或者为0<br>
&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
blend;&nbsp;&nbsp; // 混合模式（blending mode）<br>
};</p>
<p>HGE 中图元对应的结构体总含有这3个部分：顶点，纹理句柄，混合模式</p>
<p>struct hgeVertex</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp; float <strong>x</strong>,
<strong>y</strong>;&nbsp;&nbsp; // 屏幕的 x，y 坐标</p>
<p>&nbsp;&nbsp;&nbsp; float <strong>z</strong>;&nbsp;&nbsp; //
Z-order，范围 [0, 1]</p>
<p>&nbsp;&nbsp;&nbsp; DWORD <strong>col</strong>;&nbsp;&nbsp; //
顶点的颜色</p>
<p>&nbsp;&nbsp;&nbsp; float <strong>tx</strong>,
<strong>ty</strong>;&nbsp;&nbsp; // 纹理的 x，y 坐标（赋值前需要规格化坐标间隔，使得
tx，ty 取值范围在[0,1]）</p>
<p>};</p>
<p>规格化坐标间隔在后面的例子中会谈到。不过先要谈到的一点是 tx，ty 的值超过 1 也是合法的</p>
<p>&nbsp;</p>
<p>1. 颜色的表示：</p>
<p>颜色使用32位表示，从左开始，8位为 Alpha 通道，8位红色，8位绿色，8位蓝色</p>
<p>对于后24位，如果全部为0，表示黑色，如果全部为1，表示白色</p>
<p>&nbsp;</p>
<p>2. 定义颜色的运算：</p>
<p>我们把颜色看成一个四维向量，即 alpha 通道，红色，绿色，蓝色这四个分量</p>
<p>&nbsp;&nbsp;&nbsp; &lt;1&gt; 颜色是可以相乘的</p>
<p>&nbsp;&nbsp;&nbsp; 颜色的相乘是对应的四个分量分别相乘的结果，即：alpha 通道的值与 alpha
通道的值相乘，红色的值与红色的值相乘，绿色的值与绿色的值相乘，蓝色的值与蓝色的值相乘。</p>
<p>&nbsp;&nbsp;&nbsp; &lt;2&gt; 颜色是可以相加的</p>
<p>&nbsp;&nbsp;&nbsp; 同上，对应分量相加。</p>
<p>颜色的每个分量使用浮点数表示，范围是[0-1]，相加操作可能导致溢出，一种处理的方式就是，如果溢出，则设定值为1。</p>
<p>&nbsp;</p>
<p>3. 混合模式：</p>
<p>1）BLEND_COLORADD</p>
<p>表示顶点的颜色与纹理的纹元（texel）颜色相加，这使得纹理变亮，可见顶点颜色为 0x00000000
将不造成任何影响。</p>
<p>2）BLEND_COLORMUL</p>
<p>表示顶点的颜色与纹理的纹元颜色相乘，这使得纹理变暗，可见顶点颜色为 0xFFFFFFFF 将不造成任何影响。</p>
<p>
注意：必须在1），2）中做一个选择，且只能选择1），2）中的一个。处理的对象是<strong>纹理颜色</strong>和<strong>顶点颜色</strong>。</p>
<p>这里有一个技巧：</p>
<p>
如果我们需要在程序中显示一个气球，这个气球的颜色不断变化，这时候我们并不需要准备多张不同颜色的气球纹理，而只需要一张白色的气球纹理，设置
blend 为 BLEND_COLORMUL，白色的R,G,B值被表示成
1.0，也就是说，纹理颜色和顶点颜色相乘的结果是顶点的颜色，那么就可以通过修改顶点颜色，得到任意颜色的气球了。</p>
<p>3）BLEND_ALPHABLEND</p>
<p>渲染时，将对象的像素颜色（而非顶点的颜色）与当前屏幕的对应像素颜色进行 alpha 混合。alpha 混合使用到 alpha
通道，对于两个像素颜色进行如下操作，得到一个颜色：</p>
<p>R(C)=alpha*R(B)+(1-alpha)*R(A)<br>
G(C)=alpha*G(B)+(1-alpha)*G(A)<br>
B(C)=alpha*B(B)+(1-alpha)*B(A)</p>
<p>这里的BLEND_ALPHABLEND使用的是对象像素的颜色的 alpha 通道。可见如果对象像素颜色 alpha 通道为
0，那么结果就是只有当前屏幕的像素颜色，也就是常常说的 100% 透明，因此，我们可以理解 alpha
混合就是一个是图像透明的操作，0 表示完全透明，255 表示完全不透明。</p>
<p>4）BLEND_ALPHAADD</p>
<p>渲染时，将对象的像素颜色与当前屏幕的对应像素颜色相加，结果是有了变亮的效果。</p>
<p>
注意：这里的3），4）必选其一，且只能选其一。处理的对象是<strong>对象像素颜色</strong>和<strong>屏幕像素颜色</strong>。选择
BLEND_ALPHABLEND 并且设定对象的 alpha 通道为 FF，可使此组参数无效。</p>
<p>5）BLEND_ZWRITE</p>
<p>渲染时，写像素的 Z-order 到 Z-buffer</p>
<p>6）BLEND_NOZWRITE</p>
<p>渲染时，不写像素的 Z-order 到 Z-buffer</p>
<p>这里一样是二者选一</p>
<p>设置举例：</p>
<p>quad.blend=BLEND_ALPHAADD | BLEND_COLORMUL |
BLEND_ZWRITE;&nbsp;&nbsp; // quad 为 hgeQuad 变量</p>
<p>&nbsp;</p>
<p>4. HGE 渲染</p>
<p>1）定义和初始化 hgeQuad 结构体：</p>
<p>hgeQuad quad;&nbsp;&nbsp; // 定义四边形</p>
<p>2）初始化 hgeQuad 变量：</p>
<p>// 设置混合模式</p>
<p>quad.blend=BLEND_ALPHAADD | BLEND_COLORMUL | BLEND_ZWRITE;</p>
<p>// 加载纹理</p>
<p>quad.tex = pHGE-&gt;Texture_Load("particles.png");</p>
<p>注意，读取硬盘上资源的时候，可能会失败，因此通常都需要检查，例如：</p>
<p>if (!quad.tex)</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp; MessageBox(NULL, "Load particles.png",
"Error", 0);</p>
<p>}</p>
<p>// 初始化顶点</p>
<p>for(int i=0;i&lt;4;i++)<br>
{<br>
&nbsp;&nbsp;&nbsp; // 设置顶点的 z 坐标<br>
&nbsp;&nbsp;&nbsp; quad.v[i].z=0.5f;<br>
&nbsp;&nbsp;&nbsp; // 设置顶点的颜色，颜色的格式为 0xAARRGGBB<br>
&nbsp;&nbsp;&nbsp; quad.v[i].col=0xFFFFA000;<br>
}</p>
<p>// 这里假定载入的纹理大小为
128*128，现在截取由点（96，64），（128，64），（128，96），（96，96）这四个点围成的图形。</p>
<p>quad.v[0].tx=96.0/128.0; quad.v[0].ty=64.0/128.0;&nbsp;&nbsp; //
规格化坐标间隔<br>
quad.v[1].tx=128.0/128.0; quad.v[1].ty=64.0/128.0;<br>
quad.v[2].tx=128.0/128.0; quad.v[2].ty=96.0/128.0;<br>
quad.v[3].tx=96.0/128.0; quad.v[3].ty=96.0/128.0;</p>
<p>注意，对于 hgeQuad 结构体，顶点 quad.v[0] 表示左上那个点，quad.v[1]
表示右上的点，quad.v[2] 表示右下的点，quad.v[3] 表示左下的点。</p>
<p>// 设置 hgeQuad 在屏幕中的位置</p>
<p>float x=100.0f, y=100.0f;</p>
<p>quad.v[0].x=x-16; quad.v[0].y=y-16;<br>
quad.v[1].x=x+16; quad.v[1].y=y-16;<br>
quad.v[2].x=x+16; quad.v[2].y=y+16;</p>
<p>quad.v[3].x=x-16; quad.v[3].y=y+16;</p>
<p>&nbsp;</p>
<p>3）设置渲染函数（render function）：</p>
<p>System_SetState(HGE_RENDERFUNC,RenderFunc);</p>
<p>RenderFunc 原型和帧函数一样：</p>
<p>bool RenderFunc();</p>
<p>4）编写 RenderFunc 函数：</p>
<p>bool RenderFunc()<br>
{<br>
&nbsp;&nbsp; &nbsp;pHGE-&gt;Gfx_BeginScene();&nbsp;&nbsp; //
在如何渲染之前，必须调用这个函数<br>
&nbsp;&nbsp; &nbsp;pHGE-&gt;Gfx_Clear(0);&nbsp;&nbsp; //
清屏，使用黑色，即颜色为 0<br>
&nbsp;&nbsp; &nbsp;pHGE-&gt;Gfx_RenderQuad(&amp;quad);&nbsp;&nbsp;
// 渲染<br>
&nbsp;&nbsp; &nbsp;pHGE-&gt;Gfx_EndScene();&nbsp;&nbsp; //
结束渲染，并且更新窗口<br>
&nbsp;&nbsp; &nbsp;return false;&nbsp;&nbsp; // 必须返回 false<br>
}</p>
<p>补充：Load 函数是和 Free 函数成对出现的，即在硬盘上加载了资源之后，需要 Free 它们，例如：</p>
<p>quad.tex = pHGE-&gt;Texture_Load("particles");</p>
<p>// ...</p>
<p>pHGE-&gt;Texture_Free(quad.tex);</p>
<p>&nbsp;</p>
<p>这里不得不谈一下规格化坐标间隔，而这之前，需要说说 Texture_GetWidth(xxx) 和
Texture_GetHeight(xxx) 函数，如果这样调用：Texture_GetWidth(xxx)
获取的是处于显存中的纹理宽度，而 Texture_GetWidth(xxx, true)
获取到的是图像文件的宽度，需要特别主义的是，对于同一张纹理来说，这两个值可能是不一样的，那么在规格化坐标间隔的时候，应该明确的是，对于一个
w*h 图像的图片，那么对于图中点（x,y）应该转换成为：</p>
<p>tx = x / pHGE-&gt;GetWidth(xxx);<br>
ty = y / pHGE-&gt;GetHeight(xxx);</p>
<p>而不能写成：</p>
<p>tx = x / pHGE-&gt;GetWidth(xxx, true);</p>
<p>ty = y / pHGE-&gt;GetHeight(xxx, true);</p>
<p>这里要注意一下 x，y 的含义</p>
<p>最后再谈一下 tx 和 ty，实际上 tx，ty 大于 1 也是合法的，例如：</p>
<p>tx = 800 / 64;</p>
<p>ty = 600 / 64;</p>
<p>这会使得图片重复，而具体的含义，可以通过实现来体会</p>
<p>&nbsp;</p>
<p><strong>音效：</strong></p>
<p>使用音效是很简单的</p>
<p>1. 载入音效：</p>
<p>HEFFECT hEffect = pHGE-&gt;Effect_Load("sound.mp3");</p>
<p>2. 播放：</p>
<p>pHGE-&gt;Effect_PlayEx(hEffect);</p>
<p>或者 pHGE-&gt;Effect_Play(hEffect);</p>
<p>1）Effect_Play 函数只接受一个参数就是音效的句柄 HEFFECT xx;</p>
<p>2）Effect_PlayEx 函数较为强大，一共有四个参数：</p>
<p>HCHANNEL Effect_PlayEx(<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; HEFFECT
effect,&nbsp;&nbsp; // 音效的句柄<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; int volume =
100,&nbsp;&nbsp; // 音量，100为最大，范围是[0, 100]<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; int pan =
0,&nbsp;&nbsp; // 范围是[-100, 100]，-100表示只使用左声道，100表示只使用右声道<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; float pitch =
1.0,&nbsp;&nbsp; // 播放速度，1.0
表示正常速度，值越大播放速度越快，值越小播放越慢。这个值要大于0才有效（不可以等于0）<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; bool loop =
false&nbsp;&nbsp; // 是否循环播放，false表示不循环<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; );</p>
<p>&nbsp;</p>
<p><strong>输入：</strong></p>
<p>仅仅需要调用函数 pHGE-&gt;Input_GetKeyState(HGEK_xxx);
来判断输入，应该在帧函数中调用它，例如：</p>
<p>bool FrameFunc()</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp; if
(pHGE-&gt;Input_GetKeyState(HGEK_LBUTTOM))</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; // ...</p>
<p>&nbsp;&nbsp;&nbsp; if (pHGE-&gt;Input_GetKeyState(HGEK_UP))</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; // ...</p>
<p>}</p><img src ="http://www.cppblog.com/keigoliye/aggbug/103812.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/keigoliye/" target="_blank">暗夜教父</a> 2009-12-23 16:31 <a href="http://www.cppblog.com/keigoliye/archive/2009/12/23/103812.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>