3D C/C++ tutorials -> OpenGL 2.1 -> Interactive water surface, light reflection and refraction, caustic
Use for personal or educational purposes only. Commercial and other profit uses strictly prohibited. Exploitation of content on a website or in a publication prohibited.
opengl_21_tutorials_win32_framework.h
...
#define MAX_WAVES 32
class CWave
{
public:
float StartTime, Speed, MaxY, FrequencyMPIM2;
vec2 Position;
public:
CWave();
~CWave();
};
...
#define WMS 10.0f // water mesh size
#define WMR 256 // water mesh resolution
#define WNBMTR 512 // water normal bump map texture resolution, must be >= WMR
#define MAXY 0.0625f // waves amplitude
class COpenGLRenderer
{
protected:
int Width, Height;
mat3x3 NormalMatrix;
mat4x4 ModelMatrix, ViewMatrix, ViewMatrixInverse, ProjectionMatrix, ProjectionBiasMatrixInverse;
protected:
CTexture Texture[3];
CShaderProgram SceneProgram, WaterBumpMapProgram, WaterNormalMapProgram, WaterProgram;
GLuint TexCoordsVBO, NormalsVBO, VerticesVBO, WaterVerticesVBO;
GLuint WaterNormalBumpMapTexture, ReflectionTexture, RefractionTexture, DepthTexture, FBO;
float WaterLevel;
CWave Waves[MAX_WAVES];
int Wave, WaterVerticesCount;
public:
bool Pause, WireFrame, Water, Caustic;
vec3 LightColor, LightPosition;
public:
CString Text;
public:
COpenGLRenderer();
~COpenGLRenderer();
bool Init();
void Render(float FrameTime);
void Resize(int Width, int Height);
void Destroy();
void RenderScene(int ClipType);
void AddWaveByMouseClick(int x, int y);
void SetWaveInShaders(int Wave);
void IncreaseWaterLevel(float Increment);
void InitWaterVertexBuffer();
void InitSceneVertexBuffers();
};
...
opengl_21_tutorials_win32_framework.cpp
...
CWave::CWave()
{
Speed = 1.0f;
FrequencyMPIM2 = 4.0f * (float)M_PI * 2.0f;
}
CWave::~CWave()
{
}
...
COpenGLRenderer::COpenGLRenderer()
{
Pause = false;
WireFrame = false;
Water = true;
Caustic = true;
WaterLevel = 0.5f;
Wave = 0;
Camera.SetViewMatrixPointer(&ViewMatrix, &ViewMatrixInverse);
}
COpenGLRenderer::~COpenGLRenderer()
{
}
bool COpenGLRenderer::Init()
{
bool Error = false;
if(!GLEW_ARB_texture_non_power_of_two)
{
ErrorLog.Append("GL_ARB_texture_non_power_of_two not supported!\r\n");
Error = true;
}
if(!GLEW_ARB_texture_float)
{
ErrorLog.Append("GL_ARB_texture_float not supported!\r\n");
Error = true;
}
if(!GLEW_EXT_framebuffer_object)
{
ErrorLog.Append("GL_EXT_framebuffer_object not supported!\r\n");
Error = true;
}
char *TextureFileName[] = {"kocka.jpg", "podlaha.jpg", "stena.jpg"};
for(int i = 0; i < 3; i++)
{
Error |= !Texture[i].LoadTexture2D(TextureFileName[i]);
}
Error |= !SceneProgram.Load("scene.vs", "scene.fs");
Error |= !WaterBumpMapProgram.Load("waterbumpmap.vs", "waterbumpmap.fs");
Error |= !WaterNormalMapProgram.Load("waternormalmap.vs", "waternormalmap.fs");
Error |= !WaterProgram.Load("water.vs", "water.fs");
if(Error)
{
return false;
}
glUseProgram(SceneProgram);
glUniform1i(glGetUniformLocation(SceneProgram, "Texture"), 0);
glUniform1i(glGetUniformLocation(SceneProgram, "WaterNormalBumpMapTexture"), 1);
glUniform1f(glGetUniformLocation(SceneProgram, "ODWMS"), 1.0f / WMS);
glUseProgram(0);
glUseProgram(WaterBumpMapProgram);
glUniform1f(glGetUniformLocation(WaterBumpMapProgram, "WMS"), WMS);
glUniform1f(glGetUniformLocation(WaterBumpMapProgram, "WMSD2"), WMS / 2.0f);
glUseProgram(0);
glUseProgram(WaterNormalMapProgram);
glUniform1f(glGetUniformLocation(WaterNormalMapProgram, "ODWNBMTR"), 1.0f / WNBMTR);
glUniform1f(glGetUniformLocation(WaterNormalMapProgram, "WMSDWNBMTRM2"), WMS / WNBMTR * 2.0f);
glUseProgram(0);
glUseProgram(WaterProgram);
glUniform1i(glGetUniformLocation(WaterProgram, "WaterNormalBumpMapTexture"), 0);
glUniform1i(glGetUniformLocation(WaterProgram, "ReflectionTexture"), 1);
glUniform1i(glGetUniformLocation(WaterProgram, "RefractionTexture"), 2);
glUniform1f(glGetUniformLocation(WaterProgram, "ODWMS"), 1.0f / WMS);
glUseProgram(0);
SceneProgram.UniformLocations = new GLuint[6];
SceneProgram.UniformLocations[0] = glGetUniformLocation(SceneProgram, "ClipType");
SceneProgram.UniformLocations[1] = glGetUniformLocation(SceneProgram, "WaterLevel");
SceneProgram.UniformLocations[2] = glGetUniformLocation(SceneProgram, "NormalMatrix");
SceneProgram.UniformLocations[3] = glGetUniformLocation(SceneProgram, "ModelMatrix");
SceneProgram.UniformLocations[4] = glGetUniformLocation(SceneProgram, "Texturing");
SceneProgram.UniformLocations[5] = glGetUniformLocation(SceneProgram, "Caustic");
WaterBumpMapProgram.UniformLocations = new GLuint[1 + MAX_WAVES * 5];
WaterBumpMapProgram.UniformLocations[0] = glGetUniformLocation(WaterBumpMapProgram, "Time");
CString Variable;
for(int Wave = 0; Wave < MAX_WAVES; Wave++)
{
Variable.Set("Waves[%d].Position", Wave);
WaterBumpMapProgram.UniformLocations[1 + Wave * 5 + 0] = glGetUniformLocation(WaterBumpMapProgram, Variable);
Variable.Set("Waves[%d].StartTime", Wave);
WaterBumpMapProgram.UniformLocations[1 + Wave * 5 + 1] = glGetUniformLocation(WaterBumpMapProgram, Variable);
Variable.Set("Waves[%d].Speed", Wave);
WaterBumpMapProgram.UniformLocations[1 + Wave * 5 + 2] = glGetUniformLocation(WaterBumpMapProgram, Variable);
Variable.Set("Waves[%d].MaxY", Wave);
WaterBumpMapProgram.UniformLocations[1 + Wave * 5 + 3] = glGetUniformLocation(WaterBumpMapProgram, Variable);
Variable.Set("Waves[%d].FrequencyMPIM2", Wave);
WaterBumpMapProgram.UniformLocations[1 + Wave * 5 + 4] = glGetUniformLocation(WaterBumpMapProgram, Variable);
}
WaterProgram.UniformLocations = new GLuint[3];
WaterProgram.UniformLocations[0] = glGetUniformLocation(WaterProgram, "WaterLevel");
WaterProgram.UniformLocations[1] = glGetUniformLocation(WaterProgram, "CameraPosition");
WaterProgram.UniformLocations[2] = glGetUniformLocation(WaterProgram, "NormalMatrix");
glGenBuffers(1, &TexCoordsVBO);
glGenBuffers(1, &NormalsVBO);
glGenBuffers(1, &VerticesVBO);
glGenBuffers(1, &WaterVerticesVBO);
InitSceneVertexBuffers();
InitWaterVertexBuffer();
glGenTextures(1, &WaterNormalBumpMapTexture);
glBindTexture(GL_TEXTURE_2D, WaterNormalBumpMapTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, WNBMTR, WNBMTR, 0, GL_RGBA, GL_FLOAT, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glGenTextures(1, &ReflectionTexture);
glGenTextures(1, &RefractionTexture);
glGenTextures(1, &DepthTexture);
glGenFramebuffersEXT(1, &FBO);
LightColor = vec3(1.0f, 1.0f, 1.0f);
LightPosition = vec3(0.0f, 2.75f, -4.75f);
glLightfv(GL_LIGHT0, GL_AMBIENT, &vec4(LightColor * 0.25f, 1.0f));
glLightfv(GL_LIGHT0, GL_DIFFUSE, &vec4(LightColor * 0.75f, 1.0f));
glLightfv(GL_LIGHT0, GL_SPECULAR, &vec4(LightColor, 1.0f));
glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 1.0f / 128.0f);
glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 1.0f / 256.0f);
Camera.Look(vec3(0.0f, 1.75f, 5.0f), vec3(0.0f, 1.5f, 0.0f), true);
// Camera.Look(vec3(0.0f, 1.75f, 4.0f), vec3(0.0f, 0.5f, 0.0f));
srand(GetTickCount());
return true;
}
void COpenGLRenderer::Render(float FrameTime)
{
// add wave ---------------------------------------------------------------------------------------------------------------
static DWORD LastTime = GetTickCount();
if(!Pause)
{
DWORD Time = GetTickCount();
if(Time - LastTime > 125)
{
Waves[Wave].Position.x = -WMS / 2.0f + WMS * (float)rand() / (float)RAND_MAX;
Waves[Wave].Position.y = -WMS / 2.0f + WMS * (float)rand() / (float)RAND_MAX;
Waves[Wave].StartTime = (float)Time * 0.001f;
Waves[Wave].MaxY = MAXY * (float)rand() / (float)RAND_MAX;
LastTime = Time;
SetWaveInShaders(Wave++);
Wave %= MAX_WAVES;
}
}
// render water normal / bump map texture ---------------------------------------------------------------------------------
glViewport(0, 0, WNBMTR, WNBMTR);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, FBO);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, WaterNormalBumpMapTexture, 0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0);
glUseProgram(WaterBumpMapProgram);
glUniform1f(WaterBumpMapProgram.UniformLocations[0], (float)GetTickCount() * 0.001f);
glBegin(GL_QUADS);
glVertex2f(0.0f, 0.0f);
glVertex2f(1.0f, 0.0f);
glVertex2f(1.0f, 1.0f);
glVertex2f(0.0f, 1.0f);
glEnd();
glUseProgram(0);
glBindTexture(GL_TEXTURE_2D, WaterNormalBumpMapTexture);
glUseProgram(WaterNormalMapProgram);
glBegin(GL_QUADS);
glVertex2f(0.0f, 0.0f);
glVertex2f(1.0f, 0.0f);
glVertex2f(1.0f, 1.0f);
glVertex2f(0.0f, 1.0f);
glEnd();
glUseProgram(0);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
// set viewport, reset modelview matrix and set light position ------------------------------------------------------------
glViewport(0, 0, Width, Height);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glLightfv(GL_LIGHT0, GL_POSITION, &vec4(LightPosition, 1.0f));
// render reflection texture ----------------------------------------------------------------------------------------------
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, FBO);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, ReflectionTexture, 0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, DepthTexture, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadMatrixf(&ViewMatrix);
RenderScene(1);
glTranslatef(0.0f, WaterLevel * 2.0f, 0.0f);
glScalef(1.0f, -1.0f, 1.0f);
glCullFace(GL_FRONT);
RenderScene(1);
glCullFace(GL_BACK);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
// render scene / refraction texture --------------------------------------------------------------------------------------
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadMatrixf(&ViewMatrix);
RenderScene(0);
glBindTexture(GL_TEXTURE_2D, RefractionTexture);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, Width, Height);
glBindTexture(GL_TEXTURE_2D, 0);
// render water surface ------------------------------------------------------------------------------------------------------
if(Water)
{
glEnable(GL_DEPTH_TEST);
if(WireFrame)
{
glClear(GL_COLOR_BUFFER_BIT);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, WaterNormalBumpMapTexture);
glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, ReflectionTexture);
glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, RefractionTexture);
glBindBuffer(GL_ARRAY_BUFFER, WaterVerticesVBO);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 8, (void*)0);
glUseProgram(WaterProgram);
glUniform1f(WaterProgram.UniformLocations[0], WaterLevel);
glUniform3fv(WaterProgram.UniformLocations[1], 1, &Camera.Position);
vec3 X = Camera.X, Y = vec3(0.0f, 1.0f, 0.0f), Z = cross(X, Y);
NormalMatrix = mat3x3(X.x, Y.x, Z.x, X.y, Y.y, Z.y, X.z, Y.z, Z.z);
glUniformMatrix3fv(WaterProgram.UniformLocations[2], 1, GL_FALSE, &NormalMatrix);
glDrawArrays(GL_QUADS, 0, WaterVerticesCount);
glUseProgram(0);
glDisableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0);
if(WireFrame) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glDisable(GL_DEPTH_TEST);
}
// rotate object ----------------------------------------------------------------------------------------------------------
if(!Pause)
{
static float a = 0.0f;
ModelMatrix = translate(0.0f, 1.5f, 0.0f) * rotate(a, vec3(0.0f, 1.0f, 0.0f)) * rotate(a, vec3(1.0f, 0.0f, 0.0f));
a += 22.5f * FrameTime;
}
}
void COpenGLRenderer::Resize(int Width, int Height)
{
this->Width = Width;
this->Height = Height;
ProjectionMatrix = perspective(45.0f, (float)Width / (float)Height, 0.125f, 512.0f);
ProjectionBiasMatrixInverse = inverse(ProjectionMatrix) * BiasMatrixInverse;
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(&ProjectionMatrix);
glBindTexture(GL_TEXTURE_2D, ReflectionTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glBindTexture(GL_TEXTURE_2D, RefractionTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glBindTexture(GL_TEXTURE_2D, DepthTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, Width, Height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
}
void COpenGLRenderer::Destroy()
{
for(int i = 0; i < 3; i++)
{
Texture[i].Destroy();
}
SceneProgram.Destroy();
WaterBumpMapProgram.Destroy();
WaterNormalMapProgram.Destroy();
WaterProgram.Destroy();
glDeleteBuffers(1, &TexCoordsVBO);
glDeleteBuffers(1, &NormalsVBO);
glDeleteBuffers(1, &VerticesVBO);
glDeleteBuffers(1, &WaterVerticesVBO);
glDeleteTextures(1, &WaterNormalBumpMapTexture);
glDeleteTextures(1, &ReflectionTexture);
glDeleteTextures(1, &RefractionTexture);
glDeleteTextures(1, &DepthTexture);
if(GLEW_EXT_framebuffer_object)
{
glDeleteFramebuffersEXT(1, &FBO);
}
}
void COpenGLRenderer::RenderScene(int ClipType)
{
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glBindBuffer(GL_ARRAY_BUFFER, TexCoordsVBO);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 8, (void*)0);
glBindBuffer(GL_ARRAY_BUFFER, NormalsVBO);
glEnableClientState(GL_NORMAL_ARRAY);
glNormalPointer(GL_FLOAT, 12, (void*)0);
glBindBuffer(GL_ARRAY_BUFFER, VerticesVBO);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 12, (void*)0);
glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, WaterNormalBumpMapTexture);
glActiveTexture(GL_TEXTURE0);
glUseProgram(SceneProgram);
glUniform1i(SceneProgram.UniformLocations[0], ClipType);
glUniform1f(SceneProgram.UniformLocations[1], WaterLevel);
glUniform1i(SceneProgram.UniformLocations[5], Caustic ? 1 : 0);
glUniformMatrix3fv(SceneProgram.UniformLocations[2], 1, GL_FALSE, &mat3x3());
glUniformMatrix4fv(SceneProgram.UniformLocations[3], 1, GL_FALSE, &mat4x4());
glUniform1i(SceneProgram.UniformLocations[4], 1);
glColor3f(1.0f, 1.0f, 1.0f);
glBindTexture(GL_TEXTURE_2D, Texture[0]);
glDrawArrays(GL_QUADS, 0, 96);
glBindTexture(GL_TEXTURE_2D, Texture[1]);
glDrawArrays(GL_QUADS, 96, 4);
glBindTexture(GL_TEXTURE_2D, Texture[2]);
glDrawArrays(GL_QUADS, 100, 80);
glUniform1i(SceneProgram.UniformLocations[4], 0);
glDrawArrays(GL_QUADS, 180, 4);
glUniformMatrix3fv(SceneProgram.UniformLocations[2], 1, GL_FALSE, &mat3x3(ModelMatrix));
glUniformMatrix4fv(SceneProgram.UniformLocations[3], 1, GL_FALSE, &ModelMatrix);
glColor3f(0.33f, 0.66f, 1.0f);
glDrawArrays(GL_QUADS, 184, 72);
glUseProgram(0);
glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
}
void COpenGLRenderer::AddWaveByMouseClick(int x, int y)
{
float s = (float)x / (float)(Width - 1);
float t = 1.0f - (float)y / (float)(Height - 1);
vec4 Position = ViewMatrixInverse * (ProjectionBiasMatrixInverse * vec4(s, t, 0.5f, 1.0f));
Position /= Position.w;
vec3 Ray = normalize(*(vec3*)&Position - Camera.Position);
vec3 Normal = vec3(0.0f, 1.0f, 0.0f);
float D = -dot(Normal, vec3(0.0f, WaterLevel, 0.0f));
float NdotR = -dot(Normal, Ray);
if(NdotR != 0.0f)
{
float Distance = (dot(Normal, Camera.Position) + D) / NdotR;
if(Distance > 0.0)
{
vec3 Position = Ray * Distance + Camera.Position;
float WMSD2 = WMS / 2.0f, MWMSD2 = -WMSD2;
if(Position.x >= MWMSD2 && Position.x <= WMSD2 && Position.z >= MWMSD2 && Position.z <= WMSD2)
{
Waves[Wave].Position.x = Position.x;
Waves[Wave].Position.y = Position.z;
Waves[Wave].StartTime = (float)GetTickCount() * 0.001f;
Waves[Wave].MaxY = MAXY;
SetWaveInShaders(Wave++);
Wave %= MAX_WAVES;
}
}
}
}
void COpenGLRenderer::SetWaveInShaders(int Wave)
{
glUseProgram(WaterBumpMapProgram);
glUniform2fv(WaterBumpMapProgram.UniformLocations[1 + Wave * 5 + 0], 1, &Waves[Wave].Position);
glUniform1f(WaterBumpMapProgram.UniformLocations[1 + Wave * 5 + 1], Waves[Wave].StartTime);
glUniform1f(WaterBumpMapProgram.UniformLocations[1 + Wave * 5 + 2], Waves[Wave].Speed);
glUniform1f(WaterBumpMapProgram.UniformLocations[1 + Wave * 5 + 3], Waves[Wave].MaxY);
glUniform1f(WaterBumpMapProgram.UniformLocations[1 + Wave * 5 + 4], Waves[Wave].FrequencyMPIM2);
glUseProgram(0);
}
void COpenGLRenderer::IncreaseWaterLevel(float Increment)
{
WaterLevel += Increment;
if(WaterLevel < 0.25f) WaterLevel = 0.25f;
if(WaterLevel > 2.75f) WaterLevel = 2.75f;
}
void COpenGLRenderer::InitWaterVertexBuffer()
{
WaterVerticesCount = WMR * WMR * 4;
vec2 *WaterVertices = new vec2[WaterVerticesCount];
float WMSD2 = WMS / 2.0f, MWMSD2 = -WMSD2, WMSDWMR = WMS / (float)WMR;
int i = 0;
for(int y = 0; y < WMR; y++)
{
for(int x = 0; x < WMR; x++)
{
WaterVertices[i].x = MWMSD2 + x * WMSDWMR;
WaterVertices[i++].y = WMSD2 - y * WMSDWMR;
WaterVertices[i].x = MWMSD2 + (x + 1) * WMSDWMR;
WaterVertices[i++].y = WMSD2 - y * WMSDWMR;
WaterVertices[i].x = MWMSD2 + (x + 1) * WMSDWMR;
WaterVertices[i++].y = WMSD2 - (y + 1) * WMSDWMR;
WaterVertices[i].x = MWMSD2 + x * WMSDWMR;
WaterVertices[i++].y = WMSD2 - (y + 1) * WMSDWMR;
}
}
glBindBuffer(GL_ARRAY_BUFFER, WaterVerticesVBO);
glBufferData(GL_ARRAY_BUFFER, WaterVerticesCount * 2 * 4, WaterVertices, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
delete [] WaterVertices;
}
void COpenGLRenderer::InitSceneVertexBuffers()
{
vec2 *TexCoords = new vec2[256];
vec3 *Normals = new vec3[256];
vec3 *Vertices = new vec3[256];
...
glBindBuffer(GL_ARRAY_BUFFER, TexCoordsVBO);
glBufferData(GL_ARRAY_BUFFER, 256 * 2 * 4, TexCoords, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, NormalsVBO);
glBufferData(GL_ARRAY_BUFFER, 256 * 3 * 4, Normals, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, VerticesVBO);
glBufferData(GL_ARRAY_BUFFER, 256 * 3 * 4, Vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
delete [] TexCoords;
delete [] Normals;
delete [] Vertices;
}
...
void CWnd::OnKeyDown(UINT Key)
{
switch(Key)
{
case VK_F1:
OpenGLRenderer.WireFrame = !OpenGLRenderer.WireFrame;
break;
case VK_F2:
OpenGLRenderer.Water = !OpenGLRenderer.Water;
break;
case VK_SPACE:
OpenGLRenderer.Pause = !OpenGLRenderer.Pause;
break;
case VK_ADD:
OpenGLRenderer.IncreaseWaterLevel(0.05f);
break;
case VK_SUBTRACT:
OpenGLRenderer.IncreaseWaterLevel(-0.05f);
break;
}
}
void CWnd::OnLButtonDown(int cx, int cy)
{
OpenGLRenderer.AddWaveByMouseClick(cx, cy);
}
...
waterbumpmap.vs
#version 120
uniform float WMS, WMSD2;
varying vec2 Position;
void main()
{
Position = vec2(gl_Vertex.x * WMS - WMSD2, WMSD2 - gl_Vertex.y * WMS);
gl_Position = gl_Vertex * 2.0 - 1.0;
}
waterbumpmap.fs
#version 120
#define MAX_WAVES 32
struct CWave
{
float StartTime, Speed, MaxY, FrequencyMPIM2;
vec2 Position;
};
uniform float Time;
uniform CWave Waves[MAX_WAVES];
varying vec2 Position;
void main()
{
float y = 0.0;
for(int i = 0; i < MAX_WAVES; i++)
{
float d = distance(Waves[i].Position, Position);
float t = Time - Waves[i].StartTime - d / Waves[i].Speed;
if(t > 0.0)
{
float maxy = Waves[i].MaxY - Waves[i].MaxY * t;
if(maxy > 0.0)
{
y -= sin(t * Waves[i].FrequencyMPIM2) * maxy / (1.0 + d);
}
}
}
gl_FragColor = vec4(0.0, 1.0, 0.0, y);
}
waternormalmap.vs
#version 120
void main()
{
gl_TexCoord[0] = gl_Vertex;
gl_Position = gl_Vertex * 2.0 - 1.0;
}
waternormalmap.fs
#version 120
uniform sampler2D WaterNormalBumpMapTexture;
uniform float ODWNBMTR, WMSDWNBMTRM2;
void main()
{
float y[5];
y[0] = texture2D(WaterNormalBumpMapTexture, vec2(gl_TexCoord[0].s + ODWNBMTR, gl_TexCoord[0].t)).a;
y[1] = texture2D(WaterNormalBumpMapTexture, vec2(gl_TexCoord[0].s, gl_TexCoord[0].t + ODWNBMTR)).a;
y[2] = texture2D(WaterNormalBumpMapTexture, vec2(gl_TexCoord[0].s - ODWNBMTR, gl_TexCoord[0].t)).a;
y[3] = texture2D(WaterNormalBumpMapTexture, vec2(gl_TexCoord[0].s, gl_TexCoord[0].t - ODWNBMTR)).a;
y[4] = texture2D(WaterNormalBumpMapTexture, gl_TexCoord[0].st).a;
vec3 Normal = normalize(vec3(y[2] - y[0], WMSDWNBMTRM2, y[1] - y[3]));
gl_FragColor = vec4(Normal, y[4]);
}
scene.vs
#version 120
uniform mat3x3 NormalMatrix;
uniform mat4x4 ModelMatrix;
varying vec3 Position, Normal;
void main()
{
Position = (ModelMatrix * gl_Vertex).xyz;
Normal = NormalMatrix * gl_Normal;
gl_FrontColor = gl_Color;
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = gl_ModelViewProjectionMatrix * vec4(Position, 1.0);
}
scene.fs
#version 120
uniform int ClipType, Texturing, Caustic;
uniform float WaterLevel, ODWMS;
uniform sampler2D Texture, WaterNormalBumpMapTexture;
varying vec3 Position, Normal;
void main()
{
if(ClipType == 1) if(Position.y < WaterLevel) discard;
if(ClipType == 2) if(Position.y > WaterLevel) discard;
vec3 LightDirection = gl_LightSource[0].position.xyz - Position;
float LightDistance2 = dot(LightDirection, LightDirection);
float LightDistance = sqrt(LightDistance2);
LightDirection /= LightDistance;
float NdotLD = max(0.0, dot(Normal, LightDirection));
float Attenuation = gl_LightSource[0].constantAttenuation;
Attenuation += gl_LightSource[0].linearAttenuation * LightDistance;
Attenuation += gl_LightSource[0].quadraticAttenuation * LightDistance2;
vec3 Light = (gl_LightSource[0].ambient.rgb + gl_LightSource[0].diffuse.rgb * NdotLD) / Attenuation;
if(Caustic == 1 && NdotLD > 0.0 && gl_LightSource[0].position.y != WaterLevel)
{
vec2 TexCoord = vec2(Position.x * ODWMS + 0.5, 0.5 - Position.z * ODWMS);
float waterlevel = WaterLevel + texture2D(WaterNormalBumpMapTexture, TexCoord).a;
if((gl_LightSource[0].position.y > WaterLevel && Position.y > waterlevel) || (gl_LightSource[0].position.y < WaterLevel && Position.y < waterlevel))
{
LightDirection.xz = gl_LightSource[0].position.xz - Position.xz;
LightDirection.y = WaterLevel * 2.0 - gl_LightSource[0].position.y - Position.y;
LightDistance2 = dot(LightDirection, LightDirection);
LightDistance = sqrt(LightDistance2);
LightDirection /= LightDistance;
NdotLD = max(0.0, dot(Normal, LightDirection));
Attenuation = gl_LightSource[0].constantAttenuation;
Attenuation += gl_LightSource[0].linearAttenuation * LightDistance;
Attenuation += gl_LightSource[0].quadraticAttenuation * LightDistance2;
}
float Distance = (WaterLevel - Position.y) / LightDirection.y;
TexCoord = LightDirection.xz * Distance + Position.xz;
TexCoord.s = TexCoord.s * ODWMS + 0.5;
TexCoord.t = 0.5 - TexCoord.t * ODWMS;
vec3 WaterNormal = texture2D(WaterNormalBumpMapTexture, TexCoord).rgb;
float WNdotLD = abs(dot(WaterNormal, LightDirection));
float Caustic = NdotLD * pow(WNdotLD, 4.0) / Attenuation;
Light += gl_LightSource[0].specular.rgb * Caustic;
}
gl_FragColor.rgb = gl_Color.rgb;
if(Texturing == 1) gl_FragColor.rgb *= texture2D(Texture, gl_TexCoord[0].st).rgb;
gl_FragColor.rgb *= Light;
}
water.vs
#version 120
uniform sampler2D WaterNormalBumpMapTexture;
uniform float WaterLevel, ODWMS;
varying vec3 Position;
void main()
{
Position.xz = gl_Vertex.xy;
gl_TexCoord[0].st = vec2(gl_Vertex.x, -gl_Vertex.y) * ODWMS + 0.5;
Position.y = WaterLevel + texture2D(WaterNormalBumpMapTexture, gl_TexCoord[0].st).a;
gl_TexCoord[1] = gl_ModelViewProjectionMatrix * vec4(Position, 1.0);
gl_Position = gl_TexCoord[1];
}
water.fs
#version 120
uniform sampler2D WaterNormalBumpMapTexture, ReflectionTexture, RefractionTexture;
uniform float WaterLevel;
uniform vec3 CameraPosition;
uniform mat3x3 NormalMatrix;
varying vec3 Position;
void main()
{
vec2 TexCoord = gl_TexCoord[1].st / gl_TexCoord[1].w * 0.5 + 0.5;
vec3 Normal = NormalMatrix * normalize(texture2D(WaterNormalBumpMapTexture, gl_TexCoord[0].st).rgb);
vec2 Offset = Normal.xz * vec2(0.05, -0.05);
if(CameraPosition.y > WaterLevel)
{
vec3 Reflection = texture2D(ReflectionTexture, TexCoord + Offset).rgb;
vec3 Refraction;
Refraction.r = texture2D(RefractionTexture, TexCoord - Offset * 0.8).r;
Refraction.g = texture2D(RefractionTexture, TexCoord - Offset * 0.9).g;
Refraction.b = texture2D(RefractionTexture, TexCoord - Offset * 1.0).b;
vec3 LightDirection = normalize(Position - gl_LightSource[0].position.xyz);
vec3 LightDirectionReflected = reflect(LightDirection, Normal);
vec3 CameraDirection = normalize(CameraPosition - Position);
float CDdotLDR = max(dot(CameraDirection, LightDirectionReflected), 0.0);
vec3 Specular = gl_LightSource[0].specular.rgb * pow(CDdotLDR, 128.0);
float NdotCD = max(dot(Normal, CameraDirection), 0.0);
gl_FragColor.rgb = mix(Reflection, Refraction, NdotCD) + Specular;
}
else
{
gl_FragColor.r = texture2D(RefractionTexture, TexCoord - Offset * 0.8).r;
gl_FragColor.g = texture2D(RefractionTexture, TexCoord - Offset * 0.9).g;
gl_FragColor.b = texture2D(RefractionTexture, TexCoord - Offset * 1.0).b;
}
}
Download
|