Added render test for clouds lighting

This will allow to validate future changes
This commit is contained in:
Michaël Lemaire 2016-01-26 02:26:43 +01:00
parent 32c83c1b36
commit 094dfbf783
9 changed files with 126 additions and 17 deletions

View file

@ -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);
}

View file

@ -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;

View file

@ -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();

View file

@ -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() {

View file

@ -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;
};

View file

@ -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) {

View file

@ -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;

View file

@ -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;
}

View file

@ -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);