0

I am writing a raytracer and shading using the Phong illumination model. In this example, my material does not have any ambient term. When I render, I get the following image:

diffuse and specularity render

As you can see, there appears to be a lighter ring around the specularity. If I disable specularity entirely and only render the diffuse, I obtain the following:

diffuse only render

So, this only happens when I introduce specularity. I have done shading multiple times and I have never seen this before. The entire shading code is as follows (I normalized a few more vectors than I needed to just to make sure this wasn't the issue):

    glm::vec3 shading(std::shared_ptr<scene::scene> scene, std::shared_ptr<hit> hit, uint16_t level)
    {
        glm::vec3 normal = glm::normalize(hit->normal);
        glm::vec3 result = hit->mat->ka; // Initialize ambient lighting with unit (global) intensity.
        glm::vec3 point = hit->r.origin + (glm::normalize(hit->r.direction) * hit->t); // Surface vertex.
        glm::vec3 viewing = glm::normalize(scene->camera_pos - point); // Direction vector to the camera.
        // Iterate through every light source in the scene, as this contributes to the overall lighting.
        for (auto& light_source : scene->lights)
        {
            glm::vec3 light = glm::normalize(light_source.position - point); // Direction vector to the light.
            // Calculate diffuse.
            float diffuse_dot = glm::dot(normal, light); // N . L
            if (diffuse_dot > 0.0f)
            {
                glm::vec3 diffuse = light_source.intensity * (diffuse_dot * hit->mat->kd); // Compute diffuse component.
                result += diffuse; // Add diffuse component to result.
            }
            // Calculate specularity.
            glm::vec3 reflected = glm::reflect(-light, normal); // GLM takes opposite light vector.
            float specularity_dot = glm::dot(viewing, reflected); // R . V
            if (specularity_dot > 0.0f)
            {
                float specularity_coefficient = glm::pow(specularity_dot, hit->mat->ns); // Add specularity component to result.
                glm::vec3 specularity = light_source.intensity * (specularity_coefficient * hit->mat->ks);
                result += specularity; // Add specularity.
            }
        }
        return glm::clamp(result, 0.0f, 1.0f);
    }

The colour is written to a PPM file, and each pixel is written as such:

   // Writes the color; currently not thread safe.
    void write_color(std::ostream& out, glm::vec3& color)
    {
        out << static_cast<int>(255.999 * color.x) << ' '
            << static_cast<int>(255.999 * color.y) << ' '
            << static_cast<int>(255.999 * color.z) << '\n';
    }

Lastly, the material is defined as follows:

    static std::shared_ptr<rt::material> material_blue = std::make_shared<rt::material>(
        glm::vec3{ 0.0f }, // Ka
        glm::vec3{ 0.0f, 0.0f, 1.0f }, // Kd
        glm::vec3{ 1.0f, 1.0f, 1.0f }, // Ks
        10.0f, // Ns/Shininess/the power the specularity is raised to
        // -snip-
    );

I suspect that this may have something to do with glm::clamp. If I change my code at the end to be the code below, I get the debug render shown below.

        if (result.x > 1.0f || result.y > 1.0f || result.z > 1.0f)
        {
            return glm::vec3{ 1.0f, 0.0f, 0.0f };
        }
        return glm::clamp(result, 0.0f, 1.0f);

debug render

This shape looks awfully familiar to the shape the outline of this ring makes. Therefore I suspect it could be an issue related to clamping, but countless hours of debugging have not gotten me further.

TL;DR I have a weird "ring" around my specularity (see first image) and I want to know how to get rid of it.

1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56
Paul Hübner
  • 3
  • 1
  • 3
  • You're seeing the reflection of the white light off of a shiny object. The shininess value for you material is "10". If you reduce this you'll get less of a glare. You could also change/reduce the light source's intensity. – 1201ProgramAlarm Dec 21 '21 at 18:43
  • @Rabbid76 How do you envision this ocurring? None of my materials (the one I use is shown in the post) have any Kd or Ks with negative values, and specularity only gets added if the dot product is positive. Perhaps I am missing something but I see no possibility of a vector with a negative component getting added to `result` – Paul Hübner Dec 21 '21 at 18:47
  • Sample the pixels; I suspect it isn't a ring, just a light circle, and the dark part is an optical illusion. You are clampimg, which messes with hue when you clamp blue first. You can test this by making the specularity red (in a different channel) than diffuse. So you have a (relatively sharp; ^10)ring of saturated blue, then increasing RG, then white. – Yakk - Adam Nevraumont Dec 21 '21 at 18:47
  • @1201ProgramAlarm Here's a render with [0.9f](https://imgur.com/a/t7bGqqG). That doesn't look right to me still. The source intensity is <1, 1, 1>, FWIW. – Paul Hübner Dec 21 '21 at 18:50
  • @Yakk-AdamNevraumont here is a render at [500 samples per pixel](https://imgur.com/a/8Vk6EJq) with the same material. Using Kd <0, 0, 1> and Ks <1, 0, 1> and 100 samples/px I get [this](https://imgur.com/a/FULYeWY). Using Kd <0, 0, 1> and Ks <1, 0, 0> (100 samples/px again) I get [this](https://imgur.com/a/eZeZmiv). Is that what you meant? – Paul Hübner Dec 21 '21 at 19:05
  • @Rabbid76 Yes, but I only add to the result if `specularity_dot` is non-negative, and I don't see how my `specularity_component` could be negative. I have added a conditional breakpoint that never gets triggered. – Paul Hübner Dec 21 '21 at 19:16
  • @Rabbid76 In the original screenshots, it is not enabled. I have antialiasing using supersampling built in though, [here is a render at 500 samples per pixel](https://imgur.com/a/8Vk6EJq). – Paul Hübner Dec 21 '21 at 19:19
  • @PaulHübner What is the image format? – Rabbid76 Dec 21 '21 at 19:23
  • @Rabbid76 PPM, 480x270. The images I have uploaded all have been converted using [this website](https://www.cs.rhodes.edu/welshc/COMP141_F16/ppmReader.html). However, the ring still persists in my image viewer IrfanView. – Paul Hübner Dec 21 '21 at 19:24
  • I mean examine the actual RGB values. As numbers. Not the mk 1 eyeball. Is the ring actually brighter than pixels inside of it or is it an optical illusion? – Yakk - Adam Nevraumont Dec 22 '21 at 00:15
  • @Yakk-AdamNevraumont I don't have the numbers at hand anymore, but when I checked then there seemed nothing off about them, just a regular gradient. So I guess it could be an opical illusion, just I have never seen this before neither in my own work or anywhere else really... – Paul Hübner Dec 22 '21 at 12:20
  • Drop the diffuse blue to 0.5. That should give you room for brightness to ramp up before blue is clamped. Alternatively, modify clamp so that it turns excess brightness in one channel into more white (r=white*brightness( max(r.x-1,0), max(r.y-1,0), max(r.z-1,0)) + r) before clamping. – Yakk - Adam Nevraumont Dec 22 '21 at 18:50

0 Answers0