Skip to content

Commit edce6b9

Browse files
authored
Merge pull request #436 from RayTracing/image-texture
image_texture rewrite
2 parents c77a07a + 06c6a5a commit edce6b9

File tree

4 files changed

+86
-53
lines changed

4 files changed

+86
-53
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ Change Log -- Ray Tracing in One Weekend
1818
- Fix: Improve image size and aspect ratio calculation to make size changes easier
1919
- Fix: Added `t` parameter back into `hit_record` at correct place
2020

21+
### _The Next Week_
22+
- Change: Large rewrite of the `image_texture` class. Now handles image loading too. (#434)
23+
2124

2225
----------------------------------------------------------------------------------------------------
2326
# v3.0.2 (in progress)

books/RayTracingTheNextWeek.html

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1739,41 +1739,63 @@
17391739

17401740
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
17411741
#include "rtweekend.h"
1742+
#include "rtw_stb_image.h"
1743+
1744+
#include <iostream>
17421745

17431746
class image_texture : public texture {
17441747
public:
1745-
image_texture() {}
1746-
image_texture(unsigned char *pixels, int A, int B)
1747-
: data(pixels), nx(A), ny(B) {}
1748+
const static int bytes_per_pixel = 3;
1749+
1750+
image_texture()
1751+
: data(nullptr), width(0), height(0), bytes_per_scanline(0) {}
1752+
1753+
image_texture(const char* filename) {
1754+
auto components_per_pixel = bytes_per_pixel;
1755+
1756+
data = stbi_load(
1757+
filename, &width, &height, &components_per_pixel, components_per_pixel);
1758+
1759+
if (!data) {
1760+
std::cerr << "ERROR: Could not load texture image file '" << filename << "'.\n";
1761+
width = height = 0;
1762+
}
1763+
1764+
bytes_per_scanline = bytes_per_pixel * width;
1765+
}
17481766

17491767
~image_texture() {
17501768
delete data;
17511769
}
17521770

1753-
virtual color value(double u, double v, const point3& p) const {
1754-
// If we have no texture data, then always emit cyan (as a debugging aid).
1771+
virtual color value(double u, double v, const vec3& p) const {
1772+
// If we have no texture data, then return solid cyan as a debugging aid.
17551773
if (data == nullptr)
17561774
return color(0,1,1);
17571775

1758-
auto i = static_cast<int>(( u)*nx);
1759-
auto j = static_cast<int>((1-v)*ny-0.001);
1776+
// Clamp input texture coordinates to [0,1] x [1,0]
1777+
u = clamp(u, 0.0, 1.0);
1778+
v = 1.0 - clamp(v, 0.0, 1.0); // Flip V to image coordinates
1779+
1780+
auto i = static_cast<int>(u * width);
1781+
auto j = static_cast<int>(v * height);
17601782

1761-
if (i < 0) i = 0;
1762-
if (j < 0) j = 0;
1763-
if (i > nx-1) i = nx-1;
1764-
if (j > ny-1) j = ny-1;
1783+
// Clamp integer mapping, since actual coordinates should be less than 1.0
1784+
if (i >= width) i = width-1;
1785+
if (j >= height) j = height-1;
17651786

1766-
auto r = static_cast<int>(data[3*i + 3*nx*j+0]) / 255.0;
1767-
auto g = static_cast<int>(data[3*i + 3*nx*j+1]) / 255.0;
1768-
auto b = static_cast<int>(data[3*i + 3*nx*j+2]) / 255.0;
1787+
const auto color_scale = 1.0 / 255.0;
1788+
auto pixel = data + j*bytes_per_scanline + i*bytes_per_pixel;
17691789

1770-
return color(r, g, b);
1790+
return color(color_scale*pixel[0], color_scale*pixel[1], color_scale*pixel[2]);
17711791
}
17721792

1773-
public:
1793+
private:
17741794
unsigned char *data;
1775-
int nx, ny;
1795+
int width, height;
1796+
int bytes_per_scanline;
17761797
};
1798+
17771799
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17781800
[Listing [img-texture]: <kbd>[texture.h]</kbd> Image texture class]
17791801

@@ -1807,11 +1829,8 @@
18071829

18081830
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
18091831
hittable_list earth() {
1810-
int nx, ny, nn;
1811-
unsigned char* texture_data = stbi_load("earthmap.jpg", &nx, &ny, &nn, 0);
1812-
1813-
auto earth_surface =
1814-
make_shared<lambertian>(make_shared<image_texture>(texture_data, nx, ny));
1832+
auto earth_texture = make_shared<image_texture>("earthmap.jpg");
1833+
auto earth_surface = make_shared<lambertian>(earth_texture);
18151834
auto globe = make_shared<sphere>(point3(0,0,0), 2, earth_surface);
18161835

18171836
return hittable_list(globe);
@@ -2856,9 +2875,7 @@
28562875
objects.add(make_shared<constant_medium>(
28572876
boundary, .0001, make_shared<solid_color>(1,1,1)));
28582877

2859-
int nx, ny, nn;
2860-
auto tex_data = stbi_load("earthmap.jpg", &nx, &ny, &nn, 0);
2861-
auto emat = make_shared<lambertian>(make_shared<image_texture>(tex_data, nx, ny));
2878+
auto emat = make_shared<lambertian>(make_shared<image_texture>("earthmap.jpg"));
28622879
objects.add(make_shared<sphere>(point3(400,200,400), 100, emat));
28632880
auto pertext = make_shared<noise_texture>(0.1);
28642881
objects.add(make_shared<sphere>(point3(220,280,300), 80, make_shared<lambertian>(pertext)));

src/TheNextWeek/main.cc

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
#include "hittable_list.h"
1919
#include "material.h"
2020
#include "moving_sphere.h"
21-
#include "rtw_stb_image.h"
2221
#include "sphere.h"
2322
#include "texture.h"
2423

@@ -128,11 +127,8 @@ hittable_list two_perlin_spheres() {
128127

129128

130129
hittable_list earth() {
131-
int nx, ny, nn;
132-
unsigned char* texture_data = stbi_load("earthmap.jpg", &nx, &ny, &nn, 0);
133-
134-
auto earth_surface =
135-
make_shared<lambertian>(make_shared<image_texture>(texture_data, nx, ny));
130+
auto earth_texture = make_shared<image_texture>("earthmap.jpg");
131+
auto earth_surface = make_shared<lambertian>(earth_texture);
136132
auto globe = make_shared<sphere>(point3(0,0,0), 2, earth_surface);
137133

138134
return hittable_list(globe);
@@ -246,10 +242,7 @@ hittable_list cornell_final() {
246242

247243
auto pertext = make_shared<noise_texture>(0.1);
248244

249-
int nx, ny, nn;
250-
unsigned char* tex_data = stbi_load("earthmap.jpg", &nx, &ny, &nn, 0);
251-
252-
auto mat = make_shared<lambertian>(make_shared<image_texture>(tex_data, nx, ny));
245+
auto mat = make_shared<lambertian>(make_shared<image_texture>("earthmap.jpg"));
253246

254247
auto red = make_shared<lambertian>(make_shared<solid_color>(.65, .05, .05));
255248
auto white = make_shared<lambertian>(make_shared<solid_color>(.73, .73, .73));
@@ -322,9 +315,7 @@ hittable_list final_scene() {
322315
boundary = make_shared<sphere>(point3(0,0,0), 5000, make_shared<dielectric>(1.5));
323316
objects.add(make_shared<constant_medium>(boundary, .0001, make_shared<solid_color>(1,1,1)));
324317

325-
int nx, ny, nn;
326-
auto tex_data = stbi_load("earthmap.jpg", &nx, &ny, &nn, 0);
327-
auto emat = make_shared<lambertian>(make_shared<image_texture>(tex_data, nx, ny));
318+
auto emat = make_shared<lambertian>(make_shared<image_texture>("earthmap.jpg"));
328319
objects.add(make_shared<sphere>(point3(400,200,400), 100, emat));
329320
auto pertext = make_shared<noise_texture>(0.1);
330321
objects.add(make_shared<sphere>(point3(220,280,300), 80, make_shared<lambertian>(pertext)));

src/common/texture.h

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
#include "rtweekend.h"
1515

1616
#include "perlin.h"
17+
#include "rtw_stb_image.h"
18+
19+
#include <iostream>
1720

1821

1922
class texture {
@@ -77,36 +80,55 @@ class noise_texture : public texture {
7780

7881
class image_texture : public texture {
7982
public:
80-
image_texture() {}
81-
image_texture(unsigned char *pixels, int A, int B) : data(pixels), nx(A), ny(B) {}
83+
const static int bytes_per_pixel = 3;
84+
85+
image_texture()
86+
: data(nullptr), width(0), height(0), bytes_per_scanline(0) {}
87+
88+
image_texture(const char* filename) {
89+
auto components_per_pixel = bytes_per_pixel;
90+
91+
data = stbi_load(
92+
filename, &width, &height, &components_per_pixel, components_per_pixel);
93+
94+
if (!data) {
95+
std::cerr << "ERROR: Could not load texture image file '" << filename << "'.\n";
96+
width = height = 0;
97+
}
98+
99+
bytes_per_scanline = bytes_per_pixel * width;
100+
}
82101

83102
~image_texture() {
84103
delete data;
85104
}
86105

87106
virtual color value(double u, double v, const vec3& p) const {
88-
// If we have no texture data, then always emit cyan (as a debugging aid).
107+
// If we have no texture data, then return solid cyan as a debugging aid.
89108
if (data == nullptr)
90109
return color(0,1,1);
91110

92-
auto i = static_cast<int>(( u)*nx);
93-
auto j = static_cast<int>((1-v)*ny-0.001);
111+
// Clamp input texture coordinates to [0,1] x [1,0]
112+
u = clamp(u, 0.0, 1.0);
113+
v = 1.0 - clamp(v, 0.0, 1.0); // Flip V to image coordinates
114+
115+
auto i = static_cast<int>(u * width);
116+
auto j = static_cast<int>(v * height);
94117

95-
if (i < 0) i = 0;
96-
if (j < 0) j = 0;
97-
if (i > nx-1) i = nx-1;
98-
if (j > ny-1) j = ny-1;
118+
// Clamp integer mapping, since actual coordinates should be less than 1.0
119+
if (i >= width) i = width-1;
120+
if (j >= height) j = height-1;
99121

100-
auto r = static_cast<int>(data[3*i + 3*nx*j+0]) / 255.0;
101-
auto g = static_cast<int>(data[3*i + 3*nx*j+1]) / 255.0;
102-
auto b = static_cast<int>(data[3*i + 3*nx*j+2]) / 255.0;
122+
const auto color_scale = 1.0 / 255.0;
123+
auto pixel = data + j*bytes_per_scanline + i*bytes_per_pixel;
103124

104-
return color(r, g, b);
125+
return color(color_scale*pixel[0], color_scale*pixel[1], color_scale*pixel[2]);
105126
}
106127

107-
public:
128+
private:
108129
unsigned char *data;
109-
int nx, ny;
130+
int width, height;
131+
int bytes_per_scanline;
110132
};
111133

112134

0 commit comments

Comments
 (0)