diff options
Diffstat (limited to 'pbr')
42 files changed, 1626 insertions, 177 deletions
diff --git a/pbr/1.1.lighting/default.frag b/pbr/1.1.lighting/default.frag new file mode 100644 index 0000000..9351a1b --- /dev/null +++ b/pbr/1.1.lighting/default.frag @@ -0,0 +1,15 @@ +#version 330 core + +in vert_t { + vec2 tex_coords; +} vert; + +out vec4 frag_color; + +uniform sampler2D colorbuffer; + +void main(void) +{ + vec3 color = vec3(texture(colorbuffer, vert.tex_coords)); + frag_color = vec4(color, 1.0); +} diff --git a/pbr/1.1.lighting/default.vert b/pbr/1.1.lighting/default.vert index d83afbf..1b953d7 100644 --- a/pbr/1.1.lighting/default.vert +++ b/pbr/1.1.lighting/default.vert @@ -1,2 +1,17 @@ #version 330 core -layout(location = 0) + +layout(location = 0) in vec3 position; +layout(location = 2) in vec2 tex_coords; + +out vert_t { + vec2 tex_coords; +} vert; + +uniform mat4 model; + +void main(void) +{ + gl_Position = model * vec4(position, 1.0); + + vert.tex_coords = tex_coords; +} diff --git a/pbr/1.1.lighting/hdr.frag b/pbr/1.1.lighting/hdr.frag new file mode 100644 index 0000000..01650ae --- /dev/null +++ b/pbr/1.1.lighting/hdr.frag @@ -0,0 +1,18 @@ +#version 330 core + +in vert_t { + vec2 tex_coords; +} vert; + +out vec4 frag_color; + +uniform sampler2D colorbuffer; + +void main(void) +{ + const float gamma = 2.2; + vec3 color = vec3(texture(colorbuffer, vert.tex_coords)); + color /= color + vec3(1.0); + color = pow(color, vec3(1.0 / gamma)); + frag_color = vec4(color, 1.0); +} diff --git a/pbr/1.1.lighting/gbuffer.vert b/pbr/1.1.lighting/hdr.vert index c53439c..1b953d7 100644 --- a/pbr/1.1.lighting/gbuffer.vert +++ b/pbr/1.1.lighting/hdr.vert @@ -1,9 +1,17 @@ #version 330 core layout(location = 0) in vec3 position; -layout(location = 1) in vec3 normal; layout(location = 2) in vec2 tex_coords; out vert_t { vec2 tex_coords; } vert; + +uniform mat4 model; + +void main(void) +{ + gl_Position = model * vec4(position, 1.0); + + vert.tex_coords = tex_coords; +} diff --git a/pbr/1.1.lighting/lighting b/pbr/1.1.lighting/lighting Binary files differnew file mode 100755 index 0000000..a68bd2b --- /dev/null +++ b/pbr/1.1.lighting/lighting diff --git a/pbr/1.1.lighting/lighting.c b/pbr/1.1.lighting/lighting.c index 87621ba..d2a941d 100644 --- a/pbr/1.1.lighting/lighting.c +++ b/pbr/1.1.lighting/lighting.c @@ -1,126 +1,155 @@ #include "GL/glew.h" #include "GLFW/glfw3.h" -#include "pwyazh.h" #include "common.h" -typedef struct { - Arena *arena; - Input input; - Camera camera; - Mesh *cube; - GLFWwindow *window; -} state_t; - -static S32 width = 1600; -static S32 height = 800; - -void die(const char *fmt, ...) -{ - fprintf(stderr, "error: "); - va_list args; - va_start(args, fmt); - vprintf(fmt, args); - va_end(args); - fprintf(stderr, "\n"); - exit(1); -} - -void init_glfw(state_t *state) { - glfwSetErrorCallback(error_callback); - if (glfwInit() == GLFW_FALSE) - die("failed to initialize glfw"); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); - state->window = glfwCreateWindow(width, height, "prb lighting", 0, 0); - if (!state->window) - die("failed to create window"); - glfwSetInputMode(state->window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); - glfwMakeContextCurrent(state->window); -} - -void init_gl(void) -{ - if (glewInit() != GLEW_OK) - die("failed to initialize glew"); - glEnable(GL_DEPTH_TEST); -} - -state_t init_state(void) -{ - state_t state = { - .arena = arena_alloc(Kilobytes(256)), - .input = input_init(), - .camera = { - .pos = v3f(0.0f, 2.0f, 5.0f), - .fovx = 90.0f, - .near = 0.1f, - .far = 100.0f, - .yaw = 0.0f, - .pitch = 0.0f - }, - }; - return state; -} - int main(void) { - state_t state = init_state(); + state_t state = init_state(1600, 800); init_glfw(&state); init_gl(); - // meshes - Mesh *cube = mesh_load_obj(state.arena, "../../data/models/cube.obj"); + /* meshes */ + Mesh *sphere = mesh_load_obj(state.arena, "../../data/models/sphere.obj"); + Mesh *quad = mesh_gen_quad(state.arena); + + /* shaders */ + add_shader("default.vert", "default.frag", 0); + add_shader("pbr.vert", "pbr.frag", 0); + add_shader("hdr.vert", "hdr.frag", 0); + + light_t lights[4] = { + {{-6.0f, 0.0f, 7.0f}, {300.0f, 300.0f, 300.0f}}, + {{-4.0f, 0.0f, 6.0f}, {300.0f, 300.0f, 300.0f}}, + {{ 1.0f, 0.0f, 5.0f}, {300.0f, 300.0f, 300.0f}}, + {{ 3.0f, 0.0f, 8.0f}, {300.0f, 300.0f, 300.0f}}, + }; - // shaders - U32 shader = load_shader("default.vert", "default.frag"); + /* framebuffers */ + U32 hdr_fbo, hdr_colorbuffer, hdr_rbo; + glGenFramebuffers(1, &hdr_fbo); + glGenTextures(1, &hdr_colorbuffer); + glBindTexture(GL_TEXTURE_2D, hdr_colorbuffer); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, state.width, state.height, 0, GL_RGBA, GL_FLOAT, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glGenRenderbuffers(1, &hdr_rbo); + glBindRenderbuffer(GL_RENDERBUFFER, hdr_rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, state.width, state.height); + glBindFramebuffer(GL_FRAMEBUFFER, hdr_fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, hdr_colorbuffer, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, hdr_rbo); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + die("failed to create framebuffer"); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + F32 time = 0; + + S32 show_gamma = 0; - F64 last_time = glfwGetTime(); while (!glfwWindowShouldClose(state.window)) { - F64 time = glfwGetTime(); - F32 dt = time - last_time; - last_time = time; - - input_update_last_state(&state.input); - - glfwPollEvents(); - process_glfw_keyboard(state.window, &state.input); - process_glfw_mouse_pos(state.window, &state.input); - - if (key_first_press(state.input.exit)) - glfwSetWindowShouldClose(state.window, GLFW_TRUE); - - F32 speed = 2.0f; - V3F dv = get_dv_camera_first_person(&state.input, &state.camera, speed, dt); - V3F camera_dp = v3f_add(camera_dp, dv); - camera_dp = v3f_scalef(camera_dp, 0.8f); - state.camera.pos = v3f_add(state.camera.pos, camera_dp); - - F32 sensitivity = 0.1f; - state.input.mouse_offset = v2f_scalef(state.input.mouse_offset, sensitivity); - state.camera.yaw += state.input.mouse_offset.x; - state.camera.pitch += state.input.mouse_offset.y; - if (state.camera.pitch > 89.0f) - state.camera.pitch = 89.0f; - if (state.camera.pitch < -89.0f) - state.camera.pitch = -89.0f; - - // update - - // render - F32 ar = (F32)width / (F32)height; + handle_glfw_events(state.window, &state.input); + F32 dt = lock_framerate(60); + fps_info(dt, 10); + + /* update */ + update_camera_first_person(&state.camera, &state.input, dt, 2.0f); + F32 limit = 4.0f; + for (U32 i = 0; i < (sizeof(lights) / sizeof(lights[0])); i++) + lights[i].position.y = sinf(time + i) * limit; + time += dt; + + if (key_first_press(state.input.jump)) + show_gamma = 1 - show_gamma; + + /* render */ + F32 ar = 0; + if (show_gamma) { + ar = (F32)state.width / 2.0f / (F32)state.height; + } else { + ar = (F32)state.width / (F32)state.height; + } MAT4 projection = camera_persp(state.camera, ar); MAT4 view = get_view_matrix(&state.camera); - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClearColor(0.16f, 0.16f, 0.16f, 1.0f); + + glBindFramebuffer(GL_FRAMEBUFFER, hdr_fbo); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + U32 shader = find_shader("pbr"); + if (!shader) die("wha"); glUseProgram(shader); - mesh_draw(cube); + shader_set_mat4fv(shader, "projection", projection); + shader_set_mat4fv(shader, "view", view); + shader_set_3fv(shader, "camera", state.camera.pos); + shader_set_3fv(shader, "color", v3f(0.04, 0.04, 0.04)); + shader_set_1f(shader, "ao", 1.0f); + for (U32 i = 0; i < (sizeof(lights) / sizeof(lights[0])); i++) { + char str[512]; + snprintf(str, 512, "lights[%d].position", i); + shader_set_3fv(shader, str, lights[i].position); + snprintf(str, 512, "lights[%d].color", i); + shader_set_3fv(shader, str, lights[i].color); + } + S32 rows = 7; + S32 cols = 7; + F32 offset = 3.0f; + for (S32 row = 0; row < rows; row++) { + shader_set_1f(shader, "metallic", (F32)row / (F32)rows); + for (S32 col = 0; col < cols; col++) { + shader_set_1f(shader, "roughness", clamp(0.05f, (F32)col / (F32)cols, 1.0f)); + V3F pos = {(col - cols / 2.0f) * offset, (row - rows / 2.0f) * offset, 0.0f}; + MAT4 model = mat4_make_translate(pos); + shader_set_mat4fv(shader, "model", model); + mesh_draw(sphere); + } + } + + for (U32 i = 0; i < (sizeof(lights) / sizeof(lights[0])); i++) { + MAT4 model = mat4_make_scale(v3f(0.5f, 0.5f, 0.5f)); + model = mat4_translate(model, lights[i].position); + shader_set_mat4fv(shader, "model", model); + mesh_draw(sphere); + } + glUseProgram(0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + if (show_gamma) { + shader = find_shader("default"); + if (!shader) die("!"); + glUseProgram(shader); + glBindTexture(GL_TEXTURE_2D, hdr_colorbuffer); + MAT4 model = mat4_make_scale(v3f(0.5f, 1.0f, 1.0f)); + model = mat4_translate(model, v3f(-0.5f, 0.0f, 0.0f)); + shader_set_mat4fv(shader, "model", model); + mesh_draw(quad); + glUseProgram(0); + + shader = find_shader("hdr"); + if (!shader) die("!"); + glUseProgram(shader); + glBindTexture(GL_TEXTURE_2D, hdr_colorbuffer); + model = mat4_make_scale(v3f(0.5f, 1.0f, 1.0f)); + model = mat4_translate(model, v3f(0.5f, 0.0f, 0.0f)); + shader_set_mat4fv(shader, "model", model); + mesh_draw(quad); + glUseProgram(0); + } else { + shader = find_shader("hdr"); + if (!shader) die("!"); + glUseProgram(shader); + glBindTexture(GL_TEXTURE_2D, hdr_colorbuffer); + shader_set_mat4fv(shader, "model", mat4_identity()); + mesh_draw(quad); + glUseProgram(0); + } + glfwSwapBuffers(state.window); } diff --git a/pbr/1.1.lighting/occlusion.frag b/pbr/1.1.lighting/occlusion.frag deleted file mode 100644 index 41a436b..0000000 --- a/pbr/1.1.lighting/occlusion.frag +++ /dev/null @@ -1,14 +0,0 @@ -#version 330 core - -in vert_t { - vec2 tex_coords; -} vert; - -out vec4 frag_color; - -uniform sampler2D colorbuffer; - -void main() -{ - frag_color = vec4(vec3(texture(colorbuffer, vert.tex_coords).r), 1.0); -} diff --git a/pbr/1.1.lighting/pbr.frag b/pbr/1.1.lighting/pbr.frag new file mode 100644 index 0000000..db9edea --- /dev/null +++ b/pbr/1.1.lighting/pbr.frag @@ -0,0 +1,105 @@ +#version 330 core + +in vert_t { + vec3 position; + vec3 normal; +} vert; + +out vec4 frag_color; + +uniform vec3 camera; + +uniform vec3 color; +uniform float metallic; +uniform float roughness; +uniform float ao; + +struct light_t { + vec3 position; + vec3 color; +}; + +const int light_count = 4; +uniform light_t lights[light_count]; + +const float PI = 3.14159265358; + +vec3 fresnel_shlick(float costheta, vec3 f0) +{ + return f0 + (1.0 - f0) * pow(1.0 - costheta, 5.0); +} + +float distribution_ggx(vec3 n, vec3 h, float roughness) +{ + float a = roughness * roughness; + float a2 = a * a; + float ndoth = max(dot(n, h), 0.0); + float ndoth2 = ndoth * ndoth; + + float num = a2; + float denom = (ndoth2 * (a2 - 1.0) + 1.0); + denom = PI * denom * denom; + + return num / denom; +} + +float geometry_shlick_ggx(float ndotv, float roughness) +{ + float r = roughness + 1.0; + float k = r * r / 8.0; + + float num = ndotv; + float denom = ndotv * (1.0 - k) + k; + + return num / denom; +} + +float geometry_smith(vec3 n, vec3 v, vec3 l, float roughness) +{ + float ndotv = max(dot(n, v), 0.0); + float ndotl = max(dot(n, l), 0.0); + float ggx2 = geometry_shlick_ggx(ndotv, roughness); + float ggx1 = geometry_shlick_ggx(ndotl, roughness); + + return ggx1 * ggx2; +} + +void main(void) +{ + vec3 n = vert.normal; + vec3 v = normalize(camera - vert.position); + + vec3 lo = vec3(0.0); + for (int i = 0; i < light_count; i++) { + vec3 l = normalize(lights[i].position - vert.position); + vec3 h = normalize(v + l); + float ndotv = max(dot(n, v), 0.0); + float ndotl = max(dot(n, l), 0.0); + + float distance = length(lights[i].position - vert.position); + float attenuation = 1.0 / (distance * distance); + + vec3 radiance = lights[i].color * attenuation; + + vec3 f0 = vec3(0.04); + f0 = mix(f0, color, metallic); + vec3 f = fresnel_shlick(max(dot(h, v), 0.0), f0); + + float ndf = distribution_ggx(n, h, roughness); + float g = geometry_smith(n, v, l, roughness); + + vec3 numerator = ndf * g * f; + float denominator = 4.0 * ndotv * ndotl; + vec3 specular = numerator / max(denominator, 0.001); + + vec3 ks = f; + vec3 kd = vec3(1.0) - ks; + + kd *= 1.0 - metallic; + + lo += (kd * color / PI + specular) * radiance * ndotl; + } + + vec3 ambient = vec3(0.03) * color * ao; + frag_color = vec4(ambient + lo, 1.0); +} diff --git a/pbr/1.1.lighting/pbr.vert b/pbr/1.1.lighting/pbr.vert new file mode 100644 index 0000000..e6e3800 --- /dev/null +++ b/pbr/1.1.lighting/pbr.vert @@ -0,0 +1,21 @@ +#version 330 core + +layout(location = 0) in vec3 position; +layout(location = 1) in vec3 normal; + +out vert_t { + vec3 position; + vec3 normal; +} vert; + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 model; + +void main(void) +{ + gl_Position = projection * view * model * vec4(position, 1.0); + + vert.position = vec3(model * vec4(position, 1.0)); + vert.normal = normalize(mat3(transpose(inverse(model))) * normal); +} diff --git a/pbr/1.1.lighting/ssao.frag b/pbr/1.1.lighting/ssao.frag deleted file mode 100644 index dd03754..0000000 --- a/pbr/1.1.lighting/ssao.frag +++ /dev/null @@ -1,45 +0,0 @@ -#version 330 core - -in vert_t { - vec2 tex_coords; -} vert; - -out float frag_color; - -uniform sampler2D positions; -uniform sampler2D normals; -uniform sampler2D noise; - -uniform vec3 samples[64]; -uniform mat4 projection; - -const vec2 noise_scale = vec2(1600.0 / 4.0, 900.0 / 4.0); - -void main() -{ - vec3 position = texture(positions, vert.tex_coords).rgb; - vec3 normal = texture(normals, vert.tex_coords).rgb; - vec3 random = texture(noise, vert.tex_coords * noise_scale).rgb; - - vec3 tangent = normalize(random - normal * dot(random, normal)); - vec3 bitangent = cross(normal, tangent); - mat3 TBN = mat3(tangent, bitangent, normal); - - int nsamples = 64; - float radius = 0.5; - float bias = 0.025; - float occlusion = 0.0; - for (int i = 0; i < nsamples; i++) { - vec3 sample = TBN * samples[i]; - sample = position + sample * radius; - vec4 offset = vec4(sample, 1.0); - offset = projection * offset; - offset.xyz /= offset.w; - offset.xyz = offset.xyz * 0.5 + 0.5; - float depth = texture(positions, offset.xy).z; - float range_check = smoothstep(0.0, 1.0, radius / abs(position.z - depth)); - occlusion += (depth >= sample.z + bias ? 1.0 : 0.0) * range_check; - } - occlusion = 1.0 - (occlusion / nsamples); - frag_color = occlusion; -} diff --git a/pbr/1.1.lighting/ssao.vert b/pbr/1.1.lighting/ssao.vert deleted file mode 100644 index eed3be3..0000000 --- a/pbr/1.1.lighting/ssao.vert +++ /dev/null @@ -1,14 +0,0 @@ -#version 330 core - -layout(location = 0) in vec3 position; -layout(location = 2) in vec2 tex_coords; - -out vert_t { - vec2 tex_coords; -} vert; - -void main() -{ - vert.tex_coords = tex_coords; - gl_Position = vec4(position, 1.0); -} diff --git a/pbr/1.2.textured/build.sh b/pbr/1.2.textured/build.sh new file mode 100755 index 0000000..5c5427f --- /dev/null +++ b/pbr/1.2.textured/build.sh @@ -0,0 +1,5 @@ +#!/bin/sh +. ../../config +TARGET='textured' +set -x +gcc -o $TARGET $CFLAGS $INCLUDE $LFLAGS $TARGET.c $LIBS diff --git a/pbr/1.2.textured/hdr.frag b/pbr/1.2.textured/hdr.frag new file mode 100644 index 0000000..01650ae --- /dev/null +++ b/pbr/1.2.textured/hdr.frag @@ -0,0 +1,18 @@ +#version 330 core + +in vert_t { + vec2 tex_coords; +} vert; + +out vec4 frag_color; + +uniform sampler2D colorbuffer; + +void main(void) +{ + const float gamma = 2.2; + vec3 color = vec3(texture(colorbuffer, vert.tex_coords)); + color /= color + vec3(1.0); + color = pow(color, vec3(1.0 / gamma)); + frag_color = vec4(color, 1.0); +} diff --git a/pbr/1.2.textured/hdr.vert b/pbr/1.2.textured/hdr.vert new file mode 100644 index 0000000..1b953d7 --- /dev/null +++ b/pbr/1.2.textured/hdr.vert @@ -0,0 +1,17 @@ +#version 330 core + +layout(location = 0) in vec3 position; +layout(location = 2) in vec2 tex_coords; + +out vert_t { + vec2 tex_coords; +} vert; + +uniform mat4 model; + +void main(void) +{ + gl_Position = model * vec4(position, 1.0); + + vert.tex_coords = tex_coords; +} diff --git a/pbr/1.2.textured/pbr.frag b/pbr/1.2.textured/pbr.frag new file mode 100644 index 0000000..f0aaeca --- /dev/null +++ b/pbr/1.2.textured/pbr.frag @@ -0,0 +1,126 @@ +#version 330 core + +in vert_t { + vec3 position; + vec3 normal; + vec2 tex_coords; +} vert; + +out vec4 frag_color; + +uniform vec3 camera; + +uniform sampler2D color_map; +uniform sampler2D normal_map; +uniform sampler2D metallic_map; +uniform sampler2D roughness_map; +uniform sampler2D ao_map; + +struct light_t { + vec3 position; + vec3 color; +}; + +const int light_count = 4; +uniform light_t lights[light_count]; + +const float PI = 3.14159265358; + +vec3 get_normal_from_map() +{ + vec3 tangent = texture(normal_map, vert.tex_coords).xyz * 2.0 - 1.0; + vec3 q1 = dFdx(vert.position); + vec3 q2 = dFdy(vert.position); + vec2 st1 = dFdx(vert.tex_coords); + vec2 st2 = dFdy(vert.tex_coords); + vec3 n = normalize(vert.normal); + vec3 t = normalize(q1*st2.t - q2*st1.t); + vec3 b = -normalize(cross(n, t)); + mat3 tbn = mat3(t, b, n); + return normalize(tbn * tangent); +} + +vec3 fresnel_shlick(float costheta, vec3 f0) +{ + return f0 + (1.0 - f0) * pow(1.0 - costheta, 5.0); +} + +float distribution_ggx(vec3 n, vec3 h, float roughness) +{ + float a = roughness * roughness; + float a2 = a * a; + float ndoth = max(dot(n, h), 0.0); + float ndoth2 = ndoth * ndoth; + + float num = a2; + float denom = (ndoth2 * (a2 - 1.0) + 1.0); + denom = PI * denom * denom; + + return num / denom; +} + +float geometry_shlick_ggx(float ndotv, float roughness) +{ + float r = roughness + 1.0; + float k = r * r / 8.0; + + float num = ndotv; + float denom = ndotv * (1.0 - k) + k; + + return num / denom; +} + +float geometry_smith(vec3 n, vec3 v, vec3 l, float roughness) +{ + float ndotv = max(dot(n, v), 0.0); + float ndotl = max(dot(n, l), 0.0); + float ggx2 = geometry_shlick_ggx(ndotv, roughness); + float ggx1 = geometry_shlick_ggx(ndotl, roughness); + + return ggx1 * ggx2; +} + +void main(void) +{ + vec3 color = pow(texture(color_map, vert.tex_coords).rgb, vec3(2.2)); + float metallic = texture(metallic_map, vert.tex_coords).r; + float roughness = texture(roughness_map, vert.tex_coords).r; + float ao = texture(ao_map, vert.tex_coords).r; + + vec3 n = get_normal_from_map(); + vec3 v = normalize(camera - vert.position); + + vec3 lo = vec3(0.0); + for (int i = 0; i < light_count; i++) { + vec3 l = normalize(lights[i].position - vert.position); + vec3 h = normalize(v + l); + float ndotv = max(dot(n, v), 0.0); + float ndotl = max(dot(n, l), 0.0); + + float distance = length(lights[i].position - vert.position); + float attenuation = 1.0 / (distance * distance); + + vec3 radiance = lights[i].color * attenuation; + + vec3 f0 = vec3(0.04); + f0 = mix(f0, color, metallic); + vec3 f = fresnel_shlick(max(dot(h, v), 0.0), f0); + + float ndf = distribution_ggx(n, h, roughness); + float g = geometry_smith(n, v, l, roughness); + + vec3 numerator = ndf * g * f; + float denominator = 4.0 * ndotv * ndotl; + vec3 specular = numerator / max(denominator, 0.001); + + vec3 ks = f; + vec3 kd = vec3(1.0) - ks; + + kd *= 1.0 - metallic; + + lo += (kd * color / PI + specular) * radiance * ndotl; + } + + vec3 ambient = vec3(0.03) * color * ao; + frag_color = vec4(ambient + lo, 1.0); +} diff --git a/pbr/1.2.textured/pbr.vert b/pbr/1.2.textured/pbr.vert new file mode 100644 index 0000000..0ff1bdb --- /dev/null +++ b/pbr/1.2.textured/pbr.vert @@ -0,0 +1,24 @@ +#version 330 core + +layout(location = 0) in vec3 position; +layout(location = 1) in vec3 normal; +layout(location = 2) in vec2 tex_coords; + +out vert_t { + vec3 position; + vec3 normal; + vec2 tex_coords; +} vert; + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 model; + +void main(void) +{ + gl_Position = projection * view * model * vec4(position, 1.0); + + vert.position = vec3(model * vec4(position, 1.0)); + vert.normal = normalize(mat3(transpose(inverse(model))) * normal); + vert.tex_coords = tex_coords; +} diff --git a/pbr/1.2.textured/textured b/pbr/1.2.textured/textured Binary files differnew file mode 100755 index 0000000..e339406 --- /dev/null +++ b/pbr/1.2.textured/textured diff --git a/pbr/1.2.textured/textured.c b/pbr/1.2.textured/textured.c new file mode 100644 index 0000000..e8ed096 --- /dev/null +++ b/pbr/1.2.textured/textured.c @@ -0,0 +1,151 @@ +#include "GL/glew.h" +#include "GLFW/glfw3.h" +#include "common.h" + +int main(void) +{ + state_t state = init_state(1600, 800); + + init_glfw(&state); + init_gl(); + + /* meshes */ + Mesh *sphere = mesh_load_obj(state.arena, "../../data/models/sphere.obj"); + Mesh *cube = mesh_load_obj(state.arena, "../../data/models/cube.obj"); + Mesh *quad = mesh_gen_quad(state.arena); + + /* shaders */ + add_shader("pbr.vert", "pbr.frag", 0); + add_shader("hdr.vert", "hdr.frag", 0); + + U32 shader = find_shader("pbr"); + if (!shader) + die("not again"); + glUseProgram(shader); + shader_set_1i(shader, "color_map", 0); + shader_set_1i(shader, "normal_map", 1); + shader_set_1i(shader, "metallic_map", 2); + shader_set_1i(shader, "roughness_map", 3); + shader_set_1i(shader, "ao_map", 4); + glUseProgram(0); + + light_t lights[4] = { + {{-6.0f, 0.0f, 7.0f}, {300.0f, 300.0f, 300.0f}}, + {{-4.0f, 0.0f, 6.0f}, {300.0f, 300.0f, 300.0f}}, + {{ 1.0f, 0.0f, 5.0f}, {300.0f, 300.0f, 300.0f}}, + {{ 3.0f, 0.0f, 8.0f}, {300.0f, 300.0f, 300.0f}}, + }; + + /* textures */ + U32 color_map = load_texture("../../data/textures/pbr/albedo.png"); + U32 normal_map = load_texture("../../data/textures/pbr/normal.png"); + U32 metallic_map = load_texture("../../data/textures/pbr/metallic.png"); + U32 roughness_map = load_texture("../../data/textures/pbr/roughness.png"); + U32 ao_map = load_texture("../../data/textures/pbr/ao.png"); + + /* framebuffers */ + U32 hdr_fbo, hdr_colorbuffer, hdr_rbo; + glGenFramebuffers(1, &hdr_fbo); + glGenTextures(1, &hdr_colorbuffer); + glBindTexture(GL_TEXTURE_2D, hdr_colorbuffer); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, state.width, state.height, 0, GL_RGBA, GL_FLOAT, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glGenRenderbuffers(1, &hdr_rbo); + glBindRenderbuffer(GL_RENDERBUFFER, hdr_rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, state.width, state.height); + glBindFramebuffer(GL_FRAMEBUFFER, hdr_fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, hdr_colorbuffer, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, hdr_rbo); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + die("failed to create framebuffer"); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + F32 time = 0; + + while (!glfwWindowShouldClose(state.window)) { + handle_glfw_events(state.window, &state.input); + F32 dt = lock_framerate(60); + fps_info(dt, 10); + + /* update */ + update_camera_first_person(&state.camera, &state.input, dt, 2.0f); + F32 limit = 4.0f; + for (U32 i = 0; i < (sizeof(lights) / sizeof(lights[0])); i++) + lights[i].position.y = sinf(2.0f * time + i) * limit; + time += dt; + + /* render */ + F32 ar = (F32)state.width / (F32)state.height; + MAT4 projection = camera_persp(state.camera, ar); + MAT4 view = get_view_matrix(&state.camera); + + glClearColor(0.16f, 0.16f, 0.16f, 1.0f); + + glBindFramebuffer(GL_FRAMEBUFFER, hdr_fbo); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + shader = find_shader("pbr"); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, color_map); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, normal_map); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, metallic_map); + glActiveTexture(GL_TEXTURE3); + glBindTexture(GL_TEXTURE_2D, roughness_map); + glActiveTexture(GL_TEXTURE4); + glBindTexture(GL_TEXTURE_2D, ao_map); + + if (!shader) die("wha"); + glUseProgram(shader); + shader_set_mat4fv(shader, "projection", projection); + shader_set_mat4fv(shader, "view", view); + shader_set_3fv(shader, "camera", state.camera.pos); + for (U32 i = 0; i < (sizeof(lights) / sizeof(lights[0])); i++) { + char str[512]; + snprintf(str, 512, "lights[%d].position", i); + shader_set_3fv(shader, str, lights[i].position); + snprintf(str, 512, "lights[%d].color", i); + shader_set_3fv(shader, str, lights[i].color); + } + S32 rows = 7; + S32 cols = 7; + F32 offset = 3.0f; + for (S32 row = 0; row < rows; row++) { + for (S32 col = 0; col < cols; col++) { + V3F pos = {(col - cols / 2.0f) * offset, (row - rows / 2.0f) * offset, 0.0f}; + MAT4 model = mat4_make_translate(pos); + shader_set_mat4fv(shader, "model", model); + mesh_draw(cube); + } + } + + for (U32 i = 0; i < (sizeof(lights) / sizeof(lights[0])); i++) { + MAT4 model = mat4_make_scale(v3f(0.5f, 0.5f, 0.5f)); + model = mat4_translate(model, lights[i].position); + shader_set_mat4fv(shader, "model", model); + mesh_draw(sphere); + } + + glUseProgram(0); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + shader = find_shader("hdr"); + if (!shader) die("!"); + glUseProgram(shader); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, hdr_colorbuffer); + shader_set_mat4fv(shader, "model", mat4_identity()); + mesh_draw(quad); + glUseProgram(0); + + glfwSwapBuffers(state.window); + } + + return 0; +} diff --git a/pbr/2.1.1.ibl_irradiance_conversion/background.frag b/pbr/2.1.1.ibl_irradiance_conversion/background.frag new file mode 100644 index 0000000..e3891eb --- /dev/null +++ b/pbr/2.1.1.ibl_irradiance_conversion/background.frag @@ -0,0 +1,17 @@ +#version 330 core + +in vert_t { + vec3 position; +} vert; + +out vec4 frag_color; + +uniform samplerCube cubemap; + +void main(void) +{ + vec3 color = texture(cubemap, vert.position).rgb; + color /= color + vec3(1.0); + color = pow(color, vec3(1.0 / 2.2)); + frag_color = vec4(color, 1.0); +} diff --git a/pbr/2.1.1.ibl_irradiance_conversion/background.vert b/pbr/2.1.1.ibl_irradiance_conversion/background.vert new file mode 100644 index 0000000..25b9778 --- /dev/null +++ b/pbr/2.1.1.ibl_irradiance_conversion/background.vert @@ -0,0 +1,19 @@ +#version 330 core + +layout(location = 0) in vec3 position; + +out vert_t { + vec3 position; +} vert; + +uniform mat4 projection; +uniform mat4 view; + +void main(void) +{ + vert.position = position; + + mat4 rotate_view = mat4(mat3(view)); + vec4 clip_position = projection * rotate_view * vec4(position, 1.0); + gl_Position = clip_position.xyww; +} diff --git a/pbr/2.1.1.ibl_irradiance_conversion/build.sh b/pbr/2.1.1.ibl_irradiance_conversion/build.sh new file mode 100755 index 0000000..834030a --- /dev/null +++ b/pbr/2.1.1.ibl_irradiance_conversion/build.sh @@ -0,0 +1,5 @@ +#!/bin/sh +. ../../config +TARGET='ibl_irradiance_conversion' +set -x +gcc -o $TARGET $CFLAGS $INCLUDE $LFLAGS $TARGET.c $LIBS diff --git a/pbr/2.1.1.ibl_irradiance_conversion/cubemap.frag b/pbr/2.1.1.ibl_irradiance_conversion/cubemap.frag new file mode 100644 index 0000000..349347a --- /dev/null +++ b/pbr/2.1.1.ibl_irradiance_conversion/cubemap.frag @@ -0,0 +1,25 @@ +#version 330 core + +in vert_t { + vec3 position; +} vert; + +out vec4 frag_color; + +uniform sampler2D map; + +const vec2 inv_atan = vec2(0.1591, 0.3183); +vec2 sample_spherical_map(vec3 v) +{ + vec2 uv = vec2(atan(v.z, v.x), asin(v.y)); + uv *= inv_atan; + uv += 0.5; + return uv; +} + +void main(void) +{ + vec2 uv = sample_spherical_map(normalize(vert.position)); + vec3 color = texture(map, uv).rgb; + frag_color = vec4(color, 1.0); +} diff --git a/pbr/2.1.1.ibl_irradiance_conversion/cubemap.vert b/pbr/2.1.1.ibl_irradiance_conversion/cubemap.vert new file mode 100644 index 0000000..8f8862e --- /dev/null +++ b/pbr/2.1.1.ibl_irradiance_conversion/cubemap.vert @@ -0,0 +1,17 @@ +#version 330 core + +layout(location = 0) in vec3 position; + +out vert_t { + vec3 position; +} vert; + +uniform mat4 projection; +uniform mat4 view; + +void main(void) +{ + gl_Position = projection * view * vec4(position, 1.0); + + vert.position = position; +} diff --git a/pbr/2.1.1.ibl_irradiance_conversion/hdr.frag b/pbr/2.1.1.ibl_irradiance_conversion/hdr.frag new file mode 100644 index 0000000..01650ae --- /dev/null +++ b/pbr/2.1.1.ibl_irradiance_conversion/hdr.frag @@ -0,0 +1,18 @@ +#version 330 core + +in vert_t { + vec2 tex_coords; +} vert; + +out vec4 frag_color; + +uniform sampler2D colorbuffer; + +void main(void) +{ + const float gamma = 2.2; + vec3 color = vec3(texture(colorbuffer, vert.tex_coords)); + color /= color + vec3(1.0); + color = pow(color, vec3(1.0 / gamma)); + frag_color = vec4(color, 1.0); +} diff --git a/pbr/2.1.1.ibl_irradiance_conversion/hdr.vert b/pbr/2.1.1.ibl_irradiance_conversion/hdr.vert new file mode 100644 index 0000000..1b953d7 --- /dev/null +++ b/pbr/2.1.1.ibl_irradiance_conversion/hdr.vert @@ -0,0 +1,17 @@ +#version 330 core + +layout(location = 0) in vec3 position; +layout(location = 2) in vec2 tex_coords; + +out vert_t { + vec2 tex_coords; +} vert; + +uniform mat4 model; + +void main(void) +{ + gl_Position = model * vec4(position, 1.0); + + vert.tex_coords = tex_coords; +} diff --git a/pbr/2.1.1.ibl_irradiance_conversion/ibl_irradiance_conversion b/pbr/2.1.1.ibl_irradiance_conversion/ibl_irradiance_conversion Binary files differnew file mode 100755 index 0000000..cc43012 --- /dev/null +++ b/pbr/2.1.1.ibl_irradiance_conversion/ibl_irradiance_conversion diff --git a/pbr/2.1.1.ibl_irradiance_conversion/ibl_irradiance_conversion.c b/pbr/2.1.1.ibl_irradiance_conversion/ibl_irradiance_conversion.c new file mode 100644 index 0000000..0ffbe7c --- /dev/null +++ b/pbr/2.1.1.ibl_irradiance_conversion/ibl_irradiance_conversion.c @@ -0,0 +1,193 @@ +#include "GL/glew.h" +#include "GLFW/glfw3.h" +#include "common.h" + +int main(void) +{ + state_t state = init_state(1600, 800); + + init_glfw(&state); + init_gl(); + glDepthFunc(GL_LEQUAL); + + /* meshes */ + Mesh *cube = mesh_load_obj(state.arena, "../../data/models/cube.obj"); + Mesh *quad = mesh_gen_quad(state.arena); + + /* shaders */ + add_shader("pbr.vert", "pbr.frag", 0); + add_shader("hdr.vert", "hdr.frag", 0); + add_shader("cubemap.vert", "cubemap.frag", 0); + add_shader("background.vert", "background.frag", 0); + + light_t lights[4] = { + {{-10.0f, 0.0f, 20.0f}, {300.0f, 300.0f, 300.0f}}, + {{-5.0f, 0.0f, 20.0f}, {300.0f, 300.0f, 300.0f}}, + {{ 5.0f, 0.0f, 20.0f}, {300.0f, 300.0f, 300.0f}}, + {{ 10.0f, 0.0f, 20.0f}, {300.0f, 300.0f, 300.0f}}, + }; + + /* textures */ + U32 hdr_texture = load_hdr_texture("../../data/textures/loigerwiesen.hdr"); + + /* framebuffers */ + U32 hdr_fbo, hdr_colorbuffer, hdr_rbo; + glGenFramebuffers(1, &hdr_fbo); + glGenTextures(1, &hdr_colorbuffer); + glBindTexture(GL_TEXTURE_2D, hdr_colorbuffer); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, state.width, state.height, 0, GL_RGBA, GL_FLOAT, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glGenRenderbuffers(1, &hdr_rbo); + glBindRenderbuffer(GL_RENDERBUFFER, hdr_rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, state.width, state.height); + glBindFramebuffer(GL_FRAMEBUFFER, hdr_fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, hdr_colorbuffer, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, hdr_rbo); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + die("failed to create framebuffer"); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + U32 capture_fbo, capture_rbo; + glGenFramebuffers(1, &capture_fbo); + glGenRenderbuffers(1, &capture_rbo); + glBindFramebuffer(GL_FRAMEBUFFER, capture_fbo); + glBindRenderbuffer(GL_RENDERBUFFER, capture_rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 512, 512); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, capture_rbo); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + die("failed to create framebuffer"); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + U32 cubemap; + glGenTextures(1, &cubemap); + glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap); + for (U32 i = 0; i < 6; i++) + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, 512, 512, 0, GL_RGB, GL_FLOAT, 0); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + MAT4 capture_projection = perspective(90.0f, 1.0f, 0.1f, 10.0f); + V3F capture_origin = {0}; + MAT4 capture_views[6] = { + look_at(capture_origin, v3f( 1.0f, 0.0f, 0.0f), v3f(0.0f, -1.0f, 0.0f)), + look_at(capture_origin, v3f(-1.0f, 0.0f, 0.0f), v3f(0.0f, -1.0f, 0.0f)), + look_at(capture_origin, v3f( 0.0f, 1.0f, 0.0f), v3f(0.0f, 0.0f, 1.0f)), + look_at(capture_origin, v3f( 0.0f, -1.0f, 0.0f), v3f(0.0f, 0.0f, -1.0f)), + look_at(capture_origin, v3f( 0.0f, 0.0f, 1.0f), v3f(0.0f, -1.0f, 0.0f)), + look_at(capture_origin, v3f( 0.0f, 0.0f, -1.0f), v3f(0.0f, -1.0f, 0.0f)), + }; + + glBindFramebuffer(GL_FRAMEBUFFER, capture_fbo); + U32 shader = find_shader("cubemap"); + if (!shader) die("waht"); + glUseProgram(shader); + shader_set_mat4fv(shader, "projection", capture_projection); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, hdr_texture); + + glViewport(0, 0, 512, 512); + for (U32 i = 0; i < 6; i++) { + shader_set_mat4fv(shader, "view", capture_views[i]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cubemap, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + mesh_draw(cube); + } + glUseProgram(0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + glViewport(0, 0, state.width, state.height); + + F32 time = 0; + + while (!glfwWindowShouldClose(state.window)) { + handle_glfw_events(state.window, &state.input); + F32 dt = lock_framerate(60); + fps_info(dt, 10); + + /* update */ + update_camera_first_person(&state.camera, &state.input, dt, 2.0f); + F32 limit = 4.0f; + for (U32 i = 0; i < (sizeof(lights) / sizeof(lights[0])); i++) + lights[i].position.y = sinf(time + i) * limit; + time += dt; + + /* render */ + F32 ar = (F32)state.width / (F32)state.height; + MAT4 projection = camera_persp(state.camera, ar); + MAT4 view = get_view_matrix(&state.camera); + + glClearColor(0.16f, 0.16f, 0.16f, 1.0f); + + glBindFramebuffer(GL_FRAMEBUFFER, hdr_fbo); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + shader = find_shader("pbr"); + if (!shader) die("wha"); + glUseProgram(shader); + shader_set_mat4fv(shader, "projection", projection); + shader_set_mat4fv(shader, "view", view); + shader_set_3fv(shader, "camera", state.camera.pos); + shader_set_3fv(shader, "color", v3f(0.04, 0.04, 0.04)); + shader_set_1f(shader, "ao", 1.0f); + for (U32 i = 0; i < (sizeof(lights) / sizeof(lights[0])); i++) { + char str[512]; + snprintf(str, 512, "lights[%d].position", i); + shader_set_3fv(shader, str, lights[i].position); + snprintf(str, 512, "lights[%d].color", i); + shader_set_3fv(shader, str, lights[i].color); + } + S32 rows = 7; + S32 cols = 7; + F32 offset = 3.0f; + for (S32 row = 0; row < rows; row++) { + shader_set_1f(shader, "metallic", (F32)row / (F32)rows); + for (S32 col = 0; col < cols; col++) { + shader_set_1f(shader, "roughness", clamp(0.05f, (F32)col / (F32)cols, 1.0f)); + V3F pos = {(col - cols / 2.0f) * offset, (row - rows / 2.0f) * offset, 0.0f}; + MAT4 model = mat4_make_translate(pos); + shader_set_mat4fv(shader, "model", model); + mesh_draw(cube); + } + } + + for (U32 i = 0; i < (sizeof(lights) / sizeof(lights[0])); i++) { + MAT4 model = mat4_make_scale(v3f(0.5f, 0.5f, 0.5f)); + model = mat4_translate(model, lights[i].position); + shader_set_mat4fv(shader, "model", model); + mesh_draw(cube); + } + + glUseProgram(0); + + shader = find_shader("background"); + glUseProgram(shader); + shader_set_mat4fv(shader, "projection", projection); + shader_set_mat4fv(shader, "view", view); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap); + mesh_draw(cube); + glUseProgram(0); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + shader = find_shader("hdr"); + if (!shader) die("failed to find hdr shader!"); + glUseProgram(shader); + glBindTexture(GL_TEXTURE_2D, hdr_colorbuffer); + shader_set_mat4fv(shader, "model", mat4_identity()); + mesh_draw(quad); + glUseProgram(0); + + glfwSwapBuffers(state.window); + } + + return 0; +} diff --git a/pbr/2.1.1.ibl_irradiance_conversion/pbr.frag b/pbr/2.1.1.ibl_irradiance_conversion/pbr.frag new file mode 100644 index 0000000..db9edea --- /dev/null +++ b/pbr/2.1.1.ibl_irradiance_conversion/pbr.frag @@ -0,0 +1,105 @@ +#version 330 core + +in vert_t { + vec3 position; + vec3 normal; +} vert; + +out vec4 frag_color; + +uniform vec3 camera; + +uniform vec3 color; +uniform float metallic; +uniform float roughness; +uniform float ao; + +struct light_t { + vec3 position; + vec3 color; +}; + +const int light_count = 4; +uniform light_t lights[light_count]; + +const float PI = 3.14159265358; + +vec3 fresnel_shlick(float costheta, vec3 f0) +{ + return f0 + (1.0 - f0) * pow(1.0 - costheta, 5.0); +} + +float distribution_ggx(vec3 n, vec3 h, float roughness) +{ + float a = roughness * roughness; + float a2 = a * a; + float ndoth = max(dot(n, h), 0.0); + float ndoth2 = ndoth * ndoth; + + float num = a2; + float denom = (ndoth2 * (a2 - 1.0) + 1.0); + denom = PI * denom * denom; + + return num / denom; +} + +float geometry_shlick_ggx(float ndotv, float roughness) +{ + float r = roughness + 1.0; + float k = r * r / 8.0; + + float num = ndotv; + float denom = ndotv * (1.0 - k) + k; + + return num / denom; +} + +float geometry_smith(vec3 n, vec3 v, vec3 l, float roughness) +{ + float ndotv = max(dot(n, v), 0.0); + float ndotl = max(dot(n, l), 0.0); + float ggx2 = geometry_shlick_ggx(ndotv, roughness); + float ggx1 = geometry_shlick_ggx(ndotl, roughness); + + return ggx1 * ggx2; +} + +void main(void) +{ + vec3 n = vert.normal; + vec3 v = normalize(camera - vert.position); + + vec3 lo = vec3(0.0); + for (int i = 0; i < light_count; i++) { + vec3 l = normalize(lights[i].position - vert.position); + vec3 h = normalize(v + l); + float ndotv = max(dot(n, v), 0.0); + float ndotl = max(dot(n, l), 0.0); + + float distance = length(lights[i].position - vert.position); + float attenuation = 1.0 / (distance * distance); + + vec3 radiance = lights[i].color * attenuation; + + vec3 f0 = vec3(0.04); + f0 = mix(f0, color, metallic); + vec3 f = fresnel_shlick(max(dot(h, v), 0.0), f0); + + float ndf = distribution_ggx(n, h, roughness); + float g = geometry_smith(n, v, l, roughness); + + vec3 numerator = ndf * g * f; + float denominator = 4.0 * ndotv * ndotl; + vec3 specular = numerator / max(denominator, 0.001); + + vec3 ks = f; + vec3 kd = vec3(1.0) - ks; + + kd *= 1.0 - metallic; + + lo += (kd * color / PI + specular) * radiance * ndotl; + } + + vec3 ambient = vec3(0.03) * color * ao; + frag_color = vec4(ambient + lo, 1.0); +} diff --git a/pbr/2.1.1.ibl_irradiance_conversion/pbr.vert b/pbr/2.1.1.ibl_irradiance_conversion/pbr.vert new file mode 100644 index 0000000..e6e3800 --- /dev/null +++ b/pbr/2.1.1.ibl_irradiance_conversion/pbr.vert @@ -0,0 +1,21 @@ +#version 330 core + +layout(location = 0) in vec3 position; +layout(location = 1) in vec3 normal; + +out vert_t { + vec3 position; + vec3 normal; +} vert; + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 model; + +void main(void) +{ + gl_Position = projection * view * model * vec4(position, 1.0); + + vert.position = vec3(model * vec4(position, 1.0)); + vert.normal = normalize(mat3(transpose(inverse(model))) * normal); +} diff --git a/pbr/2.1.2.ibl_irradiance/background.frag b/pbr/2.1.2.ibl_irradiance/background.frag new file mode 100644 index 0000000..e3891eb --- /dev/null +++ b/pbr/2.1.2.ibl_irradiance/background.frag @@ -0,0 +1,17 @@ +#version 330 core + +in vert_t { + vec3 position; +} vert; + +out vec4 frag_color; + +uniform samplerCube cubemap; + +void main(void) +{ + vec3 color = texture(cubemap, vert.position).rgb; + color /= color + vec3(1.0); + color = pow(color, vec3(1.0 / 2.2)); + frag_color = vec4(color, 1.0); +} diff --git a/pbr/2.1.2.ibl_irradiance/background.vert b/pbr/2.1.2.ibl_irradiance/background.vert new file mode 100644 index 0000000..25b9778 --- /dev/null +++ b/pbr/2.1.2.ibl_irradiance/background.vert @@ -0,0 +1,19 @@ +#version 330 core + +layout(location = 0) in vec3 position; + +out vert_t { + vec3 position; +} vert; + +uniform mat4 projection; +uniform mat4 view; + +void main(void) +{ + vert.position = position; + + mat4 rotate_view = mat4(mat3(view)); + vec4 clip_position = projection * rotate_view * vec4(position, 1.0); + gl_Position = clip_position.xyww; +} diff --git a/pbr/2.1.2.ibl_irradiance/build.sh b/pbr/2.1.2.ibl_irradiance/build.sh new file mode 100755 index 0000000..fdecd76 --- /dev/null +++ b/pbr/2.1.2.ibl_irradiance/build.sh @@ -0,0 +1,5 @@ +#!/bin/sh +. ../../config +TARGET='ibl_irradiance' +set -x +gcc -o $TARGET $CFLAGS $INCLUDE $LFLAGS $TARGET.c $LIBS diff --git a/pbr/2.1.2.ibl_irradiance/convolution.frag b/pbr/2.1.2.ibl_irradiance/convolution.frag new file mode 100644 index 0000000..69ce8af --- /dev/null +++ b/pbr/2.1.2.ibl_irradiance/convolution.frag @@ -0,0 +1,33 @@ +#version 330 core + +in vert_t { + vec3 position; +} vert; + +out vec4 frag_color; + +uniform samplerCube cubemap; + +const float PI = 3.14159265359; + +void main(void) +{ + vec3 normal = normalize(vert.position); + vec3 up = vec3(0.0, 1.0, 0.0); + vec3 right = cross(up, normal); + up = cross(normal, right); + + vec3 irradiance = vec3(0.0); + float delta = 0.025; + int nsamples = 0; + for (float phi = 0.0; phi < 2.0 * PI; phi += delta) { + for (float theta = 0.0; theta < 0.5 * PI; theta += delta) { + vec3 tangent = vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta)); + vec3 v = tangent.x * right + tangent.y * up + tangent.z * normal; + irradiance += texture(cubemap, v).rgb * cos(theta) * sin(theta); + nsamples++; + } + } + irradiance *= PI / nsamples; + frag_color = vec4(irradiance, 1.0); +} diff --git a/pbr/2.1.2.ibl_irradiance/convolution.vert b/pbr/2.1.2.ibl_irradiance/convolution.vert new file mode 100644 index 0000000..8f8862e --- /dev/null +++ b/pbr/2.1.2.ibl_irradiance/convolution.vert @@ -0,0 +1,17 @@ +#version 330 core + +layout(location = 0) in vec3 position; + +out vert_t { + vec3 position; +} vert; + +uniform mat4 projection; +uniform mat4 view; + +void main(void) +{ + gl_Position = projection * view * vec4(position, 1.0); + + vert.position = position; +} diff --git a/pbr/2.1.2.ibl_irradiance/cubemap.frag b/pbr/2.1.2.ibl_irradiance/cubemap.frag new file mode 100644 index 0000000..349347a --- /dev/null +++ b/pbr/2.1.2.ibl_irradiance/cubemap.frag @@ -0,0 +1,25 @@ +#version 330 core + +in vert_t { + vec3 position; +} vert; + +out vec4 frag_color; + +uniform sampler2D map; + +const vec2 inv_atan = vec2(0.1591, 0.3183); +vec2 sample_spherical_map(vec3 v) +{ + vec2 uv = vec2(atan(v.z, v.x), asin(v.y)); + uv *= inv_atan; + uv += 0.5; + return uv; +} + +void main(void) +{ + vec2 uv = sample_spherical_map(normalize(vert.position)); + vec3 color = texture(map, uv).rgb; + frag_color = vec4(color, 1.0); +} diff --git a/pbr/2.1.2.ibl_irradiance/cubemap.vert b/pbr/2.1.2.ibl_irradiance/cubemap.vert new file mode 100644 index 0000000..8f8862e --- /dev/null +++ b/pbr/2.1.2.ibl_irradiance/cubemap.vert @@ -0,0 +1,17 @@ +#version 330 core + +layout(location = 0) in vec3 position; + +out vert_t { + vec3 position; +} vert; + +uniform mat4 projection; +uniform mat4 view; + +void main(void) +{ + gl_Position = projection * view * vec4(position, 1.0); + + vert.position = position; +} diff --git a/pbr/2.1.2.ibl_irradiance/hdr.frag b/pbr/2.1.2.ibl_irradiance/hdr.frag new file mode 100644 index 0000000..01650ae --- /dev/null +++ b/pbr/2.1.2.ibl_irradiance/hdr.frag @@ -0,0 +1,18 @@ +#version 330 core + +in vert_t { + vec2 tex_coords; +} vert; + +out vec4 frag_color; + +uniform sampler2D colorbuffer; + +void main(void) +{ + const float gamma = 2.2; + vec3 color = vec3(texture(colorbuffer, vert.tex_coords)); + color /= color + vec3(1.0); + color = pow(color, vec3(1.0 / gamma)); + frag_color = vec4(color, 1.0); +} diff --git a/pbr/2.1.2.ibl_irradiance/hdr.vert b/pbr/2.1.2.ibl_irradiance/hdr.vert new file mode 100644 index 0000000..1b953d7 --- /dev/null +++ b/pbr/2.1.2.ibl_irradiance/hdr.vert @@ -0,0 +1,17 @@ +#version 330 core + +layout(location = 0) in vec3 position; +layout(location = 2) in vec2 tex_coords; + +out vert_t { + vec2 tex_coords; +} vert; + +uniform mat4 model; + +void main(void) +{ + gl_Position = model * vec4(position, 1.0); + + vert.tex_coords = tex_coords; +} diff --git a/pbr/2.1.2.ibl_irradiance/ibl_irradiance b/pbr/2.1.2.ibl_irradiance/ibl_irradiance Binary files differnew file mode 100755 index 0000000..4561ae2 --- /dev/null +++ b/pbr/2.1.2.ibl_irradiance/ibl_irradiance diff --git a/pbr/2.1.2.ibl_irradiance/ibl_irradiance.c b/pbr/2.1.2.ibl_irradiance/ibl_irradiance.c new file mode 100644 index 0000000..e6f1390 --- /dev/null +++ b/pbr/2.1.2.ibl_irradiance/ibl_irradiance.c @@ -0,0 +1,230 @@ +#include "GL/glew.h" +#include "GLFW/glfw3.h" +#include "common.h" + +int main(void) +{ + state_t state = init_state(1600, 800); + + init_glfw(&state); + init_gl(); + glDepthFunc(GL_LEQUAL); + + /* meshes */ + Mesh *cube = mesh_load_obj(state.arena, "../../data/models/cube.obj"); + Mesh *sphere = mesh_load_obj(state.arena, "../../data/models/sphere.obj"); + Mesh *quad = mesh_gen_quad(state.arena); + + /* shaders */ + add_shader("pbr.vert", "pbr.frag", 0); + add_shader("hdr.vert", "hdr.frag", 0); + add_shader("cubemap.vert", "cubemap.frag", 0); + add_shader("background.vert", "background.frag", 0); + add_shader("convolution.vert", "convolution.frag", 0); + + light_t lights[4] = { + {{-10.0f, 0.0f, 20.0f}, {300.0f, 300.0f, 300.0f}}, + {{-5.0f, 0.0f, 20.0f}, {300.0f, 300.0f, 300.0f}}, + {{ 5.0f, 0.0f, 20.0f}, {300.0f, 300.0f, 300.0f}}, + {{ 10.0f, 0.0f, 20.0f}, {300.0f, 300.0f, 300.0f}}, + }; + + /* textures */ + U32 hdr_texture = load_hdr_texture("../../data/textures/loigerwiesen.hdr");; + + /* framebuffers */ + U32 hdr_fbo, hdr_colorbuffer, hdr_rbo; + glGenFramebuffers(1, &hdr_fbo); + glGenTextures(1, &hdr_colorbuffer); + glBindTexture(GL_TEXTURE_2D, hdr_colorbuffer); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, state.width, state.height, 0, GL_RGBA, GL_FLOAT, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glGenRenderbuffers(1, &hdr_rbo); + glBindRenderbuffer(GL_RENDERBUFFER, hdr_rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, state.width, state.height); + glBindFramebuffer(GL_FRAMEBUFFER, hdr_fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, hdr_colorbuffer, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, hdr_rbo); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + die("failed to create framebuffer"); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + U32 capture_fbo, capture_rbo; + glGenFramebuffers(1, &capture_fbo); + glGenRenderbuffers(1, &capture_rbo); + glBindFramebuffer(GL_FRAMEBUFFER, capture_fbo); + glBindRenderbuffer(GL_RENDERBUFFER, capture_rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 512, 512); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, capture_rbo); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + die("failed to create framebuffer"); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + U32 cubemap; + glGenTextures(1, &cubemap); + glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap); + for (U32 i = 0; i < 6; i++) + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, 512, 512, 0, GL_RGB, GL_FLOAT, 0); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + MAT4 capture_projection = perspective(90.0f, 1.0f, 0.1f, 10.0f); + V3F capture_origin = {0}; + MAT4 capture_views[6] = { + look_at(capture_origin, v3f( 1.0f, 0.0f, 0.0f), v3f(0.0f, -1.0f, 0.0f)), + look_at(capture_origin, v3f(-1.0f, 0.0f, 0.0f), v3f(0.0f, -1.0f, 0.0f)), + look_at(capture_origin, v3f( 0.0f, 1.0f, 0.0f), v3f(0.0f, 0.0f, 1.0f)), + look_at(capture_origin, v3f( 0.0f, -1.0f, 0.0f), v3f(0.0f, 0.0f, -1.0f)), + look_at(capture_origin, v3f( 0.0f, 0.0f, 1.0f), v3f(0.0f, -1.0f, 0.0f)), + look_at(capture_origin, v3f( 0.0f, 0.0f, -1.0f), v3f(0.0f, -1.0f, 0.0f)), + }; + + glBindFramebuffer(GL_FRAMEBUFFER, capture_fbo); + U32 shader = find_shader("cubemap"); + if (!shader) die("waht"); + glUseProgram(shader); + shader_set_mat4fv(shader, "projection", capture_projection); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, hdr_texture); + + glViewport(0, 0, 512, 512); + for (U32 i = 0; i < 6; i++) { + shader_set_mat4fv(shader, "view", capture_views[i]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cubemap, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + mesh_draw(cube); + } + glUseProgram(0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + U32 irradiance_map; + glGenTextures(1, &irradiance_map); + glBindTexture(GL_TEXTURE_CUBE_MAP, irradiance_map); + for (U32 i = 0; i < 6; i++) + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, 32, 32, 0, GL_RGB, GL_FLOAT, 0); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + + glBindFramebuffer(GL_FRAMEBUFFER, capture_fbo); + glBindRenderbuffer(GL_RENDERBUFFER, capture_rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 32, 32); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + glBindFramebuffer(GL_FRAMEBUFFER, capture_fbo); + shader = find_shader("convolution"); + glUseProgram(shader); + shader_set_mat4fv(shader, "projection", capture_projection); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap); + glViewport(0, 0, 32, 32); + for (U32 i = 0; i < 6; i++) { + shader_set_mat4fv(shader, "view", capture_views[i]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, irradiance_map, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + mesh_draw(cube); + } + glUseProgram(0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + glViewport(0, 0, state.width, state.height); + + F32 time = 0; + + while (!glfwWindowShouldClose(state.window)) { + handle_glfw_events(state.window, &state.input); + F32 dt = lock_framerate(60); + fps_info(dt, 10); + + /* update */ + update_camera_first_person(&state.camera, &state.input, dt, 2.0f); + F32 limit = 4.0f; + for (U32 i = 0; i < (sizeof(lights) / sizeof(lights[0])); i++) + lights[i].position.y = sinf(time + i) * limit; + time += dt; + + /* render */ + F32 ar = (F32)state.width / (F32)state.height; + MAT4 projection = camera_persp(state.camera, ar); + MAT4 view = get_view_matrix(&state.camera); + + glClearColor(0.16f, 0.16f, 0.16f, 1.0f); + + glBindFramebuffer(GL_FRAMEBUFFER, hdr_fbo); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + shader = find_shader("pbr"); + glUseProgram(shader); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, irradiance_map); + shader_set_mat4fv(shader, "projection", projection); + shader_set_mat4fv(shader, "view", view); + shader_set_3fv(shader, "camera", state.camera.pos); + shader_set_3fv(shader, "color", v3f(0.04, 0.04, 0.04)); + shader_set_1f(shader, "ao", 1.0f); + for (U32 i = 0; i < (sizeof(lights) / sizeof(lights[0])); i++) { + char str[512]; + snprintf(str, 512, "lights[%d].position", i); + shader_set_3fv(shader, str, lights[i].position); + snprintf(str, 512, "lights[%d].color", i); + shader_set_3fv(shader, str, lights[i].color); + } + S32 rows = 7; + S32 cols = 7; + F32 offset = 3.0f; + for (S32 row = 0; row < rows; row++) { + shader_set_1f(shader, "metallic", (F32)row / (F32)rows); + for (S32 col = 0; col < cols; col++) { + shader_set_1f(shader, "roughness", clamp(0.05f, (F32)col / (F32)cols, 1.0f)); + V3F pos = {(col - cols / 2.0f) * offset, (row - rows / 2.0f) * offset, 0.0f}; + MAT4 model = mat4_make_translate(pos); + shader_set_mat4fv(shader, "model", model); + mesh_draw(sphere); + } + } + + for (U32 i = 0; i < (sizeof(lights) / sizeof(lights[0])); i++) { + MAT4 model = mat4_make_scale(v3f(0.5f, 0.5f, 0.5f)); + model = mat4_translate(model, lights[i].position); + shader_set_mat4fv(shader, "model", model); + mesh_draw(sphere); + } + + glUseProgram(0); + + shader = find_shader("background"); + glUseProgram(shader); + shader_set_mat4fv(shader, "projection", projection); + shader_set_mat4fv(shader, "view", view); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, irradiance_map); + mesh_draw(cube); + glUseProgram(0); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + shader = find_shader("hdr"); + if (!shader) die("failed to find hdr shader!"); + glUseProgram(shader); + glBindTexture(GL_TEXTURE_2D, hdr_colorbuffer); + shader_set_mat4fv(shader, "model", mat4_identity()); + mesh_draw(quad); + glUseProgram(0); + + glfwSwapBuffers(state.window); + } + + return 0; +} diff --git a/pbr/2.1.2.ibl_irradiance/pbr.frag b/pbr/2.1.2.ibl_irradiance/pbr.frag new file mode 100644 index 0000000..88cf01d --- /dev/null +++ b/pbr/2.1.2.ibl_irradiance/pbr.frag @@ -0,0 +1,114 @@ +#version 330 core + +in vert_t { + vec3 position; + vec3 normal; +} vert; + +out vec4 frag_color; + +uniform vec3 camera; + +uniform vec3 color; +uniform float metallic; +uniform float roughness; +uniform float ao; + +uniform samplerCube irradiance_map; + +struct light_t { + vec3 position; + vec3 color; +}; + +const int light_count = 4; +uniform light_t lights[light_count]; + +const float PI = 3.14159265358; + +vec3 fresnel_shlick(float costheta, vec3 f0) +{ + return f0 + (1.0 - f0) * pow(1.0 - costheta, 5.0); +} + +float distribution_ggx(vec3 n, vec3 h, float roughness) +{ + float a = roughness * roughness; + float a2 = a * a; + float ndoth = max(dot(n, h), 0.0); + float ndoth2 = ndoth * ndoth; + + float num = a2; + float denom = (ndoth2 * (a2 - 1.0) + 1.0); + denom = PI * denom * denom; + + return num / denom; +} + +float geometry_shlick_ggx(float ndotv, float roughness) +{ + float r = roughness + 1.0; + float k = r * r / 8.0; + + float num = ndotv; + float denom = ndotv * (1.0 - k) + k; + + return num / denom; +} + +float geometry_smith(vec3 n, vec3 v, vec3 l, float roughness) +{ + float ndotv = max(dot(n, v), 0.0); + float ndotl = max(dot(n, l), 0.0); + float ggx2 = geometry_shlick_ggx(ndotv, roughness); + float ggx1 = geometry_shlick_ggx(ndotl, roughness); + + return ggx1 * ggx2; +} + +void main(void) +{ + vec3 n = vert.normal; + vec3 v = normalize(camera - vert.position); + + vec3 f0 = vec3(0.04); + f0 = mix(f0, color, metallic); + + vec3 lo = vec3(0.0); + for (int i = 0; i < light_count; i++) { + vec3 l = normalize(lights[i].position - vert.position); + vec3 h = normalize(v + l); + float ndotv = max(dot(n, v), 0.0); + float ndotl = max(dot(n, l), 0.0); + + float distance = length(lights[i].position - vert.position); + float attenuation = 1.0 / (distance * distance); + + vec3 radiance = lights[i].color * attenuation; + + vec3 f = fresnel_shlick(max(dot(h, v), 0.0), f0); + + float ndf = distribution_ggx(n, h, roughness); + float g = geometry_smith(n, v, l, roughness); + + vec3 numerator = ndf * g * f; + float denominator = 4.0 * ndotv * ndotl; + vec3 specular = numerator / max(denominator, 0.001); + + vec3 ks = f; + vec3 kd = vec3(1.0) - ks; + + kd *= 1.0 - metallic; + + lo += (kd * color / PI + specular) * radiance * ndotl; + } + + vec3 ks = fresnel_shlick(max(dot(n, v), 0.0), f0); + vec3 kd = 1.0 - ks; + kd *= 1.0 - metallic; + vec3 irradiance = texture(irradiance_map, n).rgb; + vec3 diffuse = irradiance * color; + vec3 ambient = kd * diffuse * ao; + + frag_color = vec4(ambient + lo, 1.0); +} diff --git a/pbr/2.1.2.ibl_irradiance/pbr.vert b/pbr/2.1.2.ibl_irradiance/pbr.vert new file mode 100644 index 0000000..e6e3800 --- /dev/null +++ b/pbr/2.1.2.ibl_irradiance/pbr.vert @@ -0,0 +1,21 @@ +#version 330 core + +layout(location = 0) in vec3 position; +layout(location = 1) in vec3 normal; + +out vert_t { + vec3 position; + vec3 normal; +} vert; + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 model; + +void main(void) +{ + gl_Position = projection * view * model * vec4(position, 1.0); + + vert.position = vec3(model * vec4(position, 1.0)); + vert.normal = normalize(mat3(transpose(inverse(model))) * normal); +} |