<p>I think you have the wrong order of matrix multiplication.</p>
<pre class="lang-cpp prettyprint-override"><code>    _transform_world_to_object = translate * rotate * scale;
    _transform_object_to_world = glm::inverse(_transform_world_to_object);
</code></pre>
<p><code>translate * rotate * scale</code> is the appropriate multiplication/composition order for a conventional object-to-world coordinate transformation: first, scale the object, then rotate the scaled object, then translate the rotated object.</p>
<p>But you are instead doing them in the opposite order. Thus, the translation amount is scaled by the scale, so your z distance of 4 becomes 4 × 0.5 = 2, closer to the camera.</p>
<p>Try this instead:</p>
<pre><code>    _transform_world_to_object = scale * rotate * translate;
</code></pre>
<p>Or you can compute the object-to-world matrix directly (this will require changing all your minus signs) and world-to-object as the inverse of it. Different path, same result.</p>

CC BY-SA 4.0
2023-02-01T20:53:03.853Z
I completely missed that, Yo're absolutely right. After changing it, I got my expected results. Thank You!
Cedric
CC BY-SA 4.0
2023-02-01T19:30:02.317Z
2023-02-01T19:30:02.317Z
Kevin Reid

<p>I'm writing a ray tracer but having trouble converting rays and normals between world space and object space.</p>
<p>I am creating a Ray, then transforming it with transformation matrices, calculating intersections and return them.</p>
<p>Constructing transformation matrices:</p>
<pre class="lang-cpp prettyprint-override"><code>    glm::mat4 rotate = glm::eulerAngleYXZ(- _rotation.y, - _rotation.x, - _rotation.z);
    glm::mat4 translate = glm::translate(- _position);
    glm::mat4 scale = glm::scale(glm::vec3(1.0f) / _scale);

    _transform_world_to_object = translate * rotate * scale;
    _transform_object_to_world = glm::inverse(_transform_world_to_object);
</code></pre>
<p>Checking Ray Sphere intersection in object space, and adding according intersections with their normal, position and distance (in world space) to provided list:</p>
<pre class="lang-cpp prettyprint-override"><code>    void Sphere::computeAllRayIntersections(Ray&amp; ray, IntersectionList&amp; intersections) {
    //assuming ray has unit direction vector (easier calculation), then compensate for length later in distances
    float ray_to_sphere_ray_dir_dot = glm::dot(glm::normalize(ray.getDirection()), ray.getOrigin());
    float radicand = glm::dot(ray_to_sphere_ray_dir_dot, ray_to_sphere_ray_dir_dot) - (glm::dot(ray.getOrigin(), ray.getOrigin()) - 1.0f);

    if(radicand &lt; 0.0f) {
      return;
    }

    float d1 = (- ray_to_sphere_ray_dir_dot + sqrtf(radicand)) / glm::length(ray.getDirection());

    glm::vec3 position1 = ray.positionAtDistance(d1);
    glm::vec3 normal1 = glm::normalize(ray.positionAtDistance(d1));
    //invert normal when ray starting point lies in sphere:
    if(glm::length(ray.getOrigin()) &lt; 1.0f) {
      normal1 *= -1.0f;
    }

    Ray hit1_data(position1, normal1);
    hit1_data = this-&gt;transformHitObjectToWorld(hit1_data);
   intersections.addIntersection(std::make_unique&lt;Intersection&gt;(
      Intersection(
        (IGeometricObject*) this,
        glm::normalize(hit1_data.getDirection()),
        hit1_data.getOrigin(),
        d1)));

    if(radicand == 0.0f) { //stop after calculating only intersection
      return;
    }

    float d2 = (- ray_to_sphere_ray_dir_dot - sqrtf(radicand)) / glm::length(ray.getDirection());

    glm::vec3 position2 = ray.positionAtDistance(d2);
    glm::vec3 normal2 = glm::normalize(ray.positionAtDistance(d2));
    //invert normal when ray starting point lies in sphere:
    if(glm::length(ray.getOrigin()) &lt; 1.0f) {
      normal2 *= -1.0f;
    }

    Ray hit2_data(position2, normal2);
    hit2_data = this-&gt;transformHitObjectToWorld(hit2_data);
   intersections.addIntersection(std::make_unique&lt;Intersection&gt;(
      Intersection(
        (IGeometricObject*) this,
        glm::normalize(hit2_data.getDirection()),
        hit2_data.getOrigin(),
        d2)));                                                           
  }
