diff --git a/CMakeLists.txt b/CMakeLists.txt index ab25fe22..5df9d436 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,8 @@ file(GLOB_RECURSE EXTERNAL_FILES file(GLOB_RECURSE SHADER_FILES "shaders/*.frag" "shaders/*.vert" + "shaders/*.tesc" + "shaders/*.tese" "shaders/*.geom" "shaders/*.comp" "shaders/*.glsl" diff --git a/README.md b/README.md index 28dc3b2f..e382b110 100644 --- a/README.md +++ b/README.md @@ -18,3 +18,15 @@ make If you have a problem, please send a mail to - alexandre.lamure@epita.fr - gregoire.angerand@gmail.com + +### TODO +in file : +- main.cpp +- ocean_control.tess +- ocean_evaluation.tess +- ocean.frag +- ocean.vert + +Look if need for file for tesselation. + +separate task to do. \ No newline at end of file diff --git a/data/cubemap.png b/data/cubemap.png new file mode 100644 index 00000000..adb98890 Binary files /dev/null and b/data/cubemap.png differ diff --git a/data/empty.glb b/data/empty.glb new file mode 100644 index 00000000..b7291fcb Binary files /dev/null and b/data/empty.glb differ diff --git a/data/ocean_texture.png b/data/ocean_texture.png new file mode 100644 index 00000000..68c1e7bf Binary files /dev/null and b/data/ocean_texture.png differ diff --git a/data/sphere.glb b/data/sphere.glb new file mode 100755 index 00000000..2d77bd60 Binary files /dev/null and b/data/sphere.glb differ diff --git a/shaders/ifft_horizontal_waves.comp b/shaders/ifft_horizontal_waves.comp new file mode 100644 index 00000000..bed18a9e --- /dev/null +++ b/shaders/ifft_horizontal_waves.comp @@ -0,0 +1,67 @@ +#version 450 + +layout(local_size_x = 1, local_size_y = 16) in; + +layout(binding = 0, rgba16f) uniform image2D waves_real; +layout(binding = 1, rgba16f) uniform image2D waves_img; +layout(binding = 2, rgba16f) uniform image2D tmp_real; +layout(binding = 3, rgba16f) uniform image2D tmp_img; + +const float PI = 3.14159265359; + +vec2 cmul(vec2 a, vec2 b) { + return vec2(a.x*b.x - a.y*b.y, a.x*b.y + a.y*b.x); +} + +void main(){ + int y = int(gl_GlobalInvocationID.y); + int size = imageSize(waves_real).x; + if (y >= size) + return; + + vec2 Dx[512]; + vec2 Dy[512]; + vec2 Dz[512]; + + for (int x = 0; x < size; x++) { + vec4 r = imageLoad(waves_real, ivec2(x, y)); + vec4 i = imageLoad(waves_img, ivec2(x, y)); + Dx[x] = vec2(r.r, i.r); + Dy[x] = vec2(r.g, i.g); + Dz[x] = vec2(r.b, i.b); + } + + for (int s = 1; s <= int(log2(size)); s++) { + int m = 1 << s; + int m2 = m >> 1; + float theta = 2.0 * PI / float(m); + for (int k = 0; k < size; k += m) { + for (int j = 0; j < m2; j++) { + float ang = theta * float(j); + vec2 w = vec2(cos(ang), sin(ang)); + vec2 tx = cmul(w, Dx[k + j + m2]); + vec2 ty = cmul(w, Dy[k + j + m2]); + vec2 tz = cmul(w, Dz[k + j + m2]); + vec2 ux = Dx[k + j]; + vec2 uy = Dy[k + j]; + vec2 uz = Dz[k + j]; + Dx[k + j] = ux + tx; + Dy[k + j] = uy + ty; + Dz[k + j] = uz + tz; + Dx[k + j + m2] = ux - tx; + Dy[k + j + m2] = uy - ty; + Dz[k + j + m2] = uz - tz; + } + } + } + + for (int x = 0; x < size; x++) { + Dx[x] /= float(size); + Dy[x] /= float(size); + Dz[x] /= float(size); + vec3 real_row = vec3(Dx[x].x, Dy[x].x, Dz[x].x); + vec3 img_row = vec3(Dx[x].y, Dy[x].y, Dz[x].y); + imageStore(tmp_real, ivec2(x, y), vec4(real_row, 1.0)); + imageStore(tmp_img, ivec2(x, y), vec4(img_row, 1.0)); + } +} \ No newline at end of file diff --git a/shaders/ifft_vertical_waves.comp b/shaders/ifft_vertical_waves.comp new file mode 100644 index 00000000..f7aa7833 --- /dev/null +++ b/shaders/ifft_vertical_waves.comp @@ -0,0 +1,70 @@ +#version 450 + +layout(local_size_x = 16, local_size_y = 1) in; + +layout(binding = 0, rgba16f) uniform image2D tmp_real; +layout(binding = 1, rgba16f) uniform image2D tmp_img; +layout(binding = 2, rgba16f) uniform image2D result_real; +// layout(binding = 3, rgba16f) uniform image2D result_img; + +const float PI = 3.14159265359; + +vec2 cmul(vec2 a, vec2 b) { + return vec2(a.x*b.x - a.y*b.y, a.x*b.y + a.y*b.x); +} + +void main(){ + int x = int(gl_GlobalInvocationID.x); + int size = imageSize(tmp_real).x; + if (x >= size) + return; + + vec2 Dx[512]; + vec2 Dy[512]; + vec2 Dz[512]; + + for (int y = 0; y < size; y++) { + vec4 r = imageLoad(tmp_real, ivec2(x, y)); + vec4 i = imageLoad(tmp_img, ivec2(x, y)); + Dx[y] = vec2(r.r, i.r); + Dy[y] = vec2(r.g, i.g); + Dz[y] = vec2(r.b, i.b); + } + + for (int s = 1; s <= int(log2(size)); s++) { + int m = 1 << s; + int m2 = m >> 1; + float theta = 2.0 * PI / float(m); + for (int k = 0; k < size; k += m) { + for (int j = 0; j < m2; j++) { + float ang = theta * float(j); + vec2 w = vec2(cos(ang), sin(ang)); + vec2 tx = cmul(w, Dx[k + j + m2]); + vec2 ty = cmul(w, Dy[k + j + m2]); + vec2 tz = cmul(w, Dz[k + j + m2]); + vec2 ux = Dx[k + j]; + vec2 uy = Dy[k + j]; + vec2 uz = Dz[k + j]; + Dx[k + j] = ux + tx; + Dy[k + j] = uy + ty; + Dz[k + j] = uz + tz; + Dx[k + j + m2] = ux - tx; + Dy[k + j + m2] = uy - ty; + Dz[k + j + m2] = uz - tz; + } + } + } + + for (int y = 0; y < size; y++) { + Dx[y] /= float(size); + Dy[y] /= float(size); + Dz[y] /= float(size); + + // Apply frequency shift: multiply by (-1)^(x+y) + float sign = ((x + y) & 1) == 0 ? 1.0 : -1.0; + vec3 real_col = vec3(Dx[y].x, Dy[y].x, Dz[y].x) * sign; + + imageStore(result_real, ivec2(x, y), vec4(real_col, 1.0)); + // imageStore(result_img, ivec2(x, y), vec4(img_col, 1.0)); + } +} \ No newline at end of file diff --git a/shaders/init_waves.comp b/shaders/init_waves.comp new file mode 100644 index 00000000..5bcc15ac --- /dev/null +++ b/shaders/init_waves.comp @@ -0,0 +1,83 @@ +#version 450 + +layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in; + +layout(binding = 0, rgba16f) uniform image2D h0k; + +uniform float octave; + +const float wind_speed = 100.0; +const vec2 wind_direction = normalize(vec2(1.0, 1.0)); +const float amplitude = 0.001; + +const float PI = 3.14159265359; +const float G = 9.8; // Gravity (m/s**2) + +float phillips_spectrum(vec2 k) { + float k_length = length(k); + if (k_length < 0.0001) return 0.0; + + float k_length_2 = k_length * k_length; + float k_length_4 = k_length_2 * k_length_2; + + float speed_2 = wind_speed * wind_speed; + float L = speed_2 / G; + float L2 = L * L; + + vec2 k_norm = k / k_length; + float k_dot_w = dot(k_norm, wind_direction); + float k_dot_w_2 = k_dot_w * k_dot_w; + + if (k_dot_w < 0.0) k_dot_w_2 *= 0.07; + + float l = L* 0.001; + + // Phillips spectrum + return amplitude * exp(-1.0 / (k_length_2 * L2)) / k_length_4 * k_dot_w_2 * exp(-k_length_2 * l * l); +} + +uint hash(uint seed) { + uint state = seed * 747796405u + 2891336453u; + uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; + return (word >> 22u) ^ word; +} + +float random(uint seed) { + seed = hash(seed); + return float(seed) / 4294967296.0; +} + +vec2 gaussianRandom(uint seed) { + float u1 = random(seed); + float u2 = random(seed); + float r = sqrt(-2.0 * log(u1)); + float t = 2.0 * PI * u2; + return vec2(r * cos(t), r * sin(t)); +} + +void main() { + ivec2 uv = ivec2(gl_GlobalInvocationID.xy); + uint size = imageSize(h0k).x; + + if (uv.x >= size || uv.y >= size) + return; + + vec2 x = vec2(uv) - float(size) / 2.0; + + float wave_size = 8192.0 / pow(2.0, octave); + vec2 k = 2.0 * PI * x / wave_size; + + vec2 rg = gaussianRandom(uint(uv.x * size + uv.y)); + + // h0 k + float P = phillips_spectrum(k); + vec2 h0 = rg * sqrt(P / 2.0); + + // h0 -k + vec2 k_minus = -k; + float P_minus = phillips_spectrum(k_minus); + vec2 h0_minus = rg * sqrt(P_minus / 2.0); + + // Store in h0k image + imageStore(h0k, uv, vec4(h0.x, h0.y, h0_minus.x, h0_minus.y)); +} \ No newline at end of file diff --git a/shaders/lit.frag b/shaders/lit.frag index c8b2f031..8d7aae72 100644 --- a/shaders/lit.frag +++ b/shaders/lit.frag @@ -11,6 +11,7 @@ // #define DEBUG_ENV layout(location = 0) out vec4 out_color; +layout(location = 1) out vec4 out_position; layout(location = 0) in vec3 in_normal; layout(location = 1) in vec2 in_uv; @@ -41,21 +42,23 @@ layout(binding = 1) buffer PointLights { PointLight point_lights[]; }; +uniform uint is_main; + void main() { const vec3 normal_map = unpack_normal_map(texture(in_normal_texture, in_uv).xy); const vec3 normal = normal_map.x * in_tangent + - normal_map.y * in_bitangent + - normal_map.z * in_normal; + normal_map.y * in_bitangent + + normal_map.z * in_normal; const vec4 albedo_tex = texture(in_texture, in_uv); const vec3 base_color = in_color.rgb * albedo_tex.rgb * base_color_factor; const float alpha = albedo_tex.a; -#ifdef ALPHA_TEST + #ifdef ALPHA_TEST if(alpha <= alpha_cutoff) { discard; } -#endif + #endif const vec4 metal_rough_tex = texture(in_metal_rough, in_uv); const float roughness = metal_rough_tex.g * metal_rough_factor.y; // as per glTF spec @@ -67,7 +70,7 @@ void main() { vec3 acc = texture(in_emissive, in_uv).rgb * emissive_factor; acc += eval_ibl(in_envmap, brdf_lut, normal, view_dir, base_color, metallic, roughness) * frame.ibl_intensity; - { + if (is_main == 0){ float shadow = get_shadow(shadow_map, frame.sun_view_proj, in_position); if (shadow > 0.) { acc += shadow * frame.sun_color * eval_brdf(normal, view_dir, frame.sun_dir, base_color, metallic, roughness); @@ -91,6 +94,7 @@ void main() { out_color = vec4(acc, alpha); + out_position = vec4(in_position, 1.); #ifdef DEBUG_NORMAL @@ -108,5 +112,4 @@ void main() { #ifdef DEBUG_ENV out_color = vec4(texture(in_envmap, normal).rgb, 1.0); #endif -} - +} \ No newline at end of file diff --git a/shaders/ocean.comp b/shaders/ocean.comp new file mode 100644 index 00000000..88714c26 --- /dev/null +++ b/shaders/ocean.comp @@ -0,0 +1,105 @@ +#version 450 + +layout(local_size_x = 48, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0) buffer Vertex { + vec3 vertices[]; +}; + +const vec3 first_layer[16] = { + vec3(-1., 0., -1.), + vec3(-1., 0., 0.), + vec3(0., 0., 0.), + vec3(0., 0., -1.), + + vec3(-1., 0., 0.), + vec3(-1., 0., 1.), + vec3(0., 0., 1.), + vec3(0., 0., 0.), + + vec3(0., 0., 0.), + vec3(0., 0., 1.), + vec3(1., 0., 1.), + vec3(1., 0., 0.), + + vec3(0., 0., -1.), + vec3(0., 0., 0.), + vec3(1., 0., 0.), + vec3(1., 0., -1.) +}; + +const vec3 other_layer[48] = { + vec3(-1., 0., -1.), + vec3(-1., 0., -.5), + vec3(-.5, 0., -.5), + vec3(-.5, 0., -1.), + + vec3(-.5, 0., -1.), + vec3(-.5, 0., -.5), + vec3(0., 0., -.5), + vec3(0., 0., -1.), + + vec3(0., 0., -1.), + vec3(0., 0., -.5), + vec3(.5, 0., -.5), + vec3(.5, 0., -1.), + + vec3(.5, 0., -1.), + vec3(.5, 0., -.5), + vec3(1., 0., -.5), + vec3(1., 0., -1.), + + + vec3(-1., 0., -.5), + vec3(-1., 0., 0.), + vec3(-.5, 0., 0.), + vec3(-.5, 0., -.5), + + vec3(.5, 0., -.5), + vec3(.5, 0., 0.), + vec3(1., 0., 0.), + vec3(1., 0., -.5), + + + vec3(-1., 0., 0.), + vec3(-1., 0., .5), + vec3(-.5, 0., .5), + vec3(-.5, 0., 0.), + + vec3(.5, 0., 0.), + vec3(.5, 0., .5), + vec3(1., 0., .5), + vec3(1., 0., 0.), + + + vec3(-1., 0., .5), + vec3(-1., 0., 1.), + vec3(-.5, 0., 1.), + vec3(-.5, 0., .5), + + vec3(-.5, 0., .5), + vec3(-.5, 0., 1.), + vec3(0., 0., 1.), + vec3(0., 0., .5), + + vec3(0., 0., .5), + vec3(0., 0., 1.), + vec3(.5, 0., 1.), + vec3(.5, 0., .5), + + vec3(.5, 0., .5), + vec3(.5, 0., 1.), + vec3(1., 0., 1.), + vec3(1., 0., .5) +}; + +void main() { + if (gl_GlobalInvocationID.y < 1) { + uint index = gl_GlobalInvocationID.x; + if (index < 16) + vertices[index] = first_layer[gl_GlobalInvocationID.x]; + } else { + uint index = (gl_GlobalInvocationID.y * 48 - 32) + gl_GlobalInvocationID.x; + vertices[index] = other_layer[gl_GlobalInvocationID.x] * pow(2, float(gl_GlobalInvocationID.y) * 0.9); + } +} \ No newline at end of file diff --git a/shaders/ocean.frag b/shaders/ocean.frag new file mode 100644 index 00000000..eefefa53 --- /dev/null +++ b/shaders/ocean.frag @@ -0,0 +1,109 @@ +#version 450 + +#include "utils.glsl" +#include "lighting.glsl" + +// TODO: implement ocean fragment shader +// maybe lighting and transparency + +layout(location = 0) out vec4 out_color; + +layout(location = 0) in vec3 in_normal; +layout(location = 1) in vec2 in_uv; +layout(location = 2) in vec3 in_color; +layout(location = 3) in vec3 in_position; +layout(location = 4) in vec3 in_tangent; +layout(location = 5) in vec3 in_bitangent; + +layout(binding = 0) uniform sampler2D in_texture; +layout(binding = 1) uniform sampler2D in_normal_texture; +layout(binding = 2) uniform sampler2D in_metal_rough; +layout(binding = 3) uniform sampler2D in_emissive; + +uniform vec3 base_color_factor; +uniform vec2 metal_rough_factor; +uniform vec3 emissive_factor; +uniform float alpha_cutoff; + +layout(binding = 4) uniform samplerCube in_envmap; +layout(binding = 5) uniform sampler2D brdf_lut; + +layout(binding = 14) uniform sampler2D in_object_position; + +layout(binding = 0) uniform Data { + FrameData frame; +}; + +layout(binding = 1) buffer PointLights { + PointLight point_lights[]; +}; + +void main() { + const vec3 normal_map = unpack_normal_map(texture(in_normal_texture, in_uv).xy); + const vec3 normal = normal_map.x * in_tangent + + normal_map.y * in_bitangent + + normal_map.z * in_normal; + + const vec4 albedo_tex = texture(in_texture, in_uv); + const vec3 base_color = in_color.rgb * albedo_tex.rgb * base_color_factor; + float alpha = albedo_tex.a; + vec4 obj_pos = texelFetch(in_object_position, ivec2(gl_FragCoord.xy), 0); + if (obj_pos.w == 0.) { + alpha = 1.; + } else { + alpha = 1.0f - exp(min((obj_pos.y - in_position.y), 0.) * 2.); + } + + #ifdef ALPHA_TEST + if(alpha <= alpha_cutoff) { + discard; + } + #endif + + const vec4 metal_rough_tex = texture(in_metal_rough, in_uv); + const float roughness = metal_rough_tex.g * metal_rough_factor.y; // as per glTF spec + const float metallic = metal_rough_tex.b * metal_rough_factor.x; // as per glTF spec + + + const vec3 to_view = (frame.camera.position - in_position); + const vec3 view_dir = normalize(to_view); + + vec3 acc = texture(in_emissive, in_uv).rgb * emissive_factor; + acc += eval_ibl(in_envmap, brdf_lut, normal, view_dir, base_color, metallic, roughness) * frame.ibl_intensity; + { + for(uint i = 0; i != frame.point_light_count; ++i) { + PointLight light = point_lights[i]; + const vec3 to_light = (light.position - in_position); + const float dist = length(to_light); + const vec3 light_vec = to_light / dist; + + const float att = attenuation(dist, light.radius); + if(att <= 0.0f) { + continue; + } + + acc += eval_brdf(normal, view_dir, light_vec, base_color, metallic, roughness) * att * light.color; + } + } + + + out_color = vec4(acc, alpha); + + + #ifdef DEBUG_NORMAL + out_color = vec4(normal * 0.5 + 0.5, 1.0); + #endif + + #ifdef DEBUG_METAL + out_color = vec4(vec3(metallic), 1.0); + #endif + + #ifdef DEBUG_ROUGH + out_color = vec4(vec3(roughness), 1.0); + #endif + + #ifdef DEBUG_ENV + out_color = vec4(texture(in_envmap, normal).rgb, 1.0); + #endif +} + diff --git a/shaders/ocean.tesc b/shaders/ocean.tesc new file mode 100644 index 00000000..c55c1bf2 --- /dev/null +++ b/shaders/ocean.tesc @@ -0,0 +1,39 @@ +#version 450 + +//TODO: implement ocean tesselation control shader +// see if use of quads or triangles +// implementation done for quads here + +layout(location = 0) in vec3 in_normal[]; +layout(location = 1) in vec2 in_uv[]; +layout(location = 2) in vec3 in_color[]; +layout(location = 4) in vec3 in_tangent[]; +layout(location = 5) in vec3 in_bitangent[]; + +layout(location = 0) out vec3 out_normal[]; +layout(location = 1) out vec2 out_uv[]; +layout(location = 2) out vec3 out_color[]; +layout(location = 4) out vec3 out_tangent[]; +layout(location = 5) out vec3 out_bitangent[]; + +layout (vertices = 4) out; + +uniform float tesselation_level; + +void main() { + + gl_TessLevelOuter[0] = tesselation_level; + gl_TessLevelOuter[1] = tesselation_level; + gl_TessLevelOuter[2] = tesselation_level; + gl_TessLevelOuter[3] = tesselation_level; + gl_TessLevelInner[0] = tesselation_level; + gl_TessLevelInner[1] = tesselation_level; + + gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; + + out_normal[gl_InvocationID] = in_normal[gl_InvocationID]; + out_uv[gl_InvocationID] = in_uv[gl_InvocationID]; + out_color[gl_InvocationID] = in_color[gl_InvocationID]; + out_tangent[gl_InvocationID] = out_tangent[gl_InvocationID]; + out_bitangent[gl_InvocationID] = in_bitangent[gl_InvocationID]; +} \ No newline at end of file diff --git a/shaders/ocean.tese b/shaders/ocean.tese new file mode 100644 index 00000000..643bc112 --- /dev/null +++ b/shaders/ocean.tese @@ -0,0 +1,64 @@ +#version 450 + +#include "utils.glsl" + +// TODO: implement ocean tesselation evaluation shader +// implement different octave waves and displacement here +// implement cascade approch for waves +// implment Jacobien for normal calculation +// chose a M between 0.3 and 0.5 as threshold for foam generation -> color + +layout(location = 0) in vec3 in_normal[]; +layout(location = 1) in vec2 in_uv[]; +layout(location = 2) in vec3 in_color[]; +layout(location = 4) in vec3 in_tangent[]; +layout(location = 5) in vec3 in_bitangent[]; + +layout(location = 0) out vec3 out_normal; +layout(location = 1) out vec2 out_uv; +layout(location = 2) out vec3 out_color; +layout(location = 3) out vec3 out_position; +layout(location = 4) out vec3 out_tangent; +layout(location = 5) out vec3 out_bitangent; + +layout (quads, equal_spacing, ccw) in; + +layout(binding = 0) uniform Data { + FrameData frame; +}; + +layout (binding = 6)uniform sampler2D waves[4]; +layout (binding = 10)uniform sampler2D jacobien[4]; + +const float m = 0.4; // foam threshold +const float k = 2.0; // foam gain + +vec4 mix_4_values(vec4 a, vec4 b, vec4 c, vec4 d) { + vec4 v1 = mix(a, b, gl_TessCoord.x); + vec4 v2 = mix(d, c, gl_TessCoord.x); + return mix(v1, v2, gl_TessCoord.y); +} + +void main() { + vec4 p = mix_4_values(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_in[2].gl_Position, gl_in[3].gl_Position); + out_position = p.xyz; + out_normal = mix_4_values(vec4(in_normal[0], 0.), vec4(in_normal[1], 0.), vec4(in_normal[2], 0.), vec4(in_normal[3], 0.)).xyz; + out_uv = mix_4_values(vec4(in_uv[0], 0., 0.), vec4(in_uv[1], 0., 0.), vec4(in_uv[2], 0., 0.), vec4(in_uv[3], 0., 0.)).xy; + out_color = mix_4_values(vec4(in_color[0], 0.), vec4(in_color[1], 0.), vec4(in_color[2], 0.), vec4(in_color[3], 0.)).xyz; + out_tangent = mix_4_values(vec4(in_tangent[0], 0.), vec4(in_tangent[1], 0.), vec4(in_tangent[2], 0.), vec4(in_tangent[3], 0.)).xyz; + out_bitangent = mix_4_values(vec4(in_bitangent[0], 0.), vec4(in_bitangent[1], 0.), vec4(in_bitangent[2], 0.), vec4(in_bitangent[3], 0.)).xyz; + + int octave = 4 - int(clamp(length(frame.camera.position - p.xyz) / 100., 0., 3.)); + vec3 mouvement = vec3(0.0, 0.0, 0.0); + vec4 jac = vec4(0.0, 0.0, 0.0, 0.0); + for (int i = 0; i < octave; i++) { + mouvement += texture(waves[i], out_uv).xyz; + jac += texture(jacobien[i], out_uv); + } + out_position = p.xyz + mouvement; + float j = (1.0 + jac.x) * (1.0 + jac.y) - jac.z * jac.w; + + out_color = mix(vec3(0.1, 0.3, 0.8), vec3(1.0, 1.0, 1.0), clamp(k * smoothstep(m + 0.1, m - 0.1, j), 0.0, 1.0)); + + gl_Position = frame.camera.view_proj * vec4(out_position, 1.0); +} \ No newline at end of file diff --git a/shaders/ocean.vert b/shaders/ocean.vert new file mode 100644 index 00000000..457d1a4e --- /dev/null +++ b/shaders/ocean.vert @@ -0,0 +1,39 @@ +#version 450 + +#include "utils.glsl" + +// TODO: implement ocean vertex shader +// then give it to tesselation + +layout(location = 0) in vec3 in_pos; +layout(location = 1) in vec3 in_normal; +layout(location = 2) in vec2 in_uv; +layout(location = 3) in vec4 in_tangent_bitangent_sign; +layout(location = 4) in vec3 in_color; + +layout(location = 0) out vec3 out_normal; +layout(location = 1) out vec2 out_uv; +layout(location = 2) out vec3 out_color; +layout(location = 4) out vec3 out_tangent; +layout(location = 5) out vec3 out_bitangent; + +layout(binding = 0) uniform Data { + FrameData frame; +}; + +uniform mat4 model; + +void main() { + const vec4 position = model * vec4(in_pos, 1.0); + + out_normal = normalize(mat3(model) * in_normal); + out_tangent = normalize(mat3(model) * in_tangent_bitangent_sign.xyz); + out_bitangent = cross(out_tangent, out_normal) * (in_tangent_bitangent_sign.w > 0.0 ? 1.0 : -1.0); + + out_uv = in_uv; + out_color = in_color; + + gl_Position = position; +} + + diff --git a/shaders/waves.comp b/shaders/waves.comp new file mode 100644 index 00000000..1583422d --- /dev/null +++ b/shaders/waves.comp @@ -0,0 +1,76 @@ +#version 450 + +layout(local_size_x = 16, local_size_y = 16) in; + +layout(binding = 0, rgba16f) uniform image2D displacement; // Displacement +layout(binding = 1, rgba16f) uniform image2D displacement_img; // Displacement +layout(binding = 2, rgba16f) uniform image2D jacobian; // Jacobian + +layout(binding = 3, rgba16f) uniform image2D h0k; + +const float PI = 3.14159265359; +const float G = 9.81; + +uniform float time; +uniform float octave; + +vec2 iMult(vec2 a, vec2 b) { + return vec2(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x); +} + +vec2 conjugate(vec2 c) { + return vec2(c.x, -c.y); +} + +void main() { + ivec2 uv = ivec2(gl_GlobalInvocationID.xy); + uint size = imageSize(h0k).x; + + if (uv.x >= size || uv.y >= size) + return; + + vec2 x = vec2(uv) - float(size) / 2.0; + + float wave_size = 2048.0 / pow(2.0, octave); + vec2 k = 2.0 * PI * x / wave_size; + float k_length = length(k); + + if (k_length < 1e-4) { + imageStore(displacement, uv, vec4(0.0)); + imageStore(displacement_img, uv, vec4(0.0)); + imageStore(jacobian, uv, vec4(0.0)); + return; + } + + float w = sqrt(G * k_length); + + vec4 h0 = imageLoad(h0k, uv); + vec2 h0_k = h0.xy; + vec2 h0_minus_k = h0.zw; + + // Compute h(k,t) + float coswt = cos(w * time); + float sinwt = sin(w * time); + vec2 exp_plus = vec2(coswt, sinwt); + vec2 exp_minus = vec2(coswt, -sinwt); + + // Dy + vec2 h_t = iMult(h0_k, exp_plus) + iMult(conjugate(h0_minus_k), exp_minus); + + + // Dx, Dz + vec2 k_norm = k / k_length; + vec2 dx = iMult(vec2(0.0, -k_norm.x), h_t); + vec2 dz = iMult(vec2(0.0, -k_norm.y), h_t); + + imageStore(displacement, uv, vec4(dx.x, h_t.x, dz.x,0.0)); + imageStore(displacement_img, uv, vec4(dx.y, h_t.y, dz.y,0.0)); + + // Jacobian + vec2 Jxx = iMult(vec2(0.0, k.x * k_norm.x), h_t); + vec2 Jzz = iMult(vec2(0.0, k.y * k_norm.y), h_t); + vec2 Jxz = iMult(vec2(0.0, k.x * k_norm.y), h_t); + vec2 Jzx = iMult(vec2(0.0, k.y * k_norm.x), h_t); + + imageStore(jacobian, uv, vec4(Jxx.x, Jzz.x, Jxz.x, Jzx.x)); +} \ No newline at end of file diff --git a/src/Material.cpp b/src/Material.cpp index 4ee7a56b..6b0ce160 100644 --- a/src/Material.cpp +++ b/src/Material.cpp @@ -33,6 +33,10 @@ void Material::set_texture(u32 slot, std::shared_ptr tex) { } } +std::shared_ptr Material::get_program() { + return _program; +} + bool Material::is_opaque() const { return _blend_mode == BlendMode::None; } diff --git a/src/Material.h b/src/Material.h index 2c7d2c94..bc16eb6f 100644 --- a/src/Material.h +++ b/src/Material.h @@ -30,10 +30,12 @@ class Material { Material(); void set_program(std::shared_ptr prog); + void set_main_program(std::shared_ptr prog); void set_blend_mode(BlendMode blend); void set_depth_test_mode(DepthTestMode depth); void set_double_sided(bool double_sided); void set_texture(u32 slot, std::shared_ptr tex); + std::shared_ptr get_program(); bool is_opaque() const; @@ -46,12 +48,13 @@ class Material { _program->set_uniform(FWD(args)...); } - void bind(PassType pass_type=PassType::MAIN) const; + void bind(PassType pass_type=PassType::DEFAULT) const; static Material textured_pbr_material(bool alpha_test = false); private: std::shared_ptr _program; + std::shared_ptr _main_program; std::shared_ptr _depth_program; std::shared_ptr _deferred_program; std::shared_ptr _point_light_program; diff --git a/src/Ocean.cpp b/src/Ocean.cpp new file mode 100644 index 00000000..2e70b625 --- /dev/null +++ b/src/Ocean.cpp @@ -0,0 +1,154 @@ +// +// Created by remi on 24/01/2026. +// + +#include "Ocean.h" + +#include "Scene.h" + +namespace OM3D { + Ocean::Ocean() : _program(Program::from_file("ocean.comp")) { + const auto material = std::make_shared(Material::textured_pbr_material()); + if(auto [is_ok, value] = TextureData::from_file(std::string(data_path) + "ocean_texture.png"); is_ok) { + const auto ocean_texture = std::make_shared(value); + material->set_texture(0u, ocean_texture); + } + material->set_texture(3u, default_black_texture()); + material->set_program(Program::from_files( + "ocean.frag", + "ocean.tese", + "ocean.tesc", + "ocean.vert", + 4 + )); + + material->set_stored_uniform(HASH("alpha_cutoff"), 0.f); + material->set_stored_uniform(HASH("base_color_factor"), glm::vec3(1., 1., .1)); + material->set_stored_uniform(HASH("metal_rough_factor"), glm::vec2(1., 1.)); + material->set_stored_uniform(HASH("emissive_factor"), glm::vec3(1., 1., 1.)); + _material = material; + _waves_generator = Waves(); + } + + void Ocean::set_iteration(const size_t iteration) { + if (_iteration != iteration) { + _iteration = iteration; + _update_ocean = true; + } + } + + std::shared_ptr> Ocean::get_ocean( + const Camera &camera, + const float y_level, + const float min_size, + const float tesselation_level) const { + const float y_dist = 1.f + glm::abs(camera.position().y - y_level); + const float tile_size = min_size * y_dist; + glm::vec2 xz = {camera.position().x, camera.position().z}; + xz = glm::floor(xz / tile_size) * tile_size; + for (auto &obj : *_result) { + obj.set_transform({ + tile_size, 0., 0., 0., + 0., 1, 0., 0., + 0., 0., tile_size, 0., + xz.x, y_level, xz.y, 1., + }); + obj.material().set_uniform(HASH("tesselation_level"), tesselation_level); + } + + std::vector textures = _waves_generator.get_waves(); + + for (size_t i = 0; i < textures.size() && i < 4; ++i) { + _material->set_texture(6 + static_cast(i), std::make_shared(std::move(textures[i * 2]))); + _material->set_texture(10 + static_cast(i), std::make_shared(std::move(textures[i * 2 + 1]))); + } + return _result; + } + + std::shared_ptr> Ocean::get_ocean( + const Camera &camera, + const float y_level, + const float min_size, + const float tesselation_level) { + if (_iteration == 0) { + _update_ocean = false; + *_result = std::vector(); + } + if (_update_ocean) { + _update_ocean = false; + compute_ocean(); + } + return static_cast(this)->get_ocean(camera, y_level, min_size, tesselation_level); + } + + void Ocean::compute_ocean() { + if (_iteration < 1) + _result = std::make_shared>(); + + const size_t count = (_iteration * 12 - 8); + const TypedBuffer ocean(nullptr, count * 4); + + + { + ocean.bind(BufferUsage::Storage, 0); + + _program->bind(); + + glDispatchCompute(1, _iteration, 1); + glMemoryBarrier(GL_ALL_BARRIER_BITS); + } + + const auto data = ocean.get_data(); + auto result = std::vector(count); + + for (size_t i = 0; i < count; ++i) { + const auto d = &data[i * 4]; + result[i] = SceneObject( + std::make_shared( + MeshData{ + std::vector{ + Vertex { + d[0], + {0., 1., 0.}, + {0., 0.}, + {1.0f, 0.0f, 0.0f, -1.0f}, + { 1., 1., 1. }, + }, + Vertex { + d[1], + {0., 1., 0.}, + {0., 1.}, + {1.0f, 0.0f, 0.0f, -1.0f}, + { 1., 1., 1. }, + }, + Vertex { + d[2], + {0., 1., 0.,}, + {1., 1.}, + {1.0f, 0.0f, 0.0f, -1.0f}, + { 1., 1., 1. }, + }, + Vertex { + d[3], + {0., 1., 0.,}, + {1., 0.}, + {1.0f, 0.0f, 0.0f, -1.0f}, + { 1., 1., 1. }, + }, + }, + { 0, 1, 2, 3 }, + } + ), + _material + ); + result[i].set_transform({ + 1, 0., 0., 0., + 0., 1., 0., 0., + 0., 0., 1., 0., + 0, 0., 0, 1., + }); + } + + *_result = std::move(result); + } +} diff --git a/src/Ocean.h b/src/Ocean.h new file mode 100644 index 00000000..ee2b022b --- /dev/null +++ b/src/Ocean.h @@ -0,0 +1,63 @@ +// +// Created by remi on 21/01/2026. +// + +#ifndef OM3D_SQUARE_HH +#define OM3D_SQUARE_HH + +#include "Waves.h" + +namespace OM3D { + class Ocean { + private: + mutable Waves _waves_generator; + std::shared_ptr _material; + const Vertex _model_vertices[4] = { + Vertex{ + {0., 0., 0.}, + {0., 1., 0.}, + {0., 0.}, + {1.0f, 0.0f, 0.0f, 0.0f}, + {1., 1., 1.}, + }, + Vertex{ + {0., 0., 1.}, + {0., 1., 0.}, + {0., 1.}, + {1.0f, 0.0f, 0.0f, 0.0f}, + {1., 1., 1.}, + }, + Vertex{ + {1., 0., 1.,}, + {0., 1., 0.,}, + {1., 1.}, + {1.0f, 0.0f, 0.0f, 0.0f}, + {1., 1., 1.}, + }, + Vertex{ + {1., 0., 0.,}, + {0., 1., 0.,}, + {1., 0.}, + {1.0f, 0.0f, 0.0f, 0.0f}, + {1., 1., 1.}, + }, + }; + std::shared_ptr _program; + std::shared_ptr> _result = std::make_shared>(); + size_t _iteration = 5; + bool _update_ocean = true; + + void compute_ocean(); + + public: + + Ocean(); + + void set_iteration(size_t iteration); + + [[nodiscard]] std::shared_ptr> get_ocean(const Camera &camera, float y_level, float min_size, float tesselation_level) const; + [[nodiscard]] std::shared_ptr> get_ocean(const Camera &camera, float y_level, float min_size, float tesselation_level); + }; +} + +#endif //OM3D_SQUARE_HH diff --git a/src/PassType.h b/src/PassType.h index 89f1b7ea..de7cb38c 100644 --- a/src/PassType.h +++ b/src/PassType.h @@ -6,6 +6,7 @@ #define OM3D_PASSTYPE_HH enum class PassType { + DEFAULT, MAIN, DEPTH, SHADOW, @@ -13,6 +14,7 @@ enum class PassType { SUN_IBL, POINT_LIGHT, ALPHA_LIGHT, + OCEAN, }; #endif //OM3D_PASSTYPE_HH \ No newline at end of file diff --git a/src/Program.cpp b/src/Program.cpp index eec6f4ab..b46c72c8 100644 --- a/src/Program.cpp +++ b/src/Program.cpp @@ -120,6 +120,32 @@ static void link_program(GLuint handle) { } +Program::Program( + const std::string &frag, + const std::string &tese, + const std::string &tesc, + const std::string &vert, + const u32 patch_size + ) : _handle(glCreateProgram()), _program_type(TESSELLATION), _patch_size(patch_size) { + const GLuint vert_handle = create_shader(vert, GL_VERTEX_SHADER); + const GLuint tesc_handle = create_shader(tesc, GL_TESS_CONTROL_SHADER); + const GLuint tese_handle = create_shader(tese, GL_TESS_EVALUATION_SHADER); + const GLuint frag_handle = create_shader(frag, GL_FRAGMENT_SHADER); + + glAttachShader(_handle.get(), vert_handle); + glAttachShader(_handle.get(), tesc_handle); + glAttachShader(_handle.get(), tese_handle); + glAttachShader(_handle.get(), frag_handle); + + link_program(_handle.get()); + + glDeleteShader(vert_handle); + glDeleteShader(tesc_handle); + glDeleteShader(tese_handle); + glDeleteShader(frag_handle); + + fetch_uniform_locations(); +} Program::Program(const std::string& frag, const std::string& vert) : _handle(glCreateProgram()) { const GLuint vert_handle = create_shader(vert, GL_VERTEX_SHADER); @@ -136,7 +162,7 @@ Program::Program(const std::string& frag, const std::string& vert) : _handle(glC fetch_uniform_locations(); } -Program::Program(const std::string& comp) : _handle(glCreateProgram()), _is_compute(true) { +Program::Program(const std::string& comp) : _handle(glCreateProgram()), _program_type(COMPUTE) { const GLuint comp_handle = create_shader(comp, GL_COMPUTE_SHADER); glAttachShader(_handle.get(), comp_handle); @@ -178,8 +204,12 @@ void Program::bind() const { glUseProgram(_handle.get()); } -bool Program::is_compute() const { - return _is_compute; +ProgramType Program::get_program_type() const { + return _program_type; +} + +GLint Program::get_patch_size() const { + return _patch_size; } std::shared_ptr Program::from_file(const std::string& comp, Span defines) { @@ -213,6 +243,36 @@ std::shared_ptr Program::from_files(const std::string& frag, const std: return program; } +std::shared_ptr Program::from_files( + const std::string& frag, + const std::string& tese, + const std::string& tesc, + const std::string& vert, + const u32 patch_size, + Span defines + ) { + static std::unordered_map, std::weak_ptr, CollectionHasher>> loaded; + + std::vector key(defines.begin(), defines.end()); + key.emplace_back(frag); + key.emplace_back(tese); + key.emplace_back(tesc); + key.emplace_back(vert); + + auto& weak_program = loaded[key]; + auto program = weak_program.lock(); + if(!program) { + program = std::make_shared( + read_shader(frag, defines), + read_shader(tese, defines), + read_shader(tesc, defines), + read_shader(vert, defines), + patch_size); + weak_program = program; + } + return program; +} + int Program::find_location(u32 hash) { const auto it = std::lower_bound(_uniform_locations.begin(), _uniform_locations.end(), UniformLocationInfo{hash, 0}); return (it == _uniform_locations.end() || it->name_hash != hash) ? -1 : it->location; diff --git a/src/Program.h b/src/Program.h index cc405447..e10c07b8 100644 --- a/src/Program.h +++ b/src/Program.h @@ -14,8 +14,16 @@ #include #include +#include "glad/gl.h" + namespace OM3D { +enum ProgramType { + DEFAULT, + COMPUTE, + TESSELLATION, +}; + // all possible uniform types using UniformValue = std::variant< u32, @@ -49,16 +57,31 @@ class Program : NonCopyable { Program(Program&&) = default; Program& operator=(Program&&) = default; + Program( + const std::string& frag, + const std::string& tese, + const std::string& tesc, + const std::string& vert, + u32 patch_size + ); Program(const std::string& frag, const std::string& vert); Program(const std::string& comp); ~Program(); void bind() const; - bool is_compute() const; + [[nodiscard]] ProgramType get_program_type() const; + [[nodiscard]] GLint get_patch_size() const; static std::shared_ptr from_file(const std::string& comp, Span defines = {}); static std::shared_ptr from_files(const std::string& frag, const std::string& vert, Span defines = {}); + static std::shared_ptr from_files( + const std::string& frag, + const std::string& tese, + const std::string& tesc, + const std::string& vert, + u32 patch_size, + Span defines = {}); void set_uniform(u32 name_hash, u32 value); void set_uniform(u32 name_hash, float value); @@ -84,8 +107,8 @@ class Program : NonCopyable { GLHandle _handle; std::vector _uniform_locations; - bool _is_compute = false; - + ProgramType _program_type = DEFAULT; + GLint _patch_size = 0; }; } diff --git a/src/Scene.cpp b/src/Scene.cpp index 77bcfadd..7ad85771 100644 --- a/src/Scene.cpp +++ b/src/Scene.cpp @@ -6,6 +6,7 @@ #include #include +#include "Ocean.h" #include "glad/gl.h" #include "glm/ext/quaternion_geometric.hpp" @@ -30,6 +31,10 @@ void Scene::add_sphere(const std::shared_ptr &obj) { _sphere = obj; } +void Scene::add_ocean(const std::shared_ptr> &obj) { + _ocean = obj; +} + Span Scene::objects() const { return _objects; } @@ -46,7 +51,7 @@ const Camera& Scene::camera() const { return _camera; } -Camera Scene::get_sun_camera(std::vector *visible_objects) const{ +Camera Scene::get_depth_camera(std::vector *visible_objects, glm::vec3 direction) const{ bool to_delete = false; if (visible_objects == nullptr) { to_delete = true; @@ -102,9 +107,9 @@ Camera Scene::get_sun_camera(std::vector *visible_objects) c )); cam.set_view(glm::lookAt( - bounding_sphere.origin + glm::normalize(_sun_direction) * bounding_sphere.radius, + bounding_sphere.origin + glm::normalize(direction) * (bounding_sphere.radius + eps), bounding_sphere.origin, - glm::vec3(0, 1, 0) + direction != glm::vec3{0., 1., 0.} ? glm::vec3(0, 1, 0) : glm::vec3(0, 0, 1) )); return cam; @@ -162,6 +167,10 @@ void Scene::render_sky() const { // Render the sky _sky_material.bind(); _sky_material.set_uniform(HASH("intensity"), _ibl_intensity); + + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_GEQUAL); + draw_full_screen_triangle(); } @@ -175,7 +184,7 @@ void Scene::set_frame_buffer(TypedBuffer &buffer) const { mapping[0].sun_color = _sun_color; mapping[0].sun_dir = glm::normalize(_sun_direction); mapping[0].ibl_intensity = _ibl_intensity; - mapping[0].sun_view_proj = get_sun_camera().view_proj_matrix(); + mapping[0].sun_view_proj = get_depth_camera(nullptr, _sun_direction).view_proj_matrix(); buffer.bind(BufferUsage::Uniform, 0); } @@ -227,7 +236,6 @@ void Scene::render_main(const PassType pass_type) const { set_frame_buffer(buffer); set_light(light_buffer); bind_envmap(); - render_sky(); auto [opaques, transparents] = get_opaque_transparent(_camera); @@ -236,9 +244,12 @@ void Scene::render_main(const PassType pass_type) const { obj->render(pass_type); } glPopDebugGroup(); // Opaques + + render_sky(); + glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Transparents"); for(const SceneObject* obj : transparents) { - obj->render(pass_type); + obj->render(PassType::ALPHA_LIGHT); } glPopDebugGroup(); // Transparents } @@ -277,7 +288,7 @@ void Scene::render_depth(const PassType pass_type) const { void Scene::render_shadow([[maybe_unused]] const PassType pass_type) const { TypedBuffer buffer(nullptr, 1); - const auto sun_camera = get_sun_camera(); + const auto sun_camera = get_depth_camera(nullptr, _sun_direction); set_frame_buffer_shadow(buffer, sun_camera); auto [opaques, _] = get_opaque_transparent(sun_camera); @@ -300,7 +311,7 @@ void Scene::render_sun_ibl([[maybe_unused]] const PassType pass_type) const { draw_full_screen_triangle(TEXTURE_FUNC_ADD); } -void Scene::render_point_lights([[maybe_unused]] const PassType pass_type) const { +void Scene::render_point_lights(const PassType pass_type) const { TypedBuffer frame_data_buffer(nullptr, 1); TypedBuffer point_light_buffer(nullptr, std::max(_point_lights.size(), static_cast(1))); @@ -311,7 +322,7 @@ void Scene::render_point_lights([[maybe_unused]] const PassType pass_type) const set_light(point_light_buffer); for (size_t i = 0; i < _point_lights.size(); ++i) { - [[maybe_unused]] const BoundingSphere bounding_sphere = { + const BoundingSphere bounding_sphere = { _point_lights[i].position(), _point_lights[i].radius() }; @@ -325,6 +336,24 @@ void Scene::render_point_lights([[maybe_unused]] const PassType pass_type) const } } +void Scene::render_ocean([[maybe_unused]] const PassType pass_type) const { + TypedBuffer frame_data_buffer(nullptr, 1); + TypedBuffer point_light_buffer(nullptr, std::max(_point_lights.size(), static_cast(1))); + + bind_envmap(); + bind_brdf(); + + set_frame_buffer(frame_data_buffer); + set_light(point_light_buffer); + + + glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Ocean"); + for(const SceneObject &obj : *_ocean) { + obj.render(PassType::ALPHA_LIGHT); + } + glPopDebugGroup(); // Ocean +} + void Scene::render(const PassType pass_type) const { switch (pass_type) { case PassType::DEPTH: @@ -347,6 +376,13 @@ void Scene::render(const PassType pass_type) const { break; case PassType::ALPHA_LIGHT: render_alpha_lights(pass_type); + break; + case PassType::DEFAULT: + render_main(pass_type); + break; + case PassType::OCEAN: + render_ocean(pass_type); + break; } } diff --git a/src/Scene.h b/src/Scene.h index 1488b6e0..5c3aacaf 100644 --- a/src/Scene.h +++ b/src/Scene.h @@ -8,7 +8,6 @@ #include #include -#include "BufferMapping.h" #include "BufferMapping.h" #include "PassType.h" #include "shader_structs.h" @@ -22,11 +21,12 @@ class Scene : NonMovable { static Result> from_gltf(const std::string& file_name); - void render(PassType pass_type=PassType::MAIN) const; + void render(PassType pass_type=PassType::DEFAULT) const; void add_object(SceneObject obj); void add_light(PointLight obj); void add_sphere(const std::shared_ptr &obj); + void add_ocean(const std::shared_ptr> &obj); Span objects() const; Span point_lights() const; @@ -39,7 +39,7 @@ class Scene : NonMovable { void set_sun(float altitude, float azimuth, glm::vec3 color = glm::vec3(1.0f)); void set_frame_buffer(TypedBuffer &buffer) const; - Camera get_sun_camera(std::vector *visible_objects = nullptr) const; + Camera get_depth_camera(std::vector *visible_objects = nullptr, glm::vec3 direction = {0., 1., 0.}) const; void bind_envmap(int index = 4) const; @@ -47,6 +47,7 @@ class Scene : NonMovable { std::vector _objects; std::vector _point_lights; std::shared_ptr _sphere; + std::shared_ptr> _ocean; glm::vec3 _sun_direction = glm::vec3(0.2f, 1.0f, 0.1f); glm::vec3 _sun_color = glm::vec3(1.0f); @@ -69,6 +70,7 @@ class Scene : NonMovable { void render_shadow(PassType pass_type) const; void render_sun_ibl(PassType pass_type) const; void render_point_lights(PassType pass_type) const; + void render_ocean(PassType pass_type) const; void render_alpha_lights(PassType pass_type) const; }; diff --git a/src/SceneObject.cpp b/src/SceneObject.cpp index 29ead1f7..72f1698c 100644 --- a/src/SceneObject.cpp +++ b/src/SceneObject.cpp @@ -15,6 +15,10 @@ void SceneObject::render(const PassType pass_type, const size_t i) const { return; } switch(pass_type) { + case PassType::MAIN: + _material->set_depth_test_mode(DepthTestMode::Standard); + _material->set_blend_mode(BlendMode::None); + break; case PassType::DEPTH: _material->set_depth_test_mode(DepthTestMode::Standard); break; @@ -24,15 +28,17 @@ void SceneObject::render(const PassType pass_type, const size_t i) const { _material->set_stored_uniform(HASH("index"), static_cast(i)); break; case PassType::ALPHA_LIGHT: - _material->set_depth_test_mode(DepthTestMode::None); + _material->set_depth_test_mode(DepthTestMode::Standard); + _material->set_blend_mode(BlendMode::Alpha); break; default: _material->set_depth_test_mode(DepthTestMode::Equal); break; } _material->set_stored_uniform(HASH("model"), transform()); + _material->set_stored_uniform(HASH("is_main"), static_cast(pass_type == PassType::MAIN)); _material->bind(pass_type); - _mesh->draw(); + _mesh->draw(_material->get_program()->get_program_type(), _material->get_program()->get_patch_size()); } const Material& SceneObject::material() const { diff --git a/src/StaticMesh.cpp b/src/StaticMesh.cpp index debebc4c..d9c5f363 100644 --- a/src/StaticMesh.cpp +++ b/src/StaticMesh.cpp @@ -2,6 +2,7 @@ #include +#include "Program.h" #include "glm/glm.hpp" #include "glm/geometric.hpp" @@ -45,7 +46,7 @@ namespace OM3D { return _bounding_sphere; } - void StaticMesh::draw() const { + void StaticMesh::draw(const ProgramType program_type, const GLint patch_size) const { _vertex_buffer.bind(BufferUsage::Attribute); _index_buffer.bind(BufferUsage::Index); @@ -70,7 +71,13 @@ namespace OM3D { audit_bindings(); } - glDrawElements(GL_TRIANGLES, int(_index_buffer.element_count()), GL_UNSIGNED_INT, nullptr); + if (program_type == TESSELLATION) { + glPatchParameteri(GL_PATCH_VERTICES, patch_size); + glDrawElements(GL_PATCHES, int(_index_buffer.element_count()), GL_UNSIGNED_INT, nullptr); + } + else { + glDrawElements(GL_TRIANGLES, int(_index_buffer.element_count()), GL_UNSIGNED_INT, nullptr); + } } } diff --git a/src/StaticMesh.h b/src/StaticMesh.h index 44ddf4aa..35e9562a 100644 --- a/src/StaticMesh.h +++ b/src/StaticMesh.h @@ -8,6 +8,8 @@ #include "BoundingSphere.h" #include "Camera.h" +#include "Program.h" +#include "glad/gl.h" namespace OM3D { @@ -25,7 +27,7 @@ class StaticMesh : NonCopyable { StaticMesh(const MeshData& data); - void draw() const; + void draw(ProgramType program_type = DEFAULT, GLint patch_size = 4) const; bool collide(const Camera& camera, const glm::mat4 &transform) const; BoundingSphere get_bounding_sphere() const; diff --git a/src/Texture.cpp b/src/Texture.cpp index 442a67f9..08340959 100644 --- a/src/Texture.cpp +++ b/src/Texture.cpp @@ -8,6 +8,7 @@ #include #include +#include namespace OM3D { diff --git a/src/TypedBuffer.h b/src/TypedBuffer.h index 78eb7900..ed621832 100644 --- a/src/TypedBuffer.h +++ b/src/TypedBuffer.h @@ -5,6 +5,8 @@ #include +#include "glad/gl.h" + namespace OM3D { template @@ -26,8 +28,13 @@ class TypedBuffer : public ByteBuffer { BufferMapping map(AccessType access = AccessType::ReadWrite) { return BufferMapping(ByteBuffer::map_internal(access), byte_size(), handle()); } -}; + std::vector get_data() const { + std::vector data(element_count()); + glGetNamedBufferSubData(handle().get(), 0, byte_size(), data.data()); + return data; + } +}; } #endif // TYPEDBUFFER_H diff --git a/src/Waves.cpp b/src/Waves.cpp new file mode 100644 index 00000000..5a73553e --- /dev/null +++ b/src/Waves.cpp @@ -0,0 +1,91 @@ +#include "Waves.h" +#include + +namespace OM3D { + Waves::Waves() : _program(Program::from_file("waves.comp")), + _ifft_h(Program::from_file("ifft_horizontal_waves.comp")), + _ifft_v(Program::from_file("ifft_vertical_waves.comp")){ + auto init_waves_program = Program::from_file("init_waves.comp"); + + _init_waves_textures.clear(); + for (size_t i = 0; i < octave; i++) + { + Texture wave(glm::uvec2(_size, _size), ImageFormat::RGBA16_FLOAT, WrapMode::Repeat); + + init_waves_program->set_uniform("octave", static_cast(i)); + init_waves_program->bind(); + + // bind textures + wave.bind_as_image(0, AccessType::WriteOnly); + + glDispatchCompute((_size + 15) / 16, (_size + 15) / 16, 1); + glMemoryBarrier(GL_ALL_BARRIER_BITS); + + _init_waves_textures.push_back(std::move(wave)); + } + // delete program + init_waves_program = nullptr; + } + + std::vector Waves::get_waves(){ + std::vector Textures; + + for (size_t i = 0; i < octave; i++) + { + Texture wave( glm::uvec2(_size, _size), ImageFormat::RGBA16_FLOAT, WrapMode::Repeat); + + Texture wave_img( glm::uvec2(_size, _size), ImageFormat::RGBA16_FLOAT, WrapMode::Repeat); + + Texture jacobien( glm::uvec2(_size, _size), ImageFormat::RGBA16_FLOAT, WrapMode::Repeat); + + _program->set_uniform("time", _time); + _program->set_uniform("octave", static_cast(i)); + _program->bind(); + + // bind textures + wave.bind_as_image(0, AccessType::WriteOnly); + wave_img.bind_as_image(1, AccessType::WriteOnly); + jacobien.bind_as_image(2, AccessType::WriteOnly); + _init_waves_textures[i].bind_as_image(3, AccessType::ReadOnly); + + glDispatchCompute(_size / 16, _size / 16, 1); + glMemoryBarrier(GL_ALL_BARRIER_BITS); + + Textures.push_back(std::move(IFFT(wave, wave_img))); + Textures.push_back(std::move(jacobien)); + } + _time += 0.03f; + return Textures; + } + + Texture Waves::IFFT(Texture &input_real, Texture &input_img) { + Texture tmp_real( glm::uvec2(_size, _size), ImageFormat::RGBA16_FLOAT, WrapMode::Repeat); + Texture tmp_img( glm::uvec2(_size, _size), ImageFormat::RGBA16_FLOAT, WrapMode::Repeat); + + _ifft_h->bind(); + + input_real.bind_as_image(0, AccessType::ReadOnly); + input_img .bind_as_image(1, AccessType::ReadOnly); + tmp_real.bind_as_image(2, AccessType::WriteOnly); + tmp_img.bind_as_image(3, AccessType::WriteOnly); + + glDispatchCompute(1, (_size + 15) / 16, 1); + glMemoryBarrier(GL_ALL_BARRIER_BITS); + + Texture result_real( glm::uvec2(_size, _size), ImageFormat::RGBA16_FLOAT, WrapMode::Repeat); + Texture result_img( glm::uvec2(_size, _size), ImageFormat::RGBA16_FLOAT, WrapMode::Repeat); + + _ifft_v->bind(); + + tmp_real.bind_as_image(0, AccessType::ReadOnly); + tmp_img.bind_as_image(1, AccessType::ReadOnly); + result_real.bind_as_image(2, AccessType::WriteOnly); + // result_img.bind_as_image(3, AccessType::WriteOnly); + + glDispatchCompute((_size + 15) / 16, 1, 1); + glMemoryBarrier(GL_ALL_BARRIER_BITS); + + return result_real; + } + +} \ No newline at end of file diff --git a/src/Waves.h b/src/Waves.h new file mode 100644 index 00000000..83c3538a --- /dev/null +++ b/src/Waves.h @@ -0,0 +1,25 @@ +#ifndef OM3D_WAVES_HH +#define OM3D_WAVES_HH + +#include "SceneObject.h" + +namespace OM3D { + class Waves { + private: + std::shared_ptr _program; + std::shared_ptr _ifft_h; + std::shared_ptr _ifft_v; + int _size = 512; + float _time = 0.f; + std::vector _init_waves_textures; + + public: + size_t octave = 4; + + Waves(); + [[nodiscard]] std::vector get_waves(); + Texture IFFT(Texture &input_real, Texture &input_img); + }; +} + +#endif //OM3D_WAVES_HH \ No newline at end of file diff --git a/src/graphics.cpp b/src/graphics.cpp index e1b62c15..8619df5e 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -123,7 +123,7 @@ void init_graphics() { brdf_lut_texture = Texture(glm::uvec2(256), ImageFormat::RG16_UNORM, WrapMode::Clamp); std::shared_ptr brdf_program = Program::from_file("brdf.comp"); - DEBUG_ASSERT(brdf_program && brdf_program->is_compute()); + DEBUG_ASSERT(brdf_program && brdf_program->get_program_type() == COMPUTE); brdf_program->bind(); brdf_lut_texture.bind_as_image(0, AccessType::WriteOnly); @@ -184,7 +184,7 @@ void draw_full_screen_triangle(const char flags) { glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE); } else { - glClearColor(0.5f, 0.7f, 0.8f, 0.0f); + glClearColor(0.0f, 0.0f, .15f, 0.0f); glDisable(GL_BLEND); } diff --git a/src/main.cpp b/src/main.cpp index 49bc633d..4de8499f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -31,7 +32,7 @@ static const char * states[STATES_SIZE] = { "No deferred", }; -static const char* current_state = states[0]; +static const char* current_state = states[6]; static float delta_time = 0.0f; static float sun_altitude = 45.0f; @@ -39,8 +40,13 @@ static float sun_azimuth = 45.0f; static float sun_intensity = 7.0f; static float ibl_intensity = 1.0f; static float exposure = 0.33f; +static float ocean_size = 2.f; +static int ocean_iteration = 5.f; +static float tesselation_level = 10.f; +static float y_level = 0.f; static std::shared_ptr sphere; +static std::unique_ptr ocean; static std::unique_ptr scene; static std::shared_ptr envmap; @@ -108,7 +114,7 @@ void process_inputs(GLFWwindow* window, Camera& camera) { speed *= 10.0f; } - if(movement.length() > 0.0f) { + if(glm::length(movement) > 0.0f) { const glm::vec3 new_pos = camera.position() + movement * delta_time * speed; camera.set_view(glm::lookAt(new_pos, new_pos + camera.forward(), camera.up())); } @@ -116,7 +122,7 @@ void process_inputs(GLFWwindow* window, Camera& camera) { if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS) { const glm::vec2 delta = glm::vec2(mouse_pos - new_mouse_pos) * 0.01f; - if(delta.length() > 0.0f) { + if(glm::length(delta) > 0.0f) { glm::mat4 rot = glm::rotate(glm::mat4(1.0f), delta.x, glm::vec3(0.0f, 1.0f, 0.0f)); rot = glm::rotate(rot, delta.y, camera.right()); camera.set_view(glm::lookAt(camera.position(), camera.position() + (glm::mat3(rot) * camera.forward()), (glm::mat3(rot) * camera.up()))); @@ -142,6 +148,7 @@ void load_envmap(const std::string& filename) { } } + void load_scene(const std::string& filename) { if(auto res = Scene::from_gltf(filename); res.is_ok) { scene = std::move(res.value); @@ -149,6 +156,7 @@ void load_scene(const std::string& filename) { scene->set_ibl_intensity(ibl_intensity); scene->set_sun(sun_altitude, sun_azimuth, glm::vec3(sun_intensity)); scene->add_sphere(sphere); + scene->add_ocean(ocean->get_ocean(scene->camera(), y_level, ocean_size, tesselation_level)); } else { std::cerr << "Unable to load scene (" << filename << ")" << std::endl; } @@ -271,6 +279,20 @@ void gui(ImGuiRenderer& imgui) { ImGui::EndMenu(); } + if(scene && ImGui::BeginMenu("Ocean parameter")) { + ImGui::InputInt("Number of iteration", &ocean_iteration); + ImGui::DragFloat("Tesselation level", &tesselation_level, 1.f, 0.f, 100.f, "%.1f"); + ImGui::DragFloat("Minimum panel size", &ocean_size, .1f, 0.f, 100.f, "%.1f"); + ImGui::DragFloat("Y level", &y_level); + + if (ocean_iteration < 0) + ocean_iteration = 0; + + ocean->set_iteration(ocean_iteration); + + ImGui::EndMenu(); + } + if(scene && ImGui::BeginMenu("Scene Info")) { ImGui::Text("%u objects", u32(scene->objects().size())); ImGui::Text("%u point lights", u32(scene->point_lights().size())); @@ -392,8 +414,10 @@ void gui(ImGuiRenderer& imgui) { void load_default_scene() { load_sphere(std::string(data_path) + "sphere.glb"); + ocean = std::make_unique(Ocean()); + ocean->set_iteration(ocean_iteration); load_scene(std::string(data_path) + "DamagedHelmet.glb"); - load_envmap(std::string(data_path) + "pretoria_gardens.jpg"); + load_envmap(std::string(data_path) + "cubemap.png"); // Add lights { @@ -425,14 +449,16 @@ struct RendererState { state.shadow_texture = Texture(glm::uvec2(2048,2048), ImageFormat::Depth32_FLOAT, WrapMode::Clamp, TEXTURE_FLAG_COMPARE | TEXTURE_FLAG_LINEAR); state.albedo_roughness_texture = Texture(size, ImageFormat::RGBA8_sRGB, WrapMode::Clamp); state.normal_metalness_texture = Texture(size, ImageFormat::RGBA8_UNORM, WrapMode::Clamp); + state.position_texture = Texture(size, ImageFormat::RGBA16_FLOAT, WrapMode::Clamp); - state.main_framebuffer = Framebuffer(&state.depth_texture, std::array{&state.lit_hdr_texture}); + state.main_framebuffer = Framebuffer(&state.depth_texture, std::array{&state.lit_hdr_texture, &state.position_texture}); state.tone_map_framebuffer = Framebuffer(nullptr, std::array{&state.tone_mapped_texture}); state.depth_framebuffer = Framebuffer(&state.depth_texture); state.shadow_framebuffer = Framebuffer(&state.shadow_texture); state.deferred_framebuffer = Framebuffer(&state.depth_texture, std::array{&state.lit_hdr_texture, &state.albedo_roughness_texture, &state.normal_metalness_texture}); state.debug_framebuffer = Framebuffer(nullptr, std::array{&state.tone_mapped_texture}); state.sun_ibl_framebuffer = Framebuffer(nullptr, std::array{&state.lit_hdr_texture}); + state.ocean_framebuffer = Framebuffer(&state.depth_texture, std::array{&state.lit_hdr_texture}); } return state; @@ -447,6 +473,7 @@ struct RendererState { Texture albedo_roughness_texture; Texture normal_metalness_texture; Texture sun_texture; + Texture position_texture; Framebuffer main_framebuffer; Framebuffer tone_map_framebuffer; @@ -455,6 +482,7 @@ struct RendererState { Framebuffer deferred_framebuffer; Framebuffer debug_framebuffer; Framebuffer sun_ibl_framebuffer; + Framebuffer ocean_framebuffer; }; @@ -486,6 +514,7 @@ int main(int argc, char** argv) { load_default_scene(); + // TODO: create program for sea (maybe not here) and delete unused auto tonemap_program = Program::from_files("tonemap.frag", "screen.vert"); auto debug_program = Program::from_files("debug.frag", "screen.vert"); auto sun_ibl_program = Program::from_files("sun_ibl.frag", "screen.vert"); @@ -520,84 +549,126 @@ int main(int argc, char** argv) { glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Frame"); { - PROFILE_GPU("Z-prepass"); - glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Z-prepass"); + PROFILE_GPU("Ocean Compute"); + glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Ocean Compute"); + scene->add_ocean(ocean->get_ocean(scene->camera(), y_level, ocean_size, tesselation_level)); - renderer.depth_framebuffer.bind(true, false); - scene->render(PassType::DEPTH); + for (const auto &obj : scene->objects()) { + obj.material().set_uniform("y_level", y_level); + } - glPopDebugGroup(); // Z-prepass + glPopDebugGroup(); // Ocean Compute } - { - PROFILE_GPU("Shadow Pass"); - glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Shadow Pass"); + if (current_state == states[6]) { + { + PROFILE_GPU("Main Pass"); + glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Main Pass"); - renderer.shadow_framebuffer.bind(true, false); - scene->render(PassType::SHADOW); + renderer.main_framebuffer.bind(true, true); + renderer.shadow_texture.bind(6); - glPopDebugGroup(); // Shadow Pass - } + scene->render(PassType::MAIN); - { - PROFILE_GPU("Deferred Pass"); - glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Deferred Pass"); + glPopDebugGroup(); // Main Pass + } - renderer.deferred_framebuffer.bind(false, true); - scene->render(PassType::DEFFERED); + { + PROFILE_GPU("Ocean Pass"); + glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Ocean Pass"); - glPopDebugGroup(); // Deferred Pass - } + renderer.ocean_framebuffer.bind(false, false); + renderer.position_texture.bind(14); - { - PROFILE_GPU("Sun & IBL Pass"); - glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Sun & IBL Pass"); + scene->render(PassType::OCEAN); - renderer.sun_ibl_framebuffer.bind(false, false); + glPopDebugGroup(); // Ocean Pass + } + } else { + { + PROFILE_GPU("Z-prepass"); + glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Z-prepass"); - renderer.depth_texture.bind(0); - renderer.albedo_roughness_texture.bind(1); - renderer.normal_metalness_texture.bind(2); - renderer.shadow_texture.bind(6); - sun_ibl_program->bind(); + renderer.depth_framebuffer.bind(true, false); + scene->render(PassType::DEPTH); - scene->render(PassType::SUN_IBL); + glPopDebugGroup(); // Z-prepass + } - glPopDebugGroup(); // Sun & IBL Pass - } - { - PROFILE_GPU("Point Lights Pass"); - glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Point Lights Pass"); + { + PROFILE_GPU("Shadow Pass"); + glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Shadow Pass"); - renderer.sun_ibl_framebuffer.bind(false, false); + renderer.shadow_framebuffer.bind(true, false); + scene->render(PassType::SHADOW); - renderer.shadow_texture.bind(6); - renderer.depth_texture.bind(7); - renderer.albedo_roughness_texture.bind(8); - renderer.normal_metalness_texture.bind(9); + glPopDebugGroup(); // Shadow Pass + } - scene->render(PassType::POINT_LIGHT); + { + PROFILE_GPU("Deferred Pass"); + glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Deferred Pass"); - glPopDebugGroup(); - } + renderer.deferred_framebuffer.bind(false, true); + scene->render(PassType::DEFFERED); + glPopDebugGroup(); // Deferred Pass + } - // Render the scene - { - PROFILE_GPU("Alpha Pass"); - glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Alpha Pass"); + { + PROFILE_GPU("Sun & IBL Pass"); + glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Sun & IBL Pass"); + + renderer.sun_ibl_framebuffer.bind(false, false); + + renderer.depth_texture.bind(0); + renderer.albedo_roughness_texture.bind(1); + renderer.normal_metalness_texture.bind(2); + renderer.shadow_texture.bind(6); + + sun_ibl_program->bind(); + + scene->render(PassType::SUN_IBL); + + glPopDebugGroup(); // Sun & IBL Pass + } + + { + PROFILE_GPU("Point Lights Pass"); + glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Point Lights Pass"); + + renderer.sun_ibl_framebuffer.bind(false, false); + + renderer.shadow_texture.bind(6); + renderer.depth_texture.bind(7); + renderer.albedo_roughness_texture.bind(8); + renderer.normal_metalness_texture.bind(9); + + scene->render(PassType::POINT_LIGHT); - renderer.main_framebuffer.bind(false, false); - renderer.shadow_texture.bind(6); + glPopDebugGroup(); + } + + + // TODO: redo to show opaque and transparent objects (sea dont now type -> to decide) + // Render the scene + { + PROFILE_GPU("Alpha Pass"); + glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Alpha Pass"); - scene->render(PassType::ALPHA_LIGHT); + renderer.main_framebuffer.bind(false, false); + renderer.shadow_texture.bind(6); - glPopDebugGroup(); // Alpha Pass + scene->render(PassType::ALPHA_LIGHT); + + glPopDebugGroup(); // Alpha Pass + } } + // TODO: keep but modify a bit // Apply a tonemap as a full screen pass { PROFILE_GPU("Tonemap");