这些都是超出White Paper的经验总结,如有错误,请多多指正谢谢!
1、使用哪个苦力
你可以使用GPU或者是CPU,计算随时间变化的高程。听起来用GPU的Vertex Shader计算高程好像非常先进,其实不然。因为GPU与CPU的运作机制不同,每个FPS,传入时间Uniform变量会导致GPU效率低下,在NVIDIA的GPU优化指南中提到过这一点。所以我还是推荐用CPU计算高程,因为牵涉到大量的三角函数的计算,不可能舍得宝贵的GPU资源浪费在这些CPU可以运作如飞的计算上。Vertex Shader应该做矩阵变换,光照向量等等。如果可能用汇编代码优化一下,把传入Vertex Shader的Normals给Normalize了。
2、I want get the Mesh
贴关键的完整代码。
#ifndef RK_HPP
#include "rk.hpp"
#endif
extern GLint AttribTangentSlot;
extern GLint LightPositionLoc;
CStreamModel::CStreamModel(short n = 64,float gl = 1.0f) : N(n),GridLength(gl)
{
//准备物理参数
EnvCubeMap = -1;//立方体环境贴图
NormalMap = -1;//发现贴图,也就是凹凸图
RefractMap = -1;
omiga = new float[4];
A = new float[4];
L = new float[4];
Q = new float[4];
A[0] = 0.05f;
A[1] = 0.05f;
A[2] = 0.02f;
A[3] = 0.03f;
L[0] = 1.0f;
L[1] = 1.2f;
L[2] = 2.0f;
L[3] = 1.2f;
for(int i=0;i<4;i++){
omiga[i] = sqrt(9.8f*2.0f*3.14f/L[i]);//L = 0.01
Q[i] = 1/(omiga[i]*A[i]*4.0f);
}
//Q = 0.01235;
Mesh = new float[N*N*3];
VertexIndex = new unsigned short [N*N*3];
TexCoord = new float[N*N*2];
Binormal = new float[N*N*3];
Tangent = new float[N*N*3];
Normal = new float[N*N*3];
//生成顶点索引
//优化乘法 *N可以表示为 <<6
int cnt = 0;
for(int i=0; i<N-2; i++)
{
for(int j=0; j<N; j++)
{
VertexIndex[cnt] = i*N + j; cnt++;
VertexIndex[cnt] = i*N + j + N; cnt++;
}
i++;
j = N-1;
VertexIndex[cnt] = i*N + j + N; cnt++;
VertexIndex[cnt] = i*N + j; cnt++;
for(j=N-2;j>=0;j--)
{
VertexIndex[cnt] = i*N + j + N; cnt++;
VertexIndex[cnt] = i*N + j; cnt++;
}
}
cout<<"size of VertexIndex : "<<cnt<<endl;
int p = 0;
for(int x = 0; x < N; x++){
for(int z = 0; z < N; z++){
TexCoord[p*2] = float(x) / float(N);
TexCoord[1+p*2] = float(z) / float(N);
p++;
}
}
for(int i=0; i<16; i++)
cout<<"TexCoord : "<<TexCoord[i]<<endl;
PixelProcesser = new CPixel();
PixelProcesser->LoadTextureFromBMP(".\\TEXTURES\\Waterbump.bmp",NormalMap);
if ( glIsTexture(NormalMap)){
cout<<"NormalMap Texture Loaded Ok"<<endl;
}else{
cout<<"NormalMap Faild !"<<endl;
_sleep(2000);
exit(-1);
}
PixelProcesser->LoadTextureFromBMP(".\\TEXTURES\\WaterRefract.bmp",RefractMap);
if ( glIsTexture(RefractMap) ){
cout<<"WaterRefract Texture Loaded Ok"<<endl;
}else{
cout<<"RefractMap Faild !"<<endl;
_sleep(2000);
exit(-1);
}
char *szCubeFace[6] = {".\\TEXTURES\\RIGHT.bmp",".\\TEXTURES\\LEFT.bmp",".\\TEXTURES\\TOP.bmp",".\\TEXTURES\\BOTTOM.bmp",".\\TEXTURES\\BACK.bmp",".\\TEXTURES\\FRONT.bmp"};
PixelProcesser->LoadTextureFrom6CUBEMAP(szCubeFace,EnvCubeMap);
if ( glIsTexture(EnvCubeMap)){
cout<<"CubeMap Texture Loaded Ok"<<endl;
}else{
cout<<"cubeMap Faild !"<<endl;
_sleep(2000);
exit(-1);
}
};
void CStreamModel::ReBuildHeightMap()
{
//Dx Dy还没有选择,就用从内向外的斜方向 (-0.1,0,1)
//生成网格与纹理坐标还有TBN向量
//测试:只有一个方向
//i 行 j列 OpenGL是反的
/**/
//cout<<"N is : "<<N<<", And N^2 is : "<<N*N<<endl;
//float fi = 0.23; //fi*t相位
float D[4][2]={};
D[0][0] = 0.23f;
D[0][1] = -0.08f;
D[1][0] = 0.12f;
D[1][1] = 0.34f;
D[2][0] = -0.23f;
D[2][1] = -0.1f;
D[3][0] = -0.2f;
D[3][1] = 0.01f;
int i = 0;
int p = 0;//位置
for( int i = 0 ; i < N ; i++ ){
for( int j = 0 ; j < N ; j++ ){
float x = i / 8.0 - 4.0f;
float z = j / 8.0 - 4.0f;
//定点需要的三角计算
float sigemaCx = 0.0f,sigemaCz = 0.0f;
float sigemaS = 0.0f;
//向量需要的三角计算
float N1 = 0.0f;
float N2 = 0.0f;
float N3 = 0.0f;
float T1 = 0.0f;
float T2 = 0.0f;
float T3 = 0.0f;
for(int k=0;k<4;k++){
float C = cos(omiga[k]*(D[k][0]*x+D[k][1]*z)+t);
float S = sin(omiga[k]*(D[k][0]*x+D[k][1]*z)+t);
sigemaCx += Q[k]*A[k]*D[k][0]*C;
sigemaCz += Q[k]*A[k]*D[k][1]*C;
sigemaS += A[k]*C;
float OAC = omiga[k]*A[k]*C;
float OAS = omiga[k]*A[k]*S;
N1 += D[k][0]*OAC;
N2 += D[k][1]*OAC;
N3 += Q[k]*OAS;
T1 += Q[k]*D[k][0]*D[k][1]*OAS;
T2 += Q[k]*pow(D[k][1],2)*OAS;
T3 += D[k][1]*OAC;
};
Mesh[p*3] = x + sigemaCx;
Mesh[p*3+2] = z + sigemaCz;
Mesh[p*3+1] = sigemaS;
Normal[ p*3 ] = - N1;
Normal[ p*3 +2] = - N2;
Normal[ p*3 +1] = 1 - N3;
Tangent[ p*3 ] = -T1;
Tangent[ p*3 +2] = 1-T2;
Tangent[ p*3 +1] = T3;
p++;
}
}
//glEnableClientState(GL_INDEX_ARRAY);
//glIndexPointer(GL_SHORT,0,VertexIndex);
t+=0.1f;
};
void CStreamModel::Draw()
{
//glutSolidSphere(1.0,32,32);
glUniform3f(::LightPositionLoc,0,0,10);
ReBuildHeightMap();
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableVertexAttribArray(::AttribTangentSlot);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2,GL_FLOAT,0,TexCoord);
glVertexPointer(3,GL_FLOAT,0,Mesh);
glNormalPointer(GL_FLOAT,0,Normal);
glVertexAttribPointer(::AttribTangentSlot,3,GL_FLOAT,0,0,Tangent);
glActiveTexture(GL_TEXTURE2);
glEnable(GL_TEXTURE_CUBE_MAP);
glBindTexture(GL_TEXTURE_CUBE_MAP,EnvCubeMap);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D,NormalMap);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D,RefractMap);
glPushMatrix();
glTranslatef(0,0,15);
glDrawElements(GL_TRIANGLE_STRIP,7936,GL_UNSIGNED_SHORT,VertexIndex);
glPopMatrix();
// glPopMatrix();
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableVertexAttribArray(::AttribTangentSlot);
}; 代码很臭,大虾不要见笑。在绘制函数中,最最关键的就是
ReBuildHeightMap()这个函数。每个fps它都将被渲染器执行一次。公式参考了GPU GEMS。把参数解释一下。Omiga,角频率;A,水波的振幅;L,波长;Q很特殊,是一个可以控制水波形状的参数,太大了会导致一个环。可惜我没有学过流体力学那个NS方程,实在不清楚怎么回事。把顶点的高程,向量计算后,就可以传入Shader计算了。可是,且慢。
3、Vertex Or Triangles
地形的索引是如何做的?这里水波的索引也就是如何做的。地形也就是个三角形拼成的大网。所以,当兴冲冲的计算了一个1024x1024的巨大Water Vertex Mesh,却发现需要的是三角形。看我上面的代码中的一段,保准你看的舒心,用的省心。(鸡蛋皮鞋乱飞中)。
4、人要脸树要皮
好了,可以贴图了。由于水是没有颜色的,他只是靠反射折射获取颜色。所以说,在Pixel(Fragment) Shader完成的工作是进行纹理贴图的颜色调制。这个公式来自微软研究院的几个家伙的一White Paper。C
Water = F
aboveC
reflect+(1-F
above)C
refract+A
shadowC
specular 准备好你的折射图,也就是水底的贴图,立方体贴图也就是反射图,以及你的Bump贴图。这里阴影贴图一般用BumpMapping了。那个GPU GEMS2中用Vertex Texture Fetch生成水波的,不是打击其他人,需要一个非常有经验的美工才行呵呵。F是Fresnel系数,也就是衰减系数。简便的公式是
Fresnel = 1 - EyeDir*N或者是写作1-cos(b)
视觉向量与向量的点乘。我不知道应该转换到正切空间中,从几何的角度来说应该是一样的。
上面的混色公式用GLSL还是cg还是FX都可以很简单的使用。
关键就这些的差不多了。只需要一个很小的框架程序就可以很简单的作一切了。
我从来不封装DLL,对于任何人来说,那些都是神秘的二进制。我只把我掌握的东西奉献给大家,让大多数人明白背后的道理。
posted on 2006-08-03 09:50
周波 阅读(1128)
评论(0) 编辑 收藏 引用 所属分类:
无庸技术