</code></pre>
<p>With the following code I transform a point,vector pair between coordinate spaces: The conversion from world to object space doesn't normalize and doesn't uses normal formula, while converting the intersection from object to world space does. This differentiation is based on <a href="http://www.terathon.com/gdc12_lengyel.pdf" rel="nofollow noreferrer">this presentation</a>.</p>
<pre class="lang-cpp prettyprint-override"><code>    Ray Ray::transform(glm::mat4 transform, bool normalize_direction, bool is_normal) {
    glm::vec3 transformed_direction;
    if(is_normal) {
      transformed_direction = glm::transpose(glm::inverse(transform)) * glm::vec4(_direction, 0.0f);
    }
    else {
      transformed_direction = transform * glm::vec4(_direction, 0.0f);
    }
    //transformed_direction = transform * glm::vec4(_direction, 0.0f); 
    if(normalize_direction) {
      transformed_direction = glm::normalize(transformed_direction);
    }
    return Ray(transform * glm::vec4(_origin, 1.0f), transformed_direction);
  }
</code></pre>
<pre class="lang-cpp prettyprint-override"><code>    Ray BaseGeometricObject::transformRayWorldToObject(Ray&amp; ray) {
    return ray.transform(_transform_world_to_object, false, false);
  }
  Ray BaseGeometricObject::transformHitObjectToWorld(Ray&amp; ray) {
    return ray.transform(_transform_object_to_world, false, true);
  }
</code></pre>

<p>When rendering with uniform scaling, the output image looks (disregarding the normal) good:</p>
<pre class="lang-json prettyprint-override"><code>{
    &quot;Type&quot;: &quot;Sphere&quot;,
    &quot;Material&quot;: &quot;Red&quot;,
    &quot;Point&quot; : {
        &quot;x&quot;: 0,
        &quot;y&quot;: 0,
        &quot;z&quot;: 4
    },
    &quot;Rotation&quot;: {
        &quot;x&quot;: 0,
        &quot;y&quot;: 0,
        &quot;z&quot;: 0
    },
    &quot;Scale&quot;: {
        &quot;x&quot;: 1,
        &quot;y&quot;: 1,
        &quot;z&quot;: 1
    }
}
</code></pre>
<p><a href="https://i.sstatic.net/FhsvG.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/FhsvG.png" alt="Image rendered correctly" /></a></p>
<p>But when I use non uniform scaling, the sphere gets seemingly distorted on a axis not modified by scale:</p>
<pre class="lang-json prettyprint-override"><code>{
    &quot;Type&quot;: &quot;Sphere&quot;,
    &quot;Material&quot;: &quot;Red&quot;,
    &quot;Point&quot; : {
        &quot;x&quot;: 0,
        &quot;y&quot;: 0,
        &quot;z&quot;: 4
    },
    &quot;Rotation&quot;: {
        &quot;x&quot;: 0,
        &quot;y&quot;: 0,
        &quot;z&quot;: 0
    },
    &quot;Scale&quot;: {
      &quot;x&quot;: 1,
      &quot;y&quot;: 1,
      &quot;z&quot;: 0.5
     }
}
</code></pre>
<p><a href="https://i.sstatic.net/uuevl.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/uuevl.png" alt="Image rendered incorrectly" /></a></p>
<p>My Camera is positioned as follows: (it's transformation matrices should't change the ray, they are applied using the Ray transform functions with a matrix built from translate * rotate).</p>
<pre class="lang-json prettyprint-override"><code>{
    &quot;FOV&quot;: 60,
    &quot;Position&quot;: {
        &quot;x&quot;:  0.0,
        &quot;y&quot;:  0.0,
        &quot;z&quot;:  0.0
    },
  &quot;Rotation&quot;: {
    &quot;x&quot;: 0.0,
    &quot;y&quot;: 0.0,
    &quot;z&quot;: 0.0
  }
}
</code></pre>
<p>Now I'm wondering whether my code is correct or not. Have I got something conceptually wrong with the coordinate transformations? I've searched a lot on the internet, but didn't find a complete guide on how to perform that for points, vectors and normals.</p>

