diff --git a/.vscode/launch.json b/.vscode/launch.json
index 6a639b7..cba0ece 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -8,7 +8,7 @@
"name": "(gdb) app",
"type": "cppdbg",
"request": "launch",
- "program": "${workspaceFolder}/src/out/dbg/carcockpit",
+ "program": "${workspaceFolder}/src/out/dbg/carcockpit-opengles-xorg",
"args": ["--window", "--res-path=../res"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/src",
diff --git a/src/carcockpit/shaders/shader_pbr.cpp b/src/carcockpit/shaders/shader_pbr.cpp
index 57a1b1a..bc72540 100644
--- a/src/carcockpit/shaders/shader_pbr.cpp
+++ b/src/carcockpit/shaders/shader_pbr.cpp
@@ -30,7 +30,7 @@ along with this program. If not, see .
using namespace ruis::render;
-constexpr ruis::vec4 default_light_position{5.0f, 5.0f, 5.0f, 1.0f};
+constexpr ruis::vec3 default_light_position{5.0f, 5.0f, 5.0f};
constexpr ruis::vec3 default_light_intensity{2.0f, 2.0f, 2.0f};
shader_pbr::shader_pbr() :
@@ -44,10 +44,13 @@ shader_pbr::shader_pbr() :
uniform highp mat4 matrix; // mvp matrix
uniform highp mat4 mat4_mv; // modelview matrix
+
+ // TODO: remove?
uniform highp mat4 mat4_p; // projection matrix
+
uniform highp mat3 mat3_n; // normal matrix (mat3)
- uniform vec4 light_position;
+ uniform vec3 light_position;
uniform vec3 light_intensity;
varying highp vec3 light_dir;
@@ -57,23 +60,27 @@ shader_pbr::shader_pbr() :
void main()
{
// Transform normal and tangent to eye space
- vec3 norm = normalize(mat3_n * a2);
- vec3 tang = normalize(mat3_n * a3);
- vec3 binormal = normalize(mat3_n * a4);
- // Matrix for transformation to tangent space
- mat3 mat3_to_local = mat3( tang.x, binormal.x, norm.x,
- tang.y, binormal.y, norm.y,
- tang.z, binormal.z, norm.z ) ;
- // Get the position in eye coordinates
- vec3 pos = vec3( mat4_mv * a0 );
- // Transform light dir. and view dir. to tangent space
- light_dir = normalize( mat3_to_local * (light_position.xyz - pos) );
- view_dir = mat3_to_local * normalize(-pos);
- // Pass along the texture coordinate
+ vec3 normal = normalize(mat3_n * a2);
+ vec3 tangent = normalize(mat3_n * a3);
+ vec3 bitangent = normalize(mat3_n * a4);
+
+ // matrix for transformation to tangent space
+ mat3 mat3_to_tangent = mat3(
+ tangent.x, bitangent.x, normal.x,
+ tangent.y, bitangent.y, normal.y,
+ tangent.z, bitangent.z, normal.z
+ );
+
+ // get the position in eye coordinates
+ vec3 pos = vec3( mat4_mv * a0 );
+
+ // Transform light direction and view direction to tangent space
+ light_dir = normalize( mat3_to_tangent * (light_position - pos) );
+ view_dir = mat3_to_tangent * normalize(-pos);
+
tc = vec2(a1.x, 1.0 - a1.y);
gl_Position = matrix * a0;
- }
-
+ }
)qwertyuiop",
R"qwertyuiop(
precision highp float;
@@ -87,14 +94,21 @@ shader_pbr::shader_pbr() :
uniform sampler2D texture2; // roughness map tex
uniform samplerCube texture3; // cube map
- uniform vec4 light_position;
+ uniform vec3 light_position;
uniform vec3 light_intensity;
- const vec3 Kd = vec3(0.5, 0.5, 0.5); // Diffuse reflectivity
- const vec3 Ka = vec3(0.1, 0.1, 0.1); // Ambient reflectivity
- const vec3 Ks = vec3(0.7, 0.7, 0.7); // Specular reflectivity
-
- vec3 phong_model( vec3 norm, vec3 diffuse_reflectivity, float ambient_occlusion, float glossiness, float metalness )
+ const vec3 Kd = vec3(0.5, 0.5, 0.5); // Diffuse reflectivity
+ const vec3 Ka = vec3(0.1, 0.1, 0.1); // Ambient reflectivity
+ const vec3 Ks = vec3(0.7, 0.7, 0.7); // Specular reflectivity
+
+ // TODO: is it still called Phong?
+ vec3 phong_model(
+ vec3 norm,
+ vec3 diffuse_reflectivity,
+ float ambient_occlusion,
+ float glossiness,
+ float metalness
+ )
{
vec3 r_env = reflect( view_dir, norm );
vec3 env_refl = textureCube( texture3, r_env).xyz;
@@ -102,9 +116,9 @@ shader_pbr::shader_pbr() :
vec3 r = reflect( -light_dir, norm );
float sDotN = max( dot(light_dir, norm) , 0.0 );
- vec3 ambient = (Ka) * light_intensity;
- vec3 diffuse = max( Kd - metalness, 0.0 ) * sDotN * light_intensity;
- vec3 spec = Ks * pow( max( dot(r, view_dir), 0.0 ), glossiness ) * light_intensity;
+ vec3 ambient = light_intensity * Ka;
+ vec3 diffuse = light_intensity * max( Kd - metalness, 0.0 ) * sDotN;
+ vec3 spec = light_intensity * Ks * pow( max( dot(r, view_dir), 0.0 ), glossiness );
return (( ambient + diffuse ) * diffuse_reflectivity + spec ) + (env_refl * metalness);
}
@@ -114,7 +128,9 @@ shader_pbr::shader_pbr() :
vec3 arm = texture2D( texture2, tc ).xyz;
float gloss = ((1.0 - pow(arm.y, 0.2) ) * 100.0 + 1.0 );
+ // TODO: why multiply by 2 and subtract 1? Add comment.
vec4 normal4 = 2.0 * texture2D( texture1, tc ) - 1.0;
+
vec3 normal = normalize(normal4.xyz);
normal = vec3(normal.x, -normal.y, normal.z);
@@ -129,7 +145,7 @@ shader_pbr::shader_pbr() :
sampler_cube(this->get_uniform("texture3")),
mat4_modelview(this->get_uniform("mat4_mv")),
mat3_normal(this->get_uniform("mat3_n")),
- vec4_light_position(this->get_uniform("light_position")),
+ vec3_light_position(this->get_uniform("light_position")),
vec3_light_intensity(this->get_uniform("light_intensity"))
{}
@@ -137,7 +153,7 @@ void shader_pbr::render(
const ruis::render::vertex_array& va,
const r4::matrix4& mvp,
const r4::matrix4& modelview,
- const r4::matrix4& projection,
+ const r4::matrix4& projection, // TODO: remove?
const ruis::render::texture_2d& tex_color,
const ruis::render::texture_2d& tex_normal,
const ruis::render::texture_2d& tex_roughness,
@@ -169,7 +185,7 @@ void shader_pbr::render(
normal.invert();
normal.transpose();
- this->set_uniform4f(this->vec4_light_position, light_pos[0], light_pos[1], light_pos[2], light_pos[3]);
+ this->set_uniform3f(this->vec3_light_position, light_pos[0], light_pos[1], light_pos[2]);
this->set_uniform3f(this->vec3_light_intensity, light_int[0], light_int[1], light_int[2]);
this->set_uniform_matrix4f(this->mat4_modelview, modelview);
this->set_uniform_matrix3f(mat3_normal, normal);
diff --git a/src/carcockpit/shaders/shader_pbr.hpp b/src/carcockpit/shaders/shader_pbr.hpp
index 2a572bb..5a6b3ca 100644
--- a/src/carcockpit/shaders/shader_pbr.hpp
+++ b/src/carcockpit/shaders/shader_pbr.hpp
@@ -49,10 +49,11 @@ class shader_pbr : public ruis::render::opengles::shader_base
GLint mat4_modelview;
GLint mat3_normal;
- GLint vec4_light_position;
+ GLint vec3_light_position;
GLint vec3_light_intensity;
shader_pbr();
+
void render(
const ruis::render::vertex_array& va,
const r4::matrix4& mvp,
diff --git a/src/carcockpit/shaders/shader_phong.cpp b/src/carcockpit/shaders/shader_phong.cpp
index e7a736e..bacc557 100644
--- a/src/carcockpit/shaders/shader_phong.cpp
+++ b/src/carcockpit/shaders/shader_phong.cpp
@@ -50,8 +50,8 @@ shader_phong::shader_phong() :
void main(void)
{
tc0 = vec2(a1.x, 1.0 - a1.y);
- norm = normalize( mat3_n * a2 );
- pos = vec3( mat4_mv * a0 );
+ norm = normalize( mat3_n * a2 );
+ pos = vec3( mat4_mv * a0 );
gl_Position = matrix * a0;
}
)qwertyuiop",
diff --git a/src/ruis/render/scene/gltf_loader.cpp b/src/ruis/render/scene/gltf_loader.cpp
index 69c8a03..80675d3 100644
--- a/src/ruis/render/scene/gltf_loader.cpp
+++ b/src/ruis/render/scene/gltf_loader.cpp
@@ -630,10 +630,10 @@ utki::shared_ref gltf_loader::create_vao_with_tangen
std::vector bitangents; // texture y-axis
tangents.resize(num_vertices);
- std::fill(tangents.begin(), tangents.end(), ruis::vec3(0, 0, 0));
+ std::ranges::fill(tangents, ruis::vec3(0, 0, 0));
bitangents.resize(num_vertices);
- std::fill(bitangents.begin(), bitangents.end(), ruis::vec3(0, 0, 0));
+ std::ranges::fill(bitangents, ruis::vec3(0, 0, 0));
// Calculate the vertex tangents and bitangents.
for (uint32_t i = 0; i < num_triangles; ++i) {
@@ -652,30 +652,51 @@ utki::shared_ref gltf_loader::create_vao_with_tangen
auto edge1 = p1 - p0;
auto edge2 = p2 - p0;
- auto tex_edge_1 = t1 - t0;
- auto tex_edge_2 = t2 - t0;
-
- // Calculate the triangle face tangent and bitangent.
-
- auto det = tex_edge_1.cross(tex_edge_2);
-
- constexpr auto epsilon = ruis::real(1e-6f);
+ auto tex_edge1 = t1 - t0;
+ auto tex_edge2 = t2 - t0;
+
+ // Calculate the triangle tangent and bitangent.
+ //
+ // We want to map vectors (1, 0) and (0, 1) from texture space to 3d space (to tangent and bitangent vectors).
+ //
+ // vec2 te1, te2 : triangle edges in texture space
+ // vec3 e1, e2 : triangle edges in 3d space
+ //
+ // Vectors (1, 0) and (0, 1) as a linear combination of te1 and te2:
+ //
+ // (1, 0) = te1 * a11 + te2 * a21
+ // (0, 1) = te1 * a12 + te2 * a22
+ //
+ // We need to solve these equations for a11, a12, a21, a22. The system of equations can be written in matrix
+ // form:
+ //
+ // | te1.x te2.x | * | a11 a12 | = | 1 0 | <-> T * A = I
+ // | te1.y te2.y | | a21 a22 | | 0 1 |
+ //
+ // Solving this matrix equation is actually finding a right inverse matrix A for matrix T.
+ //
+ // Then, tangent and bitangent vectors are linear combinations of e1 and e2 with same aXX coefficients.
+ //
+ // tangent = e1 * a11 + e2 * a21
+ // bitangent = e1 * a12 + e2 * a22
+
+ auto t = r4::matrix(
+ tex_edge1, // row 0
+ tex_edge2 // row 1
+ )
+ .transpose(); // rows become columns
+
+ constexpr auto epsilon = ruis::real(1e-5f);
auto tangent = ruis::vec3(1, 0, 0);
auto bitangent = ruis::vec3(0, 1, 0);
using std::abs;
- if (abs(det) >= epsilon) {
- auto det_reciprocal = ruis::real(1) / det;
-
- // TODO: figure out what is happening here and refactor with vector ops
- tangent[0] = (tex_edge_2[1] * edge1[0] - tex_edge_1[1] * edge2[0]) * det_reciprocal;
- tangent[1] = (tex_edge_2[1] * edge1[1] - tex_edge_1[1] * edge2[1]) * det_reciprocal;
- tangent[2] = (tex_edge_2[1] * edge1[2] - tex_edge_1[1] * edge2[2]) * det_reciprocal;
+ if (abs(t.det()) >= epsilon) {
+ auto a = t.inv();
- bitangent[0] = (-tex_edge_2[0] * edge1[0] + tex_edge_1[0] * edge2[0]) * det_reciprocal;
- bitangent[1] = (-tex_edge_2[0] * edge1[1] + tex_edge_1[0] * edge2[1]) * det_reciprocal;
- bitangent[2] = (-tex_edge_2[0] * edge1[2] + tex_edge_1[0] * edge2[2]) * det_reciprocal;
+ tangent = edge1 * a[0][0] + edge2 * a[1][0];
+ bitangent = edge1 * a[0][1] + edge2 * a[1][1];
}
// Accumulate the tangents and bitangents.
@@ -691,25 +712,23 @@ utki::shared_ref gltf_loader::create_vao_with_tangen
// Orthogonalize and normalize the vertex tangents.
for (uint32_t i = 0; i < num_vertices; ++i) {
- // Gram-Schmidt orthogonalize tangent with normal.
+ // Tangent and bitangent are not necessarily ortogonal to each other, but those have to be
+ // ortogonal to the normal.
- auto n_dot_t = normals[i] * tangents[i]; // dot product
+ // Ortogonalize the tangent and bitangent to the normal.
- tangents[i] -= normals[i] * n_dot_t;
+ tangents[i] -= normals[i].dot(tangents[i]) * normals[i];
+ bitangents[i] -= normals[i].dot(bitangents[i]) * normals[i];
tangents[i].normalize();
+ bitangents[i].normalize();
- ruis::vec3 bitangent_other = normals[i].cross(tangents[i]);
-
- auto b_dot_b = bitangent_other * bitangents[i];
-
- auto sign = b_dot_b < ruis::real(0) ? ruis::real(-1) : ruis::real(1);
-
- bitangents[i] = bitangent_other * sign;
+ // TODO: The triangle in texture space can be wound in different direction than in object space,
+ // need to flip the tangent basis to make normals from normal map point towards triangle normal direction.
}
- auto tangents_vbo = factory_v.create_vertex_buffer(utki::make_span(tangents));
- auto bitangents_vbo = factory_v.create_vertex_buffer(utki::make_span(bitangents));
+ auto tangents_vbo = factory_v.create_vertex_buffer(tangents);
+ auto bitangents_vbo = factory_v.create_vertex_buffer(bitangents);
auto vao = factory_v.create_vertex_array(
{utki::shared_ref(position_accessor.get().vbo),