<p>If you look closely at the edge of the sphere, in the first image it is sharp, and in the second image it's blurry, blending into the background color. This suggests to me that it's not the texture, but the ray distribution on the image plane that's the issue. The second image might be distributing rays over a larger area per pixel, for instance by using a wider AA filter, or a wider lens aperture (defocus blur).</p>

CC BY-SA 4.0
2020-12-02T18:52:01.127Z
2020-12-02T18:52:01.127Z
Nathan Reed
<p>I am extending <a href="https://github.com/rogerallen/raytracinginoneweekendincuda" rel="nofollow noreferrer">Roger Allen's Ray Tracing in One Weekend in CUDA</a> (and also <a href="https://developer.nvidia.com/blog/accelerated-ray-tracing-cuda/" rel="nofollow noreferrer">here</a>) by following <a href="https://raytracing.github.io/books/RayTracingTheNextWeek.html#imagetexturemapping" rel="nofollow noreferrer">Peter Shirley's Ray Tracing: The Next Week</a>; specifically Image Texture Mapping.</p>
<p>The image I obtain from CUDA is not as sharp as the CPU version. Shirley's book uses <code>double</code> and Allen restricts to <code>float</code> but could that be the only reason?</p>
<p>The texture source for both is a 24 bit depth jpeg file.</p>
<p>In both cases 100x sampling per pixel used for antialiasing.</p>
<p>Is there anything to be done in the kernel to improve the image quality?</p>
<pre><code>    class imageTextureX :public textureX {
public:
    __device__ imageTextureX(uint32_t width, uint32_t height, cudaTextureObject_t textureObject) :_width(width), _height(height), _textureObj(textureObject) {}
    __forceinline__ __device__  vec3 value(float u, float v, const vec3&amp; p) const override {
        //Flip Y-Coordinate;
        v = 1.0f - v;
        //De-normalize coordinate(s)
        uint32_t X = u * _width;
        //Re-normalize
        constexpr float pixelOffset{ 0.5f };
        const float denominator = 1.0f / (float)(3 * _width);
        u = (float)(3 * X + pixelOffset);

        vec3 colorVal;
        colorVal.e[0] = (float)tex2D&lt;uint8_t&gt;(_textureObj, u++ * denominator, v);
        colorVal.e[1] = (float)tex2D&lt;uint8_t&gt;(_textureObj, u++ * denominator, v);
        colorVal.e[2] = (float)tex2D&lt;uint8_t&gt;(_textureObj, u * denominator, v);
        return colorVal / 255.0f;
    }
private:
    cudaTextureObject_t _textureObj;
    uint32_t _width;
    uint32_t _height;
};
</code></pre>
<p>Texture definition code</p>
<pre><code>//Load Texture Image from Disk
    cudaArray* cuArray;
    cudaTextureObject_t textureObject;
    int32_t textureWidth, textureHeight;
    {
        Timer tim(&quot;Prepare Image Texture&quot;);
        const static uint32_t bytesPerPixel{ 3u };
        uint8_t* pHostData;
        uint32_t bytesPerScanline;
        cudaChannelFormatDesc channelFormatDesc;
        cudaResourceDesc  resourceDesc{};
        cudaTextureDesc textureDesc{};
        int32_t componentsPerPixel = bytesPerPixel;
        pHostData = stbi_load(textureFilename.c_str(), &amp;textureWidth, &amp;textureHeight, &amp;componentsPerPixel, componentsPerPixel);
        if (nullptr == pHostData) {
            std::cerr &lt;&lt; &quot;ERROR: Could not load texture image file '&quot; &lt;&lt; textureFilename &lt;&lt; std::endl;
            return;
        }
        bytesPerScanline = bytesPerPixel * textureWidth;

        channelFormatDesc = cudaCreateChannelDesc(8, 0, 0, 0, cudaChannelFormatKindUnsigned);
        checkCudaErrors(cudaMallocArray(&amp;cuArray, &amp;channelFormatDesc, bytesPerScanline, textureHeight));
        checkCudaErrors(cudaMemcpyToArray(cuArray, 0, 0, pHostData, bytesPerScanline * textureHeight * sizeof(uint8_t), cudaMemcpyHostToDevice));
        
        resourceDesc.resType = cudaResourceTypeArray;
        resourceDesc.res.array.array = cuArray;

        textureDesc.normalizedCoords = true;
        textureDesc.filterMode = cudaFilterModePoint;
        textureDesc.addressMode[0] = cudaAddressModeWrap;
        textureDesc.addressMode[1] = cudaAddressModeWrap;
        textureDesc.readMode = cudaReadModeElementType;
        checkCudaErrors(cudaCreateTextureObject(&amp;textureObject, &amp;resourceDesc, &amp;textureDesc, nullptr));
        STBI_FREE(pHostData);
    }
