diff --git a/src/interface/commandline/tests.cpp b/src/interface/commandline/tests.cpp index e1ede13..7d83f4a 100644 --- a/src/interface/commandline/tests.cpp +++ b/src/interface/commandline/tests.cpp @@ -66,8 +66,24 @@ static void testGroundShadowQuality() } } +static void testRasterizationQuality() +{ + Scenery scenery; + scenery.autoPreset(12); + + SoftwareCanvasRenderer renderer(&scenery); + renderer.setSize(800, 600); + renderer.enablePostprocess(false); + for (int i = 0; i < 6; i++) + { + renderer.setQuality((double)i / 5.0); + startTestRender(&renderer, "rasterization_quality", i); + } +} + void runTestSuite() { testGroundShadowQuality(); + testRasterizationQuality(); } diff --git a/src/render/software/Rasterizer.cpp b/src/render/software/Rasterizer.cpp index 227782c..8fc42f1 100644 --- a/src/render/software/Rasterizer.cpp +++ b/src/render/software/Rasterizer.cpp @@ -38,6 +38,8 @@ Rasterizer::Rasterizer(SoftwareRenderer* renderer, RenderProgress *progress, int this->color = new Color(color); interrupted = false; + + setQuality(0.5); } Rasterizer::~Rasterizer() @@ -50,6 +52,10 @@ void Rasterizer::interrupt() interrupted = true; } +void Rasterizer::setQuality(double) +{ +} + void Rasterizer::pushProjectedTriangle(CanvasPortion *canvas, const Vector3 &pixel1, const Vector3 &pixel2, const Vector3 &pixel3, const Vector3 &location1, const Vector3 &location2, const Vector3 &location3) { ScanPoint point1, point2, point3; diff --git a/src/render/software/Rasterizer.h b/src/render/software/Rasterizer.h index 53662d4..4a3b4d3 100644 --- a/src/render/software/Rasterizer.h +++ b/src/render/software/Rasterizer.h @@ -23,6 +23,11 @@ public: virtual Color shadeFragment(const CanvasFragment &fragment) const = 0; virtual void interrupt(); + /** + * Set the rasterization quality factor. + */ + virtual void setQuality(double factor); + /** * Abstract method to prepare for the rasterization process, and return the estimated progress count. */ diff --git a/src/render/software/SkyRasterizer.cpp b/src/render/software/SkyRasterizer.cpp index 0a429f6..c2d207a 100644 --- a/src/render/software/SkyRasterizer.cpp +++ b/src/render/software/SkyRasterizer.cpp @@ -19,9 +19,6 @@ SkyRasterizer::SkyRasterizer(SoftwareRenderer* renderer, RenderProgress *progres int SkyRasterizer::prepareRasterization() { - res_i = renderer->render_quality * 40; - res_j = renderer->render_quality * 20; - return res_i * res_j; } @@ -93,3 +90,14 @@ Color SkyRasterizer::shadeFragment(const CanvasFragment &fragment) const return result; } + +void SkyRasterizer::setQuality(int res_i, int res_j) +{ + this->res_i = res_i; + this->res_j = res_j; +} + +void SkyRasterizer::setQuality(double factor) +{ + setQuality(20.0 * (1.0 + factor * 10.0), 10.0 * (1.0 + factor * 10.0)); +} diff --git a/src/render/software/SkyRasterizer.h b/src/render/software/SkyRasterizer.h index dc5a71b..3989113 100644 --- a/src/render/software/SkyRasterizer.h +++ b/src/render/software/SkyRasterizer.h @@ -17,6 +17,9 @@ public: virtual void rasterizeToCanvas(CanvasPortion* canvas) override; virtual Color shadeFragment(const CanvasFragment &fragment) const override; + void setQuality(int res_i, int res_j); + virtual void setQuality(double factor) override; + private: int res_i; int res_j; diff --git a/src/render/software/SoftwareCanvasRenderer.cpp b/src/render/software/SoftwareCanvasRenderer.cpp index e3452e7..01a91ab 100644 --- a/src/render/software/SoftwareCanvasRenderer.cpp +++ b/src/render/software/SoftwareCanvasRenderer.cpp @@ -25,6 +25,8 @@ SoftwareCanvasRenderer::SoftwareCanvasRenderer(Scenery *scenery): progress = new RenderProgress(); samples = 1; + postprocess_enabled = true; + rasterizers.push_back(new SkyRasterizer(this, progress, 0)); rasterizers.push_back(new WaterRasterizer(this, progress, 1)); rasterizers.push_back(new TerrainRasterizer(this, progress, 2)); @@ -43,6 +45,16 @@ SoftwareCanvasRenderer::~SoftwareCanvasRenderer() } } +void SoftwareCanvasRenderer::setQuality(double factor) +{ + SoftwareRenderer::setQuality(factor); + + for (auto &rasterizer:rasterizers) + { + rasterizer->setQuality(factor); + } +} + double SoftwareCanvasRenderer::getProgress() const { return progress->get(); @@ -53,10 +65,15 @@ void SoftwareCanvasRenderer::setConfig(const RenderConfig &config) if (not started) { setSize(config.width, config.height, config.antialias); - render_quality = config.quality; + setQuality((double)(config.quality - 1) / 9.0); } } +void SoftwareCanvasRenderer::enablePostprocess(bool enabled) +{ + this->postprocess_enabled = enabled; +} + void SoftwareCanvasRenderer::setSize(int width, int height, int samples) { if (not started) @@ -94,7 +111,7 @@ void SoftwareCanvasRenderer::render() rasterize(portion); } - if (not interrupted) + if (not interrupted and postprocess_enabled) { applyPixelShader(portion); } diff --git a/src/render/software/SoftwareCanvasRenderer.h b/src/render/software/SoftwareCanvasRenderer.h index ef105b2..9673af3 100644 --- a/src/render/software/SoftwareCanvasRenderer.h +++ b/src/render/software/SoftwareCanvasRenderer.h @@ -25,6 +25,8 @@ public: inline const Canvas *getCanvas() const {return canvas;} inline bool isFinished() const {return finished;} + virtual void setQuality(double factor) override; + /** * Get the global rendering progress (0.0-1.0). */ @@ -35,6 +37,11 @@ public: */ void setConfig(const RenderConfig &config); + /** + * Enable or disable the post processing. + */ + void enablePostprocess(bool enabled); + /** * @brief Set the rendering size in pixels. * @@ -85,6 +92,8 @@ private: bool finished; bool interrupted; + bool postprocess_enabled; + ParallelWork *current_work; }; diff --git a/src/render/software/SoftwareRenderer.cpp b/src/render/software/SoftwareRenderer.cpp index c281e1b..120232d 100644 --- a/src/render/software/SoftwareRenderer.cpp +++ b/src/render/software/SoftwareRenderer.cpp @@ -23,7 +23,6 @@ SoftwareRenderer::SoftwareRenderer(Scenery* scenery): scenery(scenery) { - render_quality = 5; render_camera = new CameraDefinition; scenery->getCamera()->copy(render_camera); @@ -42,6 +41,8 @@ SoftwareRenderer::SoftwareRenderer(Scenery* scenery): lighting->registerFilter(water_renderer); lighting->registerFilter(terrain_renderer); lighting->registerFilter(clouds_renderer); + + setQuality(0.5); } SoftwareRenderer::~SoftwareRenderer() diff --git a/src/render/software/TerrainRasterizer.cpp b/src/render/software/TerrainRasterizer.cpp index 503b259..68c871c 100644 --- a/src/render/software/TerrainRasterizer.cpp +++ b/src/render/software/TerrainRasterizer.cpp @@ -16,6 +16,20 @@ TerrainRasterizer::TerrainRasterizer(SoftwareRenderer* renderer, RenderProgress { } +void TerrainRasterizer::setQuality(double base_chunk_size, double detail_factor, int max_chunk_detail) +{ + this->base_chunk_size = base_chunk_size; + this->detail_factor = detail_factor; + this->max_chunk_detail = max_chunk_detail; +} + +void TerrainRasterizer::setQuality(double factor) +{ + setQuality(5.0 - 4.5 * factor * factor, + 1.0 / (25.0 - 20.0 * factor), + 1 + 49 * factor * factor); +} + static inline Vector3 _getPoint(SoftwareRenderer* renderer, double x, double z) { return Vector3(x, renderer->getTerrainRenderer()->getHeight(x, z, true), z); @@ -69,7 +83,7 @@ void TerrainRasterizer::renderQuad(CanvasPortion *canvas, double x, double z, do } } -static void _getChunk(SoftwareRenderer* renderer, TerrainRasterizer::TerrainChunkInfo* chunk, double x, double z, double size, bool displaced) +void TerrainRasterizer::getChunk(TerrainRasterizer::TerrainChunkInfo* chunk, double x, double z, double size, bool displaced) { chunk->point_nw = renderer->getTerrainRenderer()->getResult(x, z, true, displaced).location; chunk->point_sw = renderer->getTerrainRenderer()->getResult(x, z + size, true, displaced).location; @@ -109,10 +123,10 @@ static void _getChunk(SoftwareRenderer* renderer, TerrainRasterizer::TerrainChun int coverage = renderer->render_camera->isUnprojectedBoxInView(box); if (coverage > 0) { - chunk->detail_hint = (int)ceil(sqrt((double)coverage) / (double)(25 - 2 * renderer->render_quality)); - if (chunk->detail_hint > 5 * renderer->render_quality) + chunk->detail_hint = (int)ceil(sqrt((double)coverage) * detail_factor); + if (chunk->detail_hint > max_chunk_detail) { - chunk->detail_hint = 5 * renderer->render_quality; + chunk->detail_hint = max_chunk_detail; } } else @@ -128,9 +142,7 @@ int TerrainRasterizer::performTessellation(CanvasPortion* canvas, bool displaced int chunk_factor, chunk_count, i, result; Vector3 cam = renderer->getCameraLocation(VECTOR_ZERO); double radius_int, radius_ext; - double base_chunk_size, chunk_size; - - base_chunk_size = 5.0 / (double)renderer->render_quality; + double chunk_size; chunk_factor = 1; chunk_count = 2; @@ -146,7 +158,7 @@ int TerrainRasterizer::performTessellation(CanvasPortion* canvas, bool displaced { for (i = 0; i < chunk_count - 1; i++) { - _getChunk(renderer, &chunk, cx - radius_ext + chunk_size * i, cz - radius_ext, chunk_size, displaced); + getChunk(&chunk, cx - radius_ext + chunk_size * i, cz - radius_ext, chunk_size, displaced); if (chunk.detail_hint > 0) { result += chunk.detail_hint * chunk.detail_hint; @@ -160,7 +172,7 @@ int TerrainRasterizer::performTessellation(CanvasPortion* canvas, bool displaced return result; } - _getChunk(renderer, &chunk, cx + radius_int, cz - radius_ext + chunk_size * i, chunk_size, displaced); + getChunk(&chunk, cx + radius_int, cz - radius_ext + chunk_size * i, chunk_size, displaced); if (chunk.detail_hint > 0) { result += chunk.detail_hint * chunk.detail_hint; @@ -174,7 +186,7 @@ int TerrainRasterizer::performTessellation(CanvasPortion* canvas, bool displaced return result; } - _getChunk(renderer, &chunk, cx + radius_int - chunk_size * i, cz + radius_int, chunk_size, displaced); + getChunk(&chunk, cx + radius_int - chunk_size * i, cz + radius_int, chunk_size, displaced); if (chunk.detail_hint > 0) { result += chunk.detail_hint * chunk.detail_hint; @@ -188,7 +200,7 @@ int TerrainRasterizer::performTessellation(CanvasPortion* canvas, bool displaced return result; } - _getChunk(renderer, &chunk, cx - radius_ext, cz + radius_int - chunk_size * i, chunk_size, displaced); + getChunk(&chunk, cx - radius_ext, cz + radius_int - chunk_size * i, chunk_size, displaced); if (chunk.detail_hint > 0) { result += chunk.detail_hint * chunk.detail_hint; diff --git a/src/render/software/TerrainRasterizer.h b/src/render/software/TerrainRasterizer.h index 4a34f2c..89acedc 100644 --- a/src/render/software/TerrainRasterizer.h +++ b/src/render/software/TerrainRasterizer.h @@ -24,6 +24,16 @@ public: public: TerrainRasterizer(SoftwareRenderer* renderer, RenderProgress *progress, int client_id); + /** + * Set the rasterization quality. + * + * @param base_chunk_size Size of chunks near the camera + * @param detail_factor Precision factor of a chunk's tessellation, depending on screen coverage + * @param max_chunk_detail Maximal tessellation of chunks + */ + void setQuality(double base_chunk_size, double detail_factor, int max_chunk_detail); + virtual void setQuality(double factor) override; + virtual int prepareRasterization() override; virtual void rasterizeToCanvas(CanvasPortion* canvas) override; virtual Color shadeFragment(const CanvasFragment &fragment) const override; @@ -51,6 +61,14 @@ private: void tessellateChunk(CanvasPortion* canvas, TerrainChunkInfo* chunk, int detail); void renderQuad(CanvasPortion* canvas, double x, double z, double size, double water_height); + + void getChunk(TerrainRasterizer::TerrainChunkInfo* chunk, double x, double z, double size, bool displaced); + +private: + // Quality control + double base_chunk_size; + double detail_factor; + int max_chunk_detail; }; } diff --git a/src/render/software/WaterRasterizer.cpp b/src/render/software/WaterRasterizer.cpp index ddad775..783d49d 100644 --- a/src/render/software/WaterRasterizer.cpp +++ b/src/render/software/WaterRasterizer.cpp @@ -49,17 +49,20 @@ Color WaterRasterizer::shadeFragment(const CanvasFragment &fragment) const return renderer->getWaterRenderer()->getResult(location.x, location.z).final; } +void WaterRasterizer::setQuality(double factor) +{ + base_chunk_size = 10.0 / (1.0 + factor * 7.0); + if (factor > 0.6) + { + base_chunk_size *= 0.5; + } +} + int WaterRasterizer::performTessellation(CanvasPortion *canvas) { int chunk_factor, chunk_count, i, result; Vector3 cam = renderer->getCameraLocation(VECTOR_ZERO); - double radius_int, radius_ext, base_chunk_size, chunk_size; - - base_chunk_size = 2.0 / (double)renderer->render_quality; - if (renderer->render_quality > 7) - { - base_chunk_size *= 0.5; - } + double radius_int, radius_ext, chunk_size; result = 0; chunk_factor = 1; diff --git a/src/render/software/WaterRasterizer.h b/src/render/software/WaterRasterizer.h index 4b4807c..233cde7 100644 --- a/src/render/software/WaterRasterizer.h +++ b/src/render/software/WaterRasterizer.h @@ -19,8 +19,14 @@ public: virtual void rasterizeToCanvas(CanvasPortion* canvas) override; virtual Color shadeFragment(const CanvasFragment &fragment) const override; + virtual void setQuality(double factor) override; + private: int performTessellation(CanvasPortion* canvas); + +private: + // Quality control + double base_chunk_size; }; }