笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。
CSDN视频网址:http://edu.csdn.net/lecturer/144
海水仿真渲染一直是比较困难的事情,虽然市面上有了各种海水的渲染算法,但是真正做到仿真渲染的少之又少。大多停留在试验阶段,达到了仿真的要求,但是硬件配置要求高,不利于推广。本篇博客给读者介绍的是关于海水的实施渲染以及通过算法实现船只航行轨迹效果,真实的达到了海水的渲染,海水的网格采用了多个面片网格拼接的方式,网格采用的是LOD处理的,这样就优化了效率。同时将海水的绘制以及算法实现放到C++中进行,这样对于复杂算法的实现效率明显提升。先给读者看几幅效果图:
船在海水中航行的轨迹效果图,轨迹是实时绘制的。再来一副海水到岸边产生的泡沫效果图:
除了岸边的效果图外,船在水中周围也会产生泡沫效果。最后一副效果图如下:
海水的反射折射效果。下面开始给读者介绍实现该海水的原理以及核心代码,最后把整个工程奉献给读者。
第一步:海水网格的实现,海水网格采用的是面片拼接的方式,并且面片采用了LOD运算,其在Unity中的效果如下所示:
从里向外,面片的数量逐步减少,它是根据摄像机的远近处理的,对应的核心代码如下所示:
//update the meshes with the final calculated mesh data
void updateTiles(int a, int b) {
if(skipLods) {
lodSkip++;
if(lodSkip >= lodSkipFrames+1) lodSkip=0;
}
for (int L0D=a; L0D<b; L0D++) {
//if(L0D>
//this will skip one update of the tiles higher then Lod0
if(L0D>0 && lodSkip==0 && !ticked && skipLods) { break; }
//this will skip one update of the LOD0 tiles because they got updated earlier when they should.
if(ticked2 && L0D==0) { ticked2=false; continue; }
#if !NATIVE
int den = MyIntPow (2, L0D);
int idx = 0;
for (int y=0; y<g_height; y+=den) {
for (int x=0; x<g_width; x+=den) {
int idx2 = g_width * y + x;
verticesLOD[L0D] [idx] = vertices [idx2];
//lower the far lods to eliminate gaps in the horizon when having big waves
if(L0D>0) {
if(farLodOffset!=0) {
verticesLOD[L0D] [idx].y += flodoffset[L0D] * flodFact;
}
}
tangentsLOD[L0D] [idx] = tangents [idx2];
normalsLOD[L0D] [idx++] = normals [idx2];
}
}
#else
uocean._updateTilesA(verticesLOD[L0D], vertices, tangentsLOD[L0D], tangents, normalsLOD[L0D], normals, L0D, farLodOffset, flodoffset, flodFact);
#endif
btiles_LOD[L0D].vertices = verticesLOD[L0D];
btiles_LOD[L0D].normals = normalsLOD[L0D];
btiles_LOD[L0D].tangents = tangentsLOD[L0D];
}
if(ticked) ticked = false;
}
void GenerateTiles() {
int chDist, nmaxLod=0; // Chebychev distance
for (int y=0; y<tiles; y++) {
for (int x=0; x<tiles; x++) {
chDist = System.Math.Max (System.Math.Abs (tiles / 2 - y), System.Math.Abs (tiles / 2 - x));
chDist = chDist > 0 ? chDist - 1 : 0;
if(nmaxLod<chDist) nmaxLod = chDist;
}
}
max_LOD = nmaxLod+1;
flodoffset = new float[max_LOD+1];
float ffact = farLodOffset/max_LOD;
for(int i=0; i<max_LOD+1; i++) {
flodoffset[i] = i*ffact;
}
btiles_LOD = new List<Mesh>();
tiles_LOD = new List<List<Mesh>>();
for (int L0D=0; L0D<max_LOD; L0D++) {
btiles_LOD.Add(new Mesh());
tiles_LOD.Add (new List<Mesh>());
}
GameObject tile;
int ntl = LayerMask.NameToLayer ("Water");