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 "Maths.h"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
double Maths::modInRange(double value, double min, double max) {
|
double Maths::modInRange(double value, double min, double max) {
|
||||||
double size = max - min;
|
double size = max - min;
|
||||||
|
@ -12,3 +13,12 @@ double Maths::modInRange(double value, double min, double max) {
|
||||||
return modulo * size + min;
|
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);
|
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 = 3.141592653589793238462643383279;
|
||||||
static constexpr double PI_2 = PI / 2.0;
|
static constexpr double PI_2 = PI / 2.0;
|
||||||
static constexpr double PI_4 = PI / 4.0;
|
static constexpr double PI_4 = PI / 4.0;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "SoftwareCanvasRenderer.h"
|
#include "SoftwareCanvasRenderer.h"
|
||||||
#include "TerrainRenderer.h"
|
#include "TerrainRenderer.h"
|
||||||
#include "Scenery.h"
|
#include "Scenery.h"
|
||||||
|
#include "Maths.h"
|
||||||
#include "CameraDefinition.h"
|
#include "CameraDefinition.h"
|
||||||
#include "TerrainDefinition.h"
|
#include "TerrainDefinition.h"
|
||||||
#include "AtmosphereDefinition.h"
|
#include "AtmosphereDefinition.h"
|
||||||
|
@ -24,6 +25,9 @@
|
||||||
#include "VegetationModelDefinition.h"
|
#include "VegetationModelDefinition.h"
|
||||||
#include "VegetationInstance.h"
|
#include "VegetationInstance.h"
|
||||||
#include "VegetationRenderer.h"
|
#include "VegetationRenderer.h"
|
||||||
|
#include "CloudsRenderer.h"
|
||||||
|
#include "CloudLayerDefinition.h"
|
||||||
|
#include "clouds/BaseCloudsModel.h"
|
||||||
#include "CelestialBodyDefinition.h"
|
#include "CelestialBodyDefinition.h"
|
||||||
#include "RayCastingResult.h"
|
#include "RayCastingResult.h"
|
||||||
#include "OpenGLVegetationImpostor.h"
|
#include "OpenGLVegetationImpostor.h"
|
||||||
|
@ -283,8 +287,7 @@ static void testAtmosphereBruneton() {
|
||||||
|
|
||||||
class TestRasterizer : public OverlayRasterizer {
|
class TestRasterizer : public OverlayRasterizer {
|
||||||
public:
|
public:
|
||||||
TestRasterizer(SoftwareCanvasRenderer *renderer)
|
TestRasterizer(SoftwareCanvasRenderer *renderer) : OverlayRasterizer(renderer), renderer(renderer) {
|
||||||
: OverlayRasterizer(renderer, renderer->getProgressHelper()), renderer(renderer) {
|
|
||||||
}
|
}
|
||||||
virtual Color processPixel(int, int, double relx, double rely) const override {
|
virtual Color processPixel(int, int, double relx, double rely) const override {
|
||||||
if (rely > 0.0) {
|
if (rely > 0.0) {
|
||||||
|
@ -313,8 +316,8 @@ static void testVegetationModels() {
|
||||||
class InstanceRenderer : public SoftwareCanvasRenderer, public OverlayRasterizer, public LightFilter {
|
class InstanceRenderer : public SoftwareCanvasRenderer, public OverlayRasterizer, public LightFilter {
|
||||||
public:
|
public:
|
||||||
InstanceRenderer(Scenery *scenery, const VegetationModelDefinition &model)
|
InstanceRenderer(Scenery *scenery, const VegetationModelDefinition &model)
|
||||||
: SoftwareCanvasRenderer(scenery), OverlayRasterizer(this, this->getProgressHelper()),
|
: SoftwareCanvasRenderer(scenery), OverlayRasterizer(this), instance(model, VECTOR_ZERO),
|
||||||
instance(model, VECTOR_ZERO), vegetation(renderer->getVegetationRenderer()) {
|
vegetation(renderer->getVegetationRenderer()) {
|
||||||
}
|
}
|
||||||
virtual void prepare() override {
|
virtual void prepare() override {
|
||||||
SoftwareCanvasRenderer::prepare();
|
SoftwareCanvasRenderer::prepare();
|
||||||
|
@ -454,12 +457,78 @@ static void testCelestialBodies() {
|
||||||
startTestRender(&renderer, "celestial_bodies_solar_eclipse");
|
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() {
|
void runTestSuite() {
|
||||||
testNoise();
|
testNoise();
|
||||||
testTextures();
|
testTextures();
|
||||||
testGodRays();
|
testGodRays();
|
||||||
testCelestialBodies();
|
testCelestialBodies();
|
||||||
testNearFrustum();
|
testNearFrustum();
|
||||||
|
testCloudsLighting();
|
||||||
testCloudsNearGround();
|
testCloudsNearGround();
|
||||||
testVegetationModels();
|
testVegetationModels();
|
||||||
testOpenGLVegetationImpostor();
|
testOpenGLVegetationImpostor();
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include "Color.h"
|
#include "Color.h"
|
||||||
#include "SoftwareRenderer.h"
|
#include "SoftwareCanvasRenderer.h"
|
||||||
#include "CameraDefinition.h"
|
#include "CameraDefinition.h"
|
||||||
#include "CanvasFragment.h"
|
#include "CanvasFragment.h"
|
||||||
|
|
||||||
OverlayRasterizer::OverlayRasterizer(SoftwareRenderer *renderer, RenderProgress *progress)
|
OverlayRasterizer::OverlayRasterizer(SoftwareCanvasRenderer *renderer)
|
||||||
: Rasterizer(renderer, progress, 0, COLOR_WHITE) {
|
: Rasterizer(renderer, renderer->getProgressHelper(), 0, COLOR_WHITE) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int OverlayRasterizer::prepareRasterization() {
|
int OverlayRasterizer::prepareRasterization() {
|
||||||
|
|
|
@ -15,15 +15,17 @@ namespace software {
|
||||||
*/
|
*/
|
||||||
class SOFTWARESHARED_EXPORT OverlayRasterizer : public Rasterizer {
|
class SOFTWARESHARED_EXPORT OverlayRasterizer : public Rasterizer {
|
||||||
public:
|
public:
|
||||||
OverlayRasterizer(SoftwareRenderer *renderer, RenderProgress *progress);
|
OverlayRasterizer(SoftwareCanvasRenderer *renderer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract method to implement to shade each pixel.
|
* Abstract method to implement to shade each pixel.
|
||||||
*/
|
*/
|
||||||
virtual Color processPixel(int x, int y, double relx, double rely) const = 0;
|
virtual Color processPixel(int x, int y, double relx, double rely) const = 0;
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
virtual int prepareRasterization();
|
virtual int prepareRasterization();
|
||||||
|
|
||||||
|
private:
|
||||||
virtual void rasterizeToCanvas(CanvasPortion *canvas);
|
virtual void rasterizeToCanvas(CanvasPortion *canvas);
|
||||||
virtual Color shadeFragment(const CanvasFragment &fragment, const CanvasFragment *previous) const;
|
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);
|
nightsky_renderer = new NightSky(this);
|
||||||
moon_renderer = new MoonRenderer(scenery->getAtmosphere()->childMoon());
|
moon_renderer = new MoonRenderer(scenery->getAtmosphere()->childMoon());
|
||||||
|
|
||||||
fluid_medium = new FluidMediumManager(this);
|
fluid_medium = new FluidMediumManager(this); // TODO Not used yet
|
||||||
lighting = new LightingManager();
|
lighting = new LightingManager();
|
||||||
godrays = new GodRaysSampler();
|
godrays = new GodRaysSampler();
|
||||||
|
|
||||||
|
aerial_perspective = true;
|
||||||
|
|
||||||
lighting->registerFilter(water_renderer);
|
lighting->registerFilter(water_renderer);
|
||||||
lighting->registerFilter(terrain_renderer);
|
lighting->registerFilter(terrain_renderer);
|
||||||
lighting->registerFilter(vegetation_renderer);
|
lighting->registerFilter(vegetation_renderer);
|
||||||
|
@ -111,15 +113,23 @@ void SoftwareRenderer::setQuality(double quality) {
|
||||||
render_quality = trunc_to_int(quality * 9.0) + 1;
|
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,
|
Color SoftwareRenderer::applyLightingToSurface(const Vector3 &location, const Vector3 &normal,
|
||||||
const SurfaceMaterial &material) {
|
const SurfaceMaterial &material) {
|
||||||
return lighting->apply(getCameraLocation(), location, normal, material);
|
return lighting->apply(getCameraLocation(), location, normal, material);
|
||||||
}
|
}
|
||||||
|
|
||||||
Color SoftwareRenderer::applyMediumTraversal(const Vector3 &location, const Color &color) {
|
Color SoftwareRenderer::applyMediumTraversal(const Vector3 &location, const Color &color) {
|
||||||
Color result = atmosphere_renderer->applyAerialPerspective(location, color).final;
|
if (aerial_perspective) {
|
||||||
result = clouds_renderer->getColor(getCameraLocation(), location, result);
|
Color result = atmosphere_renderer->applyAerialPerspective(location, color).final;
|
||||||
return result;
|
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) {
|
RayCastingResult SoftwareRenderer::rayWalking(const Vector3 &location, const Vector3 &direction, int, int, int, int) {
|
||||||
|
|
|
@ -82,6 +82,8 @@ class SOFTWARESHARED_EXPORT SoftwareRenderer {
|
||||||
return godrays;
|
return godrays;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setAerialPerspectiveEnabled(bool enabled);
|
||||||
|
|
||||||
virtual Color applyLightingToSurface(const Vector3 &location, const Vector3 &normal,
|
virtual Color applyLightingToSurface(const Vector3 &location, const Vector3 &normal,
|
||||||
const SurfaceMaterial &material);
|
const SurfaceMaterial &material);
|
||||||
virtual Color applyMediumTraversal(const Vector3 &location, const Color &color);
|
virtual Color applyMediumTraversal(const Vector3 &location, const Color &color);
|
||||||
|
@ -91,6 +93,8 @@ class SOFTWARESHARED_EXPORT SoftwareRenderer {
|
||||||
private:
|
private:
|
||||||
Scenery *scenery;
|
Scenery *scenery;
|
||||||
|
|
||||||
|
bool aerial_perspective;
|
||||||
|
|
||||||
FluidMediumManager *fluid_medium;
|
FluidMediumManager *fluid_medium;
|
||||||
LightingManager *lighting;
|
LightingManager *lighting;
|
||||||
GodRaysSampler *godrays;
|
GodRaysSampler *godrays;
|
||||||
|
|
|
@ -25,6 +25,7 @@ void BaseCloudsModel::getDetailRange(double *min_step, double *max_step) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
double BaseCloudsModel::getProbability(const Vector3 &, double) const {
|
double BaseCloudsModel::getProbability(const Vector3 &, double) const {
|
||||||
|
// FIXME not used !
|
||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,9 +34,11 @@ double BaseCloudsModel::getDensity(const Vector3 &) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
Color BaseCloudsModel::filterLight(const Color &light, double, double) const {
|
Color BaseCloudsModel::filterLight(const Color &light, double, double) const {
|
||||||
|
// FIXME not used !
|
||||||
return light;
|
return light;
|
||||||
}
|
}
|
||||||
|
|
||||||
Color BaseCloudsModel::applyLightExit(const Color &light, const Vector3 &, const Vector3 &) const {
|
Color BaseCloudsModel::applyLightExit(const Color &light, const Vector3 &, const Vector3 &) const {
|
||||||
|
// FIXME not used !
|
||||||
return light;
|
return light;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,12 +13,12 @@ typedef struct {
|
||||||
double rely;
|
double rely;
|
||||||
} PixelCall;
|
} PixelCall;
|
||||||
|
|
||||||
vector<PixelCall> calls;
|
static vector<PixelCall> calls;
|
||||||
|
|
||||||
|
namespace {
|
||||||
class MockOverlayRasterizer : public OverlayRasterizer {
|
class MockOverlayRasterizer : public OverlayRasterizer {
|
||||||
public:
|
public:
|
||||||
MockOverlayRasterizer(SoftwareCanvasRenderer *renderer)
|
MockOverlayRasterizer(SoftwareCanvasRenderer *renderer) : OverlayRasterizer(renderer) {
|
||||||
: OverlayRasterizer(renderer, renderer->getProgressHelper()) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Color processPixel(int x, int y, double relx, double rely) const override {
|
virtual Color processPixel(int x, int y, double relx, double rely) const override {
|
||||||
|
@ -27,6 +27,7 @@ class MockOverlayRasterizer : public OverlayRasterizer {
|
||||||
return COLOR_BLUE;
|
return COLOR_BLUE;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void checkCall(const PixelCall &call, int x, int y, double relx, double rely) {
|
void checkCall(const PixelCall &call, int x, int y, double relx, double rely) {
|
||||||
EXPECT_EQ(x, call.x);
|
EXPECT_EQ(x, call.x);
|
||||||
|
@ -45,7 +46,7 @@ TEST(OverlayRasterizer, pixelProcessing) {
|
||||||
renderer.setSoloRasterizer(&rasterizer);
|
renderer.setSoloRasterizer(&rasterizer);
|
||||||
renderer.render();
|
renderer.render();
|
||||||
|
|
||||||
ASSERT_EQ(12, (int)calls.size());
|
ASSERT_EQ(12u, calls.size());
|
||||||
checkCall(calls[0], 0, 0, -1.5, -1.0);
|
checkCall(calls[0], 0, 0, -1.5, -1.0);
|
||||||
checkCall(calls[1], 0, 2, -1.5, 1.0);
|
checkCall(calls[1], 0, 2, -1.5, 1.0);
|
||||||
checkCall(calls[2], 2, 0, 0.5, -1.0);
|
checkCall(calls[2], 2, 0, 0.5, -1.0);
|
||||||
|
|
Loading…
Reference in a new issue