</code></pre>
<p>The output images were ppm and converted to pgm for upload purposes
<a href="https://i.sstatic.net/IpNXb.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/IpNXb.png" alt="From CPU code" /></a></p>
<p><a href="https://i.sstatic.net/w7eY7.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/w7eY7.png" alt="From CUDA code" /></a></p>

CC BY-SA 4.0
2020-11-22T17:34:33.440Z
This is probably not anything to do with the texture, and almost certainly nothing to do with double vs float. Look closely at the edge of the sphere: in the first image it is sharp, and in the second blurry, blending into the background color. This suggests to me that it's the ray distribution on the image plane that's the issue. Is the second image distributing rays over a larger area per pixel, using a wider AA filter, etc?
Nathan Reed
CC BY-SA 4.0
2020-12-02T17:31:53.933Z
@NathanReed Thanks for  the response. Indeed it turned out to be the camera aperture: on the GPU it was 0.1f whereas on the CPU 0.0f. If you like to provide your input as an "answer", I will gladly mark it so.
Vectorizer
CC BY-SA 4.0
2020-12-02T18:45:56.870Z
From Ray Tracing in One Weekend:For our virtual camera, we can have a perfect sensor and never need
more light, so we only have an aperture when we want defocus blur.
Vectorizer
<raytracing><rendering><texture><antialiasing><cuda>
Why is the image from the CUDA version not as sharp as the CPU version?
Vectorizer
# Why is the image from the CUDA version not as sharp as the CPU version?

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