<raytracing><transformations><c++><glm>
Converting points, vectors, normals between world and object space for a ray tracer gives weird results
Cedric
# Converting points, vectors, normals between world and object space for a ray tracer gives weird results

(Asked by: username_0 on 2023-02-01)

I'm writing a ray tracer but having trouble converting rays and normals between world space and object space.

I am creating a Ray, then transforming it with transformation matrices, calculating intersections and return them.

Constructing transformation matrices:

```cpp
    glm::mat4 rotate = glm::eulerAngleYXZ(- _rotation.y, - _rotation.x, - _rotation.z);
    glm::mat4 translate = glm::translate(- _position);
    glm::mat4 scale = glm::scale(glm::vec3(1.0f) / _scale);

    _transform_world_to_object = translate * rotate * scale;
    _transform_object_to_world = glm::inverse(_transform_world_to_object);

```

Checking Ray Sphere intersection in object space, and adding according intersections with their normal, position and distance (in world space) to provided list:

```cpp
    void Sphere::computeAllRayIntersections(Ray& ray, IntersectionList& intersections) {
    //assuming ray has unit direction vector (easier calculation), then compensate for length later in distances
    float ray_to_sphere_ray_dir_dot = glm::dot(glm::normalize(ray.getDirection()), ray.getOrigin());
    float radicand = glm::dot(ray_to_sphere_ray_dir_dot, ray_to_sphere_ray_dir_dot) - (glm::dot(ray.getOrigin(), ray.getOrigin()) - 1.0f);

    if(radicand < 0.0f) {
      return;
    }

    float d1 = (- ray_to_sphere_ray_dir_dot + sqrtf(radicand)) / glm::length(ray.getDirection());

    glm::vec3 position1 = ray.positionAtDistance(d1);
    glm::vec3 normal1 = glm::normalize(ray.positionAtDistance(d1));
    //invert normal when ray starting point lies in sphere:
    if(glm::length(ray.getOrigin()) < 1.0f) {
      normal1 *= -1.0f;
    }

    Ray hit1_data(position1, normal1);
    hit1_data = this->transformHitObjectToWorld(hit1_data);
   intersections.addIntersection(std::make_unique<Intersection>(
      Intersection(
        (IGeometricObject*) this,
        glm::normalize(hit1_data.getDirection()),
        hit1_data.getOrigin(),
        d1)));

    if(radicand == 0.0f) { //stop after calculating only intersection
      return;
    }

    float d2 = (- ray_to_sphere_ray_dir_dot - sqrtf(radicand)) / glm::length(ray.getDirection());

    glm::vec3 position2 = ray.positionAtDistance(d2);
    glm::vec3 normal2 = glm::normalize(ray.positionAtDistance(d2));
    //invert normal when ray starting point lies in sphere:
    if(glm::length(ray.getOrigin()) < 1.0f) {
      normal2 *= -1.0f;
    }

    Ray hit2_data(position2, normal2);
    hit2_data = this->transformHitObjectToWorld(hit2_data);
   intersections.addIntersection(std::make_unique<Intersection>(
      Intersection(
        (IGeometricObject*) this,
        glm::normalize(hit2_data.getDirection()),
        hit2_data.getOrigin(),
        d2)));                                                           
  }

```

