diff options
| author | 3gg <3gg@shellblade.net> | 2023-02-11 17:56:56 -0800 | 
|---|---|---|
| committer | 3gg <3gg@shellblade.net> | 2023-02-11 17:56:56 -0800 | 
| commit | a58dbb9081a08b01931badd953ed1fe6cd5bd1c7 (patch) | |
| tree | a335076f364bdd28d341c3ef2dac318bd0d447a6 | |
| parent | 53582d2a90fb72e02056612163239b3b3f62eb4a (diff) | |
Fix IBL cubemap coordinate system and put position-normal-tangent in world space in CookTorrance.
| -rw-r--r-- | gfx/shaders/cook_torrance.frag | 10 | ||||
| -rw-r--r-- | gfx/shaders/cook_torrance.vert | 12 | ||||
| -rw-r--r-- | gfx/shaders/cubemap_filtering.vert | 27 | ||||
| -rw-r--r-- | gfx/shaders/irradiance_map.frag | 9 | ||||
| -rw-r--r-- | gfx/shaders/skyquad.frag | 2 | ||||
| -rw-r--r-- | gfx/shaders/skyquad.vert | 17 | ||||
| -rw-r--r-- | gfx/src/renderer/renderer.c | 10 | ||||
| -rw-r--r-- | gfx/src/util/ibl.c | 23 | ||||
| -rw-r--r-- | gfx/src/util/texture.c | 42 | ||||
| -rw-r--r-- | gltfview/src/game.c | 15 | 
10 files changed, 127 insertions, 40 deletions
| diff --git a/gfx/shaders/cook_torrance.frag b/gfx/shaders/cook_torrance.frag index 1adb4ae..1975491 100644 --- a/gfx/shaders/cook_torrance.frag +++ b/gfx/shaders/cook_torrance.frag | |||
| @@ -30,7 +30,7 @@ uniform float MaxReflectionLOD; | |||
| 30 | 30 | ||
| 31 | uniform vec3 CameraPosition; // World space. | 31 | uniform vec3 CameraPosition; // World space. | 
| 32 | 32 | ||
| 33 | // World-space position and normal. | 33 | // World-space position, normal and tangent. | 
| 34 | in vec3 Position; | 34 | in vec3 Position; | 
| 35 | #ifdef HAS_NORMALS | 35 | #ifdef HAS_NORMALS | 
| 36 | in vec3 Normal; | 36 | in vec3 Normal; | 
| @@ -53,13 +53,14 @@ layout (location = 0) out vec4 Colour; | |||
| 53 | /// |normalMapSample| is the normal map sample, not necessarily normalized. | 53 | /// |normalMapSample| is the normal map sample, not necessarily normalized. | 
| 54 | /// | 54 | /// | 
| 55 | /// TODO: Move to "normal.h" | 55 | /// TODO: Move to "normal.h" | 
| 56 | #if defined(HAS_NORMAL_MAP) && (defined(HAS_TANGENTS) || defined(HAS_TEXCOORDS)) | ||
| 56 | vec3 get_ws_normal(vec3 normalWs, vec3 normalMapSample) { | 57 | vec3 get_ws_normal(vec3 normalWs, vec3 normalMapSample) { | 
| 57 | vec3 N = normalize(Normal); | 58 | vec3 N = normalize(Normal); | 
| 58 | #ifdef HAS_TANGENTS | 59 | #ifdef HAS_TANGENTS | 
| 59 | //vec3 T = normalize(tangent.xyz - dot(tangent.xyz, N) * N); | 60 | //vec3 T = normalize(tangent.xyz - dot(tangent.xyz, N) * N); | 
| 60 | vec3 T = Tangent.xyz; | 61 | vec3 T = Tangent.xyz; | 
| 61 | vec3 B = Tangent.w * cross(N, T); | 62 | vec3 B = Tangent.w * cross(N, T); | 
| 62 | #else // No tangents | 63 | #elif HAS_TEXCOORDS // No tangents, but must have texcoords. | 
| 63 | vec3 pos_dx = dFdx(Position); | 64 | vec3 pos_dx = dFdx(Position); | 
| 64 | vec3 pos_dy = dFdy(Position); | 65 | vec3 pos_dy = dFdy(Position); | 
| 65 | // vec3 uv_dx = vec3(dFdx(Texcoord), 0.0); | 66 | // vec3 uv_dx = vec3(dFdx(Texcoord), 0.0); | 
| @@ -71,7 +72,7 @@ vec3 get_ws_normal(vec3 normalWs, vec3 normalMapSample) { | |||
| 71 | // vec3 T = pos_dx * uv_dy.t - pos_dy * uv_dx.t; | 72 | // vec3 T = pos_dx * uv_dy.t - pos_dy * uv_dx.t; | 
| 72 | T = normalize(T - dot(T, N) * N); | 73 | T = normalize(T - dot(T, N) * N); | 
| 73 | vec3 B = normalize(cross(N, T)); | 74 | vec3 B = normalize(cross(N, T)); | 
| 74 | #endif // HAS_TANGENTS | 75 | #endif | 
| 75 | 76 | ||
| 76 | if (gl_FrontFacing == false) { | 77 | if (gl_FrontFacing == false) { | 
| 77 | T = -T; | 78 | T = -T; | 
| @@ -83,6 +84,7 @@ vec3 get_ws_normal(vec3 normalWs, vec3 normalMapSample) { | |||
| 83 | //return normalize(s.x * T + s.y * B + s.z * N); | 84 | //return normalize(s.x * T + s.y * B + s.z * N); | 
| 84 | return normalize(mat3(T,B,N) * s); | 85 | return normalize(mat3(T,B,N) * s); | 
| 85 | } | 86 | } | 
| 87 | #endif // HAS_TANGENTS || HAS_TEXCOORDS | ||
| 86 | 88 | ||
| 87 | float trowbridge_reitz_GGX(float roughness, float NdotH) { | 89 | float trowbridge_reitz_GGX(float roughness, float NdotH) { | 
| 88 | float a = roughness * roughness; | 90 | float a = roughness * roughness; | 
| @@ -188,7 +190,7 @@ void main() | |||
| 188 | #else | 190 | #else | 
| 189 | float occlusion = 1.0; | 191 | float occlusion = 1.0; | 
| 190 | #endif | 192 | #endif | 
| 191 | float metallic = metal_roughness.x; | 193 | float metallic = metal_roughness.x; | 
| 192 | float roughness = metal_roughness.y; | 194 | float roughness = metal_roughness.y; | 
| 193 | 195 | ||
| 194 | // For a single light direction: | 196 | // For a single light direction: | 
| diff --git a/gfx/shaders/cook_torrance.vert b/gfx/shaders/cook_torrance.vert index 697bb0c..5f126c0 100644 --- a/gfx/shaders/cook_torrance.vert +++ b/gfx/shaders/cook_torrance.vert | |||
| @@ -1,7 +1,8 @@ | |||
| 1 | precision highp float; | 1 | precision highp float; | 
| 2 | 2 | ||
| 3 | uniform mat4 ModelMatrix; | 3 | uniform mat4 ModelMatrix; | 
| 4 | uniform mat4 Modelview; | 4 | // uniform mat4 Modelview; | 
| 5 | uniform mat4 View; | ||
| 5 | uniform mat4 Projection; | 6 | uniform mat4 Projection; | 
| 6 | //uniform mat4 MVP; | 7 | //uniform mat4 MVP; | 
| 7 | #ifdef HAS_JOINTS | 8 | #ifdef HAS_JOINTS | 
| @@ -34,7 +35,7 @@ layout (location = 4) in uvec4 vJoint; | |||
| 34 | layout (location = 5) in vec4 vWeight; | 35 | layout (location = 5) in vec4 vWeight; | 
| 35 | #endif | 36 | #endif | 
| 36 | 37 | ||
| 37 | // World-space position and normal. | 38 | // World-space position, normal and tangent. | 
| 38 | out vec3 Position; | 39 | out vec3 Position; | 
| 39 | #ifdef HAS_NORMALS | 40 | #ifdef HAS_NORMALS | 
| 40 | out vec3 Normal; | 41 | out vec3 Normal; | 
| @@ -54,9 +55,9 @@ void main() | |||
| 54 | vWeight.y * JointMatrices[vJoint.y] + | 55 | vWeight.y * JointMatrices[vJoint.y] + | 
| 55 | vWeight.z * JointMatrices[vJoint.z] + | 56 | vWeight.z * JointMatrices[vJoint.z] + | 
| 56 | vWeight.w * JointMatrices[vJoint.w]; | 57 | vWeight.w * JointMatrices[vJoint.w]; | 
| 57 | Position = vec3(Modelview * skinMatrix * vec4(vPosition, 1.0)); | 58 | Position = vec3(ModelMatrix * skinMatrix * vec4(vPosition, 1.0)); | 
| 58 | #else | 59 | #else | 
| 59 | Position = vec3(Modelview * vec4(vPosition, 1.0)); | 60 | Position = vec3(ModelMatrix * vec4(vPosition, 1.0)); | 
| 60 | #endif | 61 | #endif | 
| 61 | #ifdef HAS_NORMALS | 62 | #ifdef HAS_NORMALS | 
| 62 | Normal = mat3(ModelMatrix) * vNormal; | 63 | Normal = mat3(ModelMatrix) * vNormal; | 
| @@ -68,6 +69,7 @@ void main() | |||
| 68 | #ifdef HAS_TEXCOORDS | 69 | #ifdef HAS_TEXCOORDS | 
| 69 | Texcoord = vTexcoord; | 70 | Texcoord = vTexcoord; | 
| 70 | #endif | 71 | #endif | 
| 71 | gl_Position = Projection * vec4(Position, 1.0); | 72 | gl_Position = Projection * View * vec4(Position, 1.0); | 
| 73 | //gl_Position = Projection * vec4(Position, 1.0); | ||
| 72 | //gl_Position = MVP * vec4(vPosition, 1.0); | 74 | //gl_Position = MVP * vec4(vPosition, 1.0); | 
| 73 | } | 75 | } | 
| diff --git a/gfx/shaders/cubemap_filtering.vert b/gfx/shaders/cubemap_filtering.vert index cdc291c..d0cf73f 100644 --- a/gfx/shaders/cubemap_filtering.vert +++ b/gfx/shaders/cubemap_filtering.vert | |||
| @@ -3,32 +3,37 @@ precision highp float; | |||
| 3 | #define PI 3.1415926535897932384626433832795 | 3 | #define PI 3.1415926535897932384626433832795 | 
| 4 | #define FOVY (90.0 * PI / 180.0) | 4 | #define FOVY (90.0 * PI / 180.0) | 
| 5 | 5 | ||
| 6 | uniform mat4 Modelview; | 6 | uniform mat4 CameraRotation; // From camera space to world space. | 
| 7 | uniform float Flip; | ||
| 7 | 8 | ||
| 8 | layout (location = 0) in vec2 vPosition; | 9 | layout (location = 0) in vec2 vPosition; | 
| 9 | 10 | ||
| 10 | out vec3 Ray; | 11 | out vec3 Ray; | 
| 11 | 12 | ||
| 13 | // DEBUG | ||
| 14 | // out vec2 Texcoord; | ||
| 15 | |||
| 12 | // This is very similar to the skyquad vertex shader. | 16 | // This is very similar to the skyquad vertex shader. | 
| 13 | // | 17 | // | 
| 14 | // The ray is not normalized because it isn't necessary for cubemap sampling. | 18 | // The ray is not normalized because it isn't necessary for cubemap sampling. | 
| 15 | // | 19 | // | 
| 16 | // We also use a fixed fovy = 90 degrees because we want the frustum to pass | 20 | // We also use a fixed fovy = 90 degrees because we want the frustum to pass | 
| 17 | // exactly through each face of the cube. The aspect ratio is also just 1. | 21 | // exactly through each face of the cube. The aspect ratio is also just 1. | 
| 18 | // | 22 | vec3 sky_ray(vec2 FilmPosition) | 
| 19 | // |d| here appears with a positive sign in the returned vector because the | ||
| 20 | // rotation part of the Modelview matrix is responsible for orienting the ray | ||
| 21 | // appropriately. | ||
| 22 | vec3 sky_ray(vec2 Texcoord) | ||
| 23 | { | 23 | { | 
| 24 | float d = 0.5 / tan(FOVY/2.0); | 24 | float d = 0.5 / tan(FOVY/2.0); | 
| 25 | return vec3(Texcoord.x - 0.5, Texcoord.y - 0.5, d); | 25 | return vec3(FilmPosition, -d); | 
| 26 | } | 26 | } | 
| 27 | 27 | ||
| 28 | void main() | 28 | void main() | 
| 29 | { | 29 | { | 
| 30 | mat3 Rotation = mat3(Modelview); | 30 | vec2 FilmPosition = vPosition * 0.5; // map [-1,1] -> [-1/2, +1/2] | 
| 31 | Ray = Rotation * sky_ray(vPosition*0.5 + 0.5); // map [-1,1] -> [0,1] | 31 | FilmPosition *= Flip; | 
| 32 | // Should disable depth test when rendering. | 32 | Ray = mat3(CameraRotation) * sky_ray(FilmPosition); | 
| 33 | gl_Position = vec4(vPosition.xy, 0.99999, 1.0); | 33 | // TODO: Should disable depth test when rendering. | 
| 34 | gl_Position = vec4(vPosition, 0.99999, 1.0); // z=1 -> send to background | ||
| 35 | |||
| 36 | // DEBUG | ||
| 37 | // Texcoord = FilmPosition + 0.5; | ||
| 38 | // Texcoord.y = 1.0 - Texcoord.y; | ||
| 34 | } | 39 | } | 
| diff --git a/gfx/shaders/irradiance_map.frag b/gfx/shaders/irradiance_map.frag index 3cfe5b2..8200e73 100644 --- a/gfx/shaders/irradiance_map.frag +++ b/gfx/shaders/irradiance_map.frag | |||
| @@ -13,6 +13,9 @@ uniform samplerCube Sky; | |||
| 13 | 13 | ||
| 14 | in vec3 Ray; | 14 | in vec3 Ray; | 
| 15 | 15 | ||
| 16 | // DEBUG | ||
| 17 | // in vec2 Texcoord; | ||
| 18 | |||
| 16 | layout (location = 0) out vec4 Color; | 19 | layout (location = 0) out vec4 Color; | 
| 17 | 20 | ||
| 18 | void main() | 21 | void main() | 
| @@ -29,8 +32,8 @@ void main() | |||
| 29 | // T | 32 | // T | 
| 30 | vec3 N = normalize(Ray); | 33 | vec3 N = normalize(Ray); | 
| 31 | vec3 B = (abs(N.x) - 1.0 <= EPS) ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); | 34 | vec3 B = (abs(N.x) - 1.0 <= EPS) ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); | 
| 32 | vec3 T = cross(B, N); | 35 | vec3 T = normalize(cross(B, N)); | 
| 33 | B = cross(N, T); | 36 | B = normalize(cross(N, T)); | 
| 34 | 37 | ||
| 35 | int num_samples = 0; | 38 | int num_samples = 0; | 
| 36 | vec3 irradiance = vec3(0.0); | 39 | vec3 irradiance = vec3(0.0); | 
| @@ -54,6 +57,8 @@ void main() | |||
| 54 | irradiance = PI * irradiance / float(num_samples); | 57 | irradiance = PI * irradiance / float(num_samples); | 
| 55 | 58 | ||
| 56 | // For debugging in trace. | 59 | // For debugging in trace. | 
| 60 | //irradiance = texture(Sky, Ray).rgb; | ||
| 61 | // irradiance = vec3(Texcoord, 0.0); | ||
| 57 | //irradiance = pow(irradiance, vec3(1.0/2.2)); | 62 | //irradiance = pow(irradiance, vec3(1.0/2.2)); | 
| 58 | 63 | ||
| 59 | Color = vec4(irradiance, 1.0); | 64 | Color = vec4(irradiance, 1.0); | 
| diff --git a/gfx/shaders/skyquad.frag b/gfx/shaders/skyquad.frag index 39af013..9b44bfd 100644 --- a/gfx/shaders/skyquad.frag +++ b/gfx/shaders/skyquad.frag | |||
| @@ -8,4 +8,6 @@ void main() | |||
| 8 | { | 8 | { | 
| 9 | vec3 R = normalize(Ray); | 9 | vec3 R = normalize(Ray); | 
| 10 | Colour = vec4(pow(texture(Skyquad, R).rgb, vec3(1.0/2.2)), 1.0); | 10 | Colour = vec4(pow(texture(Skyquad, R).rgb, vec3(1.0/2.2)), 1.0); | 
| 11 | // Debug. | ||
| 12 | //Colour = vec4(pow(R*0.5 + 0.5, vec3(1.0 / 2.2)), 1.0); | ||
| 11 | } | 13 | } | 
| diff --git a/gfx/shaders/skyquad.vert b/gfx/shaders/skyquad.vert index f0658d6..c0c46e6 100644 --- a/gfx/shaders/skyquad.vert +++ b/gfx/shaders/skyquad.vert | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | uniform mat4 Modelview; | 1 | uniform mat4 CameraRotation; // From camera space to world space. | 
| 2 | uniform float Fovy; | 2 | uniform float Fovy; | 
| 3 | uniform float Aspect; | 3 | uniform float Aspect; | 
| 4 | 4 | ||
| @@ -8,21 +8,20 @@ out vec3 Ray; | |||
| 8 | 8 | ||
| 9 | // We would typically normalize the vector, but there is no need to do so when | 9 | // We would typically normalize the vector, but there is no need to do so when | 
| 10 | // sampling a cube map. | 10 | // sampling a cube map. | 
| 11 | vec3 sky_ray(vec2 Texcoord) | 11 | vec3 sky_ray(vec2 FilmPosition) | 
| 12 | { | 12 | { | 
| 13 | //float d = 0.5 / tan(Fovy/2.0); | 13 | //float d = 0.5 / tan(Fovy/2.0); | 
| 14 | // return vec3((Texcoord.x - 0.5) * Aspect, | 14 | // return vec3((FilmPosition.x - 0.5) * Aspect, | 
| 15 | // Texcoord.y - 0.5, | 15 | // FilmPosition.y - 0.5, | 
| 16 | // -d); | 16 | // -d); | 
| 17 | float d = 0.5 / tan(Fovy/2.0); | 17 | float d = 0.5 / tan(Fovy/2.0); | 
| 18 | return vec3((Texcoord.x - 0.5), | 18 | return vec3(FilmPosition, -d); | 
| 19 | Texcoord.y - 0.5, | ||
| 20 | -d); | ||
| 21 | } | 19 | } | 
| 22 | 20 | ||
| 23 | void main() | 21 | void main() | 
| 24 | { | 22 | { | 
| 25 | mat3 Rotation = mat3(Modelview); | 23 | vec2 FilmPosition = Position * 0.5; // map [-1,1] -> [-1/2, +1/2] | 
| 26 | Ray = Rotation * sky_ray(Position*0.5 + 0.5); // map [-1,1] -> [0,1] | 24 | Ray = mat3(CameraRotation) * sky_ray(FilmPosition); | 
| 25 | // Set z to the background. | ||
| 27 | gl_Position = vec4(Position, 0.99999, 1.0); // z=1 -> send to background | 26 | gl_Position = vec4(Position, 0.99999, 1.0); // z=1 -> send to background | 
| 28 | } | 27 | } | 
| diff --git a/gfx/src/renderer/renderer.c b/gfx/src/renderer/renderer.c index 47f1344..b0bef33 100644 --- a/gfx/src/renderer/renderer.c +++ b/gfx/src/renderer/renderer.c | |||
| @@ -114,6 +114,7 @@ typedef struct RenderState { | |||
| 114 | Renderer* renderer; | 114 | Renderer* renderer; | 
| 115 | const Scene* scene; | 115 | const Scene* scene; | 
| 116 | const Camera* camera; | 116 | const Camera* camera; | 
| 117 | const mat4* camera_rotation; // From camera to world space, rotation only. | ||
| 117 | const mat4* view_matrix; | 118 | const mat4* view_matrix; | 
| 118 | const mat4* projection; | 119 | const mat4* projection; | 
| 119 | Light* environment_light; | 120 | Light* environment_light; | 
| @@ -167,6 +168,8 @@ static void draw_recursively( | |||
| 167 | const SceneObject* object = mem_get_object(node->object); | 168 | const SceneObject* object = mem_get_object(node->object); | 
| 168 | assert(object); | 169 | assert(object); | 
| 169 | 170 | ||
| 171 | // TODO: Avoid computing matrices like Modelview or MVP if the shader does | ||
| 172 | // not use them. | ||
| 170 | const mat4 model_matrix = mat4_mul(node_transform, object->transform); | 173 | const mat4 model_matrix = mat4_mul(node_transform, object->transform); | 
| 171 | const mat4 modelview = mat4_mul(*state->view_matrix, model_matrix); | 174 | const mat4 modelview = mat4_mul(*state->view_matrix, model_matrix); | 
| 172 | const mat4 mvp = mat4_mul(*state->projection, modelview); | 175 | const mat4 mvp = mat4_mul(*state->projection, modelview); | 
| @@ -187,13 +190,13 @@ static void draw_recursively( | |||
| 187 | assert(mesh->geometry); | 190 | assert(mesh->geometry); | 
| 188 | assert(mesh->material); | 191 | assert(mesh->material); | 
| 189 | // Apply common shader uniforms not captured by materials. | 192 | // Apply common shader uniforms not captured by materials. | 
| 190 | // TODO: Avoid computing matrices like Modelview or MVP if the shader does | ||
| 191 | // not use them. | ||
| 192 | ShaderProgram* shader = mesh->shader; | 193 | ShaderProgram* shader = mesh->shader; | 
| 193 | gfx_set_mat4_uniform(shader, "ModelMatrix", &model_matrix); | 194 | gfx_set_mat4_uniform(shader, "ModelMatrix", &model_matrix); | 
| 194 | gfx_set_mat4_uniform(shader, "Modelview", &modelview); | 195 | gfx_set_mat4_uniform(shader, "Modelview", &modelview); | 
| 196 | gfx_set_mat4_uniform(shader, "View", state->view_matrix); | ||
| 195 | gfx_set_mat4_uniform(shader, "Projection", state->projection); | 197 | gfx_set_mat4_uniform(shader, "Projection", state->projection); | 
| 196 | gfx_set_mat4_uniform(shader, "MVP", &mvp); | 198 | gfx_set_mat4_uniform(shader, "MVP", &mvp); | 
| 199 | gfx_set_mat4_uniform(shader, "CameraRotation", state->camera_rotation); | ||
| 197 | gfx_set_float_uniform(shader, "Fovy", state->fovy); | 200 | gfx_set_float_uniform(shader, "Fovy", state->fovy); | 
| 198 | gfx_set_float_uniform(shader, "Aspect", state->aspect); | 201 | gfx_set_float_uniform(shader, "Aspect", state->aspect); | 
| 199 | gfx_set_vec3_uniform(shader, "CameraPosition", state->camera->spatial.p); | 202 | gfx_set_vec3_uniform(shader, "CameraPosition", state->camera->spatial.p); | 
| @@ -247,6 +250,8 @@ void gfx_render_scene( | |||
| 247 | } | 250 | } | 
| 248 | 251 | ||
| 249 | const mat4 projection = camera ? camera->camera.projection : mat4_id(); | 252 | const mat4 projection = camera ? camera->camera.projection : mat4_id(); | 
| 253 | const mat4 camera_rotation = | ||
| 254 | mat4_rotation(spatial3_transform(&camera->camera.spatial)); | ||
| 250 | const mat4 view_matrix = | 255 | const mat4 view_matrix = | 
| 251 | camera ? spatial3_inverse_transform(&camera->camera.spatial) : mat4_id(); | 256 | camera ? spatial3_inverse_transform(&camera->camera.spatial) : mat4_id(); | 
| 252 | 257 | ||
| @@ -259,6 +264,7 @@ void gfx_render_scene( | |||
| 259 | .renderer = renderer, | 264 | .renderer = renderer, | 
| 260 | .scene = scene, | 265 | .scene = scene, | 
| 261 | .camera = &camera->camera, | 266 | .camera = &camera->camera, | 
| 267 | .camera_rotation = &camera_rotation, | ||
| 262 | .view_matrix = &view_matrix, | 268 | .view_matrix = &view_matrix, | 
| 263 | .projection = &projection, | 269 | .projection = &projection, | 
| 264 | .environment_light = 0, | 270 | .environment_light = 0, | 
| diff --git a/gfx/src/util/ibl.c b/gfx/src/util/ibl.c index c9bef43..6b7465c 100644 --- a/gfx/src/util/ibl.c +++ b/gfx/src/util/ibl.c | |||
| @@ -24,7 +24,16 @@ static const CubemapFace faces[6] = { | |||
| 24 | CubemapFacePosY, // Up. | 24 | CubemapFacePosY, // Up. | 
| 25 | CubemapFaceNegY, // Down. | 25 | CubemapFaceNegY, // Down. | 
| 26 | CubemapFacePosZ, // Back. | 26 | CubemapFacePosZ, // Back. | 
| 27 | CubemapFaceNegZ, // Forward. | 27 | CubemapFaceNegZ, // Front. | 
| 28 | }; | ||
| 29 | |||
| 30 | static const float flips[6] = { | ||
| 31 | -1.0f, // Right. | ||
| 32 | -1.0f, // Left. | ||
| 33 | +1.0f, // Up. | ||
| 34 | +1.0f, // Down. | ||
| 35 | -1.0f, // Back. | ||
| 36 | -1.0f, // Front. | ||
| 28 | }; | 37 | }; | 
| 29 | 38 | ||
| 30 | IBL* gfx_make_ibl(RenderBackend* render_backend) { | 39 | IBL* gfx_make_ibl(RenderBackend* render_backend) { | 
| @@ -70,6 +79,9 @@ IBL* gfx_make_ibl(RenderBackend* render_backend) { | |||
| 70 | goto cleanup; | 79 | goto cleanup; | 
| 71 | } | 80 | } | 
| 72 | 81 | ||
| 82 | // TODO: Debug the camera rotations. Irradiance debug output should appear | ||
| 83 | // just like the input cubemap. | ||
| 84 | |||
| 73 | // Right. | 85 | // Right. | 
| 74 | ibl->rotations[0] = mat4_lookat( | 86 | ibl->rotations[0] = mat4_lookat( | 
| 75 | /*position=*/vec3_make(0, 0, 0), | 87 | /*position=*/vec3_make(0, 0, 0), | 
| @@ -95,7 +107,7 @@ IBL* gfx_make_ibl(RenderBackend* render_backend) { | |||
| 95 | /*position=*/vec3_make(0, 0, 0), | 107 | /*position=*/vec3_make(0, 0, 0), | 
| 96 | /*target=*/vec3_make(0, 0, 1), | 108 | /*target=*/vec3_make(0, 0, 1), | 
| 97 | /*up=*/vec3_make(0, 1, 0)); | 109 | /*up=*/vec3_make(0, 1, 0)); | 
| 98 | // Forward. | 110 | // Front. | 
| 99 | ibl->rotations[5] = mat4_lookat( | 111 | ibl->rotations[5] = mat4_lookat( | 
| 100 | /*position=*/vec3_make(0, 0, 0), | 112 | /*position=*/vec3_make(0, 0, 0), | 
| 101 | /*target=*/vec3_make(0, 0, -1), | 113 | /*target=*/vec3_make(0, 0, -1), | 
| @@ -226,8 +238,9 @@ Texture* gfx_make_irradiance_map( | |||
| 226 | .cubemap.texture = irradiance_map})) { | 238 | .cubemap.texture = irradiance_map})) { | 
| 227 | goto cleanup; | 239 | goto cleanup; | 
| 228 | } | 240 | } | 
| 241 | gfx_set_float_uniform(ibl->irradiance_map_shader, "Flip", flips[i]); | ||
| 229 | gfx_set_mat4_uniform( | 242 | gfx_set_mat4_uniform( | 
| 230 | ibl->irradiance_map_shader, "Modelview", &ibl->rotations[i]); | 243 | ibl->irradiance_map_shader, "CameraRotation", &ibl->rotations[i]); | 
| 231 | gfx_apply_uniforms(ibl->irradiance_map_shader); | 244 | gfx_apply_uniforms(ibl->irradiance_map_shader); | 
| 232 | gfx_render_geometry(ibl->quad); | 245 | gfx_render_geometry(ibl->quad); | 
| 233 | } | 246 | } | 
| @@ -291,8 +304,10 @@ Texture* gfx_make_prefiltered_environment_map( | |||
| 291 | .cubemap.texture = prefiltered_env_map})) { | 304 | .cubemap.texture = prefiltered_env_map})) { | 
| 292 | goto cleanup; | 305 | goto cleanup; | 
| 293 | } | 306 | } | 
| 307 | gfx_set_float_uniform( | ||
| 308 | ibl->prefiltered_environment_map_shader, "Flip", flips[i]); | ||
| 294 | gfx_set_mat4_uniform( | 309 | gfx_set_mat4_uniform( | 
| 295 | ibl->prefiltered_environment_map_shader, "Modelview", | 310 | ibl->prefiltered_environment_map_shader, "CameraRotation", | 
| 296 | &ibl->rotations[i]); | 311 | &ibl->rotations[i]); | 
| 297 | gfx_apply_uniforms(ibl->prefiltered_environment_map_shader); | 312 | gfx_apply_uniforms(ibl->prefiltered_environment_map_shader); | 
| 298 | gfx_render_geometry(ibl->quad); | 313 | gfx_render_geometry(ibl->quad); | 
| diff --git a/gfx/src/util/texture.c b/gfx/src/util/texture.c index 99241f4..0727dae 100644 --- a/gfx/src/util/texture.c +++ b/gfx/src/util/texture.c | |||
| @@ -6,6 +6,42 @@ | |||
| 6 | #define STB_IMAGE_IMPLEMENTATION | 6 | #define STB_IMAGE_IMPLEMENTATION | 
| 7 | #include <stb_image.h> | 7 | #include <stb_image.h> | 
| 8 | 8 | ||
| 9 | #include <assert.h> | ||
| 10 | |||
| 11 | static void flip_horizontally( | ||
| 12 | unsigned char* pixels, int width, int height, int components) { | ||
| 13 | assert(pixels); | ||
| 14 | |||
| 15 | for (int y = 0; y < height; ++y) { | ||
| 16 | for (int x = 0; x < width / 2; ++x) { | ||
| 17 | unsigned char* p1 = &pixels[(y * width + x) * components]; | ||
| 18 | unsigned char* p2 = &pixels[(y * width + (width - x - 1)) * components]; | ||
| 19 | |||
| 20 | for (int c = 0; c < components; ++c) { | ||
| 21 | unsigned char tmp = *p1; | ||
| 22 | *p1 = *p2; | ||
| 23 | *p2 = tmp; | ||
| 24 | p1++; | ||
| 25 | p2++; | ||
| 26 | } | ||
| 27 | } | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | // Note that the cubemap coordinate system uses the one in RenderMan: | ||
| 32 | // | ||
| 33 | // https://www.khronos.org/opengl/wiki/Cubemap_Texture | ||
| 34 | // | ||
| 35 | // This is what happens: | ||
| 36 | // | ||
| 37 | // - Cubemaps follow a left-handed coordinate system. Say, +X is right, +Y is | ||
| 38 | // up, and +Z is forward. | ||
| 39 | // - The texture coordinate system follow's DirectX's, so +V goes down, not up | ||
| 40 | // like it does in OpenGL. | ||
| 41 | // | ||
| 42 | // For this reason, we do X and Y flips when doing cubemap textures so that we | ||
| 43 | // can sample cubemaps as if they were given in the usual OpenGL coordinate | ||
| 44 | // system. | ||
| 9 | Texture* gfx_load_texture( | 45 | Texture* gfx_load_texture( | 
| 10 | RenderBackend* render_backend, const LoadTextureCmd* cmd) { | 46 | RenderBackend* render_backend, const LoadTextureCmd* cmd) { | 
| 11 | assert(render_backend); | 47 | assert(render_backend); | 
| @@ -21,6 +57,7 @@ Texture* gfx_load_texture( | |||
| 21 | switch (cmd->type) { | 57 | switch (cmd->type) { | 
| 22 | case LoadTexture: { | 58 | case LoadTexture: { | 
| 23 | const char* filepath = mstring_cstr(&cmd->data.texture.filepath); | 59 | const char* filepath = mstring_cstr(&cmd->data.texture.filepath); | 
| 60 | stbi_set_flip_vertically_on_load(0); | ||
| 24 | pixels[0] = stbi_load(filepath, &width, &height, &components, 0); | 61 | pixels[0] = stbi_load(filepath, &width, &height, &components, 0); | 
| 25 | if (!pixels[0]) { | 62 | if (!pixels[0]) { | 
| 26 | gfx_set_error("Failed to load texture file: %s", filepath); | 63 | gfx_set_error("Failed to load texture file: %s", filepath); | 
| @@ -29,6 +66,8 @@ Texture* gfx_load_texture( | |||
| 29 | } | 66 | } | 
| 30 | case LoadCubemap: | 67 | case LoadCubemap: | 
| 31 | for (int i = 0; i < 6; ++i) { | 68 | for (int i = 0; i < 6; ++i) { | 
| 69 | // Flip +Y and -Y textures vertically. | ||
| 70 | stbi_set_flip_vertically_on_load(((i == 2) || (i == 3)) ? 1 : 0); | ||
| 32 | const char* filepath = | 71 | const char* filepath = | 
| 33 | mstring_cstr(&cmd->data.cubemap.filepaths.filepath_pos_x + i); | 72 | mstring_cstr(&cmd->data.cubemap.filepaths.filepath_pos_x + i); | 
| 34 | stbi_uc* image_pixels = | 73 | stbi_uc* image_pixels = | 
| @@ -43,6 +82,9 @@ Texture* gfx_load_texture( | |||
| 43 | "components"); | 82 | "components"); | 
| 44 | break; | 83 | break; | 
| 45 | } | 84 | } | 
| 85 | if ((i != 2) && (i != 3)) { | ||
| 86 | flip_horizontally(image_pixels, width, height, components); | ||
| 87 | } | ||
| 46 | pixels[i] = image_pixels; | 88 | pixels[i] = image_pixels; | 
| 47 | old_components = components; | 89 | old_components = components; | 
| 48 | } | 90 | } | 
| diff --git a/gltfview/src/game.c b/gltfview/src/game.c index 1db7cba..f822b08 100644 --- a/gltfview/src/game.c +++ b/gltfview/src/game.c | |||
| @@ -70,8 +70,8 @@ static Texture* load_environment_map(RenderBackend* render_backend) { | |||
| 70 | mstring_make("/assets/skybox/clouds1/clouds1_west.bmp"), | 70 | mstring_make("/assets/skybox/clouds1/clouds1_west.bmp"), | 
| 71 | mstring_make("/assets/skybox/clouds1/clouds1_up.bmp"), | 71 | mstring_make("/assets/skybox/clouds1/clouds1_up.bmp"), | 
| 72 | mstring_make("/assets/skybox/clouds1/clouds1_down.bmp"), | 72 | mstring_make("/assets/skybox/clouds1/clouds1_down.bmp"), | 
| 73 | mstring_make("/assets/skybox/clouds1/clouds1_north.bmp"), | 73 | mstring_make("/assets/skybox/clouds1/clouds1_south.bmp"), | 
| 74 | mstring_make("/assets/skybox/clouds1/clouds1_south.bmp")} | 74 | mstring_make("/assets/skybox/clouds1/clouds1_north.bmp")} | 
| 75 | }); | 75 | }); | 
| 76 | } | 76 | } | 
| 77 | 77 | ||
| @@ -222,7 +222,7 @@ bool game_new(Game* game, int argc, const char** argv) { | |||
| 222 | // false}); | 222 | // false}); | 
| 223 | const bool play_result = gfx_play_animation( | 223 | const bool play_result = gfx_play_animation( | 
| 224 | anima, &(AnimationPlaySettings){.name = "Walk", .loop = true}); | 224 | anima, &(AnimationPlaySettings){.name = "Walk", .loop = true}); | 
| 225 | assert(play_result); | 225 | // assert(play_result); | 
| 226 | 226 | ||
| 227 | return true; | 227 | return true; | 
| 228 | 228 | ||
| @@ -251,6 +251,15 @@ void game_update(Game* game, double t, double dt) { | |||
| 251 | /*radius=*/2.5, | 251 | /*radius=*/2.5, | 
| 252 | /*azimuth=*/t * 0.5, /*zenith=*/0); | 252 | /*azimuth=*/t * 0.5, /*zenith=*/0); | 
| 253 | spatial3_lookat(&camera->spatial, orbit_point); | 253 | spatial3_lookat(&camera->spatial, orbit_point); | 
| 254 | |||
| 255 | // spatial3_set_position(&camera->spatial, vec3_make(0, 0, 2)); | ||
| 256 | // spatial3_lookat(&camera->spatial, vec3_make(0, 0, -1)); | ||
| 257 | |||
| 258 | // spatial3_orbit( | ||
| 259 | // &camera->spatial, vec3_make(0, 0, 0), | ||
| 260 | // /*radius=*/2.5, | ||
| 261 | // /*azimuth=*/t * 0.2, /*zenith=*/0); | ||
| 262 | // spatial3_lookat(&camera->spatial, vec3_make(0, 0, 0)); | ||
| 254 | } | 263 | } | 
| 255 | 264 | ||
| 256 | void game_render(const Game* game) { | 265 | void game_render(const Game* game) { | 
