diff options
-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) { |