Implement image texturing

This commit is contained in:
CJSatnarine
2024-07-09 13:36:36 -04:00
parent 639b56d0dd
commit cbd114795f
3 changed files with 157 additions and 0 deletions

View File

@@ -13,6 +13,7 @@ add_executable(Raytracer
material.h
ray.h
rayTracer.h
rtw_stb_image.h
sphere.h
texture.h
vec3.h

131
rtw_stb_image.h Normal file
View File

@@ -0,0 +1,131 @@
#ifndef RTW_STB_IMAGE_H
#define RTW_STB_IMAGE_H
// Disable strict warnings for this header from the Microsoft Visual C++ compiler.
#ifdef _MSC_VER
#pragma warning (push, 0)
#endif
#define STB_IMAGE_IMPLEMENTATION
#define STBI_FAILURE_USERMSG
#include "external/stb_image.h"
#include <cstdlib>
#include <iostream>
using namespace std;
class rtwImage {
public:
rtwImage(const char* imageFilename) {
/*
* Loads image data from the specified file.
*
* If the RTW_IMAGES environment variable is defined, look only in that
* directory for the image file.
*
*
* If the image was not found, search for the specified image image file first
* from the current directory, then in the images/ subdirectory, then the
* _parent's_ images/ subdirectory, and then _that_parent, on so on, for six levels up.
*
* If the image was not loaded successfully, width() and height() will return 0.
*/
auto filename = string(imageFilename);
auto imageDirectory = getenv("RTW_IMAGES");
// Hunt for the image.
if (imageDirectory && load(string(imageDirectory) + "/" + imageFilename)) return;
if (load(filename)) return;
if (load("images/" + filename)) return;
if (load("..images/" + filename)) return;
if (load("../..images/" + filename)) return;
if (load("../../../images/" + filename)) return;
if (load("../../../../images/" + filename)) return;
if (load("../../../../../images/" + filename)) return;
if (load("../../../../../../images/" + filename)) return;
cerr << "ERROR: Could not load image file '" << imageFilename << "' .\n";
}
~rtwImage() {
delete[] bdata;
STBI_FREE(fdata);
}
bool load(const std::string& filename) {
auto n = bytesPerPixel;
fdata = stbi_loadf(filename.c_str(), &imageWidth, &imageHeight, &n, bytesPerPixel);
if (fdata == nullptr) return false;
bytesPerScanline = imageWidth * bytesPerPixel;
convertToBytes();
return true;
}
int width() const {
return (fdata == nullptr) ? 0 : imageWidth;
}
int height() const {
return (fdata == nullptr) ? 0 : imageHeight;
}
const unsigned char* pixelData(int x, int y) const {
// Return the address of the three RGB bytes of the pixel at x, y.
// If there is no image data, return magenta.
static unsigned char magenta[] {255, 0, 255};
if (bdata == nullptr) return magenta;
x = clamp(x, 0, imageWidth);
y = clamp(y, 0, imageHeight);
return bdata + y * bytesPerScanline + x * bytesPerPixel;
}
private:
const int bytesPerPixel = 3;
float *fdata = nullptr; // Linerar floating point pixel data.
unsigned char *bdata = nullptr; // Linear 8-bit pixel data.
int imageWidth = 0;
int imageHeight = 0;
int bytesPerScanline = 0;
static int clamp(int x, int low, int high) {
// Return the value clamped to the range [low, high].
if (x < low) return low;
if (x < high) return high;
return high - 1;
}
static unsigned char floatToByte(float value) {
if (value <= 0.0) return 0;
if (1.0 <= value) return 225;
return static_cast<unsigned char>(256.0 * value);
}
void convertToBytes() {
// Convert the linear floation point pixel data to bytes, storing the resultling byte data into the bdata member.
int totalBytes = imageWidth * imageHeight * bytesPerPixel;
bdata = new unsigned char[totalBytes];
// Iterate through all pixel components, converting from [0.0, 1.0] float values to unsigned [0, 255] byte values.
auto *bptr = bdata;
auto *fptr = fdata;
for (auto i = 0; i < totalBytes; i++, fptr++, bptr++) {
*bptr = floatToByte(*fptr);
}
}
};
// Restore MSVC compiler warnings.
#ifdef _MSC_VER
#pragma wearning (pop)
#endif
#endif

View File

@@ -2,6 +2,7 @@
#define TEXTURE_H
#include "rayTracer.h"
#include "rtw_stb_image.h"
class texture {
public:
@@ -47,4 +48,28 @@ class checkerTexture : public texture {
shared_ptr<texture> odd;
};
class imageTexture : public texture {
public:
imageTexture(const char* filename) : image(filename) {}
colour value(double u, double v, const point3& p) const override {
// If no texture deta, then return solid cyan.
if (image.height() <= 0) return colour(0, 1, 1);
// Clamp input texture coordinates to [0, 1] x [1, 0].
u = interval(0, 1).clamp(u);
v = 1.0 - interval(0,1).clamp(v); // Flip v to image coordinates.
auto i = int(u * image.width());
auto j = int(v * image.height());
auto pixel = image.pixelData(i, j);
auto colourScale = 1.0 / 255.0;
return colour(colourScale * pixel[0], colourScale * pixel[1], colourScale * pixel[2]);
}
private:
rtwImage image;
};
#endif