<p>I am trying to write a raytracer in Rust that is able to load a .gltf file and render it. To do this I model a scene in Blende, export it to gltf (y up) and use my renderer to render the scene.</p>
<p>The Issue I am currently having is that the rotations in Blender and the exported gltf file do not seem to match up. If I for example set a rotation of <code>(0,0.5,0.5,0|XYZW)</code> in Blender, it gets exported as <code>(0,1,0,0|XYZW)</code>. If I however render this Image using my code, yet another rotation seems to be used (an image is produced corresponding to a rotation of (0.5,0,0,0.5|XYZW) in Blender).
<code>easy_gltf</code> yields the following transform matrix (world -&gt; camera):</p>
<pre><code>camera.transform = Matrix4 
[-1.0, 0.0, 0.0, 2.0
0.0, 1.0, 0.0, 10.0
0.0, 0.0, -1.0, -2
0.0, 0.0, 0.0, 1.0]
</code></pre>
<p>The camera is located at (2,2,10) in Blender, so at least the translational component appears to be working.</p>
<p>To load the gltf file, I use <code>easy_gltf</code> in Rust. The following code is used to generate the ray directions:</p>
<pre><code>//ray origin in world space
let origin_world_space = camera.position();
// the distance from the camera origin to the view plane
let z: f32 = height as f32 / (2.0 * fovy.mul(0.5).tan());

//obtain the inverse transformation Matrix
let inverse_transform = camera.transform.invert().unwrap_or_else(||
    panic!(&quot;Non invertible transform Matrix. giving up.&quot;)
);

//TODO: take ray multiplier per pixel into account here
let mut rays: Vec&lt;Ray&gt; = Vec::with_capacity(height * width);

//generate all rays for this pixel and add them to the rays vector
//TODO: use blue noise here to generate multiple rays per pixel
rays.push(generate_single_primary_ray(
    width,
    height,
    &amp;inverse_transform,
    z,
    pixel_x_coord,
    pixel_y_coord,
    origin_world_space));


fn generate_single_primary_ray(image_width: usize,
                               image_height: usize,
                               inverse_transform: &amp;Matrix4&lt;f32&gt;,
                               focal_length: f32,
                               u: usize,
                               v: usize,
                               ray_origin: Vector3&lt;f32&gt;) -&gt; Ray {
    //calculate the ray direction and translate it to world space
    let direction_view_space: Vector4&lt;f32&gt; =
        Vector4::new(u as f32 - (image_width as f32 / 2.0),
                     v as f32 - (image_height as f32 / 2.0),
                     focal_length,
                     0.0);
    //TODO: Rotation is fucked
    //x rotation has sign wrong, y and z are flipped
    let direction_world_space = inverse_transform * direction_view_space.normalize();

    Ray { source: ray_origin, direction: direction_world_space.truncate().normalize() }
}
</code></pre>
<p>As you may see from the code above, to calculate the ray direction I first calculate the distance from the camera origin to the projection plane, take the inverse of the World-to-Camera-Matrix and then map the Camera space coordinates to world space directions using the inverse transform matrix (which I get from the <code>eays_gltf</code> API -&gt; no influence on that).</p>
<p>Now I tried debugging this for hours but I cannot seem to pin down the issue. Is the export process wrong? Is easy gltf constructing the Matrix wrongly or do I have an issue in my code?</p>
<p>The code, .blend file and .gltf file are available here: <a href="https://git.cdaut.de/CDaut/raytrace-rs" rel="nofollow noreferrer">https://git.cdaut.de/CDaut/raytrace-rs</a></p>

CC BY-SA 4.0
2023-12-04T21:37:25.670Z
I don't know about easy_gltf, but I'll offer some hints that I do know: 1. Blender "up" is +Z but glTF "up" is +Y, so on export there will be a 90-deg clockwise rotation about X to change from one system to the other.  Also 2. Be careful that Blender's UI specifies `WXYZ` but in glTF the quaternion is `XYZW`, so be careful comparing the two.  And 3. of course `[0, 0.5, 0.5, 0]` is not a normalized quaternion; the middle numbers would need to be `0.707107` or such instead of `0.5`.
emackey
CC BY-SA 4.0
2023-12-04T21:37:55.180Z
Also if you could isolate a specific `.blend` and corresponding `.gltf` instead of a link to a full repo, that might help narrow down the question.
emackey
<gltf><blender><quaternions>
Issues understanding rotations exported from Blender to gltf
C. Dautermann
# Issues understanding rotations exported from Blender to gltf

