Skip to content

Instantly share code, notes, and snippets.

@roxlu
Created December 23, 2013 22:55
Show Gist options
  • Save roxlu/8106237 to your computer and use it in GitHub Desktop.
Save roxlu/8106237 to your computer and use it in GitHub Desktop.
Water simulation v0.0.0.16
#include "HeightField.h"
HeightField::HeightField()
:vert(0)
,frag(0)
,prog(0)
,vao(0)
,vbo_els(0)
,vbo(0)
,fbo(0)
,tex_u0(0)
,tex_u1(0)
,tex_v0(0)
,tex_v1(0)
,tex_norm(0)
,tex_pos(0)
,tex_texcoord(0)
,tex_gradient(0)
,tex_forces(0)
,state_diffuse(0)
,fbo_forces(0)
,frag_forces(0)
,prog_forces(0)
,vao_draw(0)
,vert_draw(0)
,frag_draw(0)
,prog_draw(0)
,vert_pos(0)
,frag_pos(0)
,prog_pos(0)
,prog_render(0)
,vert_render(0)
,frag_render(0)
,vert_norm(0)
,frag_norm(0)
,prog_norm(0)
{
}
bool HeightField::setup() {
pm.perspective(60.0f, float(W)/H, 0.01, 100.0);
#if 0
vm.lookAt(vec3(0.0, 10.0, 0.0), vec3(0.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));
#else
vm.rotateX(DEG_TO_RAD * 15);
vm.translate(0.0, -4.0, -14.0);
#endif
// initial data
float init_v[NN];
float init_u[NN];
int upper = N * 0.60;
int lower = N * 0.30;
for(int i = 0; i < N; ++i) {
for(int j = 0; j < N; ++j) {
int dx = j * N + i;
init_v[dx] = 0.0f;
init_u[dx] = 0.0f;
if(i > lower && i < upper && j > lower && j < upper) {
init_u[dx] = 0.0f;
init_v[dx] = 0.0f;
}
}
}
// create diffuse shader
vert = rx_create_shader(GL_VERTEX_SHADER, HF_DIFFUSE_VERT);
frag = rx_create_shader(GL_FRAGMENT_SHADER, HF_DIFFUSE_FRAG);
prog = rx_create_program(vert, frag);
glBindAttribLocation(prog, 0, "a_tex"); // tex
glBindFragDataLocation(prog, 0, "u_out"); // new velocity value
glBindFragDataLocation(prog, 1, "v_out"); // new height value
glLinkProgram(prog);
rx_print_shader_link_info(prog);
glUseProgram(prog);
glUniform1i(glGetUniformLocation(prog, "u_tex_u"), 0);
glUniform1i(glGetUniformLocation(prog, "u_tex_v"), 1);
glUniform1i(glGetUniformLocation(prog, "u_tex_forces"), 2);
// create draw shader for debugging
vert_draw = rx_create_shader(GL_VERTEX_SHADER, HF_TEXTURE_VS);
frag_draw = rx_create_shader(GL_FRAGMENT_SHADER, HF_DEBUG_FLOAT_FS);
prog_draw = rx_create_program(vert_draw, frag_draw);
glLinkProgram(prog_draw);
rx_print_shader_link_info(prog_draw);
glUseProgram(prog_draw);
glUniform1i(glGetUniformLocation(prog_draw, "u_tex"), 0);
// create render shader for final result.
vert_render = rx_create_shader(GL_VERTEX_SHADER, HF_RENDER_VS);
frag_render = rx_create_shader(GL_FRAGMENT_SHADER, HF_RENDER_FS);
prog_render = rx_create_program(vert_render, frag_render);
glBindAttribLocation(prog_render, 0, "a_tex");
glLinkProgram(prog_render);
rx_print_shader_link_info(prog_render);
glUseProgram(prog_render);
glUniformMatrix4fv(glGetUniformLocation(prog_render, "u_pm"), 1, GL_FALSE, pm.ptr());
glUniformMatrix4fv(glGetUniformLocation(prog_render, "u_vm"), 1, GL_FALSE, vm.ptr());
glUniform1i(glGetUniformLocation(prog_render, "u_tex_u"), 0);
glUniform1i(glGetUniformLocation(prog_render, "u_tex_norm"), 1);
// create normal shader
vert_norm = rx_create_shader(GL_VERTEX_SHADER, HF_NORMALS_VS);
frag_norm = rx_create_shader(GL_FRAGMENT_SHADER, HF_NORMALS_FS);
prog_norm = rx_create_program(vert_norm, frag_norm);
glBindAttribLocation(prog_norm, 0, "a_tex");
glBindFragDataLocation(prog_norm, 0, "norm_out");
glBindFragDataLocation(prog_norm, 1, "grad_out");
glLinkProgram(prog_norm);
rx_print_shader_link_info(prog_norm);
glUseProgram(prog_norm);
glUniform1i(glGetUniformLocation(prog_norm, "u_tex_u"), 0);
glUniform1i(glGetUniformLocation(prog_norm, "u_tex_pos"), 1);
/* create position/texcoord shader */
vert_pos = rx_create_shader(GL_VERTEX_SHADER, HF_POSITION_VS);
frag_pos = rx_create_shader(GL_FRAGMENT_SHADER, HF_POSITION_FS);
prog_pos = rx_create_program(vert_pos, frag_pos);
glBindFragDataLocation(prog_pos, 0, "pos_out");
glBindFragDataLocation(prog_pos, 1, "tex_out");
glBindAttribLocation(prog_pos, 0, "a_pos");
glLinkProgram(prog_pos);
rx_print_shader_link_info(prog_pos);
glUseProgram(prog_pos);
glUniform1i(glGetUniformLocation(prog_pos, "u_tex_u"), 0);
/* create shader that is used to draw "force textures" */
frag_forces = rx_create_shader(GL_FRAGMENT_SHADER, HF_FORCES_FS);
prog_forces = rx_create_program(vert_draw, frag_forces);
glLinkProgram(prog_forces);
rx_print_shader_link_info(prog_forces);
glUseProgram(prog_forces);
glUniform1i(glGetUniformLocation(prog_forces, "u_tex"), 0);
glGenVertexArrays(1, &vao_draw);
// create textures that will hold the height field
glGenTextures(1, &tex_u0);
glBindTexture(GL_TEXTURE_2D, tex_u0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, N, N, 0, GL_RED, GL_FLOAT, init_u);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenTextures(1, &tex_u1);
glBindTexture(GL_TEXTURE_2D, tex_u1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, N, N, 0, GL_RED, GL_FLOAT, init_u);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
float forces[NN * 2];
memset(forces, 0, sizeof(forces));
glGenTextures(1, &tex_forces);
glBindTexture(GL_TEXTURE_2D, tex_forces);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RG32F, N, N, 0, GL_RG, GL_FLOAT, forces);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenTextures(1, &tex_v0);
glBindTexture(GL_TEXTURE_2D, tex_v0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, N, N, 0, GL_RED, GL_FLOAT, init_v);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenTextures(1, &tex_v1);
glBindTexture(GL_TEXTURE_2D, tex_v1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, N, N, 0, GL_RED, GL_FLOAT, init_v);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenTextures(1, &tex_norm);
glBindTexture(GL_TEXTURE_2D, tex_norm);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, N, N, 0, GL_RGB, GL_FLOAT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenTextures(1, &tex_pos);
glBindTexture(GL_TEXTURE_2D, tex_pos);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, N, N, 0, GL_RGB, GL_FLOAT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenTextures(1, &tex_texcoord);
glBindTexture(GL_TEXTURE_2D, tex_texcoord);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RG32F, N, N, 0, GL_RG, GL_FLOAT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenTextures(1, &tex_gradient);
glBindTexture(GL_TEXTURE_2D, tex_gradient);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, N, N, 0, GL_RGB, GL_FLOAT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// fbo for the diffuse math, normals, positions, etc..
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex_u0, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, tex_u1, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, tex_v0, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, tex_v1, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT4, GL_TEXTURE_2D, tex_norm, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT5, GL_TEXTURE_2D, tex_pos, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT6, GL_TEXTURE_2D, tex_texcoord, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT7, GL_TEXTURE_2D, tex_gradient, 0);
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
printf("Error: framebuffer is not complete.\n");
::exit(EXIT_FAILURE);
}
// custom forces (fbo + tex)
glGenFramebuffers(1, &fbo_forces);
glBindFramebuffer(GL_FRAMEBUFFER, fbo_forces);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex_forces, 0);
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
printf("Framebuffer for custom forces step not complete.\n");
::exit(EXIT_FAILURE);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// create buffer
std::vector<FieldVertex> field_vertices(NN, FieldVertex());
for(int i = 0; i < N; ++i) {
for(int j = 0; j < N; ++j) {
int dx = j * N + i;
field_vertices[dx].set(i, j);
}
}
// triangle indices
for(int i = 1; i < N - 1; ++i) {
for(int j = 1; j < N - 1; ++j) {
int a = (j + 0) * N + (i + 0); // bottom left
int b = (j + 0) * N + (i + 1); // bottom right
int c = (j + 1) * N + (i + 1); // top right
int d = (j + 1) * N + (i + 0); // top left
indices.push_back(a);
indices.push_back(b);
indices.push_back(c);
indices.push_back(a);
indices.push_back(c);
indices.push_back(d);
}
}
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(FieldVertex) * field_vertices.size(), &field_vertices[0].tex[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0); // tex
glVertexAttribIPointer(0, 2, GL_INT, sizeof(FieldVertex), (GLvoid*)0); // tex
glGenBuffers(1, &vbo_els);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_els);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLint) * indices.size(), &indices[0], GL_STATIC_DRAW);
printf("height_field.tex_u0: %d\n", tex_u0);
printf("height_field.tex_u1: %d\n", tex_u1);
printf("height_field.tex_v0: %d\n", tex_v0);
printf("height_field.tex_v1: %d\n", tex_v1);
printf("height_field.tex_norm: %d\n", tex_norm);
return true;
}
void HeightField::drawTexture(GLuint tex, float x, float y, float w, float h) {
mat4 lpm;
mat4 lmm;
float hw = w * 0.5;
float hh = h * 0.5;
lpm.ortho(0, W, H, 0, 0.0f, 100.0f);
lmm.translate(x + hw, y + hh, 0.0f);
lmm.scale(hw, hh, 1.0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
glBindVertexArray(vao_draw);
glUseProgram(prog_draw);
glUniformMatrix4fv(glGetUniformLocation(prog_draw, "u_pm"), 1, GL_FALSE, lpm.ptr());
glUniformMatrix4fv(glGetUniformLocation(prog_draw, "u_mm"), 1, GL_FALSE, lmm.ptr());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
// Renders the calculated heights/normls/etc.. to screens
void HeightField::render() {
glUseProgram(prog_render);
glEnable(GL_DEPTH_TEST);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex_u0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, tex_norm);
glBindVertexArray(vao);
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, NULL);
}
// Diffuses the heights
void HeightField::diffuse() {
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glViewport(0, 0, N, N);
glUseProgram(prog);
glBindVertexArray(vao);
state_diffuse = 1 - state_diffuse;
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, tex_forces);
if(state_diffuse == 0) {
// read from u0, write to u1
// read from v0, write to v1
GLenum draw_bufs[] = { GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT3 } ;
glDrawBuffers(2, draw_bufs);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex_u0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, tex_v0);
}
else {
// read from u1, write to u0
// read from v1, write to v0
GLenum draw_bufs[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT2 } ;
glDrawBuffers(2, draw_bufs);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex_u1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, tex_v1);
}
glDrawArrays(GL_POINTS, 0, NN);
// clear the forces buffer.
GLenum forces_bufs[] = { GL_COLOR_ATTACHMENT0 } ;
glBindFramebuffer(GL_FRAMEBUFFER, fbo_forces);
glDrawBuffers(1, forces_bufs);
glClear(GL_COLOR_BUFFER_BIT);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, W, H);
}
void HeightField::calculatePositions() {
glViewport(0, 0, N, N);
GLenum drawbufs[] = { GL_COLOR_ATTACHMENT5, GL_COLOR_ATTACHMENT6 } ;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glDrawBuffers(2, drawbufs);
glUseProgram(prog_pos);
glBindVertexArray(vao);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex_u0);
glDrawArrays(GL_POINTS, 0, NN);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, W, H);
}
// This uses the current height values to produce a texture that will contain the normals
void HeightField::calculateNormals() {
GLenum drawbufs[] = { GL_COLOR_ATTACHMENT4, GL_COLOR_ATTACHMENT7 } ;
glViewport(0, 0, N, N);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glDrawBuffers(2, drawbufs);
glUseProgram(prog_norm);
glBindVertexArray(vao);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex_u0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, tex_pos);
glDrawArrays(GL_POINTS, 0, NN);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, W, H);
}
void HeightField::beginDrawForces() {
GLenum drawbufs[] = { GL_COLOR_ATTACHMENT0 } ;
glBindFramebuffer(GL_FRAMEBUFFER, fbo_forces);
glDrawBuffers(1, drawbufs);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, N, N);
}
void HeightField::endDrawForces() {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, W, H);
}
void HeightField::drawForceTexture(GLuint tex, float px, float py, float pw, float ph) {
mat4 lpm;
mat4 lmm;
float hw = 0.5 * N * pw;
float hh = 0.5 * N * ph;
#if 0
lpm.ortho(0, W, H, 0, 0.0f, 100.0f);
lmm.translate( (px * N) + hw, (py * N) + hh, 0.0f);
lmm.scale(hw, hh, 1.0);
#else
/*
lpm.ortho(0, N, N, 0, 0.0f, 100.0f);
lmm.translate( px + hw, py + hh, 0.0f);
lmm.scale(N, N, 1.0);
*/
lpm.ortho(0, N, N, 0, 0.0f, 100.0f);
lmm.translate(px * N, py * N, 0.0f);
lmm.scale(pw * N, ph * N, 1.0);
//printf("%f, %f, %d, %f\n", pw * N, ph * N, N, pw);
#endif
// glBindFramebuffer(GL_FRAMEBUFFER, 0);
// glViewport(0, 0, N, N);
glUseProgram(prog_forces);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
glBindVertexArray(vao_draw);
glUniformMatrix4fv(glGetUniformLocation(prog_forces, "u_pm"), 1, GL_FALSE, lpm.ptr());
glUniformMatrix4fv(glGetUniformLocation(prog_forces, "u_mm"), 1, GL_FALSE, lmm.ptr());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
//glViewport(0, 0, W, H);
}
#ifndef HEIGHTFIELD_H
#define HEIGHTFIELD_H
#define W 1280
#define H 720
#define N 128
#define NN (N * N)
#define DRAW_WIDTH 35.0
#define DRAW_HEIGHT 35.0
#define QUOTE_E(x) # x
#define QUOTE(x) QUOTE_E(x)
#define ROXLU_USE_OPENGL
#define ROXLU_USE_MATH
#define ROXLU_USE_PNG
#include <tinylib.h>
#include <vector>
/*
Versions:
v.0.0.0.4 - added position buffer - https://gist.github.com/roxlu/2c8a964700cb806df0e0
v.0.0.0.3.3 - rendering/normals - https://gist.github.com/roxlu/e85690f04be0968f316b
v.0.0.0.3.2 - rendering - https://gist.github.com/roxlu/aabcf9decfd6a1f30d9b
v.0.0.0.3.1 - diffuse + normals - https://gist.github.com/roxlu/ddbc776af57e98c5b6a0
v.0.0.0.3 - diffuse working - https://gist.github.com/roxlu/8b49a9932c158ac9bd16
v.0.0.0.2 - first diffuse test - https://gist.github.com/roxlu/904865ede5d32e10ff24
v.0.0.0.1 - initial version - https://gist.github.com/roxlu/b0cf42bfdad3550565c1
*/
// Awesome WebGL fluid sim: http://www.youtube.com/watch?v=f_6aTwP2lMg
// "" "" demo: http://skeelogy.github.io/skunami.js/examples/skunami_twoWayCoupling.html
// Diffuses the height field
static const char* HF_DIFFUSE_VERT = ""
"#version 150\n"
"in ivec2 a_tex; "
"uniform sampler2D u_tex_u; "
"uniform sampler2D u_tex_forces;"
"uniform sampler2D u_tex_v; "
"const float dt = 0.17; "
"out float v_new_u_value; "
"out float v_new_v_value; "
"float get_force(int i, int j) {"
" float f0 = texelFetch(u_tex_u, ivec2(a_tex.s + i, a_tex.t + j), 0).r;"
" vec2 f1 = texelFetch(u_tex_forces, ivec2(a_tex.s + i, a_tex.t + j), 0).rg;"
" return f0 + (f1.r * 5.2) - (f1.g * 9.2);"
"}"
"void main() { "
" gl_Position = vec4(-1.0 + float(a_tex.x) * (1.0 / " QUOTE(N) ") * 2.0, -1.0 + float(a_tex.y) * (1.0 / " QUOTE(N) ") * 2.0, 0.0, 1.0);"
#if 0
" float u_center = texelFetch(u_tex_u, ivec2(a_tex.s + 0, a_tex.t + 0), 0).r;"
" float u_left = texelFetch(u_tex_u, ivec2(a_tex.s - 1, a_tex.t + 0), 0).r;"
" float u_right = texelFetch(u_tex_u, ivec2(a_tex.s + 1, a_tex.t + 0), 0).r;"
" float u_top = texelFetch(u_tex_u, ivec2(a_tex.s + 0, a_tex.t - 1), 0).r;"
" float u_bottom = texelFetch(u_tex_u, ivec2(a_tex.s + 0, a_tex.t + 1), 0).r;"
#else
" float u_center = get_force( 0, 0);"
" float u_left = get_force(-1, 0);"
" float u_right = get_force( 1, 0);"
" float u_top = get_force( 0, -1);"
" float u_bottom = get_force( 0, 1);"
#endif
" float f = 8.4 * ((u_right + u_left + u_bottom + u_top) - (4.0 * u_center));"
" { "
" float max = 0.5;"
" if(f > max) { f = max; } else if( f < -max) { f = -max; } "
" } "
" float v = texelFetch(u_tex_v, a_tex, 0).r;"
" v_new_v_value = (v + f * dt); "
" v_new_u_value = u_center + v_new_v_value * dt;"
" v_new_v_value = v_new_v_value * 0.975;"
"}"
"";
static const char* HF_DIFFUSE_FRAG = ""
"#version 150\n"
"out vec4 v_out; "
"out vec4 u_out; "
"in float v_new_u_value; "
"in float v_new_v_value; "
"void main() {"
" v_out = vec4(v_new_v_value);"
" u_out = vec4(v_new_u_value);"
"}"
"";
// Positions: renders the positions (later gradients too?)
// -----------------------------------------------------------
static const char* HF_POSITION_VS = ""
"#version 150\n"
"uniform sampler2D u_tex_u;"
"in ivec2 a_tex;"
"out vec3 v_pos;"
"out vec2 v_tex;"
"const float size_y = " QUOTE(DRAW_WIDTH) ";"
"const float size_x = " QUOTE(DRAW_HEIGHT) ";"
"const float step_y = size_y / " QUOTE(N) ";"
"const float step_x = size_x / " QUOTE(N) ";"
"const float hx = size_x * 0.5;"
"const float hy = size_y * 0.5;"
"const float step_size = 2.0 * (1.0 / " QUOTE(N) ");"
"void main() {"
" gl_Position = vec4(-1.0 + float(a_tex.s) * step_size, -1.0 + a_tex.t * step_size, 0.0, 1.0);"
" float current_height = texelFetch(u_tex_u, ivec2(a_tex.s + 0, a_tex.t + 0), 0).r;"
" v_pos = vec3(-hx + (a_tex.s + 0) * step_x, current_height, -hy + a_tex.t * step_y + 0);"
" v_tex = vec2(a_tex.s * (1.0 / " QUOTE(N) "), a_tex.t * (1.0 / " QUOTE(N) "));"
"}"
"";
static const char* HF_POSITION_FS = ""
"#version 150\n"
"in vec3 v_pos;"
"in vec2 v_tex;"
"out vec4 pos_out;"
"out vec4 tex_out;"
"void main() {"
" pos_out = vec4(v_pos, 1.0);"
" tex_out = vec4(v_tex, 0.0, 1.0);"
"}"
"";
// Normals
// -----------------------------------------------------------
static const char* HF_NORMALS_VS = ""
"#version 150\n"
"uniform sampler2D u_tex_u;"
"uniform sampler2D u_tex_pos;"
"in ivec2 a_tex;"
"out vec3 v_norm;"
"const float size_y = " QUOTE(DRAW_WIDTH) ";"
"const float size_x = " QUOTE(DRAW_HEIGHT) ";"
"const float step_y = size_y / " QUOTE(N) ";"
"const float step_x = size_x / " QUOTE(N) ";"
"const float hx = size_x * 0.5;"
"const float hy = size_y * 0.5;"
"const float step_size = 2.0 * (1.0 / " QUOTE(N) ");"
"void main() {"
" gl_Position = vec4(-1.0 + float(a_tex.s) * step_size, -1.0 + a_tex.t * step_size, 0.0, 1.0);"
" v_norm = vec3(0.0, 1.0, 0.0);"
#if 0
" float current_height = texelFetch(u_tex_u, ivec2(a_tex.s + 0, a_tex.t + 0), 0).r;"
" float right_height = texelFetch(u_tex_u, ivec2(a_tex.s + 1, a_tex.t + 0), 0).r;"
" float top_height = texelFetch(u_tex_u, ivec2(a_tex.s + 0, a_tex.t - 1), 0).r;"
" vec3 current_pos = vec3(-hx + (a_tex.s + 0) * step_x, current_height, -hy + a_tex.t * step_y + 0);"
" vec3 right_pos = vec3(-hx + (a_tex.s + 1) * step_x, right_height, -hy + a_tex.t * step_y + 0);"
" vec3 top_pos = vec3(-hx + (a_tex.s + 0) * step_x, top_height, -hy + a_tex.t * step_y - 1);"
" vec3 to_right = right_pos - current_pos; "
" vec3 to_top = top_pos - current_pos;"
#else
" vec3 current_pos = texelFetch(u_tex_pos, ivec2(a_tex.s + 0, a_tex.t + 0), 0).rgb;"
" vec3 right_pos = texelFetch(u_tex_pos, ivec2(a_tex.s + 1, a_tex.t + 0), 0).rgb;"
" vec3 top_pos = texelFetch(u_tex_pos, ivec2(a_tex.s + 0, a_tex.t - 1), 0).rgb;"
" vec3 to_right = right_pos - current_pos; "
" vec3 to_top = top_pos - current_pos;"
#endif
" v_norm = normalize(cross(to_right, to_top));"
"}"
"";
static const char* HF_NORMALS_FS = ""
"#version 150\n"
"in vec3 v_norm;"
"out vec4 norm_out;"
"out vec4 grad_out;"
"void main() {"
" norm_out = vec4(v_norm, 1.0);"
" float a = atan(v_norm.z, v_norm.x);"
" vec3 up = vec3(0.0, 1.0, 0.0);"
" vec3 grad = cross(cross(v_norm, up),up);"
" grad_out = vec4(grad, 1.0);"
"}"
"";
// Renders the height field
// -----------------------------------------------------------
static const char* HF_RENDER_VS = ""
"#version 150\n"
"uniform sampler2D u_tex_u;"
"uniform sampler2D u_tex_norm;"
"uniform mat4 u_pm;"
"uniform mat4 u_vm;"
"in ivec2 a_tex;"
"out vec3 v_norm;"
"const float size_y = " QUOTE(DRAW_WIDTH) ";"
"const float size_x = " QUOTE(DRAW_HEIGHT) ";"
"const float step_y = size_y / " QUOTE(N) ";"
"const float step_x = size_x / " QUOTE(N) ";"
"const float hx = size_x * 0.5;"
"const float hy = size_y * 0.5;"
"void main() {"
" float height = texelFetch(u_tex_u, a_tex, 0).r;"
" vec4 v = vec4(-hx + a_tex.s * step_x, height, -hy + a_tex.t * step_y, 1.0);"
" gl_Position = u_pm * u_vm * v;"
" v_norm = texelFetch(u_tex_norm, a_tex, 0).rgb;"
"}"
"";
static const char* HF_RENDER_FS = ""
"#version 150\n"
"in vec3 v_norm;"
"out vec4 fragcolor;"
"void main() {"
" fragcolor = vec4(1.0, 0.0, 0.0, 1.0);"
" fragcolor.rgb = 0.5 + 0.5 * v_norm;"
"}"
"";
// Debug drawing
// -----------------------------------------------------------
static const char* HF_TEXTURE_VS = "" // also used for the forces program
"#version 150\n"
"uniform mat4 u_pm;"
"uniform mat4 u_mm;"
"const vec2 verts[4] = vec2[]("
" vec2(-1.0, 1.0), "
" vec2(-1.0, -1.0), "
" vec2( 1.0, 1.0), "
" vec2( 1.0, -1.0) "
");"
"const vec2 tex[4] = vec2[]("
" vec2(0.0, 1.0), "
" vec2(0.0, 0.0), "
" vec2(1.0, 1.0), "
" vec2(1.0, 0.0)"
");"
"out vec2 v_tex;"
"void main() {"
" vec4 vert = vec4(verts[gl_VertexID], 0.0, 1.0);"
" gl_Position = u_pm * u_mm * vert;"
" v_tex = tex[gl_VertexID];"
"}"
"";
static const char* HF_DEBUG_FLOAT_FS = ""
"#version 150\n"
"uniform sampler2D u_tex;"
"in vec2 v_tex;"
"out vec4 fragcolor;"
"void main() {"
" fragcolor.rgb = texture(u_tex, v_tex).rgb;"
" fragcolor.a = 0.0;"
"}"
"";
// Shader that is used to draw custom forces, these forces are added to the height field
// -------------------------------------------------------------------------------------
static const char* HF_FORCES_FS = ""
"#version 150\n"
"uniform sampler2D u_tex;"
"in vec2 v_tex;"
"out vec4 fragcolor;"
"void main() {"
" fragcolor = vec4(texture(u_tex, v_tex).rg, 0.0, 1.0);"
"}"
"";
struct FieldVertex {
FieldVertex(){ tex[0] = 0; tex[1] = 0; }
FieldVertex(int i, int j) { tex[0] = i; tex[1] = j; }
void set(int i, int j) { tex[0] = i; tex[1] = j; }
GLint tex[2];
};
class HeightField {
public:
HeightField();
bool setup();
void diffuse(); /* diffuse step */
void calculatePositions(); /* after the new heights have been diffused we can update the position buffer */
void calculateNormals(); /* after calling diffuse() you need to diffuse the normals */
void drawTexture(GLuint tex, float x, float y, float w, float h);
void render();
void beginDrawForces();
void drawForceTexture(GLuint tex, float px, float py, float pw, float ph); // all in percentages
void endDrawForces();
public:
/* Shared */
GLuint vao; /* generic VAO that is used to render FieldVertices */
GLuint vbo_els; /* element array buffer */
std::vector<GLint> indices; /* indices to render triangles */
/* Diffuse the height field */
GLuint vert; /* vertex shader which performns the diffuse step */
GLuint frag; /* fragment shader which writes/sets the diffused values + velocity */
GLuint prog; /* program which does the diffuse/velocity updates */
GLuint vbo; /* contains the FieldVertex data that is used to sample from the correct location in the shader */
GLuint fbo; /* we use a FBO with a couple of destination/source texture into which we write normals, u0, u1, velocities etc.. */
GLuint tex_u0; /* contains the height values for state 0 */
GLuint tex_u1; /* contains the height values for state 1 */
GLuint tex_v0; /* contains the velocity values for state 0 */
GLuint tex_v1; /* contains the velocity values for state 1 */
GLuint tex_norm; /* contains the normals of the height field */
GLuint tex_pos; /* contains the positions in world space of the vertices */
GLuint tex_texcoord; /* contains the texture coords in a range from 0-1 for the final render, see the position shader */
GLuint tex_gradient; /* contains the gradients for the current positions */
int state_diffuse; /* toggles between 0 and 1 to ping/pong the read/write buffers */
/* Custom forces (testing) */
GLuint fbo_forces;
GLuint tex_forces; /* texture that is used to add custom forces; this works by drawing something into the force texture */
GLuint frag_forces; /* fragment shader for the step where we add custom forces, we use the vert_draw vertex shader */
GLuint prog_forces;
/* Debug drawing */
GLuint vao_draw; /* vao used to draw attribute less vertices for a texture */
GLuint vert_draw; /* vertex shader to draw the u/v textures */
GLuint frag_draw; /* fragment shader to draw the u/v textures */
GLuint prog_draw; /* program to draw the u/v textures */
/* Position (info) shader */
GLuint vert_pos; /* vertex shader used to write the (world) positions of the grid */
GLuint frag_pos; /* fragment shader to write the (world) positions */
GLuint prog_pos; /* program to write positions (later maybe more info) */
/* Normal shader */
GLuint vert_norm; /* vertex shader that calculates the normals */
GLuint frag_norm; /* fragment shader that writes the normals */
GLuint prog_norm; /* the program that writes the normals */
/* Draw the height field */
GLuint prog_render; /* program that is used to render/show the result of the height field */
GLuint vert_render; /* vertex shader that is used to render the height field */
GLuint frag_render; /* fragment shader that is used to render the height field */
mat4 pm; /* projection matrix */
mat4 vm; /* view matrix */
};
#endif
#include <assert.h>
#include "Water.h"
#include "HeightField.h"
Water::Water(HeightField& hf)
:height_field(hf)
,win_w(0)
,win_h(0)
,prog(0)
,vert(0)
,frag(0)
,flow_tex(0)
,normals_tex(0)
,noise_tex(0)
,diffuse_tex(0)
,foam_tex(0)
,force_tex0(0)
{
}
GLuint Water::createTexture(std::string filename) {
int w, h, n;
unsigned char* pix;
if(!rx_load_png(rx_to_data_path(filename), &pix, w, h, n)) {
printf("Error: cannot find: %s\n", filename.c_str());
return 0;
}
GLuint tex;
GLenum format = GL_RGB;
if(n == 4) {
format = GL_RGBA;
}
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, format, GL_UNSIGNED_BYTE, pix);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
printf("Texture, w: %d, h: %d, n: %d\n", w, h, n);
delete[] pix;
pix = NULL;
return tex;
}
bool Water::setup(int w, int h) {
assert(w && h);
win_w = w;
win_h = h;
// create shader
vert = rx_create_shader(GL_VERTEX_SHADER, WATER_VS);
frag = rx_create_shader(GL_FRAGMENT_SHADER, WATER_FS);
prog = rx_create_program(vert, frag);
glBindAttribLocation(prog, 0, "a_tex");
glLinkProgram(prog);
rx_print_shader_link_info(prog);
glUseProgram(prog);
glUniform1i(glGetUniformLocation(prog, "u_tex_pos"), 0); // VS
glUniform1i(glGetUniformLocation(prog, "u_tex_norm"), 1); // VS
glUniform1i(glGetUniformLocation(prog, "u_tex_texcoord"), 2); // VS
glUniform1i(glGetUniformLocation(prog, "u_noise_tex"), 3); // FS
glUniform1i(glGetUniformLocation(prog, "u_norm_tex"), 4); // FS
glUniform1i(glGetUniformLocation(prog, "u_flow_tex"), 5); // FS
glUniform1i(glGetUniformLocation(prog, "u_diffuse_tex"), 6); // FS
glUniform1i(glGetUniformLocation(prog, "u_foam_tex"), 7); // FS
glUniformMatrix4fv(glGetUniformLocation(prog, "u_pm"), 1, GL_FALSE, height_field.pm.ptr());
glUniformMatrix4fv(glGetUniformLocation(prog, "u_vm"), 1, GL_FALSE, height_field.vm.ptr());
// load textures
normals_tex = createTexture("water_normals.png");
flow_tex = createTexture("water_flow.png");
noise_tex = createTexture("water_noise.png");
diffuse_tex = createTexture("water_diffuse.png");
foam_tex = createTexture("water_foam.png");
force_tex0 = createTexture("force.png");
return true;
}
void Water::update(float dt) {
}
void Water::draw() {
glEnable(GL_DEPTH_TEST);
glUseProgram(prog);
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, height_field.tex_pos);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, height_field.tex_norm);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, height_field.tex_texcoord);
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, noise_tex);
glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_2D, normals_tex);
glActiveTexture(GL_TEXTURE5);
glBindTexture(GL_TEXTURE_2D, flow_tex);
glActiveTexture(GL_TEXTURE6);
glBindTexture(GL_TEXTURE_2D, diffuse_tex);
glActiveTexture(GL_TEXTURE7);
glBindTexture(GL_TEXTURE_2D, foam_tex);
}
static float t = 0.0;
float time0 = fmod(t, 1.0);
float time1 = fmod(t + 0.5, 1.0);
t += 0.002;
glUniform1f(glGetUniformLocation(prog, "u_time"), t);
glUniform1f(glGetUniformLocation(prog, "u_time0"), time0);
glUniform1f(glGetUniformLocation(prog, "u_time1"), time1);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindVertexArray(height_field.vao);
glDrawElements(GL_TRIANGLES, height_field.indices.size(), GL_UNSIGNED_INT, NULL);
}
/*
Water
------
This class renders the given height fied (using most of the
internal types of this HeightField class) using a flow map
to simulate the flow of water.
*/
#ifndef WATER_H
#define WATER_H
#define ROXLU_USE_OPENGL
#define ROXLU_USE_MATH
#define ROXLU_USE_PNG
#include <tinylib.h>
#include <vector>
#include <string>
// ------------------------------------------------------
static const char* WATER_VS = ""
"#version 150\n"
"uniform mat4 u_pm;"
"uniform mat4 u_vm;"
"uniform sampler2D u_tex_pos;"
"uniform sampler2D u_tex_norm;"
"uniform sampler2D u_tex_texcoord;"
"in ivec3 a_tex;"
"out vec3 v_norm;"
"out vec3 v_pos;"
"out vec2 v_tex;"
"void main() {"
" v_pos = texelFetch(u_tex_pos, ivec2(a_tex), 0).rgb;"
" v_norm = texelFetch(u_tex_norm, ivec2(a_tex), 0).rgb;"
" v_tex = texelFetch(u_tex_texcoord, ivec2(a_tex), 0).rg;"
" gl_Position = u_pm * u_vm * vec4(v_pos, 1.0);"
"}"
"";
static const char* WATER_FS = ""
"#version 150\n"
"uniform float u_time;"
"uniform float u_time0;"
"uniform float u_time1;"
"uniform sampler2D u_noise_tex;"
"uniform sampler2D u_flow_tex;"
"uniform sampler2D u_norm_tex;" // not the same as the norm in WATER_VS
"uniform sampler2D u_diffuse_tex;"
"uniform sampler2D u_foam_tex;"
"in vec3 v_norm;"
"in vec3 v_pos;"
"in vec2 v_tex;"
"out vec4 fragcolor;"
"void main() {"
" vec2 flow_color = texture(u_flow_tex, v_tex).rg;"
" vec3 normal_color = texture(u_norm_tex, v_tex).rgb;" // bump mapping
" vec3 diffuse_color = texture(u_diffuse_tex, v_tex).rgb;"
" float noise_color = texture(u_noise_tex, v_tex).r;"
" float gradient = 1.0 - dot(v_norm, vec3(0,1,0));"
" float phase0 = (noise_color * 0.4 + u_time0);"
" float phase1 = (noise_color * 0.4 + u_time1);"
// " flow_color = normalize(v_norm.xz + flow_color);" // move the texture in the direction of the normal
" flow_color = (v_norm.xz * 0.4 + flow_color * 0.7);" // move the texture in the direction of the normal
" float tex_scale = 1.0;"
" float flow_k = 0.4;"
" vec2 texcoord0 = (v_tex * tex_scale) + (flow_color * phase0 * flow_k);"
" vec2 texcoord1 = (v_tex * tex_scale) + (flow_color * phase1 * flow_k);"
" vec3 normal0 = texture(u_norm_tex, texcoord0).rgb;"
" vec3 normal1 = texture(u_norm_tex, texcoord1).rgb;"
" float lerp = abs(0.5 - u_time0) / 0.5;"
" vec3 moved_normal = mix(normal0, normal1, lerp);"
" vec3 diffuse0 = texture(u_diffuse_tex, texcoord0).rgb;"
" vec3 diffuse1 = texture(u_diffuse_tex, texcoord1).rgb;"
" vec3 moved_diffuse = mix(diffuse0, diffuse1, lerp);"
" vec3 foam0 = texture(u_foam_tex, texcoord0 * 4.0).rgb;"
" vec3 foam1 = texture(u_foam_tex, texcoord1 * 4.0).rgb;"
" vec3 moved_foam = mix(foam0, foam1, lerp);"
" vec3 L = vec3(-5,1,0);"
" float ndl = max(dot(v_norm, L), 0.0);"
" fragcolor = vec4(v_norm * 0.5 + 0.5, 1.0);"
" fragcolor.rgb = moved_normal;"
" fragcolor.rgb = diffuse_color;"
" float height = v_pos.y / 0.1;"
" float foam_k = (height + gradient) * 0.4; "
" foam_k = v_pos.y;"
" if(foam_k > 1.0) { foam_k = 1.0; } else if (foam_k < 0.2) { foam_k = 0.2; } "
" fragcolor.rgb = moved_foam * foam_k + (1.0 - foam_k) * moved_diffuse + 0.4 * (ndl * vec3(0.0, 0.0, 0.0));"
#if 0
" if(gl_FragCoord.x < 640) {"
" fragcolor.rgb = vec3(ndl);"
// " fragcolor.rgb = v_norm * 0.5 + 0.5;"
" fragcolor.rgb = vec3(gradient);"
" }"
#endif
"}"
"";
// ------------------------------------------------------
class HeightField;
class Water {
public:
Water(HeightField& heightField);
bool setup(int winW, int winH);
void update(float dt);
void draw();
private:
GLuint createTexture(std::string filename);
public:
HeightField& height_field;
int win_w;
int win_h;
GLuint prog;
GLuint vert;
GLuint frag;
GLuint flow_tex;
GLuint normals_tex;
GLuint noise_tex;
GLuint diffuse_tex;
GLuint foam_tex;
GLuint force_tex0;
};
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment