preparations for further changes
This commit is contained in:
@@ -38,8 +38,9 @@ struct spheredefaults {
|
|||||||
float emittance = 0.0f;
|
float emittance = 0.0f;
|
||||||
float reflection = 0.0f;
|
float reflection = 0.0f;
|
||||||
float refraction = 0.0f;
|
float refraction = 0.0f;
|
||||||
bool fillInside = true;
|
bool fillInside = false;
|
||||||
float voxelSize = 10.0f;
|
float voxelSize = 1.5f;
|
||||||
|
int numPoints = 15000;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ceilingdefaults {
|
struct ceilingdefaults {
|
||||||
@@ -64,41 +65,63 @@ using PointType = Eigen::Matrix<float, 3, 1>;
|
|||||||
|
|
||||||
void createSphere(const defaults& config, const spheredefaults& sconfig, Octree<int>& grid) {
|
void createSphere(const defaults& config, const spheredefaults& sconfig, Octree<int>& grid) {
|
||||||
if (!grid.empty()) grid.clear();
|
if (!grid.empty()) grid.clear();
|
||||||
int minX = std::max(0, (int)(sconfig.centerX - sconfig.radius - 1));
|
|
||||||
int maxX = std::min(config.gridSizecube, (int)(sconfig.centerX + sconfig.radius + 1));
|
|
||||||
int minY = std::max(0, (int)(sconfig.centerY - sconfig.radius - 1));
|
|
||||||
int maxY = std::min(config.gridSizecube, (int)(sconfig.centerY + sconfig.radius + 1));
|
|
||||||
int minZ = std::max(0, (int)(sconfig.centerZ - sconfig.radius - 1));
|
|
||||||
int maxZ = std::min(config.gridSizecube, (int)(sconfig.centerZ + sconfig.radius + 1));
|
|
||||||
|
|
||||||
float radSq = sconfig.radius * sconfig.radius;
|
|
||||||
float innerRadSq = 0.0f;
|
|
||||||
if (!sconfig.fillInside) {
|
|
||||||
float innerR = std::max(0.0f, sconfig.radius - 2.0f);
|
|
||||||
innerRadSq = innerR * innerR;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
float phi = M_PI * (3.0f - std::sqrt(5.0f)); // Golden angle in radians
|
||||||
Eigen::Vector3f colorVec(sconfig.color[0], sconfig.color[1], sconfig.color[2]);
|
Eigen::Vector3f colorVec(sconfig.color[0], sconfig.color[1], sconfig.color[2]);
|
||||||
for (int x = minX; x < maxX; ++x) {
|
|
||||||
for (int y = minY; y < maxY; ++y) {
|
|
||||||
for (int z = minZ; z < maxZ; ++z) {
|
|
||||||
float dx = x - sconfig.centerX;
|
|
||||||
float dy = y - sconfig.centerY;
|
|
||||||
float dz = z - sconfig.centerZ;
|
|
||||||
float distSq = dx*dx + dy*dy + dz*dz;
|
|
||||||
|
|
||||||
bool solid = distSq <= radSq;
|
// We treat sconfig.voxelSize as an overlap multiplier.
|
||||||
|
// 1.0 gives mathematical coverage, >1.0 ensures overlap for solidity.
|
||||||
|
float overlapMultiplier = std::max(0.1f, sconfig.voxelSize);
|
||||||
|
|
||||||
if (solid) {
|
float currentRadius = sconfig.radius;
|
||||||
if (!(sconfig.fillInside) && distSq < innerRadSq) {
|
|
||||||
continue;
|
// Loop for shells. If fillInside is false, this loop runs once.
|
||||||
|
// If true, it runs until radius is negligible.
|
||||||
|
while (currentRadius > 0.5f) {
|
||||||
|
// To maintain uniform visual density, the number of points on an inner shell
|
||||||
|
// should be proportional to surface area (radius^2).
|
||||||
|
float scaleFactor = currentRadius / sconfig.radius;
|
||||||
|
int currentN = std::max(4, (int)(sconfig.numPoints * scaleFactor * scaleFactor));
|
||||||
|
|
||||||
|
// Calculate the point radius required to fully cover the surface area of the sphere.
|
||||||
|
// Surface Area = 4 * PI * R^2.
|
||||||
|
// Area per point = Surface Area / N.
|
||||||
|
// Approximate point radius r: PI * r^2 = Area per point.
|
||||||
|
// r = sqrt(4 * R^2 / N) = 2 * R / sqrt(N).
|
||||||
|
float calculatedSize = (2.0f * currentRadius) / std::sqrt((float)currentN);
|
||||||
|
|
||||||
|
// Apply user-defined multiplier for extra solidity/overlap
|
||||||
|
float finalSize = calculatedSize * overlapMultiplier * overlapMultiplier;
|
||||||
|
|
||||||
|
for (int i = 0; i < currentN; ++i) {
|
||||||
|
// Fibonacci Sphere math
|
||||||
|
float y = 1.0f - (i / (float)(currentN - 1)) * 2.0f; // y goes from 1 to -1
|
||||||
|
float radiusAtY = std::sqrt(1.0f - y * y); // Radius at this height
|
||||||
|
float theta = phi * i; // Golden angle increment
|
||||||
|
|
||||||
|
float x = std::cos(theta) * radiusAtY;
|
||||||
|
float z = std::sin(theta) * radiusAtY;
|
||||||
|
|
||||||
|
PointType pos(
|
||||||
|
sconfig.centerX + x * currentRadius,
|
||||||
|
sconfig.centerY + y * currentRadius,
|
||||||
|
sconfig.centerZ + z * currentRadius
|
||||||
|
);
|
||||||
|
|
||||||
|
// Boundary check to prevent segfaults if radius pushes out of grid bounds
|
||||||
|
if (pos.x() >= 0 && pos.x() < config.gridSizecube &&
|
||||||
|
pos.y() >= 0 && pos.y() < config.gridSizecube &&
|
||||||
|
pos.z() >= 0 && pos.z() < config.gridSizecube) {
|
||||||
|
|
||||||
|
grid.set(1, pos, true, colorVec, finalSize, true,
|
||||||
|
sconfig.light, sconfig.emittance, sconfig.refraction, sconfig.reflection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PointType pos((float)x, (float)y, (float)z);
|
if (!sconfig.fillInside) break;
|
||||||
grid.set(1,pos, true, colorVec, sconfig.voxelSize, true, sconfig.light, sconfig.emittance, sconfig.refraction, sconfig.reflection);
|
|
||||||
}
|
// Decrease radius by a fraction of the point size to ensure shells overlap
|
||||||
}
|
currentRadius -= (finalSize * 0.75f);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +145,7 @@ void addCeilingLight(const defaults& config, const ceilingdefaults& ceilingconf,
|
|||||||
void livePreview(Octree<int>& grid, defaults& config, const Camera& cam) {
|
void livePreview(Octree<int>& grid, defaults& config, const Camera& cam) {
|
||||||
std::lock_guard<std::mutex> lock(PreviewMutex);
|
std::lock_guard<std::mutex> lock(PreviewMutex);
|
||||||
updatePreview = true;
|
updatePreview = true;
|
||||||
frame currentPreviewFrame = grid.renderFrame(cam, config.outWidth, config.outHeight, frame::colormap::RGB);
|
frame currentPreviewFrame = grid.renderFrame(cam, config.outWidth, config.outHeight, frame::colormap::RGB, 4, 3);
|
||||||
|
|
||||||
glGenTextures(1, &textu);
|
glGenTextures(1, &textu);
|
||||||
glBindTexture(GL_TEXTURE_2D, textu);
|
glBindTexture(GL_TEXTURE_2D, textu);
|
||||||
@@ -217,10 +240,14 @@ int main() {
|
|||||||
Octree<int> grid(minBound, maxBound, 16, 16);
|
Octree<int> grid(minBound, maxBound, 16, 16);
|
||||||
bool gridInitialized = false;
|
bool gridInitialized = false;
|
||||||
float ghalf = config.gridSizecube / 2.f;
|
float ghalf = config.gridSizecube / 2.f;
|
||||||
Camera cam(PointType(ghalf, ghalf, ghalf), PointType(0,0,1), PointType(0,1,0), 80);
|
|
||||||
spheredefaults sphereConf;
|
spheredefaults sphereConf;
|
||||||
ceilingdefaults ceilingConf;
|
ceilingdefaults ceilingConf;
|
||||||
|
|
||||||
|
sphereConf.centerX = ghalf;
|
||||||
|
sphereConf.centerY = ghalf;
|
||||||
|
sphereConf.centerZ = ghalf;
|
||||||
|
|
||||||
bool autoRotate = false;
|
bool autoRotate = false;
|
||||||
bool autoRotateView = false;
|
bool autoRotateView = false;
|
||||||
float rotationSpeedX = 0.1f;
|
float rotationSpeedX = 0.1f;
|
||||||
@@ -241,6 +268,7 @@ int main() {
|
|||||||
float camvY = 0.f;
|
float camvY = 0.f;
|
||||||
float camvZ = 0.f;
|
float camvZ = 0.f;
|
||||||
float camspeed = 50;
|
float camspeed = 50;
|
||||||
|
Camera cam(PointType(400, 400, 400), PointType(0,0,1), PointType(0,1,0), 80, camspeed);
|
||||||
|
|
||||||
// Keyboard state tracking
|
// Keyboard state tracking
|
||||||
std::map<int, bool> keyStates;
|
std::map<int, bool> keyStates;
|
||||||
@@ -254,6 +282,9 @@ int main() {
|
|||||||
deltaTime = currentTime - lastFrameTime;
|
deltaTime = currentTime - lastFrameTime;
|
||||||
lastFrameTime = currentTime;
|
lastFrameTime = currentTime;
|
||||||
|
|
||||||
|
if (autoRotate) autoRotationTime += deltaTime;
|
||||||
|
if (autoRotateView) autoRotationAngle += deltaTime;
|
||||||
|
|
||||||
glfwPollEvents();
|
glfwPollEvents();
|
||||||
|
|
||||||
for (int i = GLFW_KEY_SPACE; i <= GLFW_KEY_LAST; i++) {
|
for (int i = GLFW_KEY_SPACE; i <= GLFW_KEY_LAST; i++) {
|
||||||
@@ -315,7 +346,7 @@ int main() {
|
|||||||
{
|
{
|
||||||
ImGui::Begin("Controls");
|
ImGui::Begin("Controls");
|
||||||
|
|
||||||
ImGui::Text("Sphere Parameters");
|
ImGui::Text("Planet");
|
||||||
float pos[3] = { sphereConf.centerX, sphereConf.centerY, sphereConf.centerZ };
|
float pos[3] = { sphereConf.centerX, sphereConf.centerY, sphereConf.centerZ };
|
||||||
if (ImGui::DragFloat3("Center (X,Y,Z)", pos, 1.0f, 0.0f, (float)config.gridSizecube)) {
|
if (ImGui::DragFloat3("Center (X,Y,Z)", pos, 1.0f, 0.0f, (float)config.gridSizecube)) {
|
||||||
sphereConf.centerX = pos[0];
|
sphereConf.centerX = pos[0];
|
||||||
@@ -324,8 +355,15 @@ int main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImGui::DragFloat("Radius", &sphereConf.radius, 0.5f, 1.0f, 250.0f);
|
ImGui::DragFloat("Radius", &sphereConf.radius, 0.5f, 1.0f, 250.0f);
|
||||||
|
|
||||||
|
// Replaced traditional voxel sizing with Point Count logic
|
||||||
|
ImGui::DragInt("Point Count", &sphereConf.numPoints, 100, 100, 200000);
|
||||||
|
ImGui::DragFloat("Density (Overlap)", &sphereConf.voxelSize, 0.05f, 0.1f, 5.0f);
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Multiplies calculated point size. >1.0 ensures solid surface.");
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::ColorEdit3("Color", sphereConf.color);
|
ImGui::ColorEdit3("Color", sphereConf.color);
|
||||||
ImGui::DragFloat("Voxel Size", &sphereConf.voxelSize, 0.1f, 0.1f, 5.0f);
|
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Checkbox("Is Light", &sphereConf.light);
|
ImGui::Checkbox("Is Light", &sphereConf.light);
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ struct Camera {
|
|||||||
float movementSpeed;
|
float movementSpeed;
|
||||||
float rotationSpeed;
|
float rotationSpeed;
|
||||||
|
|
||||||
|
Camera() : origin(Vector3f(0,0,0)), direction(Vector3f(0,0,0)), up(Vector3f(0,0,0)), fov(80), movementSpeed(10), rotationSpeed(10) {}
|
||||||
|
|
||||||
Camera(const Vector3f& pos, const Vector3f& viewdir, const Vector3f& up, float fov = 80,
|
Camera(const Vector3f& pos, const Vector3f& viewdir, const Vector3f& up, float fov = 80,
|
||||||
float moveSpeed = 1.0f, float rotSpeed = 0.5f)
|
float moveSpeed = 1.0f, float rotSpeed = 0.5f)
|
||||||
: origin(pos), direction(viewdir.normalized()), up(up.normalized()), fov(fov), movementSpeed(moveSpeed), rotationSpeed(rotSpeed) {}
|
: origin(pos), direction(viewdir.normalized()), up(up.normalized()), fov(fov), movementSpeed(moveSpeed), rotationSpeed(rotSpeed) {}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
constexpr int Dim = 3;
|
constexpr int Dim = 3;
|
||||||
constexpr int maxBounces = 4;
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class Octree {
|
class Octree {
|
||||||
@@ -384,7 +383,13 @@ private:
|
|||||||
float x = randomValueNormalDistribution(state);
|
float x = randomValueNormalDistribution(state);
|
||||||
float y = randomValueNormalDistribution(state);
|
float y = randomValueNormalDistribution(state);
|
||||||
float z = randomValueNormalDistribution(state);
|
float z = randomValueNormalDistribution(state);
|
||||||
return PointType(x,y,z).normalized();
|
PointType randomDir(x, y, z);
|
||||||
|
randomDir.normalize();
|
||||||
|
|
||||||
|
if (randomDir.dot(normal) < 0.0f) {
|
||||||
|
randomDir = -randomDir;
|
||||||
|
}
|
||||||
|
return randomDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
float rgbToGrayscale(const Eigen::Vector3f& color) const {
|
float rgbToGrayscale(const Eigen::Vector3f& color) const {
|
||||||
@@ -435,7 +440,7 @@ public:
|
|||||||
|
|
||||||
uint32_t magic;
|
uint32_t magic;
|
||||||
readVal(in, magic);
|
readVal(in, magic);
|
||||||
if (magic != 0x0C78E3) {
|
if (magic != 0x79676733) {
|
||||||
std::cerr << "Invalid Octree file format" << std::endl;
|
std::cerr << "Invalid Octree file format" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -466,6 +471,8 @@ public:
|
|||||||
if (!node || tMin > tMax) return;
|
if (!node || tMin > tMax) return;
|
||||||
if (node->isLeaf) {
|
if (node->isLeaf) {
|
||||||
for (const auto& pointData : node->points) {
|
for (const auto& pointData : node->points) {
|
||||||
|
if (!pointData->active) continue;
|
||||||
|
|
||||||
PointType toPoint = pointData->position - origin;
|
PointType toPoint = pointData->position - origin;
|
||||||
float projection = toPoint.dot(dir);
|
float projection = toPoint.dot(dir);
|
||||||
if (projection >= 0 && projection <= maxDist) {
|
if (projection >= 0 && projection <= maxDist) {
|
||||||
@@ -517,7 +524,7 @@ public:
|
|||||||
return hits;
|
return hits;
|
||||||
}
|
}
|
||||||
|
|
||||||
frame renderFrame(const Camera& cam, int height, int width, frame::colormap colorformat = frame::colormap::RGB) {
|
frame renderFrame(const Camera& cam, int height, int width, frame::colormap colorformat = frame::colormap::RGB, int samplesPerPixel = 2, int maxBounces = 4) {
|
||||||
PointType origin = cam.origin;
|
PointType origin = cam.origin;
|
||||||
PointType dir = cam.direction.normalized();
|
PointType dir = cam.direction.normalized();
|
||||||
PointType up = cam.up.normalized();
|
PointType up = cam.up.normalized();
|
||||||
@@ -549,7 +556,7 @@ public:
|
|||||||
if (bounces > maxBounces) return {0,0,0};
|
if (bounces > maxBounces) return {0,0,0};
|
||||||
|
|
||||||
auto hits = voxelTraverse(rayOrig, rayDir, rayLength, true);
|
auto hits = voxelTraverse(rayOrig, rayDir, rayLength, true);
|
||||||
if (hits.empty() && bounces < 1) {
|
if (hits.empty() && bounces == 0) {
|
||||||
return defaultColor;
|
return defaultColor;
|
||||||
} else if (hits.empty()) {
|
} else if (hits.empty()) {
|
||||||
return {0,0,0};
|
return {0,0,0};
|
||||||
@@ -626,21 +633,26 @@ public:
|
|||||||
#pragma omp parallel for schedule(dynamic) collapse(2)
|
#pragma omp parallel for schedule(dynamic) collapse(2)
|
||||||
for (int y = 0; y < height; ++y) {
|
for (int y = 0; y < height; ++y) {
|
||||||
for (int x = 0; x < width; ++x) {
|
for (int x = 0; x < width; ++x) {
|
||||||
|
int pidx = (y * width + x);
|
||||||
|
uint32_t seed = pidx * 1973 + 9277;
|
||||||
|
int idx = pidx * channels;
|
||||||
|
|
||||||
float px = (2.0f * (x + 0.5f) / width - 1.0f) * tanfovx;
|
float px = (2.0f * (x + 0.5f) / width - 1.0f) * tanfovx;
|
||||||
float py = (1.0f - 2.0f * (y + 0.5f) / height) * tanfovy;
|
float py = (1.0f - 2.0f * (y + 0.5f) / height) * tanfovy;
|
||||||
|
|
||||||
PointType rayDir = dir + (right * px) + (up * py);
|
PointType rayDir = dir + (right * px) + (up * py);
|
||||||
rayDir.normalize();
|
rayDir.normalize();
|
||||||
|
|
||||||
int pidx = (y * width + x);
|
Eigen::Vector3f accumulatedColor(0.0f, 0.0f, 0.0f);
|
||||||
uint32_t seed = pidx * 1973 + 9277;
|
|
||||||
int idx = pidx * channels;
|
|
||||||
|
|
||||||
Eigen::Vector3f color = traceRay(origin, rayDir, 0, seed);
|
for(int s = 0; s < samplesPerPixel; ++s) {
|
||||||
|
accumulatedColor += traceRay(origin, rayDir, 0, seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
Eigen::Vector3f color = accumulatedColor / static_cast<float>(samplesPerPixel);
|
||||||
|
|
||||||
color = color.cwiseMax(0.0f).cwiseMin(1.0f);
|
color = color.cwiseMax(0.0f).cwiseMin(1.0f);
|
||||||
|
|
||||||
|
|
||||||
switch(colorformat) {
|
switch(colorformat) {
|
||||||
case frame::colormap::B:
|
case frame::colormap::B:
|
||||||
colorBuffer[idx ] = static_cast<uint8_t>(rgbToGrayscale(color) * 255.0f);
|
colorBuffer[idx ] = static_cast<uint8_t>(rgbToGrayscale(color) * 255.0f);
|
||||||
@@ -667,7 +679,6 @@ public:
|
|||||||
colorBuffer[idx + 2] = static_cast<uint8_t>(color[0] * 255.0f);
|
colorBuffer[idx + 2] = static_cast<uint8_t>(color[0] * 255.0f);
|
||||||
colorBuffer[idx + 3] = 255;
|
colorBuffer[idx + 3] = 255;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user