(Asked by: username_0 on 2023-11-22)

I am trying to write a raytracer in Rust that is able to load a .gltf file and render it. To do this I model a scene in Blende, export it to gltf (y up) and use my renderer to render the scene.

The Issue I am currently having is that the rotations in Blender and the exported gltf file do not seem to match up. If I for example set a rotation of `(0,0.5,0.5,0|XYZW)` in Blender, it gets exported as `(0,1,0,0|XYZW)`. If I however render this Image using my code, yet another rotation seems to be used (an image is produced corresponding to a rotation of (0.5,0,0,0.5|XYZW) in Blender).
`easy_gltf` yields the following transform matrix (world -> camera):

```
camera.transform = Matrix4 
[-1.0, 0.0, 0.0, 2.0
0.0, 1.0, 0.0, 10.0
0.0, 0.0, -1.0, -2
0.0, 0.0, 0.0, 1.0]

```

The camera is located at (2,2,10) in Blender, so at least the translational component appears to be working.

To load the gltf file, I use `easy_gltf` in Rust. The following code is used to generate the ray directions:

```
//ray origin in world space
let origin_world_space = camera.position();
// the distance from the camera origin to the view plane
let z: f32 = height as f32 / (2.0 * fovy.mul(0.5).tan());

//obtain the inverse transformation Matrix
let inverse_transform = camera.transform.invert().unwrap_or_else(||
    panic!("Non invertible transform Matrix. giving up.")
);

//TODO: take ray multiplier per pixel into account here
let mut rays: Vec<Ray> = Vec::with_capacity(height * width);

//generate all rays for this pixel and add them to the rays vector
//TODO: use blue noise here to generate multiple rays per pixel
rays.push(generate_single_primary_ray(
    width,
    height,
    &inverse_transform,
    z,
    pixel_x_coord,
    pixel_y_coord,
    origin_world_space));

fn generate_single_primary_ray(image_width: usize,
                               image_height: usize,
                               inverse_transform: &Matrix4<f32>,
                               focal_length: f32,
                               u: usize,
                               v: usize,
                               ray_origin: Vector3<f32>) -> Ray {
    //calculate the ray direction and translate it to world space
    let direction_view_space: Vector4<f32> =
        Vector4::new(u as f32 - (image_width as f32 / 2.0),
                     v as f32 - (image_height as f32 / 2.0),
                     focal_length,
                     0.0);
    //TODO: Rotation is fucked
    //x rotation has sign wrong, y and z are flipped
    let direction_world_space = inverse_transform * direction_view_space.normalize();

    Ray { source: ray_origin, direction: direction_world_space.truncate().normalize() }
}

```

As you may see from the code above, to calculate the ray direction I first calculate the distance from the camera origin to the projection plane, take the inverse of the World-to-Camera-Matrix and then map the Camera space coordinates to world space directions using the inverse transform matrix (which I get from the `eays_gltf` API -> no influence on that).

Now I tried debugging this for hours but I cannot seem to pin down the issue. Is the export process wrong? Is easy gltf constructing the Matrix wrongly or do I have an issue in my code?

The code, .blend file and .gltf file are available here: <https://git.cdaut.de/CDaut/raytrace-rs>

**Tags:** gltf, blender, quaternions

> **Comments:**
> **username_1**: I don't know about easy_gltf, but I'll offer some hints that I do know: 1. Blender "up" is +Z but glTF "up" is +Y, so on export there will be a 90-deg clockwise rotation about X to change from one system to the other.  Also 2. Be careful that Blender's UI specifies `WXYZ` but in glTF the quaternion is `XYZW`, so be careful comparing the two.  And 3. of course `[0, 0.5, 0.5, 0]` is not a normalized quaternion; the middle numbers would need to be `0.707107` or such instead of `0.5`.  
> **username_1**: Also if you could isolate a specific `.blend` and corresponding `.gltf` instead of a link to a full repo, that might help narrow down the question.  

---

## 0 answers