I am extending [Roger Allen's Ray Tracing in One Weekend in CUDA](https://github.com/rogerallen/raytracinginoneweekendincuda) (and also [here](https://developer.nvidia.com/blog/accelerated-ray-tracing-cuda/)) by following [Peter Shirley's Ray Tracing: The Next Week](https://raytracing.github.io/books/RayTracingTheNextWeek.html#imagetexturemapping); specifically Image Texture Mapping.

The image I obtain from CUDA is not as sharp as the CPU version. Shirley's book uses `double` and Allen restricts to `float` but could that be the only reason?

The texture source for both is a 24 bit depth jpeg file.

In both cases 100x sampling per pixel used for antialiasing.

Is there anything to be done in the kernel to improve the image quality?

```
    class imageTextureX :public textureX {
public:
    __device__ imageTextureX(uint32_t width, uint32_t height, cudaTextureObject_t textureObject) :_width(width), _height(height), _textureObj(textureObject) {}
    __forceinline__ __device__  vec3 value(float u, float v, const vec3& p) const override {
        //Flip Y-Coordinate;
        v = 1.0f - v;
        //De-normalize coordinate(s)
        uint32_t X = u * _width;
        //Re-normalize
        constexpr float pixelOffset{ 0.5f };
        const float denominator = 1.0f / (float)(3 * _width);
        u = (float)(3 * X + pixelOffset);

        vec3 colorVal;
        colorVal.e[0] = (float)tex2D<uint8_t>(_textureObj, u++ * denominator, v);
        colorVal.e[1] = (float)tex2D<uint8_t>(_textureObj, u++ * denominator, v);
        colorVal.e[2] = (float)tex2D<uint8_t>(_textureObj, u * denominator, v);
        return colorVal / 255.0f;
    }
private:
    cudaTextureObject_t _textureObj;
    uint32_t _width;
    uint32_t _height;
};

```

Texture definition code

```
//Load Texture Image from Disk
    cudaArray* cuArray;
    cudaTextureObject_t textureObject;
    int32_t textureWidth, textureHeight;
    {
        Timer tim("Prepare Image Texture");
        const static uint32_t bytesPerPixel{ 3u };
        uint8_t* pHostData;
        uint32_t bytesPerScanline;
        cudaChannelFormatDesc channelFormatDesc;
        cudaResourceDesc  resourceDesc{};
        cudaTextureDesc textureDesc{};
        int32_t componentsPerPixel = bytesPerPixel;
        pHostData = stbi_load(textureFilename.c_str(), &textureWidth, &textureHeight, &componentsPerPixel, componentsPerPixel);
        if (nullptr == pHostData) {
            std::cerr << "ERROR: Could not load texture image file '" << textureFilename << std::endl;
            return;
        }
        bytesPerScanline = bytesPerPixel * textureWidth;

        channelFormatDesc = cudaCreateChannelDesc(8, 0, 0, 0, cudaChannelFormatKindUnsigned);
        checkCudaErrors(cudaMallocArray(&cuArray, &channelFormatDesc, bytesPerScanline, textureHeight));
        checkCudaErrors(cudaMemcpyToArray(cuArray, 0, 0, pHostData, bytesPerScanline * textureHeight * sizeof(uint8_t), cudaMemcpyHostToDevice));
        
        resourceDesc.resType = cudaResourceTypeArray;
        resourceDesc.res.array.array = cuArray;

        textureDesc.normalizedCoords = true;
        textureDesc.filterMode = cudaFilterModePoint;
        textureDesc.addressMode[0] = cudaAddressModeWrap;
        textureDesc.addressMode[1] = cudaAddressModeWrap;
        textureDesc.readMode = cudaReadModeElementType;
        checkCudaErrors(cudaCreateTextureObject(&textureObject, &resourceDesc, &textureDesc, nullptr));
        STBI_FREE(pHostData);
    }

```

The output images were ppm and converted to pgm for upload purposes
[![From CPU code](https://i.sstatic.net/IpNXb.png)](https://i.sstatic.net/IpNXb.png)

[![From CUDA code](https://i.sstatic.net/w7eY7.png)](https://i.sstatic.net/w7eY7.png)

**Tags:** raytracing, rendering, texture, antialiasing, cuda

> **Comments:**
> **username_1**: This is probably not anything to do with the texture, and almost certainly nothing to do with double vs float. Look closely at the edge of the sphere: in the first image it is sharp, and in the second blurry, blending into the background color. This suggests to me that it's the ray distribution on the image plane that's the issue. Is the second image distributing rays over a larger area per pixel, using a wider AA filter, etc?  
> **username_0**: @username_1 Thanks for  the response. Indeed it turned out to be the camera aperture: on the GPU it was 0.1f whereas on the CPU 0.0f. If you like to provide your input as an "answer", I will gladly mark it so.  
> **username_0**: From Ray Tracing in One Weekend:For our virtual camera, we can have a perfect sensor and never need more light, so we only have an aperture when we want defocus blur.  

---

## 1 answers

### Answer by username_1 on 2020-12-02. Score: 1 | Accepted answer

If you look closely at the edge of the sphere, in the first image it is sharp, and in the second image it's blurry, blending into the background color. This suggests to me that it's not the texture, but the ray distribution on the image plane that's the issue. The second image might be distributing rays over a larger area per pixel, for instance by using a wider AA filter, or a wider lens aperture (defocus blur).


---

