Added render test for clouds lighting
This will allow to validate future changes
This commit is contained in:
parent
32c83c1b36
commit
094dfbf783
9 changed files with 126 additions and 17 deletions
|
@ -1,6 +1,7 @@
|
|||
#include "Maths.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
double Maths::modInRange(double value, double min, double max) {
|
||||
double size = max - min;
|
||||
|
@ -12,3 +13,12 @@ double Maths::modInRange(double value, double min, double max) {
|
|||
return modulo * size + min;
|
||||
}
|
||||
}
|
||||
|
||||
double Maths::clamp(double x, double minval, double maxval) {
|
||||
return min(max(x, minval), maxval);
|
||||
}
|
||||
|
||||
double Maths::smoothstep(double edge0, double edge1, double x) {
|
||||
x = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
|
||||
return x * x * (3.0 - 2.0 * x);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,16 @@ class BASICSSHARED_EXPORT Maths {
|
|||
*/
|
||||
static double modInRange(double value, double min, double max);
|
||||
|
||||
/**
|
||||
* Constraint a value in the [min,max[ range, by clamping it.
|
||||
*/
|
||||
static double clamp(double x, double minval, double maxval);
|
||||
|
||||
/**
|
||||
* Returns 0.0 when x < edge0, 1.0 when y > edge1, and a smoothly interpolated value in-between.
|
||||
*/
|
||||
static double smoothstep(double edge0, double edge1, double x);
|
||||
|
||||
static constexpr double PI = 3.141592653589793238462643383279;
|
||||
static constexpr double PI_2 = PI / 2.0;
|
||||
static constexpr double PI_4 = PI / 4.0;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "SoftwareCanvasRenderer.h"
|
||||
#include "TerrainRenderer.h"
|
||||
#include "Scenery.h"
|
||||
#include "Maths.h"
|
||||
#include "CameraDefinition.h"
|
||||
#include "TerrainDefinition.h"
|
||||
#include "AtmosphereDefinition.h"
|
||||
|
@ -24,6 +25,9 @@
|
|||
#include "VegetationModelDefinition.h"
|
||||
#include "VegetationInstance.h"
|
||||
#include "VegetationRenderer.h"
|
||||
#include "CloudsRenderer.h"
|
||||
#include "CloudLayerDefinition.h"
|
||||
#include "clouds/BaseCloudsModel.h"
|
||||
#include "CelestialBodyDefinition.h"
|
||||
#include "RayCastingResult.h"
|
||||
#include "OpenGLVegetationImpostor.h"
|
||||
|
@ -283,8 +287,7 @@ static void testAtmosphereBruneton() {
|
|||
|
||||
class TestRasterizer : public OverlayRasterizer {
|
||||
public:
|
||||
TestRasterizer(SoftwareCanvasRenderer *renderer)
|
||||
: OverlayRasterizer(renderer, renderer->getProgressHelper()), renderer(renderer) {
|
||||
TestRasterizer(SoftwareCanvasRenderer *renderer) : OverlayRasterizer(renderer), renderer(renderer) {
|
||||
}
|
||||
virtual Color processPixel(int, int, double relx, double rely) const override {
|
||||
if (rely > 0.0) {
|
||||
|
@ -313,8 +316,8 @@ static void testVegetationModels() {
|
|||
class InstanceRenderer : public SoftwareCanvasRenderer, public OverlayRasterizer, public LightFilter {
|
||||
public:
|
||||
InstanceRenderer(Scenery *scenery, const VegetationModelDefinition &model)
|
||||
: SoftwareCanvasRenderer(scenery), OverlayRasterizer(this, this->getProgressHelper()),
|
||||
instance(model, VECTOR_ZERO), vegetation(renderer->getVegetationRenderer()) {
|
||||
: SoftwareCanvasRenderer(scenery), OverlayRasterizer(this), instance(model, VECTOR_ZERO),
|
||||
vegetation(renderer->getVegetationRenderer()) {
|
||||
}
|
||||
virtual void prepare() override {
|
||||
SoftwareCanvasRenderer::prepare();
|
||||
|
@ -454,12 +457,78 @@ static void testCelestialBodies() {
|
|||
startTestRender(&renderer, "celestial_bodies_solar_eclipse");
|
||||
}
|
||||
|
||||
static void testCloudsLighting() {
|
||||
class FakeModel : public BaseCloudsModel {
|
||||
public:
|
||||
FakeModel(CloudLayerDefinition *layer, double scale) : BaseCloudsModel(layer), scale(scale) {
|
||||
}
|
||||
virtual void getAltitudeRange(double *min_altitude, double *max_altitude) const override {
|
||||
*min_altitude = -scale;
|
||||
*max_altitude = scale;
|
||||
}
|
||||
virtual void getDetailRange(double *min_step, double *max_step) const override {
|
||||
*min_step = *max_step = scale * 0.01;
|
||||
}
|
||||
virtual double getDensity(const Vector3 &location) const override {
|
||||
return Maths::smoothstep(0.0, 0.2, 1.0 - location.getNorm() / scale);
|
||||
}
|
||||
double scale;
|
||||
};
|
||||
|
||||
class FakeRasterizer : public OverlayRasterizer {
|
||||
public:
|
||||
FakeRasterizer(SoftwareCanvasRenderer *renderer, double scale) : OverlayRasterizer(renderer), scale(scale) {
|
||||
}
|
||||
virtual Color processPixel(int, int, double relx, double rely) const override {
|
||||
auto cloud_renderer = renderer->getCloudsRenderer();
|
||||
auto atmo_renderer = renderer->getAtmosphereRenderer();
|
||||
return cloud_renderer->getColor(Vector3(relx * scale * 1.2, rely * scale * 1.2, scale),
|
||||
Vector3(relx * scale * 1.2, rely * scale * 1.2, -scale),
|
||||
atmo_renderer->getSkyColor(Vector3(relx, rely, 1.0).normalize()).final);
|
||||
}
|
||||
virtual int prepareRasterization() override {
|
||||
auto model = new FakeModel(renderer->getScenery()->getClouds()->getCloudLayer(0), scale);
|
||||
renderer->getCloudsRenderer()->setLayerModel(0, model);
|
||||
renderer->getCloudsRenderer()->setQuality(0.9);
|
||||
renderer->getLightingManager()->clearFilters();
|
||||
renderer->getLightingManager()->registerFilter(renderer->getCloudsRenderer());
|
||||
return OverlayRasterizer::prepareRasterization();
|
||||
}
|
||||
double scale;
|
||||
FakeModel *model;
|
||||
};
|
||||
|
||||
Scenery scenery;
|
||||
scenery.autoPreset(1);
|
||||
scenery.getCamera()->setTarget(VECTOR_ZERO);
|
||||
scenery.getCamera()->setLocation(Vector3(0.0, 0.0, 5.0));
|
||||
|
||||
CloudLayerDefinition layer(NULL, "test");
|
||||
scenery.getClouds()->clear();
|
||||
scenery.getClouds()->addLayer(layer);
|
||||
|
||||
SoftwareCanvasRenderer renderer(&scenery);
|
||||
FakeRasterizer rasterizer(&renderer, 10.0);
|
||||
renderer.setSize(800, 800);
|
||||
renderer.setSoloRasterizer(&rasterizer);
|
||||
renderer.getGodRaysSampler()->setEnabled(false);
|
||||
renderer.setAerialPerspectiveEnabled(false);
|
||||
|
||||
scenery.getAtmosphere()->setDayTime(10);
|
||||
startTestRender(&renderer, "clouds_lighting_day");
|
||||
scenery.getAtmosphere()->setDayTime(6, 30);
|
||||
startTestRender(&renderer, "clouds_lighting_dusk");
|
||||
scenery.getAtmosphere()->setDayTime(3);
|
||||
startTestRender(&renderer, "clouds_lighting_night");
|
||||
}
|
||||
|
||||
void runTestSuite() {
|
||||
testNoise();
|
||||
testTextures();
|
||||
testGodRays();
|
||||
testCelestialBodies();
|
||||
testNearFrustum();
|
||||
testCloudsLighting();
|
||||
testCloudsNearGround();
|
||||
testVegetationModels();
|
||||
testOpenGLVegetationImpostor();
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
#include <cmath>
|
||||
#include "Color.h"
|
||||
#include "SoftwareRenderer.h"
|
||||
#include "SoftwareCanvasRenderer.h"
|
||||
#include "CameraDefinition.h"
|
||||
#include "CanvasFragment.h"
|
||||
|
||||
OverlayRasterizer::OverlayRasterizer(SoftwareRenderer *renderer, RenderProgress *progress)
|
||||
: Rasterizer(renderer, progress, 0, COLOR_WHITE) {
|
||||
OverlayRasterizer::OverlayRasterizer(SoftwareCanvasRenderer *renderer)
|
||||
: Rasterizer(renderer, renderer->getProgressHelper(), 0, COLOR_WHITE) {
|
||||
}
|
||||
|
||||
int OverlayRasterizer::prepareRasterization() {
|
||||
|
|
|
@ -15,15 +15,17 @@ namespace software {
|
|||
*/
|
||||
class SOFTWARESHARED_EXPORT OverlayRasterizer : public Rasterizer {
|
||||
public:
|
||||
OverlayRasterizer(SoftwareRenderer *renderer, RenderProgress *progress);
|
||||
OverlayRasterizer(SoftwareCanvasRenderer *renderer);
|
||||
|
||||
/**
|
||||
* Abstract method to implement to shade each pixel.
|
||||
*/
|
||||
virtual Color processPixel(int x, int y, double relx, double rely) const = 0;
|
||||
|
||||
private:
|
||||
protected:
|
||||
virtual int prepareRasterization();
|
||||
|
||||
private:
|
||||
virtual void rasterizeToCanvas(CanvasPortion *canvas);
|
||||
virtual Color shadeFragment(const CanvasFragment &fragment, const CanvasFragment *previous) const;
|
||||
};
|
||||
|
|
|
@ -40,10 +40,12 @@ SoftwareRenderer::SoftwareRenderer(Scenery *scenery) : scenery(scenery) {
|
|||
nightsky_renderer = new NightSky(this);
|
||||
moon_renderer = new MoonRenderer(scenery->getAtmosphere()->childMoon());
|
||||
|
||||
fluid_medium = new FluidMediumManager(this);
|
||||
fluid_medium = new FluidMediumManager(this); // TODO Not used yet
|
||||
lighting = new LightingManager();
|
||||
godrays = new GodRaysSampler();
|
||||
|
||||
aerial_perspective = true;
|
||||
|
||||
lighting->registerFilter(water_renderer);
|
||||
lighting->registerFilter(terrain_renderer);
|
||||
lighting->registerFilter(vegetation_renderer);
|
||||
|
@ -111,15 +113,23 @@ void SoftwareRenderer::setQuality(double quality) {
|
|||
render_quality = trunc_to_int(quality * 9.0) + 1;
|
||||
}
|
||||
|
||||
void SoftwareRenderer::setAerialPerspectiveEnabled(bool enabled) {
|
||||
aerial_perspective = enabled;
|
||||
}
|
||||
|
||||
Color SoftwareRenderer::applyLightingToSurface(const Vector3 &location, const Vector3 &normal,
|
||||
const SurfaceMaterial &material) {
|
||||
return lighting->apply(getCameraLocation(), location, normal, material);
|
||||
}
|
||||
|
||||
Color SoftwareRenderer::applyMediumTraversal(const Vector3 &location, const Color &color) {
|
||||
if (aerial_perspective) {
|
||||
Color result = atmosphere_renderer->applyAerialPerspective(location, color).final;
|
||||
result = clouds_renderer->getColor(getCameraLocation(), location, result);
|
||||
return result;
|
||||
} else {
|
||||
return color;
|
||||
}
|
||||
}
|
||||
|
||||
RayCastingResult SoftwareRenderer::rayWalking(const Vector3 &location, const Vector3 &direction, int, int, int, int) {
|
||||
|
|
|
@ -82,6 +82,8 @@ class SOFTWARESHARED_EXPORT SoftwareRenderer {
|
|||
return godrays;
|
||||
}
|
||||
|
||||
void setAerialPerspectiveEnabled(bool enabled);
|
||||
|
||||
virtual Color applyLightingToSurface(const Vector3 &location, const Vector3 &normal,
|
||||
const SurfaceMaterial &material);
|
||||
virtual Color applyMediumTraversal(const Vector3 &location, const Color &color);
|
||||
|
@ -91,6 +93,8 @@ class SOFTWARESHARED_EXPORT SoftwareRenderer {
|
|||
private:
|
||||
Scenery *scenery;
|
||||
|
||||
bool aerial_perspective;
|
||||
|
||||
FluidMediumManager *fluid_medium;
|
||||
LightingManager *lighting;
|
||||
GodRaysSampler *godrays;
|
||||
|
|
|
@ -25,6 +25,7 @@ void BaseCloudsModel::getDetailRange(double *min_step, double *max_step) const {
|
|||
}
|
||||
|
||||
double BaseCloudsModel::getProbability(const Vector3 &, double) const {
|
||||
// FIXME not used !
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
|
@ -33,9 +34,11 @@ double BaseCloudsModel::getDensity(const Vector3 &) const {
|
|||
}
|
||||
|
||||
Color BaseCloudsModel::filterLight(const Color &light, double, double) const {
|
||||
// FIXME not used !
|
||||
return light;
|
||||
}
|
||||
|
||||
Color BaseCloudsModel::applyLightExit(const Color &light, const Vector3 &, const Vector3 &) const {
|
||||
// FIXME not used !
|
||||
return light;
|
||||
}
|
||||
|
|
|
@ -13,12 +13,12 @@ typedef struct {
|
|||
double rely;
|
||||
} PixelCall;
|
||||
|
||||
vector<PixelCall> calls;
|
||||
static vector<PixelCall> calls;
|
||||
|
||||
namespace {
|
||||
class MockOverlayRasterizer : public OverlayRasterizer {
|
||||
public:
|
||||
MockOverlayRasterizer(SoftwareCanvasRenderer *renderer)
|
||||
: OverlayRasterizer(renderer, renderer->getProgressHelper()) {
|
||||
MockOverlayRasterizer(SoftwareCanvasRenderer *renderer) : OverlayRasterizer(renderer) {
|
||||
}
|
||||
|
||||
virtual Color processPixel(int x, int y, double relx, double rely) const override {
|
||||
|
@ -27,6 +27,7 @@ class MockOverlayRasterizer : public OverlayRasterizer {
|
|||
return COLOR_BLUE;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void checkCall(const PixelCall &call, int x, int y, double relx, double rely) {
|
||||
EXPECT_EQ(x, call.x);
|
||||
|
@ -45,7 +46,7 @@ TEST(OverlayRasterizer, pixelProcessing) {
|
|||
renderer.setSoloRasterizer(&rasterizer);
|
||||
renderer.render();
|
||||
|
||||
ASSERT_EQ(12, (int)calls.size());
|
||||
ASSERT_EQ(12u, calls.size());
|
||||
checkCall(calls[0], 0, 0, -1.5, -1.0);
|
||||
checkCall(calls[1], 0, 2, -1.5, 1.0);
|
||||
checkCall(calls[2], 2, 0, 0.5, -1.0);
|
||||
|
|
Loading…
Reference in a new issue