Added texture support. Currently broken.

There is something very wrong somewhere. More testing is needed.
main
Brett 2022-11-21 00:47:12 -05:00
parent 410e9c29b0
commit 763d57327f
11 changed files with 152 additions and 11 deletions

View File

@ -32,6 +32,8 @@ namespace Raytracing {
Vec4 normal{}; Vec4 normal{};
// the length of the vector from its origin in its direction. // the length of the vector from its origin in its direction.
PRECISION_TYPE length{0}; PRECISION_TYPE length{0};
// Texture UV Coords.
PRECISION_TYPE u,v;
}; };
struct ScatterResults { struct ScatterResults {
@ -53,6 +55,7 @@ namespace Raytracing {
// returns true if the ray was scattered along with the scattered ray, otherwise will return false with empty ray. // returns true if the ray was scattered along with the scattered ray, otherwise will return false with empty ray.
// the returned vec4 is the attenuation color // the returned vec4 is the attenuation color
[[nodiscard]] virtual ScatterResults scatter(const Ray& ray, const HitData& hitData) const = 0; [[nodiscard]] virtual ScatterResults scatter(const Ray& ray, const HitData& hitData) const = 0;
[[nodiscard]] virtual Vec4 getColor(PRECISION_TYPE u, PRECISION_TYPE v, const Vec4& point) const = 0;
[[nodiscard]] Vec4 getBaseColor() const { return baseColor; } [[nodiscard]] Vec4 getBaseColor() const { return baseColor; }
virtual ~Material() = default; virtual ~Material() = default;

View File

@ -64,7 +64,10 @@ namespace Raytracing {
public: public:
explicit DiffuseMaterial(const Vec4& scatterColor): Material(scatterColor) {} explicit DiffuseMaterial(const Vec4& scatterColor): Material(scatterColor) {}
[[nodiscard]] virtual ScatterResults scatter(const Ray& ray, const HitData& hitData) const; [[nodiscard]] virtual ScatterResults scatter(const Ray& ray, const HitData& hitData) const override;
[[nodiscard]] virtual Vec4 getColor(PRECISION_TYPE u, PRECISION_TYPE v, const Vec4& point) const override {
return this->baseColor;
}
}; };
class MetalMaterial : public Material { class MetalMaterial : public Material {
@ -76,7 +79,10 @@ namespace Raytracing {
public: public:
explicit MetalMaterial(const Vec4& metalColor): Material(metalColor) {} explicit MetalMaterial(const Vec4& metalColor): Material(metalColor) {}
[[nodiscard]] virtual ScatterResults scatter(const Ray& ray, const HitData& hitData) const; [[nodiscard]] virtual ScatterResults scatter(const Ray& ray, const HitData& hitData) const override;
[[nodiscard]] virtual Vec4 getColor(PRECISION_TYPE u, PRECISION_TYPE v, const Vec4& point) const override {
return this->baseColor;
}
}; };
class BrushedMetalMaterial : public MetalMaterial { class BrushedMetalMaterial : public MetalMaterial {
@ -85,14 +91,23 @@ namespace Raytracing {
public: public:
explicit BrushedMetalMaterial(const Vec4& metalColor, PRECISION_TYPE fuzzyness): MetalMaterial(metalColor), fuzzyness(fuzzyness) {} explicit BrushedMetalMaterial(const Vec4& metalColor, PRECISION_TYPE fuzzyness): MetalMaterial(metalColor), fuzzyness(fuzzyness) {}
[[nodiscard]] virtual ScatterResults scatter(const Ray& ray, const HitData& hitData) const; [[nodiscard]] virtual ScatterResults scatter(const Ray& ray, const HitData& hitData) const override;
[[nodiscard]] virtual Vec4 getColor(PRECISION_TYPE u, PRECISION_TYPE v, const Vec4& point) const override {
return this->baseColor;
}
}; };
class TexturedMaterial : public Material { class TexturedMaterial : public Material {
protected:
int width{}, height{}, channels{}, rowWidth{};
unsigned char* data;
public: public:
explicit TexturedMaterial(const std::string& file): Material({}) { explicit TexturedMaterial(const std::string& file);
} [[nodiscard]] virtual ScatterResults scatter(const Ray& ray, const HitData& hitData) const override;
[[nodiscard]] virtual Vec4 getColor(PRECISION_TYPE u, PRECISION_TYPE v, const Vec4& point) const override;
~TexturedMaterial();
}; };
struct WorldConfig { struct WorldConfig {

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

BIN
Step 3/resources/760213.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 KiB

View File

@ -0,0 +1,2 @@
# Blender 3.3.1 MTL File: 'None'
# www.blender.org

View File

@ -0,0 +1,47 @@
# Blender 3.3.1
# www.blender.org
mtllib debug.mtl
o Cube
v -1.000000 -1.000000 1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -1.000000
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 -1.0000
vn 1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 1.0000
vn -0.0000 -1.0000 -0.0000
vn -0.0000 1.0000 -0.0000
vt 0.000000 0.000000
vt 0.000000 1.000000
vt 0.000000 0.000000
vt 0.000000 0.000000
vt 0.000000 1.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 0.000000 1.000000
vt 1.000000 1.000000
vt 1.000000 0.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 1.000000 1.000000
s 0
f 2/2/1 3/6/1 1/1/1
f 4/8/2 7/14/2 3/4/2
f 8/16/3 5/9/3 7/14/3
f 6/12/4 1/1/4 5/10/4
f 7/15/5 1/1/5 3/5/5
f 4/8/6 6/13/6 8/16/6
f 2/2/1 4/7/1 3/6/1
f 4/8/2 8/16/2 7/14/2
f 8/16/3 6/11/3 5/9/3
f 6/12/4 2/2/4 1/1/4
f 7/15/5 5/10/5 1/1/5
f 4/8/6 2/3/6 6/13/6

View File

@ -14,7 +14,7 @@ void main() {
vec4 textureColor = texture(tex, outUv); vec4 textureColor = texture(tex, outUv);
//FragColor = vec4(textureColor.rgb, 1.0f); //FragColor = vec4(textureColor.rgb, 1.0f);
if (useWhite == 0) if (useWhite == 0)
FragColor = vec4(vec3(1.0, 0.0f, 0.0f) * dot(lightDir, outNormal), 1.0f); FragColor = vec4(vec3(1.0, 0.0f, 0.0f) * dot(lightDir, outNormal) * vec3(outUv, 1.0), 1.0f);
else else
FragColor = vec4(color, 1.0f); FragColor = vec4(color, 1.0f);
} }

View File

@ -126,6 +126,7 @@ int main(int argc, char** args) {
Raytracing::ModelData spider = Raytracing::OBJLoader::loadModel(parser.getOptionValue("--resources") + "spider.obj"); Raytracing::ModelData spider = Raytracing::OBJLoader::loadModel(parser.getOptionValue("--resources") + "spider.obj");
Raytracing::ModelData house = Raytracing::OBJLoader::loadModel(parser.getOptionValue("--resources") + "house.obj"); Raytracing::ModelData house = Raytracing::OBJLoader::loadModel(parser.getOptionValue("--resources") + "house.obj");
Raytracing::ModelData plane = Raytracing::OBJLoader::loadModel(parser.getOptionValue("--resources") + "plane.obj"); Raytracing::ModelData plane = Raytracing::OBJLoader::loadModel(parser.getOptionValue("--resources") + "plane.obj");
Raytracing::ModelData debugCube = Raytracing::OBJLoader::loadModel(parser.getOptionValue("--resources") + "debug.obj");
world.add("greenDiffuse", new Raytracing::DiffuseMaterial{Raytracing::Vec4{0, 1.0, 0, 1}}); world.add("greenDiffuse", new Raytracing::DiffuseMaterial{Raytracing::Vec4{0, 1.0, 0, 1}});
world.add("redDiffuse", new Raytracing::DiffuseMaterial{Raytracing::Vec4{1.0, 0, 0, 1}}); world.add("redDiffuse", new Raytracing::DiffuseMaterial{Raytracing::Vec4{1.0, 0, 0, 1}});
@ -134,6 +135,7 @@ int main(int argc, char** args) {
world.add("greenMetal", new Raytracing::MetalMaterial{Raytracing::Vec4{0.4, 1.0, 0.4, 1}}); world.add("greenMetal", new Raytracing::MetalMaterial{Raytracing::Vec4{0.4, 1.0, 0.4, 1}});
world.add("redMetal", new Raytracing::BrushedMetalMaterial{Raytracing::Vec4{1.0, 0.4, 0.4, 1}, 0.6f}); world.add("redMetal", new Raytracing::BrushedMetalMaterial{Raytracing::Vec4{1.0, 0.4, 0.4, 1}, 0.6f});
world.add("blueMetal", new Raytracing::MetalMaterial{Raytracing::Vec4{0.4, 0.4, 1.0, 1}}); world.add("blueMetal", new Raytracing::MetalMaterial{Raytracing::Vec4{0.4, 0.4, 1.0, 1}});
world.add("test", new Raytracing::TexturedMaterial{parser.getOptionValue("--resources") + "029a_-_Survival_of_the_Idiots_349.jpg"});
world.add(new Raytracing::SphereObject({0, -100.5, -1, 0}, 100, world.getMaterial("greenDiffuse"))); world.add(new Raytracing::SphereObject({0, -100.5, -1, 0}, 100, world.getMaterial("greenDiffuse")));
@ -142,6 +144,7 @@ int main(int argc, char** args) {
world.add(new Raytracing::ModelObject({5, 1, 0}, house, world.getMaterial("redDiffuse"))); world.add(new Raytracing::ModelObject({5, 1, 0}, house, world.getMaterial("redDiffuse")));
world.add(new Raytracing::ModelObject({0, 0, -5}, house, world.getMaterial("blueDiffuse"))); world.add(new Raytracing::ModelObject({0, 0, -5}, house, world.getMaterial("blueDiffuse")));
world.add(new Raytracing::ModelObject({0, 0, 5}, house, world.getMaterial("blueDiffuse"))); world.add(new Raytracing::ModelObject({0, 0, 5}, house, world.getMaterial("blueDiffuse")));
world.add(new Raytracing::ModelObject({0, 5, 0}, debugCube, world.getMaterial("test")));
if (parser.hasOption("--gui") || parser.hasOption("-g")) { if (parser.hasOption("--gui") || parser.hasOption("-g")) {
#ifdef COMPILE_GUI #ifdef COMPILE_GUI

View File

@ -4,6 +4,7 @@
*/ */
#include "engine/world.h" #include "engine/world.h"
#include "engine/raytracing.h" #include "engine/raytracing.h"
#include "engine/image/stb_image.h"
namespace Raytracing { namespace Raytracing {
@ -53,7 +54,10 @@ namespace Raytracing {
} else } else
tlog << "ray outside sphere\n"; tlog << "ray outside sphere\n";
*/ */
return {true, RayAtRoot, normal, root}; // calculate the uv coords and normalize to [0, 1]
PRECISION_TYPE u = (atan2(-RayAtRoot.z(), RayAtRoot.x()) + std::numbers::pi) / (2 * std::numbers::pi);
PRECISION_TYPE v = acos(RayAtRoot.y()) / std::numbers::pi;
return {true, RayAtRoot, normal, root, u, v};
} }
std::pair<HitData, Object*> World::checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const { std::pair<HitData, Object*> World::checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const {
@ -132,6 +136,55 @@ namespace Raytracing {
return {shouldReflect, Ray{hitData.hitPoint, newRay + Raycaster::randomUnitVector() * fuzzyness}, getBaseColor()}; return {shouldReflect, Ray{hitData.hitPoint, newRay + Raycaster::randomUnitVector() * fuzzyness}, getBaseColor()};
} }
ScatterResults TexturedMaterial::scatter(const Ray& ray, const HitData& hitData) const {
Vec4 newRay = hitData.normal + Raytracing::Raycaster::randomUnitVector().normalize();
// rays that are close to zero are liable to floating point precision errors
if (newRay.x() < EPSILON && newRay.y() < EPSILON && newRay.z() < EPSILON && newRay.w() < EPSILON)
newRay = hitData.normal;
return {true, Ray{hitData.hitPoint, newRay}, getColor(hitData.u, hitData.v, hitData.hitPoint)};
}
Vec4 TexturedMaterial::getColor(PRECISION_TYPE u, PRECISION_TYPE v, const Vec4& point) const {
// if we are unable to load the image return the debug color.
// This causes major issues (force this to happen, you'll see), indicates issue + looks really cool.
if (!data)
Vec4{0, 1, 0.2} * Vec4{u, v, 1.0};
// if you render out the debug color above you'll notice that the UV coords are rotated.
// you can also see this from the debug view, which *as of now* is rendering based on UV coords * normals * red
// so let's transform it back and ensure that our UV coords are within image bounds.
u = clamp(u, 0, 1);
// fix that pesky issue
v = 1.0 - clamp(v, 0, 1);
auto imageX = (int)(width * u);
auto imageY = (int)(height * v);
if (imageX >= width) imageX = width-1;
if (imageY >= height) imageY = height-1;
// since stbi loads in RGB8 [0, 255] but the engine works on [0, 1] we need to scale the data down.
// this is best done with a single division followed by multiple multiplication.
// since this function needs to be cheap to run.
const PRECISION_TYPE colorFactor = 1.0 / 255.0;
const auto pixelData = data + (imageY * rowWidth + imageX * channels);
return {pixelData[0] * colorFactor, pixelData[1] * colorFactor, pixelData[2] * colorFactor};
}
TexturedMaterial::TexturedMaterial(const std::string& file) : Material({}) {
// we are going to have to ignore transparency for now. TODO:?
data = stbi_load(file.c_str(), &width, &height, &channels, 0);
if (!data)
flog << "Unable to load image file " << file << "!\n";
else
ilog << "Loaded image " << file << "!\n";
rowWidth = width * channels;
}
TexturedMaterial::~TexturedMaterial() {
delete(data);
}
static HitData checkIfTriangleGotHit(const Triangle& theTriangle, const Vec4& position, const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) { static HitData checkIfTriangleGotHit(const Triangle& theTriangle, const Vec4& position, const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) {
// MöllerTrumbore intersection algorithm // MöllerTrumbore intersection algorithm
// https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/moller-trumbore-ray-triangle-intersection // https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/moller-trumbore-ray-triangle-intersection
@ -174,8 +227,26 @@ namespace Raytracing {
normal = Vec4{edge1.y() * edge2.z(), edge1.z() * edge2.x(), edge1.x() * edge2.y()} - normal = Vec4{edge1.y() * edge2.z(), edge1.z() * edge2.x(), edge1.x() * edge2.y()} -
Vec4{edge1.z() * edge2.y(), edge1.x() * edge2.z(), edge1.y() * edge2.x()}; Vec4{edge1.z() * edge2.y(), edge1.x() * edge2.z(), edge1.y() * edge2.x()};
} }
return {true, rayIntersectionPoint, normal, t};
// calculate triangle UV
// calculate the vector that runs between the vertex and the intersection point for all three vertices
auto vertex1ToIntersect = theTriangle.vertex1 - rayIntersectionPoint;
auto vertex2ToIntersect = theTriangle.vertex2 - rayIntersectionPoint;
auto vertex3ToIntersect = theTriangle.vertex3 - rayIntersectionPoint;
// the magnitude of the cross product of two vectors is double the area formed by the triangle of their intersection.
auto fullArea = 1 / Vec4::cross(theTriangle.vertex1 - theTriangle.vertex2, theTriangle.vertex1 - theTriangle.vertex3).magnitude();
// scale the area of sub triangles to be proportion to the area of the triangle
auto areaVert1 = Vec4::cross(vertex2ToIntersect, vertex3ToIntersect).magnitude() * fullArea;
auto areaVert2 = Vec4::cross(vertex3ToIntersect, vertex1ToIntersect).magnitude() * fullArea;
auto areaVert3 = Vec4::cross(vertex1ToIntersect, vertex2ToIntersect).magnitude() * fullArea;
// that area is how much each UV factors into the final UV coord
auto uv = theTriangle.uv1 * areaVert1 + theTriangle.uv2 * areaVert2 + theTriangle.uv3 * areaVert3;
return {true, rayIntersectionPoint, normal, t, uv.x(), uv.y()};
} }
return {false, Vec4(), Vec4(), 0}; return {false, Vec4(), Vec4(), 0};
} }

View File

@ -12,7 +12,7 @@ namespace Raytracing {
OpenCL openCl {0}; OpenCL openCl {0};
void OpenCL::init() { void OpenCL::init() {
openCl = OpenCL{0};
} }
OpenCL::OpenCL(int platformID, int deviceID): m_activePlatform(platformID) { OpenCL::OpenCL(int platformID, int deviceID): m_activePlatform(platformID) {
m_CL_ERR = CL_SUCCESS; m_CL_ERR = CL_SUCCESS;