源码来自Opegl高级编程.docx
《源码来自Opegl高级编程.docx》由会员分享,可在线阅读,更多相关《源码来自Opegl高级编程.docx(36页珍藏版)》请在冰豆网上搜索。
源码来自Opegl高级编程
源码来自Opegl高级编程第7章
首先我们看他的初始化函数
ripple_init(intw,inth)
这里传入了个窗口的大小(高和宽),
glEnable(GL_TEXTURE_2D);
这个函数打开2D纹理模式
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL);
设置贴图模式
OpenGL提供了3种不同的贴图模式:
GL_MODULATE,GL_DECAL和GL_BLEND。
默认情况下,贴图模式是GL_MODULATE,在这种模式下,OpenGL会根据当前的光照系统调整物体的色彩和明暗。
第二种模式是GL_DECAL,在这种模式下所有的光照效果都是无效的,OpenGL将仅依据纹理贴图来绘制物体的表面。
最后是GL_BLEND,这种模式允许我们使用混合纹理。
在这种模式下,我们可以把当前纹理同一个颜色混合而得到一个新的纹理。
我们可以调用glTexEnvi函数来设置当前贴图模式:
纹理过滤函数glTexParameteri()
图象从纹理图象空间映射到帧缓冲图象空间(映射需要重新构造纹理图像,这样就会造成应用到多边形上的图像失真),这时就可用glTexParmeteri()函数来确定如何把纹理象素映射成像素.
GL_TEXTURE_2D:
操作2D纹理.
GL_TEXTURE_WRAP_S:
S方向上的贴图模式.
GL_CLAMP:
将纹理坐标限制在0.0,1.0的范围之内.如果超出了会如何呢.不会错误,只是会边缘拉伸填充.
glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,image1->sizeX,image1->sizeY,0,GL_RGB,GL_UNSIGNED_BYTE,image1->data)
指定纹理数据,也就是图像
接下来
for(i=0;i{t[i]=ripple_max+RIPPLE_LENGTH;//这里初始化水波的步长,也就是水波的扩散速度cx[i]=0;//这里初始化第N个水波X坐标cy[i]=0;//这里初始化第N个水波Y坐标max[i]=0;//这里初始化水波最大个数}接下来就是初始化顶点数据for(i=0;ifor(j=0;j{ripple_vertex[i][j].x[0]=i/(GRID_SIZE_X-1.0)*w;//当前绘图的顶点数据ripple_vertex[i][j].x[1]=j/(GRID_SIZE_Y-1.0)*h;ripple_vertex[i][j].dt[0]=i/(GRID_SIZE_X-1.0);//默认绘制的纹理数据ripple_vertex[i][j].dt[1]=j/(GRID_SIZE_Y-1.0);}上面初始化完之后就可以调用ripple_redraw()//进行绘图功能了{inti,j;glClear(GL_COLOR_BUFFER_BIT);//这个函数我就不多说了,一看就知道是干什么的//glBindTexture(GL_TEXTURE_2D,texture[0]);checkGlError("glBindTexture");glEnableClientState(GL_VERTEX_ARRAY);checkGlError("glEnableClientStateisforGL_VERTEX_ARRAY");//打开顶点数组功能,使之支持顶点数组的绘制glEnableClientState(GL_TEXTURE_COORD_ARRAY);checkGlError("glEnableClientStateisforGL_TEXTURE_COORD_ARRAY");//打开纹理数组功能,使之支持纹理功能的绘制glTexCoordPointer(2,GL_FLOAT,0,Textcoord);//纹理数组glVertexPointer(2,GL_FLOAT,0,VertexPointer);//顶点数组glDrawArrays(GL_TRIANGLE_STRIP,0,(GRID_SIZE_X-1)*(GRID_SIZE_Y-1)*4);/,跟据顶点数组和纹理数组,开始执行上面的纹理数据glDisableClientState(GL_VERTEX_ARRAY);checkGlError("glDisableClientState");//恢愎顶点数组功能glDisableClientState(GL_TEXTURE_COORD_ARRAY);checkGlError("glDisableClientState");纹理贴图和顶点之间的影射,我下面给出例子,下面对纹理的基础详细地说下毕竟这是程序的核心:纹理贴图,从本质上讲,是一个位图图像。在程序中,一个纹理图像就是一个一维或二维数组,存储每一个像素的颜色值(包括R、G、B、A分值,分别存储一个像素的红、绿、蓝、透明度分值)。而运用一个纹理贴图,就是把纹理图像根据纹理坐标对应到图元上。比如,现在有一个三角形,顶点坐标分别是V1、V2、V3,以及一幅纹理图像,现在,我们可以在绘制三角形之前,为顶点V1、V2、V3指定纹理坐标,使每一个顶点的坐标和它的纹理坐标一一对应一旦我们建立起这种对应关系,就可以开启纹理贴图功能,让OpenGL根据顶点的贴图坐标渲染出指定的三角形这就是纹理贴图的原理。至此我们明白了opengl的纹理基础,稍做变换就能纹出我们需要的图形出来,下面我们讲解水波原理原理来自上面的网站,有兴趣的同学们可以去看看我英文不好,暂时对这以下的话做我理解的翻译吧,如有不妥之处请指正(欢迎给我送花和扔鸡蛋^_^):Well,firstly,you'llneed2arraysofwords(integers).Yes,that'sright,words,notbytes.Thesearrayswillholdthestateofthewater.Oneholdsthecurrentstate,theotherholdsthestatefromthepreviousframe.Itisimportantthatyouhave2arrays,sinceyouneedtoknowhowthewaterhaschangedsincethelastframe,andtheframebeforethat.首先我们需要一个2个整型数组,使用这些数组保存前一帧的阵列和当前的状态,现在你有两个数组,一个用改变的帧,一个用于你之前的帧Datafromthepreviousframe(Buffer2)andtheframebeforethat(Buffer1)areusedtogether,andtheresultswrittenintoBuffer1.Buffer1containsthecurrentstateofthewater.前两个数据帧Buff1帧和buffer2帧进行&操作的结果影响到Buffer1当前内容水的状态damping=somenon-integerbetween0and1Damping=0~1之间的小数0阻值最大,1是没有阻力beginloopforeverynon-edgeelement:loopBuffer2(x,y)=(Buffer1(x-1,y)Buffer1(x+1,y)Buffer1(x,y+1)Buffer1(x,y-1))/2-Buffer2(x,y)Buffer2(x,y)=Buffer2(x,y)*dampingendloopDisplayBuffer2SwapthebuffersendloopToexplainhowandwhythisworks,imagineawavetravelingacrossa1-Dimensionalsurface(wave0).Thiswaveistravelingtotheleft.Thesmallverticalarrowsindicatetherateatwhichthewaterlevelchangeswithtime.Thefainterwavesshowthewave'spositionsonpreviousframes.Sohowdoweachievethecorrectchangesinheightforeachpartofthewave(verticalarrows)?Youmaynoticethattheheightofthewavetwoframesolder(wave2),isproportionaltothesizeofthearrows.Soaslongastheprevioustwoframesareremembered,itiseasytoworkoutthechangeinheightofeverypartofthewave.So,takealookatthecodeagain.Whentheloopstarts,Buffer1containsthestateofthewaterfromthepreviousframe(wave1),andBuffer2hasthestatebeforethat(wave2).Buffer2thereforehasinformationabouttheverticalvelocityofthewave.为了说明这工作方式,想象一下波的移动过程,小的垂直箭头指示水位的变化随时间速度而变化,用前一帧的波浪数据显示波浪,因此我们得如何实现改变箭头正确的垂直高度从而改变每一部份波?您可能注意到前二帧波的高度(wave2)和箭头是成正比的,因此只要记住前两帧就能得出了改变波高度的部份因此我们需要看代码,当循环开始,buffer1包含第一帧水的状态数据与之前的bueffer2状态,因此得出关于buffer2的垂直波的速度,Velocity(x,y)=-Buffer2(x,y)Itisalsoimportantforthewavestospreadout,sothebuffersaresmoothedeveryframe.每帧波扩散传播的Smoothed公式Smoothed(x,y)=(Buffer1(x-1,y)+Buffer1(x+1,y)+Buffer1(x,y-1)+Buffer1(x,y+1))/4Now,tocombinethetwotocalculatethenewheightofthewater.Themultiplicationbytworeducestheeffectofthevelocity.现在结合计算新的水的高度,两个Smoothed(x,y)+速度NewHeight(x,y)=Smoothed(x,y)*2+Velocity(x,y)Finally,theripplesmustloseenergy,sotheyaredamped:最后,涟漪必须失去能量,所以他们乘以一个阻值:NewHeight(x,y)=NewHeight(x,y)*dampingNote:Thiscanbeoptimisedto:注意:可以优化成这样:NewHeight(x,y)=NewHeight(x,y)-(NewHeight(x,y)/n)wherenissomepowerof2.其中n有2个能量。So,whenyouputallthattogether,youendupwithsomereasonablyfastcode.Here'stheimportantinnerloopwritteninC.所以,当你把所有这些结合在一起,你就会有一个相当于如下的代码。以下是C写的重要的内部循环中voidProcessWater(short*source,short*dest){inti;for(i=320;i<64000-320;i++){dest[i]=(((source[i-1]+source[i+1]+source[i-320]+source[i+320])>>1))-dest[i];dest[i]-=(dest[i]>>5);}}RenderingtheWaterSo,havingcodedthisallup,howdoyougoaboutrenderingit?Well,thebufferseachcontaianheightmaprepresentingtheheightofthewaterateachpixel.Asyoumayhaveguessed,therearemanywaysyoucangoaboutrenderingaheightfield,but,inthiscase,aneasyandeffectivemethodistodosimpleshadingandrefraction.Youwillneedatexturetogobehindthewater,soyoucanseetherefraction.渲染水:有了所有这些代码,然后你怎么渲染呢?每一个缓冲区的高度影射到水的像素高度,你也可能想到有许多其他的方法去得到一个高度,但在这情况下,最简单有效的方法就是做阴影和折射,你需要纹理水之后,才能看到折射foreverypixel(x,y)inthebufferXoffset=buffer(x-1,y)-buffer(x+1,y)Yoffset=buffer(x,y-1)-buffer(x,y+1)Shading=Xoffsett=texture(x+Xoffset,y+Yoffset)p=t+Shadingplotpixelat(x,y)withcolourpendloopNotes:Thismethodisbynomeansahighlyaccuratesimulationofripplingwater(thoughitisnottoofaroff).Ripplescanpassthrougheachotherquitehappily,andwillreflectoffthewalls.Onethingyoumaynotice,however,isthattheripplesarenotquitecircular.这部份我就不翻译了。致此我们对水波的的运动轨迹都有了一个大楖的轮廓,为证实我们的想法,我们使用如下描述,这是摘自opengl高级编程的一段话Themeshgeometrycanbetexturedusingsimpleproceduraltextureimages,pro-ducingagoodsimulationofwatersurfaces.Syntheticperturbationstothetexturecoordinates,asoutlinedinSection18.3.7,canalsobeusedtohelpanimatethewatersurface.Ifthereisanadequateperformancemargin,therenderingtechniquecanbefurtherembellished.Multipassormultitexturerenderingtechniquescanbeusedtolayeraddi-tionaleffectssuchassurf.Thereectivepropertiesofthewatersurfacecanalsobemoreaccuratelysimulated.Environmentmappingcanbeusedtosimulatebasicreactionsfromthesurface,suchassunsmear.ThisspecularreactioncanbemademorephysicallyaccurateusingenvironmentmappingthatincorporatestheFresnelreectionmodeldescribedinSection15.9.Thebump-mappingtechniquedescribedinSection15.10canbeusedtocreatetheillusionofrippleswithouthavingtomodeltheminthemeshgeometry.Thebumpmapscanbeupdateddynamicallytoanimatethem.Animatingwatersurfaceeffectsalsoextendstounderwaterscenes.OpticaleffectssuchascausticscanbeapproximatedusingOpenGL,asdescribedbyNishitaandNakamae(Nishita,1994),butinteractiveframeratesarenotlikelytobeachieved.Amoreefcient,iflessaccurate,techniquetomodelsucheffectsusesacaustictexturetomodu-latetheintensityofanygeometrythatliesbelowthesurface.Otherbelow-surfaceeffectscanalsobesimulated.Movementsofthewater(surge)canbesimulatedbyperturbingthevertexcoordinatesofsubmergedobjects,againusingsinusoids.Blueish-greenfogcanbeusedtosimulatelightattenuationinwater.翻译上面的:网格几何图像可以使用简单程序去纹理,能够产生了良好的水面模拟,扰动纹理的合成坐标能够画出如18.3.7描述的那样,如果有足够的性能保证,渲染技术能进一步优化,多重纹理技术可以用层来叠加,例如波浪的额外效果,这样活动水的表面属性会更加准确地模拟,环境映射可以被用来模拟基本的光源反应,例如太阳照射的表面.这镜面反应可以更准确地体现物理环境的映射,采用Fresnel反应模型描述在15.9节中,在15.10章节指出凹凸映射技术,可以创建一个涟漪模型,不必在网格中使用.凹凸的影射可动态更新显示它们。动画的水面效果也延伸到水下的场面,光学效应如焦散可以近似使用OpenGL如Nishita与Nakamae(Nishita,1994)的描述,但互动帧速率并不是很完美。如果不准确,可使用技术示范中影响纹理中调试任何几何表面强度(这句不好翻译还是看原文吧)。其他水底物体效果也可以模拟。水(风动)再次使用正弦模拟顶点水下运动的对象坐标扰动,蓝色到绿色的雾可以用于模拟光在水中的衰减。看了上面的知识我们就有了一个大檓的基础,那么我们就算不用opengl的例子我们也同样能生成水波的程序。那么先让我们来回顾一下在高中的物理课上我们所学的关于水波的知识。水波有如下几个特性:扩散:当你投一块石头到水中,你会看到一个以石头入水点为圆心所形成的一圈圈的水波,这里,你可能会被这个现象所误导,以为水波上的每一点都是以石头入水点为中心向外扩散的,这是错误的。实际上,水波上的任何一点在任何时候都是以自己为圆心向四周扩散的,之所以会形成一个环状的水波,是因为水波的内部因为扩散的对称而相互抵消了。衰减:因为水是有阻尼的,否则,当你在水池中投入石头,水波就会永不停止的震荡下去。水的折射:因为水波上不同地点的倾斜角度不同,所以,因为水的折射,我们从观察点垂直往下看到的水底并不是在观察点的正下方,而有一定的偏移。如果不考虑水面上部的光线反射,这就是我们能感觉到水波形状的原因。反射:水波遇到障碍物会反射。衍射:这个可以通过扭曲的纹理来模拟实现,那么就能看到水波的衍射现象了。好了,有了这几个特性,再运用数学和几何知识,我们就可以模拟出真实的水波了。但是,如果你曾用3DMax做过水波的动画,你就会知道要渲染出一幅真实形状的水波画面少说也得好几十秒,而我们现在需要的是实时的渲染,每秒种至少也得渲染20帧才能使得水波得以平滑的显示。考虑到手机运算的速度,我们不可能按照正弦函数或精确的公式来构造水波,不能用乘除法,更不能用sin、cos,只能用一种取近似值的快速算法,尽管这种算法存在一定误差,但是为了满足实时动画的要求,我们不得不这样做。首先我们要建立两个与水池图象一样大小的数组buffer1[PoolWidth*PoolHeight]和buffer2[PoolWidth*PoolHeight](PoolWidth=水池图象的象素宽度、PoolHeight=水池
{
t[i]=ripple_max+RIPPLE_LENGTH;//这里初始化水波的步长,也就是水波的扩散速度
cx[i]=0;//这里初始化第N个水波X坐标
cy[i]=0;//这里初始化第N个水波Y坐标
max[i]=0;//这里初始化水波最大个数
}
接下来就是初始化顶点数据
for(i=0;ifor(j=0;j{ripple_vertex[i][j].x[0]=i/(GRID_SIZE_X-1.0)*w;//当前绘图的顶点数据ripple_vertex[i][j].x[1]=j/(GRID_SIZE_Y-1.0)*h;ripple_vertex[i][j].dt[0]=i/(GRID_SIZE_X-1.0);//默认绘制的纹理数据ripple_vertex[i][j].dt[1]=j/(GRID_SIZE_Y-1.0);}上面初始化完之后就可以调用ripple_redraw()//进行绘图功能了{inti,j;glClear(GL_COLOR_BUFFER_BIT);//这个函数我就不多说了,一看就知道是干什么的//glBindTexture(GL_TEXTURE_2D,texture[0]);checkGlError("glBindTexture");glEnableClientState(GL_VERTEX_ARRAY);checkGlError("glEnableClientStateisforGL_VERTEX_ARRAY");//打开顶点数组功能,使之支持顶点数组的绘制glEnableClientState(GL_TEXTURE_COORD_ARRAY);checkGlError("glEnableClientStateisforGL_TEXTURE_COORD_ARRAY");//打开纹理数组功能,使之支持纹理功能的绘制glTexCoordPointer(2,GL_FLOAT,0,Textcoord);//纹理数组glVertexPointer(2,GL_FLOAT,0,VertexPointer);//顶点数组glDrawArrays(GL_TRIANGLE_STRIP,0,(GRID_SIZE_X-1)*(GRID_SIZE_Y-1)*4);/,跟据顶点数组和纹理数组,开始执行上面的纹理数据glDisableClientState(GL_VERTEX_ARRAY);checkGlError("glDisableClientState");//恢愎顶点数组功能glDisableClientState(GL_TEXTURE_COORD_ARRAY);checkGlError("glDisableClientState");纹理贴图和顶点之间的影射,我下面给出例子,下面对纹理的基础详细地说下毕竟这是程序的核心:纹理贴图,从本质上讲,是一个位图图像。在程序中,一个纹理图像就是一个一维或二维数组,存储每一个像素的颜色值(包括R、G、B、A分值,分别存储一个像素的红、绿、蓝、透明度分值)。而运用一个纹理贴图,就是把纹理图像根据纹理坐标对应到图元上。比如,现在有一个三角形,顶点坐标分别是V1、V2、V3,以及一幅纹理图像,现在,我们可以在绘制三角形之前,为顶点V1、V2、V3指定纹理坐标,使每一个顶点的坐标和它的纹理坐标一一对应一旦我们建立起这种对应关系,就可以开启纹理贴图功能,让OpenGL根据顶点的贴图坐标渲染出指定的三角形这就是纹理贴图的原理。至此我们明白了opengl的纹理基础,稍做变换就能纹出我们需要的图形出来,下面我们讲解水波原理原理来自上面的网站,有兴趣的同学们可以去看看我英文不好,暂时对这以下的话做我理解的翻译吧,如有不妥之处请指正(欢迎给我送花和扔鸡蛋^_^):Well,firstly,you'llneed2arraysofwords(integers).Yes,that'sright,words,notbytes.Thesearrayswillholdthestateofthewater.Oneholdsthecurrentstate,theotherholdsthestatefromthepreviousframe.Itisimportantthatyouhave2arrays,sinceyouneedtoknowhowthewaterhaschangedsincethelastframe,andtheframebeforethat.首先我们需要一个2个整型数组,使用这些数组保存前一帧的阵列和当前的状态,现在你有两个数组,一个用改变的帧,一个用于你之前的帧Datafromthepreviousframe(Buffer2)andtheframebeforethat(Buffer1)areusedtogether,andtheresultswrittenintoBuffer1.Buffer1containsthecurrentstateofthewater.前两个数据帧Buff1帧和buffer2帧进行&操作的结果影响到Buffer1当前内容水的状态damping=somenon-integerbetween0and1Damping=0~1之间的小数0阻值最大,1是没有阻力beginloopforeverynon-edgeelement:loopBuffer2(x,y)=(Buffer1(x-1,y)Buffer1(x+1,y)Buffer1(x,y+1)Buffer1(x,y-1))/2-Buffer2(x,y)Buffer2(x,y)=Buffer2(x,y)*dampingendloopDisplayBuffer2SwapthebuffersendloopToexplainhowandwhythisworks,imagineawavetravelingacrossa1-Dimensionalsurface(wave0).Thiswaveistravelingtotheleft.Thesmallverticalarrowsindicatetherateatwhichthewaterlevelchangeswithtime.Thefainterwavesshowthewave'spositionsonpreviousframes.Sohowdoweachievethecorrectchangesinheightforeachpartofthewave(verticalarrows)?Youmaynoticethattheheightofthewavetwoframesolder(wave2),isproportionaltothesizeofthearrows.Soaslongastheprevioustwoframesareremembered,itiseasytoworkoutthechangeinheightofeverypartofthewave.So,takealookatthecodeagain.Whentheloopstarts,Buffer1containsthestateofthewaterfromthepreviousframe(wave1),andBuffer2hasthestatebeforethat(wave2).Buffer2thereforehasinformationabouttheverticalvelocityofthewave.为了说明这工作方式,想象一下波的移动过程,小的垂直箭头指示水位的变化随时间速度而变化,用前一帧的波浪数据显示波浪,因此我们得如何实现改变箭头正确的垂直高度从而改变每一部份波?您可能注意到前二帧波的高度(wave2)和箭头是成正比的,因此只要记住前两帧就能得出了改变波高度的部份因此我们需要看代码,当循环开始,buffer1包含第一帧水的状态数据与之前的bueffer2状态,因此得出关于buffer2的垂直波的速度,Velocity(x,y)=-Buffer2(x,y)Itisalsoimportantforthewavestospreadout,sothebuffersaresmoothedeveryframe.每帧波扩散传播的Smoothed公式Smoothed(x,y)=(Buffer1(x-1,y)+Buffer1(x+1,y)+Buffer1(x,y-1)+Buffer1(x,y+1))/4Now,tocombinethetwotocalculatethenewheightofthewater.Themultiplicationbytworeducestheeffectofthevelocity.现在结合计算新的水的高度,两个Smoothed(x,y)+速度NewHeight(x,y)=Smoothed(x,y)*2+Velocity(x,y)Finally,theripplesmustloseenergy,sotheyaredamped:最后,涟漪必须失去能量,所以他们乘以一个阻值:NewHeight(x,y)=NewHeight(x,y)*dampingNote:Thiscanbeoptimisedto:注意:可以优化成这样:NewHeight(x,y)=NewHeight(x,y)-(NewHeight(x,y)/n)wherenissomepowerof2.其中n有2个能量。So,whenyouputallthattogether,youendupwithsomereasonablyfastcode.Here'stheimportantinnerloopwritteninC.所以,当你把所有这些结合在一起,你就会有一个相当于如下的代码。以下是C写的重要的内部循环中voidProcessWater(short*source,short*dest){inti;for(i=320;i<64000-320;i++){dest[i]=(((source[i-1]+source[i+1]+source[i-320]+source[i+320])>>1))-dest[i];dest[i]-=(dest[i]>>5);}}RenderingtheWaterSo,havingcodedthisallup,howdoyougoaboutrenderingit?Well,thebufferseachcontaianheightmaprepresentingtheheightofthewaterateachpixel.Asyoumayhaveguessed,therearemanywaysyoucangoaboutrenderingaheightfield,but,inthiscase,aneasyandeffectivemethodistodosimpleshadingandrefraction.Youwillneedatexturetogobehindthewater,soyoucanseetherefraction.渲染水:有了所有这些代码,然后你怎么渲染呢?每一个缓冲区的高度影射到水的像素高度,你也可能想到有许多其他的方法去得到一个高度,但在这情况下,最简单有效的方法就是做阴影和折射,你需要纹理水之后,才能看到折射foreverypixel(x,y)inthebufferXoffset=buffer(x-1,y)-buffer(x+1,y)Yoffset=buffer(x,y-1)-buffer(x,y+1)Shading=Xoffsett=texture(x+Xoffset,y+Yoffset)p=t+Shadingplotpixelat(x,y)withcolourpendloopNotes:Thismethodisbynomeansahighlyaccuratesimulationofripplingwater(thoughitisnottoofaroff).Ripplescanpassthrougheachotherquitehappily,andwillreflectoffthewalls.Onethingyoumaynotice,however,isthattheripplesarenotquitecircular.这部份我就不翻译了。致此我们对水波的的运动轨迹都有了一个大楖的轮廓,为证实我们的想法,我们使用如下描述,这是摘自opengl高级编程的一段话Themeshgeometrycanbetexturedusingsimpleproceduraltextureimages,pro-ducingagoodsimulationofwatersurfaces.Syntheticperturbationstothetexturecoordinates,asoutlinedinSection18.3.7,canalsobeusedtohelpanimatethewatersurface.Ifthereisanadequateperformancemargin,therenderingtechniquecanbefurtherembellished.Multipassormultitexturerenderingtechniquescanbeusedtolayeraddi-tionaleffectssuchassurf.Thereectivepropertiesofthewatersurfacecanalsobemoreaccuratelysimulated.Environmentmappingcanbeusedtosimulatebasicreactionsfromthesurface,suchassunsmear.ThisspecularreactioncanbemademorephysicallyaccurateusingenvironmentmappingthatincorporatestheFresnelreectionmodeldescribedinSection15.9.Thebump-mappingtechniquedescribedinSection15.10canbeusedtocreatetheillusionofrippleswithouthavingtomodeltheminthemeshgeometry.Thebumpmapscanbeupdateddynamicallytoanimatethem.Animatingwatersurfaceeffectsalsoextendstounderwaterscenes.OpticaleffectssuchascausticscanbeapproximatedusingOpenGL,asdescribedbyNishitaandNakamae(Nishita,1994),butinteractiveframeratesarenotlikelytobeachieved.Amoreefcient,iflessaccurate,techniquetomodelsucheffectsusesacaustictexturetomodu-latetheintensityofanygeometrythatliesbelowthesurface.Otherbelow-surfaceeffectscanalsobesimulated.Movementsofthewater(surge)canbesimulatedbyperturbingthevertexcoordinatesofsubmergedobjects,againusingsinusoids.Blueish-greenfogcanbeusedtosimulatelightattenuationinwater.翻译上面的:网格几何图像可以使用简单程序去纹理,能够产生了良好的水面模拟,扰动纹理的合成坐标能够画出如18.3.7描述的那样,如果有足够的性能保证,渲染技术能进一步优化,多重纹理技术可以用层来叠加,例如波浪的额外效果,这样活动水的表面属性会更加准确地模拟,环境映射可以被用来模拟基本的光源反应,例如太阳照射的表面.这镜面反应可以更准确地体现物理环境的映射,采用Fresnel反应模型描述在15.9节中,在15.10章节指出凹凸映射技术,可以创建一个涟漪模型,不必在网格中使用.凹凸的影射可动态更新显示它们。动画的水面效果也延伸到水下的场面,光学效应如焦散可以近似使用OpenGL如Nishita与Nakamae(Nishita,1994)的描述,但互动帧速率并不是很完美。如果不准确,可使用技术示范中影响纹理中调试任何几何表面强度(这句不好翻译还是看原文吧)。其他水底物体效果也可以模拟。水(风动)再次使用正弦模拟顶点水下运动的对象坐标扰动,蓝色到绿色的雾可以用于模拟光在水中的衰减。看了上面的知识我们就有了一个大檓的基础,那么我们就算不用opengl的例子我们也同样能生成水波的程序。那么先让我们来回顾一下在高中的物理课上我们所学的关于水波的知识。水波有如下几个特性:扩散:当你投一块石头到水中,你会看到一个以石头入水点为圆心所形成的一圈圈的水波,这里,你可能会被这个现象所误导,以为水波上的每一点都是以石头入水点为中心向外扩散的,这是错误的。实际上,水波上的任何一点在任何时候都是以自己为圆心向四周扩散的,之所以会形成一个环状的水波,是因为水波的内部因为扩散的对称而相互抵消了。衰减:因为水是有阻尼的,否则,当你在水池中投入石头,水波就会永不停止的震荡下去。水的折射:因为水波上不同地点的倾斜角度不同,所以,因为水的折射,我们从观察点垂直往下看到的水底并不是在观察点的正下方,而有一定的偏移。如果不考虑水面上部的光线反射,这就是我们能感觉到水波形状的原因。反射:水波遇到障碍物会反射。衍射:这个可以通过扭曲的纹理来模拟实现,那么就能看到水波的衍射现象了。好了,有了这几个特性,再运用数学和几何知识,我们就可以模拟出真实的水波了。但是,如果你曾用3DMax做过水波的动画,你就会知道要渲染出一幅真实形状的水波画面少说也得好几十秒,而我们现在需要的是实时的渲染,每秒种至少也得渲染20帧才能使得水波得以平滑的显示。考虑到手机运算的速度,我们不可能按照正弦函数或精确的公式来构造水波,不能用乘除法,更不能用sin、cos,只能用一种取近似值的快速算法,尽管这种算法存在一定误差,但是为了满足实时动画的要求,我们不得不这样做。首先我们要建立两个与水池图象一样大小的数组buffer1[PoolWidth*PoolHeight]和buffer2[PoolWidth*PoolHeight](PoolWidth=水池图象的象素宽度、PoolHeight=水池
for(j=0;j{ripple_vertex[i][j].x[0]=i/(GRID_SIZE_X-1.0)*w;//当前绘图的顶点数据ripple_vertex[i][j].x[1]=j/(GRID_SIZE_Y-1.0)*h;ripple_vertex[i][j].dt[0]=i/(GRID_SIZE_X-1.0);//默认绘制的纹理数据ripple_vertex[i][j].dt[1]=j/(GRID_SIZE_Y-1.0);}上面初始化完之后就可以调用ripple_redraw()//进行绘图功能了{inti,j;glClear(GL_COLOR_BUFFER_BIT);//这个函数我就不多说了,一看就知道是干什么的//glBindTexture(GL_TEXTURE_2D,texture[0]);checkGlError("glBindTexture");glEnableClientState(GL_VERTEX_ARRAY);checkGlError("glEnableClientStateisforGL_VERTEX_ARRAY");//打开顶点数组功能,使之支持顶点数组的绘制glEnableClientState(GL_TEXTURE_COORD_ARRAY);checkGlError("glEnableClientStateisforGL_TEXTURE_COORD_ARRAY");//打开纹理数组功能,使之支持纹理功能的绘制glTexCoordPointer(2,GL_FLOAT,0,Textcoord);//纹理数组glVertexPointer(2,GL_FLOAT,0,VertexPointer);//顶点数组glDrawArrays(GL_TRIANGLE_STRIP,0,(GRID_SIZE_X-1)*(GRID_SIZE_Y-1)*4);/,跟据顶点数组和纹理数组,开始执行上面的纹理数据glDisableClientState(GL_VERTEX_ARRAY);checkGlError("glDisableClientState");//恢愎顶点数组功能glDisableClientState(GL_TEXTURE_COORD_ARRAY);checkGlError("glDisableClientState");纹理贴图和顶点之间的影射,我下面给出例子,下面对纹理的基础详细地说下毕竟这是程序的核心:纹理贴图,从本质上讲,是一个位图图像。在程序中,一个纹理图像就是一个一维或二维数组,存储每一个像素的颜色值(包括R、G、B、A分值,分别存储一个像素的红、绿、蓝、透明度分值)。而运用一个纹理贴图,就是把纹理图像根据纹理坐标对应到图元上。比如,现在有一个三角形,顶点坐标分别是V1、V2、V3,以及一幅纹理图像,现在,我们可以在绘制三角形之前,为顶点V1、V2、V3指定纹理坐标,使每一个顶点的坐标和它的纹理坐标一一对应一旦我们建立起这种对应关系,就可以开启纹理贴图功能,让OpenGL根据顶点的贴图坐标渲染出指定的三角形这就是纹理贴图的原理。至此我们明白了opengl的纹理基础,稍做变换就能纹出我们需要的图形出来,下面我们讲解水波原理原理来自上面的网站,有兴趣的同学们可以去看看我英文不好,暂时对这以下的话做我理解的翻译吧,如有不妥之处请指正(欢迎给我送花和扔鸡蛋^_^):Well,firstly,you'llneed2arraysofwords(integers).Yes,that'sright,words,notbytes.Thesearrayswillholdthestateofthewater.Oneholdsthecurrentstate,theotherholdsthestatefromthepreviousframe.Itisimportantthatyouhave2arrays,sinceyouneedtoknowhowthewaterhaschangedsincethelastframe,andtheframebeforethat.首先我们需要一个2个整型数组,使用这些数组保存前一帧的阵列和当前的状态,现在你有两个数组,一个用改变的帧,一个用于你之前的帧Datafromthepreviousframe(Buffer2)andtheframebeforethat(Buffer1)areusedtogether,andtheresultswrittenintoBuffer1.Buffer1containsthecurrentstateofthewater.前两个数据帧Buff1帧和buffer2帧进行&操作的结果影响到Buffer1当前内容水的状态damping=somenon-integerbetween0and1Damping=0~1之间的小数0阻值最大,1是没有阻力beginloopforeverynon-edgeelement:loopBuffer2(x,y)=(Buffer1(x-1,y)Buffer1(x+1,y)Buffer1(x,y+1)Buffer1(x,y-1))/2-Buffer2(x,y)Buffer2(x,y)=Buffer2(x,y)*dampingendloopDisplayBuffer2SwapthebuffersendloopToexplainhowandwhythisworks,imagineawavetravelingacrossa1-Dimensionalsurface(wave0).Thiswaveistravelingtotheleft.Thesmallverticalarrowsindicatetherateatwhichthewaterlevelchangeswithtime.Thefainterwavesshowthewave'spositionsonpreviousframes.Sohowdoweachievethecorrectchangesinheightforeachpartofthewave(verticalarrows)?Youmaynoticethattheheightofthewavetwoframesolder(wave2),isproportionaltothesizeofthearrows.Soaslongastheprevioustwoframesareremembered,itiseasytoworkoutthechangeinheightofeverypartofthewave.So,takealookatthecodeagain.Whentheloopstarts,Buffer1containsthestateofthewaterfromthepreviousframe(wave1),andBuffer2hasthestatebeforethat(wave2).Buffer2thereforehasinformationabouttheverticalvelocityofthewave.为了说明这工作方式,想象一下波的移动过程,小的垂直箭头指示水位的变化随时间速度而变化,用前一帧的波浪数据显示波浪,因此我们得如何实现改变箭头正确的垂直高度从而改变每一部份波?您可能注意到前二帧波的高度(wave2)和箭头是成正比的,因此只要记住前两帧就能得出了改变波高度的部份因此我们需要看代码,当循环开始,buffer1包含第一帧水的状态数据与之前的bueffer2状态,因此得出关于buffer2的垂直波的速度,Velocity(x,y)=-Buffer2(x,y)Itisalsoimportantforthewavestospreadout,sothebuffersaresmoothedeveryframe.每帧波扩散传播的Smoothed公式Smoothed(x,y)=(Buffer1(x-1,y)+Buffer1(x+1,y)+Buffer1(x,y-1)+Buffer1(x,y+1))/4Now,tocombinethetwotocalculatethenewheightofthewater.Themultiplicationbytworeducestheeffectofthevelocity.现在结合计算新的水的高度,两个Smoothed(x,y)+速度NewHeight(x,y)=Smoothed(x,y)*2+Velocity(x,y)Finally,theripplesmustloseenergy,sotheyaredamped:最后,涟漪必须失去能量,所以他们乘以一个阻值:NewHeight(x,y)=NewHeight(x,y)*dampingNote:Thiscanbeoptimisedto:注意:可以优化成这样:NewHeight(x,y)=NewHeight(x,y)-(NewHeight(x,y)/n)wherenissomepowerof2.其中n有2个能量。So,whenyouputallthattogether,youendupwithsomereasonablyfastcode.Here'stheimportantinnerloopwritteninC.所以,当你把所有这些结合在一起,你就会有一个相当于如下的代码。以下是C写的重要的内部循环中voidProcessWater(short*source,short*dest){inti;for(i=320;i<64000-320;i++){dest[i]=(((source[i-1]+source[i+1]+source[i-320]+source[i+320])>>1))-dest[i];dest[i]-=(dest[i]>>5);}}RenderingtheWaterSo,havingcodedthisallup,howdoyougoaboutrenderingit?Well,thebufferseachcontaianheightmaprepresentingtheheightofthewaterateachpixel.Asyoumayhaveguessed,therearemanywaysyoucangoaboutrenderingaheightfield,but,inthiscase,aneasyandeffectivemethodistodosimpleshadingandrefraction.Youwillneedatexturetogobehindthewater,soyoucanseetherefraction.渲染水:有了所有这些代码,然后你怎么渲染呢?每一个缓冲区的高度影射到水的像素高度,你也可能想到有许多其他的方法去得到一个高度,但在这情况下,最简单有效的方法就是做阴影和折射,你需要纹理水之后,才能看到折射foreverypixel(x,y)inthebufferXoffset=buffer(x-1,y)-buffer(x+1,y)Yoffset=buffer(x,y-1)-buffer(x,y+1)Shading=Xoffsett=texture(x+Xoffset,y+Yoffset)p=t+Shadingplotpixelat(x,y)withcolourpendloopNotes:Thismethodisbynomeansahighlyaccuratesimulationofripplingwater(thoughitisnottoofaroff).Ripplescanpassthrougheachotherquitehappily,andwillreflectoffthewalls.Onethingyoumaynotice,however,isthattheripplesarenotquitecircular.这部份我就不翻译了。致此我们对水波的的运动轨迹都有了一个大楖的轮廓,为证实我们的想法,我们使用如下描述,这是摘自opengl高级编程的一段话Themeshgeometrycanbetexturedusingsimpleproceduraltextureimages,pro-ducingagoodsimulationofwatersurfaces.Syntheticperturbationstothetexturecoordinates,asoutlinedinSection18.3.7,canalsobeusedtohelpanimatethewatersurface.Ifthereisanadequateperformancemargin,therenderingtechniquecanbefurtherembellished.Multipassormultitexturerenderingtechniquescanbeusedtolayeraddi-tionaleffectssuchassurf.Thereectivepropertiesofthewatersurfacecanalsobemoreaccuratelysimulated.Environmentmappingcanbeusedtosimulatebasicreactionsfromthesurface,suchassunsmear.ThisspecularreactioncanbemademorephysicallyaccurateusingenvironmentmappingthatincorporatestheFresnelreectionmodeldescribedinSection15.9.Thebump-mappingtechniquedescribedinSection15.10canbeusedtocreatetheillusionofrippleswithouthavingtomodeltheminthemeshgeometry.Thebumpmapscanbeupdateddynamicallytoanimatethem.Animatingwatersurfaceeffectsalsoextendstounderwaterscenes.OpticaleffectssuchascausticscanbeapproximatedusingOpenGL,asdescribedbyNishitaandNakamae(Nishita,1994),butinteractiveframeratesarenotlikelytobeachieved.Amoreefcient,iflessaccurate,techniquetomodelsucheffectsusesacaustictexturetomodu-latetheintensityofanygeometrythatliesbelowthesurface.Otherbelow-surfaceeffectscanalsobesimulated.Movementsofthewater(surge)canbesimulatedbyperturbingthevertexcoordinatesofsubmergedobjects,againusingsinusoids.Blueish-greenfogcanbeusedtosimulatelightattenuationinwater.翻译上面的:网格几何图像可以使用简单程序去纹理,能够产生了良好的水面模拟,扰动纹理的合成坐标能够画出如18.3.7描述的那样,如果有足够的性能保证,渲染技术能进一步优化,多重纹理技术可以用层来叠加,例如波浪的额外效果,这样活动水的表面属性会更加准确地模拟,环境映射可以被用来模拟基本的光源反应,例如太阳照射的表面.这镜面反应可以更准确地体现物理环境的映射,采用Fresnel反应模型描述在15.9节中,在15.10章节指出凹凸映射技术,可以创建一个涟漪模型,不必在网格中使用.凹凸的影射可动态更新显示它们。动画的水面效果也延伸到水下的场面,光学效应如焦散可以近似使用OpenGL如Nishita与Nakamae(Nishita,1994)的描述,但互动帧速率并不是很完美。如果不准确,可使用技术示范中影响纹理中调试任何几何表面强度(这句不好翻译还是看原文吧)。其他水底物体效果也可以模拟。水(风动)再次使用正弦模拟顶点水下运动的对象坐标扰动,蓝色到绿色的雾可以用于模拟光在水中的衰减。看了上面的知识我们就有了一个大檓的基础,那么我们就算不用opengl的例子我们也同样能生成水波的程序。那么先让我们来回顾一下在高中的物理课上我们所学的关于水波的知识。水波有如下几个特性:扩散:当你投一块石头到水中,你会看到一个以石头入水点为圆心所形成的一圈圈的水波,这里,你可能会被这个现象所误导,以为水波上的每一点都是以石头入水点为中心向外扩散的,这是错误的。实际上,水波上的任何一点在任何时候都是以自己为圆心向四周扩散的,之所以会形成一个环状的水波,是因为水波的内部因为扩散的对称而相互抵消了。衰减:因为水是有阻尼的,否则,当你在水池中投入石头,水波就会永不停止的震荡下去。水的折射:因为水波上不同地点的倾斜角度不同,所以,因为水的折射,我们从观察点垂直往下看到的水底并不是在观察点的正下方,而有一定的偏移。如果不考虑水面上部的光线反射,这就是我们能感觉到水波形状的原因。反射:水波遇到障碍物会反射。衍射:这个可以通过扭曲的纹理来模拟实现,那么就能看到水波的衍射现象了。好了,有了这几个特性,再运用数学和几何知识,我们就可以模拟出真实的水波了。但是,如果你曾用3DMax做过水波的动画,你就会知道要渲染出一幅真实形状的水波画面少说也得好几十秒,而我们现在需要的是实时的渲染,每秒种至少也得渲染20帧才能使得水波得以平滑的显示。考虑到手机运算的速度,我们不可能按照正弦函数或精确的公式来构造水波,不能用乘除法,更不能用sin、cos,只能用一种取近似值的快速算法,尽管这种算法存在一定误差,但是为了满足实时动画的要求,我们不得不这样做。首先我们要建立两个与水池图象一样大小的数组buffer1[PoolWidth*PoolHeight]和buffer2[PoolWidth*PoolHeight](PoolWidth=水池图象的象素宽度、PoolHeight=水池
ripple_vertex[i][j].x[0]=i/(GRID_SIZE_X-1.0)*w;//当前绘图的顶点数据
ripple_vertex[i][j].x[1]=j/(GRID_SIZE_Y-1.0)*h;
ripple_vertex[i][j].dt[0]=i/(GRID_SIZE_X-1.0);//默认绘制的纹理数据
ripple_vertex[i][j].dt[1]=j/(GRID_SIZE_Y-1.0);
上面初始化完之后就可以调用
ripple_redraw()//进行绘图功能了
inti,j;
glClear(GL_COLOR_BUFFER_BIT);//这个函数我就不多说了,一看就知道是干什么的
//glBindTexture(GL_TEXTURE_2D,texture[0]);checkGlError("glBindTexture");
glEnableClientState(GL_VERTEX_ARRAY);checkGlError("glEnableClientStateisforGL_VERTEX_ARRAY");//打开顶点数组功能,使之支持顶点数组的绘制
glEnableClientState(GL_TEXTURE_COORD_ARRAY);checkGlError("glEnableClientStateisforGL_TEXTURE_COORD_ARRAY");//打开纹理数组功能,使之支持纹理功能的绘制
glTexCoordPointer(2,GL_FLOAT,0,Textcoord);//纹理数组
glVertexPointer(2,GL_FLOAT,0,VertexPointer);//顶点数组
glDrawArrays(GL_TRIANGLE_STRIP,0,(GRID_SIZE_X-1)*(GRID_SIZE_Y-1)*4);/,跟据顶点数组和纹理数组,开始执行上面的纹理数据
glDisableClientState(GL_VERTEX_ARRAY);checkGlError("glDisableClientState");//恢愎顶点数组功能
glDisableClientState(GL_TEXTURE_COORD_ARRAY);checkGlError("glDisableClientState");
纹理贴图和顶点之间的影射,我下面给出例子,下面对纹理的基础详细地说下毕竟这是程序的核心:
纹理贴图,从本质上讲,是一个位图图像。
在程序中,一个纹理图像就是一个一维或二维数组,存储每一个像素的颜色值(包括R、G、B、A分值,分别存储一个像素的红、绿、蓝、透明度分值)。
而运用一个纹理贴图,就是把纹理图像根据纹理坐标对应到图元上。
比如,现在有一个三角形,顶点坐标分别是V1、V2、V3,以及一幅纹理图像,
现在,我们可以在绘制三角形之前,为顶点V1、V2、V3指定纹理坐标,使每一个顶点的坐标和它的纹理坐标一一对应
一旦我们建立起这种对应关系,就可以开启纹理贴图功能,让OpenGL根据顶点的贴图坐标渲染出指定的三角形
这就是纹理贴图的原理。
至此我们明白了opengl的纹理基础,稍做变换就能纹出我们需要的图形出来,下面我们讲解水波原理
原理来自上面的网站,有兴趣的同学们可以去看看
我英文不好,暂时对这以下的话做我理解的翻译吧,如有不妥之处请指正(欢迎给我送花和扔鸡蛋^_^):
Well,firstly,you'llneed2arraysofwords(integers).Yes,that'sright,words,notbytes.Thesearrayswillholdthestateofthewater.Oneholdsthecurrentstate,theotherholdsthestatefromthepreviousframe.Itisimportantthatyouhave2arrays,sinceyouneedtoknowhowthewaterhaschangedsincethelastframe,andtheframebeforethat.
首先我们需要一个2个整型数组,使用这些数组保存前一帧的阵列和当前的状态,现在你有两个数组,一个用改变的帧,一个用于你之前的帧
Datafromthepreviousframe(Buffer2)andtheframebeforethat(Buffer1)areusedtogether,andtheresultswrittenintoBuffer1.
Buffer1containsthecurrentstateofthewater.
前两个数据帧Buff1帧和buffer2帧进行&操作的结果影响到Buffer1当前内容水的状态
damping=somenon-integerbetween0and1
Damping=0~1之间的小数0阻值最大,1是没有阻力
beginloop
foreverynon-edgeelement:
loop
Buffer2(x,y)=(Buffer1(x-1,y)
Buffer1(x+1,y)
Buffer1(x,y+1)
Buffer1(x,y-1))/2-Buffer2(x,y)
Buffer2(x,y)=Buffer2(x,y)*damping
endloop
DisplayBuffer2
Swapthebuffers
Toexplainhowandwhythisworks,imagineawavetravelingacrossa1-Dimensionalsurface(wave0).Thiswaveistravelingtotheleft.Thesmallverticalarrowsindicatetherateatwhichthewaterlevelchangeswithtime.Thefainterwavesshowthewave'spositionsonpreviousframes.Sohowdoweachievethecorrectchangesinheightforeachpartofthewave(verticalarrows)?
Youmaynoticethattheheightofthewavetwoframesolder(wave2),isproportionaltothesizeofthearrows.Soaslongastheprevioustwoframesareremembered,itiseasytoworkoutthechangeinheightofeverypartofthewave.
So,takealookatthecodeagain.Whentheloopstarts,Buffer1containsthestateofthewaterfromthepreviousframe(wave1),andBuffer2hasthestatebeforethat(wave2).Buffer2thereforehasinformationabouttheverticalvelocityofthewave.
为了说明这工作方式,想象一下波的移动过程,小的垂直箭头指示水位的变化随时间速度而变化,用前一帧的波浪数据显示波浪,因此我们得如何实现改变箭头正确的垂直高度从而改变每一部份波?
您可能注意到前二帧波的高度(wave2)和箭头是成正比的,因此只要记住前两帧就能得出了改变波高度的部份
因此我们需要看代码,当循环开始,buffer1包含第一帧水的状态数据与之前的bueffer2状态,因此得出关于buffer2的垂直波的速度,
Velocity(x,y)=-Buffer2(x,y)
Itisalsoimportantforthewavestospreadout,sothebuffersaresmoothedeveryframe.
每帧波扩散传播的Smoothed公式
Smoothed(x,y)=(Buffer1(x-1,y)+
Buffer1(x+1,y)+
Buffer1(x,y-1)+
Buffer1(x,y+1))/4
Now,tocombinethetwotocalculatethenewheightofthewater.Themultiplicationbytworeducestheeffectofthevelocity.
现在结合计算新的水的高度,两个Smoothed(x,y)+速度
NewHeight(x,y)=Smoothed(x,y)*2+Velocity(x,y)
Finally,theripplesmustloseenergy,sotheyaredamped:
最后,涟漪必须失去能量,所以他们乘以一个阻值:
NewHeight(x,y)=NewHeight(x,y)*damping
Note:
Thiscanbeoptimisedto:
注意:
可以优化成这样:
NewHeight(x,y)=NewHeight(x,y)-(NewHeight(x,y)/n)
wherenissomepowerof2.
其中n有2个能量。
So,whenyouputallthattogether,youendupwithsomereasonablyfastcode.Here'stheimportantinnerloopwritteninC.
所以,当你把所有这些结合在一起,你就会有一个相当于如下的代码。
以下是C写的重要的内部循环中
voidProcessWater(short*source,short*dest)
inti;
for(i=320;i<64000-320;i++)
dest[i]=(
((source[i-1]+
source[i+1]+
source[i-320]+
source[i+320])>>1))-dest[i];
dest[i]-=(dest[i]>>5);
RenderingtheWater
So,havingcodedthisallup,howdoyougoaboutrenderingit?
Well,thebufferseachcontaianheightmaprepresentingtheheightofthewaterateachpixel.Asyoumayhaveguessed,therearemanywaysyoucangoaboutrenderingaheightfield,but,inthiscase,aneasyandeffectivemethodistodosimpleshadingandrefraction.Youwillneedatexturetogobehindthewater,soyoucanseetherefraction.
渲染水:
有了所有这些代码,然后你怎么渲染呢?
每一个缓冲区的高度影射到水的像素高度,你也可能想到有许多其他的方法去得到一个高度,但在这情况下,最简单有效的方法就是做阴影和折射,你需要纹理水之后,才能看到折射
foreverypixel(x,y)inthebuffer
Xoffset=buffer(x-1,y)-buffer(x+1,y)
Yoffset=buffer(x,y-1)-buffer(x,y+1)
Shading=Xoffset
t=texture(x+Xoffset,y+Yoffset)
p=t+Shading
plotpixelat(x,y)withcolourp
Notes:
Thismethodisbynomeansahighlyaccuratesimulationofripplingwater(thoughitisnottoofaroff).Ripplescanpassthrougheachotherquitehappily,andwillreflectoffthewalls.Onethingyoumaynotice,however,isthattheripplesarenotquitecircular.
这部份我就不翻译了。
致此我们对水波的的运动轨迹都有了一个大楖的轮廓,
为证实我们的想法,我们使用如下描述,这是摘自opengl高级编程的一段话
Themeshgeometrycanbetexturedusingsimpleproceduraltextureimages,pro-
ducingagoodsimulationofwatersurfaces.Syntheticperturbationstothetexture
coordinates,asoutlinedinSection18.3.7,canalsobeusedtohelpanimatethewater
surface.
Ifthereisanadequateperformancemargin,therenderingtechniquecanbefurther
embellished.Multipassormultitexturerenderingtechniquescanbeusedtolayeraddi-
tionaleffectssuchassurf.Thereectivepropertiesofthewatersurfacecanalsobemore
accuratelysimulated.Environmentmappingcanbeusedtosimulatebasicreactionsfrom
thesurface,suchassunsmear.ThisspecularreactioncanbemademorephysicallyaccurateusingenvironmentmappingthatincorporatestheFresnelreectionmodeldescribed
inSection15.9.Thebump-mappingtechniquedescribedinSection15.10canbeusedto
createtheillusionofrippleswithouthavingtomodeltheminthemeshgeometry.The
bumpmapscanbeupdateddynamicallytoanimatethem.
Animatingwatersurfaceeffectsalsoextendstounderwaterscenes.Opticaleffects
suchascausticscanbeapproximatedusingOpenGL,asdescribedbyNishitaand
Nakamae(Nishita,1994),butinteractiveframeratesarenotlikelytobeachieved.Amore
efcient,iflessaccurate,techniquetomodelsucheffectsusesacaustictexturetomodu-
latetheintensityofanygeometrythatliesbelowthesurface.Otherbelow-surfaceeffects
canalsobesimulated.Movementsofthewater(surge)canbesimulatedbyperturbing
thevertexcoordinatesofsubmergedobjects,againusingsinusoids.Blueish-greenfogcan
beusedtosimulatelightattenuationinwater.
翻译上面的:
网格几何图像可以使用简单程序去纹理,能够产生了良好的水面模拟,扰动纹理的合成坐标能够画出如18.3.7描述的那样,如果有足够的性能保证,渲染技术能进一步优化,多重纹理技术可以用层来叠加,例如波浪的额外效果,这样活动水的表面属性会更加准确地模拟,环境映射可以被用来模拟基本的光源反应,例如太阳照射的表面.这镜面反应可以更准确地体现物理环境的映射,采用Fresnel反应模型描述在15.9节中,在15.10章节指出凹凸映射技术,可以创建一个涟漪模型,不必在网格中使用.凹凸的影射可动态更新显示它们。
动画的水面效果也延伸到水下的场面,光学效应如焦散可以近似使用OpenGL如Nishita与Nakamae(Nishita,1994)的描述,但互动帧速率并不是很完美。
如果不准确,可使用技术示范中影响纹理中调试任何几何表面强度(这句不好翻译还是看原文吧)。
其他水底物体效果也可以模拟。
水(风动)再次使用正弦模拟顶点水下运动的对象坐标扰动,蓝色到绿色的雾可以用于模拟光在水中的衰减。
看了上面的知识我们就有了一个大檓的基础,那么我们就算不用opengl的例子我们也同样能生成水波的程序。
那么先让我们来回顾一下在高中的物理课上我们所学的关于水波的知识。
水波有如下几个特性:
扩散:
当你投一块石头到水中,你会看到一个以石头入水点为圆心所形成的一圈圈的水波,这里,你可能会被这个现象所误导,以为水波上的每一点都是以石头入水点为中心向外扩散的,这是错误的。
实际上,水波上的任何一点在任何时候都是以自己为圆心向四周扩散的,之所以会形成一个环状的水波,是因为水波的内部因为扩散的对称而相互抵消了。
衰减:
因为水是有阻尼的,否则,当你在水池中投入石头,水波就会永不停止的震荡下去。
水的折射:
因为水波上不同地点的倾斜角度不同,所以,因为水的折射,我们从观察点垂直往下看到的水底并不是在观察点的正下方,而有一定的偏移。
如果不考虑水面上部的光线反射,这就是我们能感觉到水波形状的原因。
反射:
水波遇到障碍物会反射。
衍射:
这个可以通过扭曲的纹理来模拟实现,那么就能看到水波的衍射现象了。
好了,有了这几个特性,再运用数学和几何知识,我们就可以模拟出真实的水波了。
但是,如果你曾用3DMax做过水波的动画,你就会知道要渲染出一幅真实形状的水波画面少说也得好几十秒,而我们现在需要的是实时的渲染,每秒种至少也得渲染20帧才能使得水波得以平滑的显示。
考虑到手机运算的速度,我们不可能按照正弦函数或精确的公式来构造水波,不能用乘除法,更不能用sin、cos,只能用一种取近似值的快速算法,尽管这种算法存在一定误差,但是为了满足实时动画的要求,我们不得不这样做。
首先我们要建立两个与水池图象一样大小的数组buffer1[PoolWidth*PoolHeight]和buffer2[PoolWidth*PoolHeight](PoolWidth=水池图象的象素宽度、PoolHeight=水池
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1