This commit is contained in:
Michaël Lemaire 2016-07-22 17:50:04 +02:00
parent ce33390321
commit fb16682876
21 changed files with 484 additions and 23 deletions

View file

@ -32,10 +32,16 @@ class BASICSSHARED_EXPORT Maths {
*/
static double zeroPoint(double segment_length, double edge0, double edge1);
static inline bool equals(double v1, double v2, double eps = DOUBLE_EPS) {
double d = v1 - v2;
return d <= eps and d >= -eps;
}
static constexpr double PI = 3.141592653589793238462643383279;
static constexpr double PI_2 = PI / 2.0;
static constexpr double PI_4 = PI / 4.0;
static constexpr double TWOPI = 2.0 * PI;
static constexpr double DOUBLE_EPS = 0.0000000001;
};
}
}

View file

@ -65,6 +65,10 @@ Vector3 Vector3::normalize() const {
}
}
bool Vector3::isNormalized() const {
return Maths::equals(getNorm(), 1.0);
}
double Vector3::dotProduct(const Vector3 &other) const {
return x * other.x + y * other.y + z * other.z;
}

View file

@ -51,6 +51,7 @@ class BASICSSHARED_EXPORT Vector3 {
Vector3 scale(double scaling) const;
double getNorm() const;
Vector3 normalize() const;
bool isNormalized() const;
Vector3 add(double x, double y, double z) const;
Vector3 add(const Vector3 &other) const;

View file

@ -1,9 +1,11 @@
#include "AtmosphereDefinition.h"
#include "CameraDefinition.h"
#include "Logs.h"
#include "RandomGenerator.h"
#include "RenderConfig.h"
#include "Scenery.h"
#include "SoftwareCanvasRenderer.h"
#include "SoftwareRayRenderer.h"
#include "TimeManager.h"
#include <cstring>
@ -17,7 +19,10 @@ static void displayHelp() {
printf(" -h Show this help\n");
printf(" -ts Run the render test suite\n");
printf(" -f x Saved file to load (str)\n");
printf(" -n Number of pictures in the sequence\n");
printf(" -r x Random seed to use (int)\n");
printf(" -s x First picture in the sequence (int)\n");
printf(" -n x Number of pictures in the sequence (int)\n");
printf(" -rt Use ray tracing renderer instead of rasterization\n");
printf(" -rw x Render width (int)\n");
printf(" -rh x Render height (int)\n");
printf(" -rq x Render quality (int, 1 to 10)\n");
@ -37,6 +42,7 @@ int main(int argc, char **argv) {
RenderConfig conf_render_params(480, 270, 1, 3);
int conf_first_picture = 0;
int conf_nb_pictures = 1;
long unsigned int conf_seed = 0;
double conf_daytime_start = -1.0;
double conf_daytime_step = 0.0;
double conf_camera_step_x = 0.0;
@ -46,6 +52,7 @@ int main(int argc, char **argv) {
double conf_wind_z = 0.0;
int outputcount;
char outputpath[500];
bool raytracing = false;
argc--;
argv++;
@ -57,10 +64,16 @@ int main(int argc, char **argv) {
} else if (strcmp(*argv, "-ts") == 0 || strcmp(*argv, "--testsuite") == 0) {
runTestSuite();
return 0;
} else if (strcmp(*argv, "-rt") == 0 || strcmp(*argv, "--raytracing") == 0) {
raytracing = true;
} else if (strcmp(*argv, "-f") == 0 || strcmp(*argv, "--file") == 0) {
if (argc--) {
conf_file_path = *(++argv);
}
} else if (strcmp(*argv, "-r") == 0 || strcmp(*argv, "--randomseed") == 0) {
if (argc--) {
conf_seed = atoi(*(++argv));
}
} else if (strcmp(*argv, "-s") == 0 || strcmp(*argv, "--start") == 0) {
if (argc--) {
conf_first_picture = atoi(*(++argv));
@ -120,10 +133,15 @@ int main(int argc, char **argv) {
printf("Initializing ...\n");
Scenery *scenery = new Scenery();
RandomGenerator generator(conf_seed);
if (conf_file_path) {
scenery->loadGlobal(conf_file_path);
} else {
if (conf_seed) {
RandomGeneratorDefault = generator;
}
printf("Using seed %lu\n", RandomGeneratorDefault.getSeed());
scenery->autoPreset();
}
@ -140,7 +158,7 @@ int main(int argc, char **argv) {
Vector3 step = {conf_camera_step_x, conf_camera_step_y, conf_camera_step_z};
camera->setLocation(camera->getLocation().add(step));
renderer = new SoftwareCanvasRenderer(scenery);
renderer = raytracing ? new SoftwareRayRenderer(scenery) : new SoftwareCanvasRenderer(scenery);
renderer->setConfig(conf_render_params);
if (outputcount >= conf_first_picture) {

View file

@ -7,7 +7,7 @@
#include "RenderConfig.h"
#include "RenderPreviewProvider.h"
#include "RenderProgress.h"
#include "SoftwareCanvasRenderer.h"
#include "SoftwareRayRenderer.h"
#include "Thread.h"
#include <QQuickItem>
#include <QSize>
@ -99,7 +99,8 @@ void RenderProcess::startRender(Scenery *scenery, const RenderConfig &config) {
delete renderer;
}
renderer = new SoftwareCanvasRenderer(scenery);
// renderer = new SoftwareCanvasRenderer(scenery);
renderer = new SoftwareRayRenderer(scenery);
renderer->setConfig(config);
destination->setCanvas(renderer->getCanvas());

View file

@ -1,4 +1,62 @@
#include "RayCastingManager.h"
RayCastingManager::RayCastingManager() {
#include "Color.h"
#include "RayIntersector.h"
#include "Vector3.h"
#include <algorithm>
#include <vector>
class RayCastingManager::pimpl {
public:
vector<shared_ptr<RayIntersector>> intersectors;
};
RayCastingManager::RayCastingManager() : impl(new pimpl()) {
}
RayCastingManager::~RayCastingManager() {
}
int RayCastingManager::getIntersectorCount() {
return (int)impl->intersectors.size();
}
shared_ptr<RayIntersector> RayCastingManager::getIntersector(int position) {
if (position >= 0 && position < getIntersectorCount()) {
return impl->intersectors[position];
} else {
return nullptr;
}
}
void RayCastingManager::unregisterAllIntersectors() {
impl->intersectors.clear();
}
static bool _cmp_priority(const shared_ptr<RayIntersector> i1, const shared_ptr<RayIntersector> i2) {
return i1->getPriority() > i2->getPriority();
}
void RayCastingManager::registerIntersector(shared_ptr<RayIntersector> intersector) {
impl->intersectors.push_back(intersector);
sort(impl->intersectors.begin(), impl->intersectors.end(), _cmp_priority);
}
Color RayCastingManager::getFinal(const Vector3 &eye, const Vector3 &direction) const {
double limit = 10000.0;
shared_ptr<RayIntersector> hit;
Vector3 hit_location;
for (auto &intersector : impl->intersectors) {
if (intersector->findIntersection(eye, direction, limit, &hit_location)) {
limit = hit_location.sub(eye).getNorm();
hit = intersector;
}
}
if (hit) {
return hit->getColorAtHit(eye, hit_location);
} else {
return COLOR_BLACK;
}
}

View file

@ -2,15 +2,44 @@
#include "software_global.h"
#include <memory>
namespace paysages {
namespace software {
typedef RayCastingResult (*FuncGeneralCastRay)(SoftwareRenderer *renderer, const Vector3 &start,
const Vector3 &direction);
/**
* Manager of ray intersectors to perform ray casting on a scenery.
*/
class SOFTWARESHARED_EXPORT RayCastingManager {
public:
RayCastingManager();
virtual ~RayCastingManager();
/**
* Get the number of registered intersectors.
*/
int getIntersectorCount();
shared_ptr<RayIntersector> getIntersector(int position);
/**
* Clear of all registered intersectors.
*/
void unregisterAllIntersectors();
/**
* Register a new intersector.
*/
void registerIntersector(shared_ptr<RayIntersector> intersector);
/**
* Get the final color received along a single ray.
*/
Color getFinal(const Vector3 &eye, const Vector3 &direction) const;
private:
class pimpl;
unique_ptr<pimpl> impl;
};
}
}

View file

@ -0,0 +1,25 @@
#include "RayIntersector.h"
#include "Color.h"
RayIntersector::RayIntersector() {
}
RayIntersector::~RayIntersector() {
}
int RayIntersector::getPriority() const {
return 0;
}
bool RayIntersector::findIntersection(const Vector3 &, const Vector3 &, double, Vector3 *) const {
return false;
}
bool RayIntersector::isInside(const Vector3 &, double *) const {
return false;
}
Color RayIntersector::getColorAtHit(const Vector3 &, const Vector3 &) const {
return COLOR_BLACK;
}

View file

@ -0,0 +1,43 @@
#pragma once
#include "software_global.h"
namespace paysages {
namespace software {
/**
* Abstract class to handle intersection with a ray.
*/
class SOFTWARESHARED_EXPORT RayIntersector {
public:
RayIntersector();
virtual ~RayIntersector();
/**
* Get the priority of this intersector.
*
* Higher priority intersectors are tested first, and should be the easy ones.
*/
virtual int getPriority() const;
/**
* Find the nearest intersection with the ray (within *limit* length).
*
* By default, it will perform ray marching, using isInside as intersection checker.
*/
virtual bool findIntersection(const Vector3 &eye, const Vector3 &direction, double limit, Vector3 *out_hit) const;
/**
* Indicate if a given location is inside a solid volume.
*/
virtual bool isInside(const Vector3 &location, double *out_distance = nullptr) const;
/**
* Get the incident color for a ray intersection at a given hit point.
*
* This should already have lighting apply, but not medium traversal between *eye* and *location*.
*/
virtual Color getColorAtHit(const Vector3 &eye, const Vector3 &location) const;
};
}
}

View file

@ -0,0 +1,35 @@
#include "SkyIntersector.h"
#include "AtmosphereRenderer.h"
#include "AtmosphereResult.h"
#include "Color.h"
#include "Maths.h"
#include "Scenery.h"
#include "Vector3.h"
#include <cassert>
SkyIntersector::SkyIntersector(BaseAtmosphereRenderer *atmosphere) : atmosphere(atmosphere) {
}
int SkyIntersector::getPriority() const {
return 1000;
}
bool SkyIntersector::findIntersection(const Vector3 &eye, const Vector3 &direction, double, Vector3 *out_hit) const {
assert(direction.isNormalized());
if (eye.y >= Scenery::ATMOSPHERE_WIDTH_SCALED) {
// Above atmosphere, intersect right in front of the eye
*out_hit = eye.add(direction.scale(0.00001));
} else {
// Intersect with upper atmosphere boundary
// TODO
*out_hit = eye.add(direction.scale(0.00001));
}
return true;
}
Color SkyIntersector::getColorAtHit(const Vector3 &eye, const Vector3 &location) const {
return atmosphere->getSkyColor(location.sub(eye).normalize()).final;
}

View file

@ -0,0 +1,28 @@
#pragma once
#include "software_global.h"
#include "RayIntersector.h"
namespace paysages {
namespace software {
/**
* Ray intersector with sky.
*
* This will always hit the upper atmosphere limit, in any direction.
* If already in the upper atmosphere, it will hit immediately in front of the eye.
*/
class SOFTWARESHARED_EXPORT SkyIntersector : public RayIntersector {
public:
SkyIntersector(BaseAtmosphereRenderer *atmosphere);
virtual int getPriority() const override;
virtual bool findIntersection(const Vector3 &eye, const Vector3 &direction, double limit,
Vector3 *out_hit) const override;
virtual Color getColorAtHit(const Vector3 &eye, const Vector3 &location) const override;
private:
BaseAtmosphereRenderer *atmosphere;
};
}
}

View file

@ -96,21 +96,7 @@ void SoftwareCanvasRenderer::render() {
for (int y = 0; y < ny; y++) {
for (int x = 0; x < nx; x++) {
CanvasPortion *portion = canvas->at(x, y);
progress->enterSub(2);
if (not interrupted) {
portion->preparePixels();
rasterize(portion);
}
if (not interrupted and postprocess_enabled) {
applyPixelShader(portion);
}
portion->discardPixels();
progress->exitSub();
renderPortion(portion, progress, &interrupted);
}
}
progress->exitSub();
@ -174,3 +160,20 @@ void SoftwareCanvasRenderer::applyPixelShader(CanvasPortion *portion) {
}
progress->exitSub();
}
void SoftwareCanvasRenderer::renderPortion(CanvasPortion *portion, RenderProgress *progress, bool *interrupt) {
progress->enterSub(2);
if (not*interrupt) {
portion->preparePixels();
rasterize(portion);
}
if (not*interrupt and postprocess_enabled) {
applyPixelShader(portion);
}
portion->discardPixels();
progress->exitSub();
}

View file

@ -108,6 +108,11 @@ class SOFTWARESHARED_EXPORT SoftwareCanvasRenderer : public SoftwareRenderer {
*/
void applyPixelShader(CanvasPortion *portion);
/**
* Perform rendering on a single canvas portion.
*/
virtual void renderPortion(CanvasPortion *portion, RenderProgress *progress, bool *interrupt);
private:
RenderProgress *progress;

View file

@ -0,0 +1,62 @@
#include "SoftwareRayRenderer.h"
#include "CameraDefinition.h"
#include "CanvasPortion.h"
#include "Color.h"
#include "RayCastingManager.h"
#include "RenderProgress.h"
#include "Scenery.h"
#include "SkyIntersector.h"
#include "TerrainIntersector.h"
#include "Vector3.h"
class SoftwareRayRenderer::pimpl {
public:
RayCastingManager manager;
};
SoftwareRayRenderer::SoftwareRayRenderer(Scenery *scenery, bool standard)
: SoftwareCanvasRenderer(scenery), impl(new pimpl) {
if (standard) {
registerStandardIntersectors();
}
}
SoftwareRayRenderer::~SoftwareRayRenderer() {
}
void SoftwareRayRenderer::registerStandardIntersectors() {
impl->manager.unregisterAllIntersectors();
impl->manager.registerIntersector(make_shared<SkyIntersector>(getAtmosphereRenderer()));
impl->manager.registerIntersector(make_shared<TerrainIntersector>(getTerrainRenderer()));
}
void SoftwareRayRenderer::renderPortion(CanvasPortion *portion, RenderProgress *progress, bool *interrupt) {
int width = portion->getWidth();
int height = portion->getHeight();
portion->preparePixels();
progress->enterSub(width * height);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
Vector3 direction =
Vector3(1.0, to_double(y) / to_double(height) - 0.5, to_double(x) / to_double(width) - 0.5).normalize();
Color color = impl->manager.getFinal(getCameraLocation(), direction);
portion->setColor(x, y, color);
progress->add();
}
if (*interrupt) {
return;
}
}
portion->discardPixels();
progress->exitSub();
}
void SoftwareRayRenderer::prepare() {
SoftwareCanvasRenderer::prepare();
registerStandardIntersectors();
}

View file

@ -0,0 +1,43 @@
#pragma once
#include "software_global.h"
#include "SoftwareCanvasRenderer.h"
#include <memory>
namespace paysages {
namespace software {
/**
* Software rendering using only ray tracing, not rasterization.
*
* Follows these steps, for each view ray :
* - Find a ray intersection with solid matter
* - Texture/light the solid matter (may need other secondary rays)
* - Apply medium traversal along the ray (e.g. atmosphere)
*/
class SOFTWARESHARED_EXPORT SoftwareRayRenderer : public SoftwareCanvasRenderer {
public:
SoftwareRayRenderer(Scenery *scenery, bool standard = true);
virtual ~SoftwareRayRenderer();
/**
* Register standard scenery intersectors.
*/
void registerStandardIntersectors();
protected:
/**
* Render a single canvas portion using ray-tracing.
*/
virtual void renderPortion(CanvasPortion *portion, RenderProgress *progress, bool *interrupt);
virtual void prepare() override;
private:
class pimpl;
unique_ptr<pimpl> impl;
};
}
}

View file

@ -0,0 +1,26 @@
#include "TerrainIntersector.h"
#include "TerrainRenderer.h"
#include "RayCastingResult.h"
TerrainIntersector::TerrainIntersector(TerrainRenderer *renderer) : renderer(renderer) {
}
int TerrainIntersector::getPriority() const {
return 0;
}
bool TerrainIntersector::findIntersection(const Vector3 &eye, const Vector3 &direction, double,
Vector3 *out_hit) const {
auto result = renderer->castRay(eye, direction);
if (result.hit) {
*out_hit = result.hit_location;
return true;
} else {
return false;
}
}
Color TerrainIntersector::getColorAtHit(const Vector3 &, const Vector3 &location) const {
return renderer->getFinalColor(location.x, location.z, 0.0001);
}

View file

@ -0,0 +1,25 @@
#pragma once
#include "software_global.h"
#include "RayIntersector.h"
namespace paysages {
namespace software {
/**
* Ray intersector with terrain.
*/
class SOFTWARESHARED_EXPORT TerrainIntersector : public RayIntersector {
public:
TerrainIntersector(TerrainRenderer *renderer);
virtual int getPriority() const override;
virtual bool findIntersection(const Vector3 &eye, const Vector3 &direction, double limit,
Vector3 *out_hit) const override;
virtual Color getColorAtHit(const Vector3 &eye, const Vector3 &location) const override;
private:
TerrainRenderer *renderer;
};
}
}

View file

@ -12,6 +12,7 @@ namespace paysages {
namespace software {
class SoftwareRenderer;
class SoftwareCanvasRenderer;
class SoftwareRayRenderer;
class RenderConfig;
class RenderProgress;
@ -46,6 +47,8 @@ class LightSource;
class RayCastingManager;
class RayCastingResult;
class RayIntersector;
class SkyIntersector;
class NightSky;
class MoonRenderer;

View file

@ -25,6 +25,7 @@ RandomGenerator::RandomGenerator(RandomGenerator::Seed seed) {
}
}
data = new RandomGeneratorPrivate(seed);
this->seed = seed;
}
RandomGenerator::~RandomGenerator() {

View file

@ -26,6 +26,13 @@ TEST(CameraDefinition, unproject) {
point = cam.project(Vector3(-25.1, 8.3, 1.3));
point = cam.unproject(point);
EXPECT_VECTOR3_COORDS(point, -25.1, 8.3, 1.3);
cam.setLocationCoords(0.3, 0.8, -2.4);
cam.setTargetCoords(5.2, -4.1, 1.5);
point = cam.project(Vector3(12.4, -1.3, 3.4));
point = cam.unproject(point);
EXPECT_VECTOR3_COORDS(point, 12.4, -1.3, 3.4);
}
TEST(CameraDefinition, getRealDepth) {

View file

@ -0,0 +1,38 @@
#include "RayCastingManager.h"
#include "BaseTestCase.h"
#include "RayIntersector.h"
class _FakeIntersectorPriority : public RayIntersector {
public:
_FakeIntersectorPriority(int priority) : priority(priority) {
}
virtual int getPriority() const override {
return priority;
}
int priority;
};
TEST(RayCastingManager, registerIntersector) {
RayCastingManager manager;
EXPECT_EQ(0, manager.getIntersectorCount());
manager.registerIntersector(make_shared<_FakeIntersectorPriority>(8));
ASSERT_EQ(1, manager.getIntersectorCount());
EXPECT_EQ(8, ((_FakeIntersectorPriority *)(manager.getIntersector(0).get()))->priority);
manager.registerIntersector(make_shared<_FakeIntersectorPriority>(15));
ASSERT_EQ(2, manager.getIntersectorCount());
EXPECT_EQ(15, ((_FakeIntersectorPriority *)(manager.getIntersector(0).get()))->priority);
EXPECT_EQ(8, ((_FakeIntersectorPriority *)(manager.getIntersector(1).get()))->priority);
manager.registerIntersector(make_shared<_FakeIntersectorPriority>(2));
ASSERT_EQ(3, manager.getIntersectorCount());
EXPECT_EQ(15, ((_FakeIntersectorPriority *)(manager.getIntersector(0).get()))->priority);
EXPECT_EQ(8, ((_FakeIntersectorPriority *)(manager.getIntersector(1).get()))->priority);
EXPECT_EQ(2, ((_FakeIntersectorPriority *)(manager.getIntersector(2).get()))->priority);
}