With the following code I transform a point,vector pair between coordinate spaces: The conversion from world to object space doesn't normalize and doesn't uses normal formula, while converting the intersection from object to world space does. This differentiation is based on [this presentation](http://www.terathon.com/gdc12_lengyel.pdf).

```cpp
    Ray Ray::transform(glm::mat4 transform, bool normalize_direction, bool is_normal) {
    glm::vec3 transformed_direction;
    if(is_normal) {
      transformed_direction = glm::transpose(glm::inverse(transform)) * glm::vec4(_direction, 0.0f);
    }
    else {
      transformed_direction = transform * glm::vec4(_direction, 0.0f);
    }
    //transformed_direction = transform * glm::vec4(_direction, 0.0f); 
    if(normalize_direction) {
      transformed_direction = glm::normalize(transformed_direction);
    }
    return Ray(transform * glm::vec4(_origin, 1.0f), transformed_direction);
  }

```

```cpp
    Ray BaseGeometricObject::transformRayWorldToObject(Ray& ray) {
    return ray.transform(_transform_world_to_object, false, false);
  }
  Ray BaseGeometricObject::transformHitObjectToWorld(Ray& ray) {
    return ray.transform(_transform_object_to_world, false, true);
  }

```

When rendering with uniform scaling, the output image looks (disregarding the normal) good:

```json
{
    "Type": "Sphere",
    "Material": "Red",
    "Point" : {
        "x": 0,
        "y": 0,
        "z": 4
    },
    "Rotation": {
        "x": 0,
        "y": 0,
        "z": 0
    },
    "Scale": {
        "x": 1,
        "y": 1,
        "z": 1
    }
}

```

[![Image rendered correctly](https://i.sstatic.net/FhsvG.png)](https://i.sstatic.net/FhsvG.png)

But when I use non uniform scaling, the sphere gets seemingly distorted on a axis not modified by scale:

```json
{
    "Type": "Sphere",
    "Material": "Red",
    "Point" : {
        "x": 0,
        "y": 0,
        "z": 4
    },
    "Rotation": {
        "x": 0,
        "y": 0,
        "z": 0
    },
    "Scale": {
      "x": 1,
      "y": 1,
      "z": 0.5
     }
}

```

[![Image rendered incorrectly](https://i.sstatic.net/uuevl.png)](https://i.sstatic.net/uuevl.png)

My Camera is positioned as follows: (it's transformation matrices should't change the ray, they are applied using the Ray transform functions with a matrix built from translate \* rotate).

```json
{
    "FOV": 60,
    "Position": {
        "x":  0.0,
        "y":  0.0,
        "z":  0.0
    },
  "Rotation": {
    "x": 0.0,
    "y": 0.0,
    "z": 0.0
  }
}

```

Now I'm wondering whether my code is correct or not. Have I got something conceptually wrong with the coordinate transformations? I've searched a lot on the internet, but didn't find a complete guide on how to perform that for points, vectors and normals.

**Tags:** raytracing, transformations, c++, glm


---

## 1 answers

### Answer by username_1 on 2023-02-01. Score: 1 | Accepted answer

I think you have the wrong order of matrix multiplication.

```cpp
    _transform_world_to_object = translate * rotate * scale;
    _transform_object_to_world = glm::inverse(_transform_world_to_object);

```

`translate * rotate * scale` is the appropriate multiplication/composition order for a conventional object-to-world coordinate transformation: first, scale the object, then rotate the scaled object, then translate the rotated object.

But you are instead doing them in the opposite order. Thus, the translation amount is scaled by the scale, so your z distance of 4 becomes 4 × 0.5 = 2, closer to the camera.

Try this instead:

```
    _transform_world_to_object = scale * rotate * translate;

```

Or you can compute the object-to-world matrix directly (this will require changing all your minus signs) and world-to-object as the inverse of it. Different path, same result.

> **Comments:**
> **username_0**: I completely missed that, Yo're absolutely right. After changing it, I got my expected results. Thank You!  

---

