diff --git a/Math/private/Topology.cpp b/Math/private/Topology.cpp index d47b0da..4b1d534 100644 --- a/Math/private/Topology.cpp +++ b/Math/private/Topology.cpp @@ -35,6 +35,10 @@ bool TrigCache::castRay(const Ray& ray) const { static halnf a, f, u, v; static halnf t; + if (ray.dir.dot(mNormal) > 0) { + return false; + } + h = ray.dir.cross(mEdgeP1P3); a = mEdgeP1P2.dot(h); diff --git a/RayTracer/applications/Rayt.cpp b/RayTracer/applications/Rayt.cpp index 4cfee71..ea6b6cd 100644 --- a/RayTracer/applications/Rayt.cpp +++ b/RayTracer/applications/Rayt.cpp @@ -51,7 +51,7 @@ void renderCommand(const String& scenePath) { auto start = get_time(); - rayt.render(scene, output); + rayt.render(scene, output, settings); auto end = get_time(); diff --git a/RayTracer/applications/SceneLoad.cpp b/RayTracer/applications/SceneLoad.cpp index 7112c9f..36e062c 100644 --- a/RayTracer/applications/SceneLoad.cpp +++ b/RayTracer/applications/SceneLoad.cpp @@ -46,26 +46,68 @@ bool loadMeshes(tp::Scene& scene, const tp::String& objetsPath) { return scene.mObjects.size(); } -bool getVector(lua_State* L, const char* name, tp::Vec3F& out, const char* parent) { - lua_getglobal(L, name); - - if (lua_istable(L, -1)) { - lua_pushstring(L, "x"); - lua_gettable(L, -2); - - if (lua_isnumber(L, -1)) { - out.x = lua_tonumber(L, -1); - } else { - printf("Component of vector '%s' is not found in %s\n\n", name, parent); - return false; - } - - lua_pop(L, 1); - - } else { - printf("Vector '%s' is not found in %s\n\n", name, parent); - return false; +// Function to read a Lua table representing RenderSettings +int readRenderSettings(lua_State* L, tp::RayTracer::RenderSettings& settings) { + lua_getglobal(L, "RenderSettings"); + if (!lua_istable(L, -1)) { + printf("RenderSettings is not a table.\n"); + return 0; // Error } + + // Read depth field + lua_getfield(L, -1, "depth"); + if (lua_isnumber(L, -1)) { + settings.depth = (int) lua_tonumber(L, -1); + } else { + printf("RenderSettings 'depth' field is missing or not a number.\n"); + lua_pop(L, 1); // Pop the 'depth' field + return 0; // Error + } + lua_pop(L, 1); // Pop the 'depth' field + + // Read spray field + lua_getfield(L, -1, "spray"); + if (lua_isnumber(L, -1)) { + settings.spray = (int) lua_tonumber(L, -1); + } else { + printf("RenderSettings 'spray' field is missing or not a number.\n"); + lua_pop(L, 1); // Pop the 'spray' field + return 0; // Error + } + lua_pop(L, 1); // Pop the 'spray' field + + return 1; // Success +} + +// Function to read a Lua table representing a light +int readLight(lua_State* L, tp::PointLight* light) { + lua_getfield(L, -1, "pos"); // Get the "pos" field from the light table + if (!lua_istable(L, -1)) { + printf("Light is missing the 'pos' table.\n"); + return 0; // Error + } + for (int i = 0; i < 3; i++) { + lua_rawgeti(L, -1, i + 1); // Index is 1-based in Lua + if (!lua_isnumber(L, -1)) { + printf("Light 'pos' field is not a number at index %d.\n", i); + lua_pop(L, 2); // Pop both the number and the 'pos' table + return 0; // Error + } + light->pos[i] = lua_tonumber(L, -1); + lua_pop(L, 1); // Pop the number + } + lua_pop(L, 1); // Pop the 'pos' table + + lua_getfield(L, -1, "intensity"); // Get the "intensity" field from the light table + if (!lua_isnumber(L, -1)) { + printf("Light is missing the 'intensity' field or it's not a number.\n"); + lua_pop(L, 1); // Pop the 'intensity' field + return 0; // Error + } + light->intensity = lua_tonumber(L, -1); + lua_pop(L, 1); // Pop the 'intensity' field + + return 1; // Success } void loadScene(tp::Scene& scene, const tp::String& scenePath, tp::RayTracer::RenderSettings& settings) { @@ -154,5 +196,38 @@ void loadScene(tp::Scene& scene, const tp::String& scenePath, tp::RayTracer::Ren scene.mCamera.setFar(100); scene.mCamera.setRatio((tp::halnf) size_y / (tp::halnf) size_x); + // ---------- LIGHTS + { + lua_getglobal(L, "Lights"); + if (!lua_istable(L, -1)) { + printf("Lights is not a table.\n"); + lua_close(L); + return; // Error + } + + // Read and process each light in the "Lights" table + int numLights = lua_rawlen(L, -1); // Get the number of lights in the table + for (int i = 1; i <= numLights; i++) { + lua_rawgeti(L, -1, i); // Get the i-th element (light) from the table + if (lua_istable(L, -1)) { + tp::PointLight light; + if (!readLight(L, &light)) { + printf("Cant read lights data\n"); + lua_close(L); + return; // Error + } + scene.mLights.append(light); + } + lua_pop(L, 1); // Pop the i-th light table + } + } + + // ----------- settings -------------- + if (!readRenderSettings(L, settings)) { + printf("Cant Read Render Settings"); + lua_close(L); + return; // Error + } + lua_close(L); } diff --git a/RayTracer/applications/rsc/cube/script.lua b/RayTracer/applications/rsc/cube/script.lua index bdb4935..d4f220a 100644 --- a/RayTracer/applications/rsc/cube/script.lua +++ b/RayTracer/applications/rsc/cube/script.lua @@ -6,3 +6,10 @@ Camera = { size_x = 400, size_y = 600, } + +Lights = { + { + pos = { 4, 3, 3 }, + intensity = 4 + } +} \ No newline at end of file diff --git a/RayTracer/applications/rsc/scene.blend1 b/RayTracer/applications/rsc/scene.blend1 deleted file mode 100644 index fe7b753..0000000 Binary files a/RayTracer/applications/rsc/scene.blend1 and /dev/null differ diff --git a/RayTracer/applications/rsc/scene/meshes.obj b/RayTracer/applications/rsc/scene/meshes.obj index 4367775..4368e9e 100644 --- a/RayTracer/applications/rsc/scene/meshes.obj +++ b/RayTracer/applications/rsc/scene/meshes.obj @@ -11,31 +11,31 @@ v -1.039076 -1.039076 -1.285234 v 1.039076 -1.039076 1.792917 v 1.039076 -1.039076 -1.285234 vt 0.625000 0.500000 -vt 0.875000 0.500000 -vt 0.875000 0.750000 vt 0.625000 0.750000 +vt 0.875000 0.750000 +vt 0.875000 0.500000 vt 0.375000 0.750000 -vt 0.625000 1.000000 vt 0.375000 1.000000 +vt 0.625000 1.000000 vt 0.375000 0.000000 -vt 0.625000 0.000000 -vt 0.625000 0.250000 vt 0.375000 0.250000 +vt 0.625000 0.250000 +vt 0.625000 0.000000 vt 0.125000 0.500000 -vt 0.375000 0.500000 vt 0.125000 0.750000 -vn 0.0000 0.0000 1.0000 -vn 1.0000 0.0000 0.0000 -vn 0.0000 -1.0000 0.0000 +vt 0.375000 0.500000 vn 0.0000 0.0000 -1.0000 vn -1.0000 0.0000 0.0000 +vn 0.0000 1.0000 0.0000 +vn 0.0000 0.0000 1.0000 +vn 1.0000 0.0000 0.0000 usemtl Material s off -f 1/1/1 5/2/1 7/3/1 3/4/1 -f 4/5/2 3/4/2 7/6/2 8/7/2 -f 8/8/3 7/9/3 5/10/3 6/11/3 -f 6/12/4 2/13/4 4/5/4 8/14/4 -f 6/11/5 5/10/5 1/1/5 2/13/5 +f 1/1/1 3/2/1 7/3/1 5/4/1 +f 4/5/2 8/6/2 7/7/2 3/2/2 +f 8/8/3 6/9/3 5/10/3 7/11/3 +f 6/12/4 8/13/4 4/5/4 2/14/4 +f 6/9/5 2/14/5 1/1/5 5/10/5 o Cube.001 v 0.411013 -0.208216 -1.264212 v 0.411013 -0.208216 -0.612621 diff --git a/RayTracer/applications/rsc/scene/script.lua b/RayTracer/applications/rsc/scene/script.lua index 7e6f251..ac2cd3b 100644 --- a/RayTracer/applications/rsc/scene/script.lua +++ b/RayTracer/applications/rsc/scene/script.lua @@ -3,6 +3,22 @@ Meshes = "meshes.obj" Camera = { pos = { 0.5, 4.5, 0.2 }, - size_x = 500, - size_y = 700, + size_x = 600, + size_y = 800, } + +Lights = { + { + pos = { -0.5, 3.5, 1 }, + intensity = 1 + }, + { + pos = { 0, 0, 1 }, + intensity = 0.5 + }, +} + +RenderSettings = { + depth = 1, + spray = 10, +} \ No newline at end of file diff --git a/RayTracer/private/RayTracer.cpp b/RayTracer/private/RayTracer.cpp index 3c5e93f..09f598d 100644 --- a/RayTracer/private/RayTracer.cpp +++ b/RayTracer/private/RayTracer.cpp @@ -10,6 +10,38 @@ #include +/* + if (1) { + const auto& points = castData.obj->mCache.TransformedPoints; + const auto& normals = castData.obj->mCache.TransformedNormals; + const auto trig = castData.trig; + +const auto& n1 = normals[trig->mP1]; +const auto& n2 = normals[trig->mP2]; +const auto& n3 = normals[trig->mP3]; + +auto v0 = points[trig->mP1]; +auto v1 = points[trig->mP2]; +auto v2 = points[trig->mP3]; + +// Calculate barycentric coordinates +Vec3F barycentric; + +// Calculate the area of the triangle +auto areaABC = (halnf) (v1 - v0).cross(v2 - v0).length(); +auto areaPBC = (halnf) (v1 - castData.hitPos).cross(v2 - castData.hitPos).length(); +auto areaPCA = (halnf) (v2 - castData.hitPos).cross(v0 - castData.hitPos).length(); + +// Calculate the barycentric coordinates +barycentric.x = areaPBC / areaABC; +barycentric.y = areaPCA / areaABC; +barycentric.z = 1.0f - barycentric.x - barycentric.y; + +// Interpolate the normal using barycentric coordinates +normal = n1 * barycentric.x + n2 * barycentric.y + n3 * barycentric.z; +} +*/ + using namespace tp; ModuleManifest* sDependencies[] = {&gModuleMath, &gModuleCommandLine, &gModuleConnection, nullptr}; @@ -28,7 +60,7 @@ void RayTracer::castRay(const Ray& ray, RayCastData& out, alnf far) { auto dist = (trig->getHitPos() - ray.pos).length2(); - if (far > dist) { + if (far > dist && dist > EPSILON) { out.trig = &trig.data(); out.hitPos = trig->getHitPos(); out.obj = &obj.data(); @@ -41,9 +73,58 @@ void RayTracer::castRay(const Ray& ray, RayCastData& out, alnf far) { } } -void RayTracer::render(const Scene& scene, RayTracer::RenderBuffer& buff) { +void RayTracer::cycle(const RayCastData& castData, LightData& out, uhalni depth) { + if (depth) { + depth--; + + Vec3F normal = castData.trig->getNormal(); + normal.normalize(); + + const auto delta1 = castData.trig->mEdgeP1P2.unitV(); + const auto delta2 = normal.cross(delta1); + + for (auto idx : Range(mSettings.spray)) { + RayCastData materialCastData; + LightData lightData; + + auto d1 = ((halnf) randomFloat() - 0.5f) * 2; + auto d2 = ((halnf) randomFloat() - 0.5f) * 2; + + auto sprayNormal = (normal + delta1 * d1 + delta2 * d2).normalize(); + + castRay({sprayNormal, castData.hitPos}, materialCastData, mScene->mCamera.getFar()); + if (materialCastData.hit) { + cycle(materialCastData, lightData, depth); + out.intensity += lightData.intensity * 0.2; + } + } + } + + // cast for light + for (auto light : mScene->mLights) { + RayCastData lightCastData; + auto dir = light->pos - castData.hitPos; + auto length = (halnf) dir.length(); + + Ray lightRay = {dir.unitV(), castData.hitPos}; + + if (lightRay.dir.dot(castData.trig->mNormal) < 0) { + continue; + } + castRay(lightRay, lightCastData, length); + + if (lightCastData.hit) { + continue; + } + + out.intensity += light->intensity / (length * length); + } +} + +void RayTracer::render(const Scene& scene, RayTracer::RenderBuffer& buff, const RenderSettings& settings) { mScene = &scene; mBuff = &buff; + mSettings = settings; auto pos = mScene->mCamera.getPos(); auto fov = mScene->mCamera.getFOV(); @@ -71,54 +152,20 @@ void RayTracer::render(const Scene& scene, RayTracer::RenderBuffer& buff) { for (RayTracer::RenderBuffer::Index i = 0; i < buff.size().x; i++) { for (RayTracer::RenderBuffer::Index j = 0; j < buff.size().y; j++) { iterPoint = planeLeftTop + ((deltaX * (halnf) i) + (deltaY * (halnf) j)); - ray.dir = (iterPoint - pos).unitV(); castRay(ray, castData, mScene->mCamera.getFar()); if (castData.hit) { - Vec3F normal = castData.trig->getNormal(); + LightData lightData; + cycle(castData, lightData, mSettings.depth); - if (1) { - const auto& points = castData.obj->mCache.TransformedPoints; - const auto& normals = castData.obj->mCache.TransformedNormals; - const auto trig = castData.trig; + lightData.intensity = clamp(lightData.intensity, 0.f, 1.f); + RGBA col = {lightData.intensity, lightData.intensity, lightData.intensity, 1.f}; - const auto& n1 = normals[trig->mP1]; - const auto& n2 = normals[trig->mP2]; - const auto& n3 = normals[trig->mP3]; - - auto v0 = points[trig->mP1]; - auto v1 = points[trig->mP2]; - auto v2 = points[trig->mP3]; - - // Calculate barycentric coordinates - Vec3F barycentric; - - // Calculate the area of the triangle - auto areaABC = (halnf) (v1 - v0).cross(v2 - v0).length(); - auto areaPBC = (halnf) (v1 - castData.hitPos).cross(v2 - castData.hitPos).length(); - auto areaPCA = (halnf) (v2 - castData.hitPos).cross(v0 - castData.hitPos).length(); - - // Calculate the barycentric coordinates - barycentric.x = areaPBC / areaABC; - barycentric.y = areaPCA / areaABC; - barycentric.z = 1.0f - barycentric.x - barycentric.y; - - // Interpolate the normal using barycentric coordinates - normal = n1 * barycentric.x + n2 * barycentric.y + n3 * barycentric.z; - } - - normal.normalize(); - - RGBA col = {normal.x * 0.5f + 0.5f, normal.y * 0.5f + 0.5f, normal.z * 0.5f + 0.5f, 1.f}; buff.set({i, j}, col); - - // auto depth = 1 - clamp((halnf) (castData.hitPos - ray.pos).length(), 0.f, mScene->mCamera.getFar()) / mScene->mCamera.getFar(); - // buff.set({i, j}, {depth, depth, depth, 1.f}); - } else { - buff.set({i, j}, RGBA(0.f, 0.f, 0.f, 0.f)); + buff.set({i, j}, 0.f); } } } diff --git a/RayTracer/public/RayTracer.hpp b/RayTracer/public/RayTracer.hpp index 812d871..7c5afd2 100644 --- a/RayTracer/public/RayTracer.hpp +++ b/RayTracer/public/RayTracer.hpp @@ -21,12 +21,19 @@ namespace tp { TopologyCache mCache; }; + struct PointLight { + Vec3F pos; + halnf fallOut = 1.f; + halnf intensity = 1.f; + }; + class Scene { public: Scene() = default; public: Buffer mObjects; + Buffer mLights; Camera mCamera; }; @@ -35,14 +42,14 @@ namespace tp { typedef Buffer2D RenderBuffer; struct RenderSettings { - alni samplesiPerPixel = 1; - alni rayBounces = 1; + uhalni depth = 2; + uhalni spray = 1; Vec2I size; }; public: RayTracer() = default; - void render(const Scene& scene, RenderBuffer& buff); + void render(const Scene& scene, RenderBuffer& buff, const RenderSettings& settings); private: struct RayCastData { @@ -52,7 +59,13 @@ namespace tp { bool hit = false; }; + struct LightData { + halnf intensity = 0; + }; + + private: void castRay(const Ray& ray, RayCastData& out, alnf far); + void cycle(const RayCastData& ray, LightData& out, uhalni depth); private: RenderSettings mSettings;