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

📄 水波的特效.htm

📁 一些3D水波生成的文档
💻 HTM
📖 第 1 页 / 共 2 页
字号:
            <TABLE align=center border=0 cellPadding=0 cellSpacing=0 
            class=BGWhite width="100%">
              <TBODY>
              <TR>
                <TD>
                  <DIV align=center><SPAN class=ColorCode>(4a+b)X0 + (4a+b)X1 + 
                  ... (4a+b)Xn = X0 + X1 + ... + Xn =&gt; 4a + b = 
                1</SPAN></DIV></TD></TR></TBODY></TABLE><BR>  找出一个最简解:a = 1/2、b = 
            -1。因为1/2可以用移位运算符“&gt;&gt;”来进行,不用进行乘除法,所以,这组解是最适用的而且是最快的。那么最后得到的公式就是:<BR><BR>
            <TABLE align=center border=0 cellPadding=0 cellSpacing=0 
            class=BGWhite width="100%">
              <TBODY>
              <TR>
                <TD>
                  <DIV align=center><SPAN class=ColorCode>X0' = (X1+X2+X3+X4) / 
                  2 - 
            X0</SPAN></DIV></TD></TR></TBODY></TABLE><BR>  好了,有了上面这个近似公式,你就可以推广到下面这个一般结论:已知某一时刻水面上任意一点的波幅,那么,在下一时刻,任意一点的波幅就等于与该点紧邻的前、后、左、右四点的波幅的和除以2、再减去该点的波幅。<BR>  应该注意到,水在实际中是存在阻尼的,否则,用上面这个公式,一旦你在水中增加一个波源,水面将永不停止的震荡下去。所以,还需要对波幅数据进行衰减处理,让每一个点在经过一次计算后,波幅都比理想值按一定的比例降低。这个衰减率经过测试,用1/32比较合适,也就是1/2^5。可以通过移位运算很快的获得。<BR>  到这里,水波特效制作中最艰难的部分已经度过了,下面是源程序中计算波幅数据的代码。<BR><BR>  <SPAN 
            class=ColorCatchword>//*******************************************************<BR>  // 
            计算波能数据缓冲区<BR>  //*******************************************************<BR></SPAN>  v<SPAN 
            class=ColorCode>oid RippleSpread()<BR>  {<BR>    for (int 
            i=BACKWIDTH; i&lt;BACKWIDTH*BACKHEIGHT-BACKWIDTH; 
            i++)<BR>    {<BR></SPAN>      <SPAN class=ColorCatchword>// 
            波能扩散<BR></SPAN>      <SPAN class=ColorCode>buf2[i] = 
            ((buf1[i-1]+<BR>          buf1[i+1]+<BR>          buf1[i-BACKWIDTH]+<BR>          buf1[i+BACKWIDTH])&gt;&gt;1)-<BR>          buf2[i];<BR></SPAN>      <SPAN 
            class=ColorCatchword>// 波能衰减<BR></SPAN>      <SPAN 
            class=ColorCode>buf2[i] -= 
            buf2[i]&gt;&gt;5;<BR>    }<BR></SPAN><BR>    <SPAN 
            class=ColorCatchword>// 交换波能数据缓冲区<BR></SPAN>    <SPAN 
            class=ColorCode>short *ptmp =buf1;<BR>    buf1 = buf2;<BR>    buf2 = 
            ptmp;<BR>  }<BR></SPAN><A 
            name=3.0></A><BR><B>3、进行渲染</B><BR>  下面再来根据算出的波幅数据对页面进行渲染。因为水的折射,当水面不与我们的视线相垂直的时候,我们所看到的水下的景物并不是在观察点的正下方,而存在一定的偏移。偏移的程度与水波的斜率,水的折射率和水的深度都有关系,如果要进行精确的计算的话,显然是很不现实的。同样,我们只需要做线形的近似处理就行了。因为水面越倾斜,所看到的水下景物偏移量就越大,所以,我们可以近似的用水面上某点的前后、左右两点的波幅之差来代表所看到水底景物的偏移量。<BR>  在程序中,用一个页面装载原始的图象,用另外一个页面来进行渲染。先用Lock函数锁定两个页面,取得指向页面内存区的指针,然后用根据偏移量将原始图象上的每一个象素复制到渲染页面上。进行页面渲染的代码如下(下面的代码为了便于理解,并没有进行优化,实际上,优化后的代码比它要麻烦许多):<BR><BR>  <SPAN 
            class=ColorCatchword>//*******************************************************<BR>  // 
            根据波能数据缓冲区对离屏页面进行渲染<BR>  //*******************************************************<BR></SPAN>  <SPAN 
            class=ColorCode>void RenderRipple()<BR>  {<BR></SPAN>    <SPAN 
            class=ColorCatchword>// 锁定两个离屏页面<BR></SPAN>    <SPAN 
            class=ColorCode>DDSURFACEDESC ddsd1, ddsd2;<BR>    ddsd1.dwSize = 
            sizeof(DDSURFACEDESC);<BR>    ddsd2.dwSize = 
            sizeof(DDSURFACEDESC);<BR>    lpDDSPic1-&gt;Lock(NULL, &amp;ddsd1, 
            DDLOCK_WAIT, NULL);<BR>    lpDDSPic2-&gt;Lock(NULL, &amp;ddsd2, 
            DDLOCK_WAIT, NULL);<BR></SPAN><BR>    <SPAN class=ColorCatchword>// 
            取得页面象素位深度,和页面内存指针<BR></SPAN>    <SPAN class=ColorCode>int 
            depth=ddsd1.ddpfPixelFormat.dwRGBBitCount/8;<BR>    BYTE *Bitmap1 = 
            (BYTE*)ddsd1.lpSurface;<BR>    BYTE *Bitmap2 = 
            (BYTE*)ddsd2.lpSurface;<BR></SPAN><BR>    <SPAN 
            class=ColorCatchword>// 下面进行页面渲染<BR></SPAN>    <SPAN 
            class=ColorCode>int xoff, yoff;<BR>    int k = BACKWIDTH;<BR>    for 
            (int i=1; i&lt;BACKHEIGHT-1; i++)<BR>    {<BR>      for (int j=0; 
            j&gt;BACKWIDTH; j++)<BR>      {<BR></SPAN>        <SPAN 
            class=ColorCatchword>// 计算偏移量<BR></SPAN>        <SPAN 
            class=ColorCode>xoff = buf1[k-1]-buf1[k+1];<BR>        yoff = 
            buf1[k-BACKWIDTH]-buf1[k+BACKWIDTH];<BR></SPAN><BR>        <SPAN 
            class=ColorCatchword>// 判断坐标是否在窗口范围内<BR></SPAN>        <SPAN 
            class=ColorCode>if ((i+yoff )&lt; 0 ) { k++; continue; 
            }<BR>        if ((i+yoff )&gt; BACKHEIGHT) { k++; continue; 
            }<BR>        if ((j+xoff )&lt; 0 ) { k++; continue; }<BR>        if 
            ((j+xoff )&gt; BACKWIDTH ) { k++; continue; 
            }<BR></SPAN><BR>        <SPAN class=ColorCatchword>// 
            计算出偏移象素和原始象素的内存地址偏移量<BR></SPAN>        <SPAN class=ColorCode>int 
            pos1, pos2;<BR>        pos1=ddsd1.lPitch*(i+yoff)+ 
            depth*(j+xoff);<BR>        pos2=ddsd2.lPitch*i+ 
            depth*j;<BR></SPAN><BR>        <SPAN class=ColorCatchword>// 
            复制象素<BR></SPAN>        <SPAN class=ColorCode>for (int d=0; d &lt; 
            depth; 
            d++)<BR>          Bitmap2[pos2++]=Bitmap1[pos1++];<BR>        k++;<BR>      }<BR>    }<BR></SPAN>    <SPAN 
            class=ColorCatchword>// 解锁页面<BR></SPAN>    <SPAN 
            class=ColorCode>lpDDSPic1-&gt;Unlock(&amp;ddsd1);<BR>    lpDDSPic2-&gt;Unlock(&amp;ddsd2);<BR>  }</SPAN><BR><A 
            name=4.0></A><BR><B>4、增加波源</B><BR>  为了形成水波,我们必须在水池中加入波源,你可以想象成向水中投入石头,形成的波源的大小和能量与石头的半径和你扔石头的力量都有关系。知道了这些,那么好,我们只要修改波能数据缓冲区buf,让它在石头入水的地点来一个负的“尖脉冲”,即让buf[x,y]=-n。经过实验,n的范围在(32~128)之间比较合适。<BR>  控制波源半径也好办,你只要以石头入水中心点为圆心,画一个以石头半径为半径的圆,让这个圆中所有的点都来这么一个负的“尖脉冲”就可以了(这里也做了近似处理)。增加波源的代码如下:<BR><BR>  <SPAN 
            class=ColorCatchword>//*****************************************************<BR>  // 
            增加波源<BR>  //*****************************************************<BR></SPAN>  <SPAN 
            class=ColorCode>void DropStone(int x,</SPAN>    <SPAN 
            class=ColorCatchword>// x坐标<BR></SPAN>      <SPAN 
            class=ColorCode>int y,</SPAN>       <SPAN class=ColorCatchword>// 
            y坐标<BR></SPAN>      <SPAN class=ColorCode>int stonesize,</SPAN>   
            <SPAN class=ColorCatchword>// 波源半径<BR></SPAN>      <SPAN 
            class=ColorCode>int stoneweight )</SPAN> <SPAN 
            class=ColorCatchword>// 波源能量<BR></SPAN>  <SPAN 
            class=ColorCode>{<BR></SPAN>    <SPAN class=ColorCatchword>// 
            判断坐标是否在屏幕范围内<BR></SPAN>    <SPAN 
            class=ColorCode>if((x+stonesize)&gt;BACKWIDTH ||<BR>      
            (y+stonesize)&gt;BACKHEIGHT||<BR>      (x-stonesize)&lt;0 || 
            (y-stonesize)&lt;0) return;<BR><BR>    for (int posx=x-stonesize; 
            posx&lt;x+stonesize; posx++)<BR>      for (int posy=y-stonesize; 
            posy&gt;y+stonesize; posy++)<BR>        if((posx-x)*(posx-x) + 
            (posy-y)*(posy-y) &lt; 
            stonesize*stonesize)<BR>          buf1[BACKWIDTH*posy+posx] = 
            -stoneweight;<BR>  }<BR></SPAN><BR>  至此,水波特效的制作原理就此就全部揭示了。在上面的推导中,每一步都进行了很多看似非常过分的近似处理,但是,你完全不必担心,事实证明,用这种方法,在速度和图象上都可以获得非常好的效果。源程序中有非常详尽的注释,仔细推敲一下,看懂它们应该不成问题。<BR>   
            这个程序是Windows下的DirectX编程,没有使用任何包装库。在我的电脑上(<SPAN class=English>AMD 
            K6-200、2MBVRam、64MBSDRam</SPAN>),320×240的画面大小,每秒可以达到25帧。与前几个程序不一样,这个程序使用了窗口模式,所以调试起来很方便。如果你对窗口模式编程不熟悉,这个程序也是一个很好的例子。<BR>  这种用数据缓冲区对图象进行水波处理的方法,有个最大的好处就是,程序运算和其示的速度与水波的复杂程度是没有关系的,无论水面是风平浪静还是波涛汹涌,程序的FPS始终保持不变,这一点你研究一下程序就应该可以看出来。实际上,如果你掌握了这种方法,将这种方法推广一下,完全可以做出另外一些特殊的效果,如烟雾、大气、阳光等,我现在也正在研究这些特效的制作,相信不久以后就会有新的收获。<BR><BR>  <IMG 
            height=16 src="水波的特效.files/download.gif" width=16> <A 
            class=BlackLink 
            href="http://imagic3d.com/ripsample.zip">演示程序及源代码</A>(<SPAN 
            class=English>LINK</SPAN>:<A class=BlackLink 
            href="http://imagic3d.com/" target=_blank>Imagic工作室</A>) <!-- #EndEditable --></TD></TR>
        <TR>
          <TD class=BGGray width="100%">
            <DIV align=right><!-- #BeginEditable "%B2%D9%D7%F7%CC%A8" --><A 
            class=BlackLink 
            href="http://mays.soage.com/develop/effect/200111/dx_Ripple.htm#7758258"><B>返回</B></A><!-- #EndEditable --> 
            </DIV></TD></TR>
        <TR>
          <TD class=BGGray width="100%"><!-- #BeginEditable "%C4%DA%C8%DD%B0%E6%C8%A8" -->
            <SCRIPT language=JavaScript src="水波的特效.files/effect.CR.js"></SCRIPT>
<!-- #EndEditable --></TD></TR></TBODY></TABLE></TD></TR>
  <TR>
    <TD width=0%></TD>
    <TD class=BGGray colSpan=2 height=3 vAlign=top></TD>
    <TD colSpan=2></TD>
    <TD colSpan=3 vAlign=top width="84%"></TD></TR></TBODY></TABLE><BR><!-- #BeginEditable "%B0%E6%C8%A8" -->
<SCRIPT language=JavaScript src="水波的特效.files/copyright.4.js"></SCRIPT>

<SCRIPT language=JavaScript src="水波的特效.files/scroll.js"></SCRIPT>

<SCRIPT language=JavaScript src="水波的特效.files/return.main.js"></SCRIPT>

<SCRIPT language=JavaScript src="水波的特效.files/return.sub.js"></SCRIPT>
<!-- #EndEditable --><!-- #EndTemplate --></BODY></HTML>

⌨️ 快捷键说明

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