polyadvent

A game engine from scratch in C
git clone git://jb55.com/polyadvent
Log | Files | Refs | README

commit 2383374a49b5d0ddd8f9cd4431960274905627c0
parent 9f66964e39c327a807f748b41dc36dedb465b1fa
Author: William Casarin <jb55@jb55.com>
Date:   Mon, 12 Nov 2018 13:33:57 -0800

initial pbr

Diffstat:
Metc/shaders/lighting.glsl | 74+++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Metc/shaders/test.f.glsl | 12++++++------
Msrc/game.c | 4++--
3 files changed, 63 insertions(+), 27 deletions(-)

diff --git a/etc/shaders/lighting.glsl b/etc/shaders/lighting.glsl @@ -14,7 +14,7 @@ // return clouds; // } -const float pi = 3.14159265; +const float PI = 3.14159265; vec3 gamma_correct(vec3 color) { return pow(color, vec3(1.0/2.2)); @@ -62,13 +62,13 @@ vec3 standard_light(vec3 color, vec4 position, vec4 normal) { float spec; bool blinn = true; if (blinn) { - const float energy_conservation = ( 8.0 + shiny ) / ( 8.0 * pi ); + const float energy_conservation = ( 8.0 + shiny ) / ( 8.0 * PI ); vec3 halfway_dir = normalize(light_dir + view_dir); // blinn-phong spec = energy_conservation * pow(max(dot(normal.xyz, halfway_dir), 0.0), shiny); } else { - const float energy_conservation = ( 2.0 + shiny ) / ( 2.0 * pi ); + const float energy_conservation = ( 2.0 + shiny ) / ( 2.0 * PI ); vec3 reflect_dir = reflect(-light_dir, normal.xyz); // phong spec = energy_conservation * pow(max(dot(view_dir, reflect_dir), 0.0), shiny); } @@ -85,34 +85,37 @@ vec3 standard_light(vec3 color, vec4 position, vec4 normal) { return final; } -float dist_ggx(vec3 N, vec3 H, float a) +float dist_ggx(vec3 N, vec3 H, float roughness) { - float a2 = a*a; + float a = roughness * roughness; float NdotH = max(dot(N, H), 0.0); float NdotH2 = NdotH*NdotH; - float nom = a2; - float denom = (NdotH2 * (a2 - 1.0) + 1.0); - denom = pi * denom * denom; + float num = a; + float denom = (NdotH2 * (a - 1.0) + 1.0); + denom = PI * denom * denom; - return nom / denom; + return num / denom; } -float geom_schlick_ggx(float NdotV, float k) +float geom_schlick_ggx(float NdotV, float roughness) { - float nom = NdotV; + float r = (roughness + 1.0); + float k = (r*r) / 8.0; + + float num = NdotV; float denom = NdotV * (1.0 - k) + k; - return nom / denom; + return num / denom; } -float geom_smith(vec3 N, vec3 V, vec3 L, float k) +float geom_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 ggx1 = geom_schlick_ggx(NdotV, k); - float ggx2 = geom_schlick_ggx(NdotL, k); + float ggx1 = geom_schlick_ggx(NdotV, roughness); + float ggx2 = geom_schlick_ggx(NdotL, roughness); return ggx1 * ggx2; } @@ -122,15 +125,48 @@ vec3 fresnel_schlick(float cos_theta, vec3 F0) return F0 + (1.0 - F0) * pow(1.0 - cos_theta, 5.0); } -vec3 pbr(vec3 V, vec3 normal) { - const float albedo = 0.2; +vec3 pbr(vec3 albedo, vec3 V, vec3 normal) { + const float ao = 2.0; const float metallic = 0.8; + const float roughness = 0.2; + vec3 radiance = sun_color * light_intensity; vec3 N = normalize(normal); vec3 L = normalize(light_dir); vec3 H = normalize(V + L); + // non-metallic is always 0.04 vec3 F0 = vec3(0.04); + // while we do vary F0 based on the metalness of a surface by linearly + // interpolating between the original F0 and the albedo value given the + // metallic property F0 = mix(F0, albedo, metallic); - float cos_theta = max(dot(H, V)) - vec3 F = fresnel_schlick(cos_theta, ) + float HdotV = max(0.0, dot(H, V)); + vec3 F = fresnel_schlick(HdotV, F0); + float NDF = dist_ggx(N, H, roughness); + float G = geom_smith(N, V, L, roughness); + + float NdotL = max(0.0, dot(N, L)); + + // Cook-Torrance BRDF + vec3 numerator = NDF * G * F; + float denominator = 4.0 * max(0.0, dot(N, V)) * NdotL; + vec3 specular = numerator / max(denominator, 0.001); + + // kS = energy of light that gets reflected + // kD = remaining energy that gets refracted + vec3 kS = F; + vec3 kD = vec3(1.0) - kS; + kD *= 1.0 - metallic; + + // because metallic surfaces don't refract light and thus have no diffuse + // reflections we enforce this property by nullifying kD if the surface is + // metallic + + // Lo = outgoing radiance + // the result of the reflectance equation's integral ∫ over Ω + vec3 Lo = (kD * albedo / PI + specular) * radiance * NdotL; + + vec3 ambient = vec3(0.03) * albedo * ao; + vec3 color = ambient + Lo; + return color; } diff --git a/etc/shaders/test.f.glsl b/etc/shaders/test.f.glsl @@ -37,14 +37,14 @@ void main() { vec4 v4_normal = vec4(vertex.normal, 1.0); // vec3 color = standard_light(vertex.color, v4_pos, v4_normal); - vec3 color = pbr(normalize(V)); + vec3 color = pbr(vertex.color, normalize(V), vertex.normal); - // if (fog_on) { - // vec3 fog = apply_fog(color, length(v_ray), camera_position, v_ray); - // color = fog; - // } + if (fog_on) { + vec3 fog = apply_fog(color, length(V), camera_position, V); + color = fog; + } - // color *= shadow_strength(v4_pos, v4_normal, vertex.shadow_coord); + color *= shadow_strength(v4_pos, v4_normal, vertex.shadow_coord); // vec3 color = reflect_env(vertex.position); frag_color = vec4(gamma_correct(color), 1.0); diff --git a/src/game.c b/src/game.c @@ -75,7 +75,7 @@ void game_init(struct game *game, int width, int height) { check_gl(); init_terrain(terrain, size); - get_entity(&terrain->entity_id)->flags |= ENT_INVISIBLE; + /* get_entity(&terrain->entity_id)->flags |= ENT_INVISIBLE; */ create_skybox(&res->skybox, &res->programs[SKYBOX_PROGRAM]); /* node_translate(&res->skybox.node, V3(-100.0, -100.0, 0.0)); */ @@ -119,7 +119,7 @@ void game_init(struct game *game, int width, int height) { res->camera.coords.azimuth = -quat_yaw(player->node.orientation) - RAD(90.0); res->camera.coords.inclination = RAD(60); - res->camera.coords.radius = 5.0; + res->camera.coords.radius = 100.0; struct entity *tower = new_entity(NULL); ok = load_model(&tower->model, "tower");