Primitive sampling

This commit is contained in:
IlyaShurupov 2023-10-18 12:05:01 +03:00
parent 97f79a6483
commit 414d4e6411
9 changed files with 242 additions and 80 deletions

View file

@ -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();

View file

@ -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);
}

View file

@ -6,3 +6,10 @@ Camera = {
size_x = 400,
size_y = 600,
}
Lights = {
{
pos = { 4, 3, 3 },
intensity = 4
}
}

View file

@ -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

View file

@ -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,
}

View file

@ -10,6 +10,38 @@
#include <cstdio>
/*
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);
}
}
}

View file

@ -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<Object> mObjects;
Buffer<PointLight> mLights;
Camera mCamera;
};
@ -35,14 +42,14 @@ namespace tp {
typedef Buffer2D<RGBA> 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;