Merge branch god_rays
This commit is contained in:
commit
8d9e3fbc94
35 changed files with 913 additions and 36 deletions
1
TODO
1
TODO
|
@ -3,6 +3,7 @@ Technlology Preview 2 :
|
|||
- Streamline the definition system, with undo and events.
|
||||
- Implement copy-on-write on definitions (to avoid copying whole heightmaps for instance).
|
||||
- Add clouds to OpenGL with 3d textures.
|
||||
- Refactor medium traversal to unify clouds, atmosphere and god rays.
|
||||
- Fix potential holes in land rendering (OpenGL and software).
|
||||
- Fix polygon culling near the camera in low-res renders (automatic tessellation ?).
|
||||
- Fix sun size not being consistent between opengl and software
|
||||
|
|
|
@ -11,3 +11,17 @@ double Interpolation::bicubic(double stencil[16], double x, double y)
|
|||
|
||||
return Interpolation::cubic(buf_cubic_y, y);
|
||||
}
|
||||
|
||||
double Interpolation::bilinear(double p[4], double x, double y)
|
||||
{
|
||||
double e1 = linear(p[0], p[1], x);
|
||||
double e2 = linear(p[3], p[2], x);
|
||||
return Interpolation::linear(e1, e2, y);
|
||||
}
|
||||
|
||||
double Interpolation::trilinear(double p[8], double x, double y, double z)
|
||||
{
|
||||
double f1 = bilinear(p, x, y);
|
||||
double f2 = bilinear(p + 4, x, y);
|
||||
return Interpolation::linear(f1, f2, z);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,13 @@ namespace basics {
|
|||
class BASICSSHARED_EXPORT Interpolation
|
||||
{
|
||||
public:
|
||||
static inline double linear(double p1, double p2, double x)
|
||||
{
|
||||
return p1 + x * (p2 - p1);
|
||||
}
|
||||
static double bilinear(double p[4], double x, double y);
|
||||
static double trilinear(double p[8], double x, double y, double z);
|
||||
|
||||
static inline double cubic(double p[4], double x)
|
||||
{
|
||||
return p[1] + 0.5 * x * (p[2] - p[0] + x * (2.0 * p[0] - 5.0 * p[1] + 4.0 * p[2] - p[3] + x * (3.0 * (p[1] - p[2]) + p[3] - p[0])));
|
||||
|
|
|
@ -20,6 +20,10 @@ public:
|
|||
inline Vector3 getStart() const {return start;}
|
||||
inline Vector3 getEnd() const {return end;}
|
||||
|
||||
inline double getXDiff() const {return end.x - start.x;}
|
||||
inline double getYDiff() const {return end.y - start.y;}
|
||||
inline double getZDiff() const {return end.z - start.z;}
|
||||
|
||||
/*!
|
||||
* \brief Keep only the intersection with a slice orthogonal to the Y axis.
|
||||
* \return true if a segment remains
|
||||
|
|
|
@ -3,10 +3,12 @@
|
|||
#include "PackStream.h"
|
||||
#include "RandomGenerator.h"
|
||||
#include "FloatNode.h"
|
||||
#include "GodRaysDefinition.h"
|
||||
|
||||
AtmosphereDefinition::AtmosphereDefinition(DefinitionNode* parent):
|
||||
DefinitionNode(parent, "atmosphere", "atmosphere")
|
||||
{
|
||||
godrays = new GodRaysDefinition(this);
|
||||
daytime = new FloatNode(this, "daytime");
|
||||
humidity = new FloatNode(this, "humidity");
|
||||
sun_radius = new FloatNode(this, "sun_radius");
|
||||
|
|
|
@ -45,6 +45,7 @@ public:
|
|||
|
||||
virtual void copy(DefinitionNode* destination) const override;
|
||||
|
||||
inline GodRaysDefinition *childGodRays() const {return godrays;}
|
||||
inline FloatNode *propDayTime() const {return daytime;}
|
||||
inline FloatNode *propHumidity() const {return humidity;}
|
||||
inline FloatNode *propSunRadius() const {return sun_radius;}
|
||||
|
@ -78,6 +79,7 @@ public:
|
|||
std::vector<Star> stars;
|
||||
|
||||
private:
|
||||
GodRaysDefinition *godrays;
|
||||
FloatNode *humidity;
|
||||
FloatNode *daytime;
|
||||
FloatNode *sun_radius;
|
||||
|
|
12
src/definition/GodRaysDefinition.cpp
Normal file
12
src/definition/GodRaysDefinition.cpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include "GodRaysDefinition.h"
|
||||
|
||||
#include "FloatNode.h"
|
||||
|
||||
GodRaysDefinition::GodRaysDefinition(DefinitionNode *parent):
|
||||
DefinitionNode(parent, "godrays", "godrays")
|
||||
{
|
||||
penetration = new FloatNode(this, "penetration", 0.01);
|
||||
resistance = new FloatNode(this, "resistance", 0.3);
|
||||
boost = new FloatNode(this, "boost", 8.0);
|
||||
}
|
||||
|
29
src/definition/GodRaysDefinition.h
Normal file
29
src/definition/GodRaysDefinition.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef GODRAYSDEFINITION_H
|
||||
#define GODRAYSDEFINITION_H
|
||||
|
||||
#include "definition_global.h"
|
||||
|
||||
#include "DefinitionNode.h"
|
||||
|
||||
namespace paysages {
|
||||
namespace definition {
|
||||
|
||||
class DEFINITIONSHARED_EXPORT GodRaysDefinition: public DefinitionNode
|
||||
{
|
||||
public:
|
||||
GodRaysDefinition(DefinitionNode *parent);
|
||||
|
||||
inline FloatNode *propPenetration() const {return penetration;}
|
||||
inline FloatNode *propResistance() const {return resistance;}
|
||||
inline FloatNode *propBoost() const {return boost;}
|
||||
|
||||
private:
|
||||
FloatNode *penetration;
|
||||
FloatNode *resistance;
|
||||
FloatNode *boost;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // GODRAYSDEFINITION_H
|
|
@ -37,7 +37,8 @@ SOURCES += \
|
|||
DiffManager.cpp \
|
||||
DefinitionWatcher.cpp \
|
||||
IntNode.cpp \
|
||||
IntDiff.cpp
|
||||
IntDiff.cpp \
|
||||
GodRaysDefinition.cpp
|
||||
|
||||
HEADERS +=\
|
||||
definition_global.h \
|
||||
|
@ -64,7 +65,8 @@ HEADERS +=\
|
|||
DiffManager.h \
|
||||
DefinitionWatcher.h \
|
||||
IntNode.h \
|
||||
IntDiff.h
|
||||
IntDiff.h \
|
||||
GodRaysDefinition.h
|
||||
|
||||
unix:!symbian {
|
||||
maemo5 {
|
||||
|
|
|
@ -30,6 +30,7 @@ namespace definition {
|
|||
class CloudsDefinition;
|
||||
class CloudLayerDefinition;
|
||||
class AtmosphereDefinition;
|
||||
class GodRaysDefinition;
|
||||
class TexturesDefinition;
|
||||
class TextureLayerDefinition;
|
||||
class TerrainDefinition;
|
||||
|
|
|
@ -5,11 +5,17 @@
|
|||
#include "TerrainDefinition.h"
|
||||
#include "AtmosphereDefinition.h"
|
||||
#include "TexturesDefinition.h"
|
||||
#include "GodRaysDefinition.h"
|
||||
#include "TextureLayerDefinition.h"
|
||||
#include "WaterDefinition.h"
|
||||
#include "SurfaceMaterial.h"
|
||||
#include "FloatNode.h"
|
||||
#include "SkyRasterizer.h"
|
||||
#include "CloudsDefinition.h"
|
||||
#include "LightComponent.h"
|
||||
#include "LightingManager.h"
|
||||
#include "LightFilter.h"
|
||||
#include "GodRaysSampler.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
|
@ -103,10 +109,94 @@ static void testCloudQuality()
|
|||
}
|
||||
}
|
||||
|
||||
static void testGodRays()
|
||||
{
|
||||
class TestLightFilter: public LightFilter
|
||||
{
|
||||
virtual bool applyLightFilter(LightComponent &light, const Vector3 &at) override
|
||||
{
|
||||
if (Vector3(0.0, 100.0, 0.0).sub(at).normalize().y > 0.97)
|
||||
{
|
||||
light.color = COLOR_BLACK;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
class TestRenderer: public SoftwareCanvasRenderer
|
||||
{
|
||||
public:
|
||||
TestRenderer(Scenery *scenery): SoftwareCanvasRenderer(scenery) {}
|
||||
private:
|
||||
virtual void prepare() override
|
||||
{
|
||||
SoftwareRenderer::prepare();
|
||||
|
||||
getLightingManager()->clearSources();
|
||||
getLightingManager()->addStaticLight(LightComponent(COLOR_WHITE, VECTOR_DOWN));
|
||||
getGodRaysSampler()->setAltitudes(0.0, 30.0);
|
||||
getGodRaysSampler()->reset();
|
||||
}
|
||||
};
|
||||
|
||||
Scenery scenery;
|
||||
scenery.autoPreset(63);
|
||||
scenery.getAtmosphere()->setDayTime(12);
|
||||
scenery.getCamera()->setLocation(Vector3(0.0, 1.0, -50.0));
|
||||
scenery.getCamera()->setTarget(Vector3(0.0, 15.0, 0.0));
|
||||
scenery.getTerrain()->height = 0.0;
|
||||
scenery.getTerrain()->validate();
|
||||
scenery.getClouds()->clear();
|
||||
|
||||
TestRenderer renderer(&scenery);
|
||||
renderer.setSize(500, 300);
|
||||
SkyRasterizer *rasterizer = new SkyRasterizer(&renderer, renderer.getProgressHelper(), 0);
|
||||
renderer.setSoloRasterizer(rasterizer);
|
||||
TestLightFilter filter;
|
||||
renderer.getLightingManager()->clearFilters();
|
||||
renderer.getLightingManager()->registerFilter(&filter);
|
||||
|
||||
// quality
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
renderer.setQuality((double)i / 5.0);
|
||||
rasterizer->setQuality(0.2);
|
||||
startTestRender(&renderer, "god_rays_quality", i);
|
||||
}
|
||||
renderer.setQuality(0.5);
|
||||
|
||||
// penetration
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
scenery.getAtmosphere()->childGodRays()->propPenetration()->setValue(0.01 + 0.02 * (double)i);
|
||||
startTestRender(&renderer, "god_rays_penetration", i);
|
||||
}
|
||||
|
||||
// resistance
|
||||
scenery.getAtmosphere()->childGodRays()->propPenetration()->setValue(0.01);
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
scenery.getAtmosphere()->childGodRays()->propResistance()->setValue(0.1 + 0.1 * (double)i);
|
||||
startTestRender(&renderer, "god_rays_resistance", i);
|
||||
}
|
||||
|
||||
// boost
|
||||
scenery.getAtmosphere()->childGodRays()->propResistance()->setValue(0.3);
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
scenery.getAtmosphere()->childGodRays()->propBoost()->setValue(2.0 + 4.0 * (double)i);
|
||||
startTestRender(&renderer, "god_rays_boost", i);
|
||||
}
|
||||
}
|
||||
|
||||
void runTestSuite()
|
||||
{
|
||||
testGroundShadowQuality();
|
||||
testRasterizationQuality();
|
||||
testCloudQuality();
|
||||
testGodRays();
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "CloudsRenderer.h"
|
||||
#include "Scenery.h"
|
||||
#include "LightingManager.h"
|
||||
#include "GodRaysSampler.h"
|
||||
#include "Logs.h"
|
||||
#include "Vector3.h"
|
||||
|
||||
|
@ -71,6 +72,7 @@ void OpenGLRenderer::initialize()
|
|||
|
||||
getCloudsRenderer()->setEnabled(false);
|
||||
getLightingManager()->setSpecularity(false);
|
||||
getGodRaysSampler()->setEnabled(false);
|
||||
|
||||
skybox->initialize();
|
||||
skybox->updateScenery();
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "AtmosphereDefinition.h"
|
||||
#include "AtmosphereModelBruneton.h"
|
||||
#include "AtmosphereResult.h"
|
||||
#include "GodRaysSampler.h"
|
||||
#include "LightComponent.h"
|
||||
#include "LightStatus.h"
|
||||
#include "Scenery.h"
|
||||
|
@ -98,7 +99,7 @@ AtmosphereResult BaseAtmosphereRenderer::getSkyColor(Vector3)
|
|||
return result;
|
||||
}
|
||||
|
||||
Vector3 BaseAtmosphereRenderer::getSunDirection(bool cache) const
|
||||
Vector3 BaseAtmosphereRenderer::getSunDirection(bool) const
|
||||
{
|
||||
AtmosphereDefinition* atmosphere = getDefinition();
|
||||
double sun_angle = (atmosphere->propDayTime()->getValue() + 0.75) * M_PI * 2.0;
|
||||
|
@ -131,7 +132,7 @@ AtmosphereResult SoftwareBrunetonAtmosphereRenderer::applyAerialPerspective(Vect
|
|||
AtmosphereDefinition* definition = getDefinition();
|
||||
AtmosphereResult result;
|
||||
|
||||
/* Get base perspective */
|
||||
// Get base perspective
|
||||
switch (definition->model)
|
||||
{
|
||||
case AtmosphereDefinition::ATMOSPHERE_MODEL_BRUNETON:
|
||||
|
@ -141,7 +142,10 @@ AtmosphereResult SoftwareBrunetonAtmosphereRenderer::applyAerialPerspective(Vect
|
|||
;
|
||||
}
|
||||
|
||||
/* Apply weather effects */
|
||||
// Apply god rays ponderation
|
||||
result.inscattering = parent->getGodRaysSampler()->apply(COLOR_BLACK, result.inscattering, location);
|
||||
|
||||
// Apply weather effects
|
||||
_applyWeatherEffects(definition, &result);
|
||||
|
||||
return result;
|
||||
|
@ -162,10 +166,10 @@ AtmosphereResult SoftwareBrunetonAtmosphereRenderer::getSkyColor(Vector3 directi
|
|||
|
||||
base = COLOR_BLACK;
|
||||
|
||||
/* Get night sky */
|
||||
// Get night sky
|
||||
base = base.add(parent->getNightSky()->getColor(camera_location.y, direction));
|
||||
|
||||
/* Get sun shape */
|
||||
// Get sun shape
|
||||
/*if (v3Dot(sun_direction, direction) >= 0)
|
||||
{
|
||||
double sun_radius = definition->sun_radius * SUN_RADIUS_SCALED * 5.0; // FIXME Why should we multiply by 5 ?
|
||||
|
@ -190,7 +194,7 @@ AtmosphereResult SoftwareBrunetonAtmosphereRenderer::getSkyColor(Vector3 directi
|
|||
}
|
||||
}*/
|
||||
|
||||
/* Get scattering */
|
||||
// Get scattering
|
||||
AtmosphereResult result;
|
||||
Vector3 location = camera_location.add(direction.scale(6421.0));
|
||||
switch (definition->model)
|
||||
|
@ -202,7 +206,10 @@ AtmosphereResult SoftwareBrunetonAtmosphereRenderer::getSkyColor(Vector3 directi
|
|||
result = BaseAtmosphereRenderer::applyAerialPerspective(location, result.base);
|
||||
}
|
||||
|
||||
/* Apply weather effects */
|
||||
// Apply god rays ponderation
|
||||
result.inscattering = parent->getGodRaysSampler()->apply(COLOR_BLACK, result.inscattering, location);
|
||||
|
||||
// Apply weather effects
|
||||
_applyWeatherEffects(definition, &result);
|
||||
|
||||
return result;
|
||||
|
|
|
@ -239,7 +239,7 @@ bool CloudBasicLayerRenderer::alterLight(BaseCloudsModel *model, LightComponent*
|
|||
}
|
||||
}
|
||||
|
||||
double miminum_light = 0.5;
|
||||
double miminum_light = 0.3;
|
||||
factor = 1.0 - (1.0 - miminum_light) * factor;
|
||||
|
||||
light->color.r *= factor;
|
||||
|
|
|
@ -198,3 +198,23 @@ bool CloudsRenderer::applyLightFilter(LightComponent &light, const Vector3 &at)
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
double CloudsRenderer::getHighestAltitude()
|
||||
{
|
||||
CloudsDefinition* definition = parent->getScenery()->getClouds();
|
||||
double highest = 0.0;
|
||||
|
||||
int n = definition->count();
|
||||
double low, high;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
BaseCloudsModel* layer_model = getLayerModel(i);
|
||||
layer_model->getAltitudeRange(&low, &high);
|
||||
if (high > highest)
|
||||
{
|
||||
highest = high;
|
||||
}
|
||||
}
|
||||
|
||||
return highest;
|
||||
}
|
||||
|
|
|
@ -67,6 +67,11 @@ public:
|
|||
* Return true if the light was altered.
|
||||
*/
|
||||
virtual bool applyLightFilter(LightComponent &light, const Vector3 &at) override;
|
||||
|
||||
/**
|
||||
* Get the highest altitude of all layers.
|
||||
*/
|
||||
double getHighestAltitude();
|
||||
private:
|
||||
double quality;
|
||||
|
||||
|
|
38
src/render/software/GodRaysResult.cpp
Normal file
38
src/render/software/GodRaysResult.cpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
#include "GodRaysResult.h"
|
||||
|
||||
GodRaysResult::GodRaysResult(double inside_length, double full_length):
|
||||
inside_length(inside_length), full_length(full_length)
|
||||
{
|
||||
}
|
||||
|
||||
Color GodRaysResult::apply(const Color &raw, const Color &atmosphered, const GodRaysParams ¶ms)
|
||||
{
|
||||
if (inside_length == 0.0)
|
||||
{
|
||||
return raw;
|
||||
}
|
||||
else if (inside_length < full_length)
|
||||
{
|
||||
double diff = full_length - inside_length;
|
||||
double factor = 1.0 - params.penetration * diff;
|
||||
double minimum = params.resistance;
|
||||
double complement = 1.0 - minimum;
|
||||
if (factor < minimum)
|
||||
{
|
||||
factor = minimum;
|
||||
}
|
||||
else
|
||||
{
|
||||
factor = pow((factor - minimum) / complement, params.boost) * complement + minimum;
|
||||
}
|
||||
|
||||
return Color(raw.r + (atmosphered.r - raw.r) * factor,
|
||||
raw.g + (atmosphered.g - raw.g) * factor,
|
||||
raw.b + (atmosphered.b - raw.b) * factor,
|
||||
atmosphered.a);
|
||||
}
|
||||
else
|
||||
{
|
||||
return atmosphered;
|
||||
}
|
||||
}
|
40
src/render/software/GodRaysResult.h
Normal file
40
src/render/software/GodRaysResult.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
#ifndef GODRAYSRESULT_H
|
||||
#define GODRAYSRESULT_H
|
||||
|
||||
#include "software_global.h"
|
||||
|
||||
#include "Color.h"
|
||||
|
||||
namespace paysages {
|
||||
namespace software {
|
||||
|
||||
/**
|
||||
* Result of god rays computation.
|
||||
*/
|
||||
class SOFTWARESHARED_EXPORT GodRaysResult
|
||||
{
|
||||
public:
|
||||
typedef struct {
|
||||
double penetration;
|
||||
double resistance;
|
||||
double boost;
|
||||
} GodRaysParams;
|
||||
|
||||
public:
|
||||
GodRaysResult() = default;
|
||||
GodRaysResult(double inside_length, double full_length);
|
||||
|
||||
/**
|
||||
* Apply the result on a base color.
|
||||
*/
|
||||
Color apply(const Color &raw, const Color &atmosphered, const GodRaysParams ¶ms);
|
||||
|
||||
private:
|
||||
double inside_length;
|
||||
double full_length;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // GODRAYSRESULT_H
|
220
src/render/software/GodRaysSampler.cpp
Normal file
220
src/render/software/GodRaysSampler.cpp
Normal file
|
@ -0,0 +1,220 @@
|
|||
#include "GodRaysSampler.h"
|
||||
|
||||
#include "GodRaysDefinition.h"
|
||||
#include "AtmosphereDefinition.h"
|
||||
#include "SoftwareRenderer.h"
|
||||
#include "Vector3.h"
|
||||
#include "SpaceSegment.h"
|
||||
#include "GodRaysResult.h"
|
||||
#include "LightingManager.h"
|
||||
#include "FloatNode.h"
|
||||
#include "LightStatus.h"
|
||||
#include "Scenery.h"
|
||||
#include "TerrainDefinition.h"
|
||||
#include "CloudsRenderer.h"
|
||||
#include "Interpolation.h"
|
||||
#include <cmath>
|
||||
|
||||
GodRaysSampler::GodRaysSampler()
|
||||
{
|
||||
enabled = true;
|
||||
bounds = new SpaceSegment();
|
||||
definition = new GodRaysDefinition(NULL);
|
||||
camera_location = new Vector3(0, 0, 0);
|
||||
lighting = NULL;
|
||||
low_altitude = -1.0;
|
||||
high_altitude = 1.0;
|
||||
sampling_step = 1.0;
|
||||
max_length = 1.0;
|
||||
data = new double[1];
|
||||
setQuality(0.5);
|
||||
}
|
||||
|
||||
GodRaysSampler::~GodRaysSampler()
|
||||
{
|
||||
delete definition;
|
||||
delete bounds;
|
||||
delete camera_location;
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
void GodRaysSampler::prepare(SoftwareRenderer *renderer)
|
||||
{
|
||||
setCameraLocation(renderer->getCameraLocation(VECTOR_ZERO));
|
||||
setLighting(renderer->getLightingManager());
|
||||
setAltitudes(renderer->getScenery()->getTerrain()->getHeightInfo().min_height, renderer->getCloudsRenderer()->getHighestAltitude());
|
||||
renderer->getScenery()->getAtmosphere()->childGodRays()->copy(definition);
|
||||
reset();
|
||||
}
|
||||
|
||||
void GodRaysSampler::reset()
|
||||
{
|
||||
*bounds = SpaceSegment(Vector3(camera_location->x - max_length, low_altitude, camera_location->z - max_length),
|
||||
Vector3(camera_location->x + max_length, high_altitude, camera_location->z + max_length));
|
||||
samples_x = round(bounds->getXDiff() / sampling_step) + 1;
|
||||
samples_y = round(bounds->getYDiff() / sampling_step) + 1;
|
||||
samples_z = round(bounds->getZDiff() / sampling_step) + 1;
|
||||
|
||||
long n = samples_x * samples_y * samples_z;
|
||||
delete[] data;
|
||||
data = new double[n];
|
||||
for (long i = 0; i < n; i++)
|
||||
{
|
||||
data[i] = -1.0;
|
||||
}
|
||||
}
|
||||
|
||||
void GodRaysSampler::setEnabled(bool enabled)
|
||||
{
|
||||
this->enabled = enabled;
|
||||
}
|
||||
|
||||
void GodRaysSampler::setLighting(LightingManager *manager)
|
||||
{
|
||||
this->lighting = manager;
|
||||
}
|
||||
|
||||
void GodRaysSampler::setQuality(double factor)
|
||||
{
|
||||
setQuality(5.0 / (factor * 8.0 + 1.0), 100.0, 5.0 / (factor * 80.0 + 1.0));
|
||||
reset();
|
||||
}
|
||||
|
||||
void GodRaysSampler::setQuality(double sampling_step, double max_length, double walk_step)
|
||||
{
|
||||
this->sampling_step = sampling_step;
|
||||
this->max_length = max_length;
|
||||
this->walk_step = walk_step;
|
||||
}
|
||||
|
||||
void GodRaysSampler::setCameraLocation(const Vector3 &location)
|
||||
{
|
||||
*camera_location = location;
|
||||
}
|
||||
|
||||
void GodRaysSampler::setAltitudes(double low, double high)
|
||||
{
|
||||
low_altitude = low;
|
||||
high_altitude = high;
|
||||
}
|
||||
|
||||
void GodRaysSampler::getSamples(int *x, int *y, int *z) const
|
||||
{
|
||||
*x = samples_x;
|
||||
*y = samples_y;
|
||||
*z = samples_z;
|
||||
}
|
||||
|
||||
Color GodRaysSampler::getRawLight(const Vector3 &location, bool filtered) const
|
||||
{
|
||||
if (lighting)
|
||||
{
|
||||
LightStatus status(lighting, location, *camera_location, filtered);
|
||||
lighting->fillStatus(status, location);
|
||||
return status.getSum();
|
||||
}
|
||||
else
|
||||
{
|
||||
return COLOR_TRANSPARENT;
|
||||
}
|
||||
}
|
||||
|
||||
double GodRaysSampler::getCachedLight(const Vector3 &location)
|
||||
{
|
||||
double x = location.x - bounds->getStart().x;
|
||||
double y = location.y - bounds->getStart().y;
|
||||
double z = location.z - bounds->getStart().z;
|
||||
|
||||
int ix = (int)floor(x / sampling_step);
|
||||
int iy = (int)floor(y / sampling_step);
|
||||
int iz = (int)floor(z / sampling_step);
|
||||
|
||||
// Check cache limits
|
||||
if (ix < 0 || ix >= samples_x - 1 || iy < 0 || iy >= samples_y - 1 || iz < 0 || iz >= samples_z - 1)
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
// Hit cache
|
||||
double p[8] = {
|
||||
getCache(ix, iy, iz),
|
||||
getCache(ix + 1, iy, iz),
|
||||
getCache(ix + 1, iy + 1, iz),
|
||||
getCache(ix, iy + 1, iz),
|
||||
getCache(ix, iy, iz + 1),
|
||||
getCache(ix + 1, iy, iz + 1),
|
||||
getCache(ix + 1, iy + 1, iz + 1),
|
||||
getCache(ix, iy + 1, iz + 1)
|
||||
};
|
||||
return Interpolation::trilinear(p,
|
||||
(x - sampling_step * double(ix)) / sampling_step,
|
||||
(y - sampling_step * double(iy)) / sampling_step,
|
||||
(z - sampling_step * double(iz)) / sampling_step);
|
||||
}
|
||||
|
||||
GodRaysResult GodRaysSampler::getResult(const SpaceSegment &segment)
|
||||
{
|
||||
Vector3 step = segment.getEnd().sub(segment.getStart());
|
||||
double max_length = step.getNorm();
|
||||
step = step.normalize().scale(walk_step);
|
||||
Vector3 walker = segment.getStart();
|
||||
double travelled = 0.0;
|
||||
double inside = 0.0;
|
||||
|
||||
if (max_length > this->max_length)
|
||||
{
|
||||
max_length = this->max_length;
|
||||
}
|
||||
|
||||
while (travelled < max_length)
|
||||
{
|
||||
double light = getCachedLight(walker);
|
||||
|
||||
inside += light * walk_step;
|
||||
|
||||
walker = walker.add(step);
|
||||
travelled += walk_step;
|
||||
}
|
||||
|
||||
return GodRaysResult(inside, travelled);
|
||||
}
|
||||
|
||||
Color GodRaysSampler::apply(const Color &raw, const Color &atmosphered, const Vector3 &location)
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
GodRaysResult result = getResult(SpaceSegment(*camera_location, location));
|
||||
|
||||
GodRaysResult::GodRaysParams params;
|
||||
params.penetration = definition->propPenetration()->getValue();
|
||||
params.resistance = definition->propResistance()->getValue();
|
||||
params.boost = definition->propBoost()->getValue();
|
||||
|
||||
return result.apply(raw, atmosphered, params);
|
||||
}
|
||||
else
|
||||
{
|
||||
return atmosphered;
|
||||
}
|
||||
}
|
||||
|
||||
inline double GodRaysSampler::getCache(int x, int y, int z)
|
||||
{
|
||||
double *cache = data + z * samples_x * samples_y + y * samples_x + x;
|
||||
if (*cache < 0.0)
|
||||
{
|
||||
Vector3 location = Vector3(bounds->getStart().x + sampling_step * (double)x,
|
||||
bounds->getStart().y + sampling_step * (double)y,
|
||||
bounds->getStart().z + sampling_step * (double)z);
|
||||
double unfiltered_power = getRawLight(location, false).getPower();
|
||||
if (unfiltered_power == 0.0)
|
||||
{
|
||||
*cache = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
*cache = getRawLight(location, true).getPower() / unfiltered_power;
|
||||
}
|
||||
}
|
||||
return *cache;
|
||||
}
|
124
src/render/software/GodRaysSampler.h
Normal file
124
src/render/software/GodRaysSampler.h
Normal file
|
@ -0,0 +1,124 @@
|
|||
#ifndef GODRAYSSAMPLER_H
|
||||
#define GODRAYSSAMPLER_H
|
||||
|
||||
#include "software_global.h"
|
||||
|
||||
namespace paysages {
|
||||
namespace software {
|
||||
|
||||
/**
|
||||
* 3D sampler for "god rays".
|
||||
*/
|
||||
class SOFTWARESHARED_EXPORT GodRaysSampler
|
||||
{
|
||||
public:
|
||||
GodRaysSampler();
|
||||
~GodRaysSampler();
|
||||
|
||||
inline const SpaceSegment &getBounds() const {return *bounds;}
|
||||
inline double getSamplingStep() const {return sampling_step;}
|
||||
inline double getMaxLength() const {return max_length;}
|
||||
inline double getWalkStep() const {return walk_step;}
|
||||
|
||||
/**
|
||||
* Prepare the sampler from a renderer.
|
||||
*/
|
||||
void prepare(SoftwareRenderer *renderer);
|
||||
|
||||
/**
|
||||
* Reset the sampler cache.
|
||||
*
|
||||
* This needs to be called after all setXXX method have been called.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Enable or disable the god rays effect.
|
||||
*/
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
/**
|
||||
* Set the lighting manager to use for raw sampling.
|
||||
*/
|
||||
void setLighting(LightingManager *manager);
|
||||
|
||||
/**
|
||||
* Set the overall quality factor (0.0-1.0).
|
||||
*/
|
||||
void setQuality(double factor);
|
||||
|
||||
/**
|
||||
* Set the quality indicators.
|
||||
*/
|
||||
void setQuality(double sampling_step, double max_length, double walk_step);
|
||||
|
||||
/**
|
||||
* Set the camera location.
|
||||
*
|
||||
* This will fix the limits of sampling data, around the camera location.
|
||||
*/
|
||||
void setCameraLocation(const Vector3 &location);
|
||||
|
||||
/**
|
||||
* Set the altitude boundaries.
|
||||
*/
|
||||
void setAltitudes(double low, double high);
|
||||
|
||||
/**
|
||||
* Get the number of samples in each dimension.
|
||||
*/
|
||||
void getSamples(int *x, int *y, int *z) const;
|
||||
|
||||
/**
|
||||
* Get the raw light at a location (without using the cached sampling).
|
||||
*/
|
||||
Color getRawLight(const Vector3 &location, bool filtered) const;
|
||||
|
||||
/**
|
||||
* Get the lighting factor at a given point, using (and filling) the cached sampling.
|
||||
*/
|
||||
double getCachedLight(const Vector3 &location);
|
||||
|
||||
/**
|
||||
* Get the god rays effect on a space segment.
|
||||
*/
|
||||
GodRaysResult getResult(const SpaceSegment &segment);
|
||||
|
||||
/**
|
||||
* Apply the god rays effect to a location.
|
||||
*
|
||||
* *raw* is the shaded color, without atmospheric effects.
|
||||
* *atmosphered* is the shaded color, with atmospheric effects applied.
|
||||
*/
|
||||
Color apply(const Color &raw, const Color &atmosphered, const Vector3 &location);
|
||||
|
||||
private:
|
||||
double getCache(int x, int y, int z);
|
||||
|
||||
private:
|
||||
bool enabled;
|
||||
SpaceSegment *bounds;
|
||||
|
||||
GodRaysDefinition *definition;
|
||||
|
||||
double sampling_step;
|
||||
double max_length;
|
||||
double walk_step;
|
||||
|
||||
int samples_x;
|
||||
int samples_y;
|
||||
int samples_z;
|
||||
|
||||
double low_altitude;
|
||||
double high_altitude;
|
||||
Vector3 *camera_location;
|
||||
|
||||
LightingManager *lighting;
|
||||
|
||||
double *data;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // GODRAYSSAMPLER_H
|
|
@ -1,5 +1,6 @@
|
|||
#include "LightComponent.h"
|
||||
|
||||
/*LightComponent::LightComponent()
|
||||
LightComponent::LightComponent(const Color &color, const Vector3 &direction, double reflection, bool altered):
|
||||
color(color), direction(direction), reflection(reflection), altered(altered)
|
||||
{
|
||||
}*/
|
||||
}
|
||||
|
|
|
@ -17,7 +17,8 @@ namespace software {
|
|||
class SOFTWARESHARED_EXPORT LightComponent
|
||||
{
|
||||
public:
|
||||
//LightComponent();
|
||||
LightComponent() = default;
|
||||
LightComponent(const Color &color, const Vector3 &direction, double reflection = 0.0, bool altered = true);
|
||||
|
||||
Color color; // Light power
|
||||
Vector3 direction; // Direction the light is travelling
|
||||
|
|
|
@ -1,18 +1,22 @@
|
|||
#include "LightStatus.h"
|
||||
|
||||
#include "LightingManager.h"
|
||||
#include "LightComponent.h"
|
||||
#include "Color.h"
|
||||
#include "SurfaceMaterial.h"
|
||||
|
||||
LightStatus::LightStatus(LightingManager *manager, const Vector3 &location, const Vector3 &eye)
|
||||
LightStatus::LightStatus(LightingManager *manager, const Vector3 &location, const Vector3 &eye, bool filtered)
|
||||
{
|
||||
this->max_power = 0.0;
|
||||
this->manager = manager;
|
||||
this->location = location;
|
||||
this->eye = eye;
|
||||
this->filtered = filtered;
|
||||
}
|
||||
|
||||
void LightStatus::pushComponent(LightComponent component)
|
||||
{
|
||||
if (filtered)
|
||||
{
|
||||
double power = component.color.getPower();
|
||||
if (component.altered && (power < max_power * 0.05 || power < 0.001))
|
||||
|
@ -30,6 +34,11 @@ void LightStatus::pushComponent(LightComponent component)
|
|||
components.push_back(component);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
components.push_back(component);
|
||||
}
|
||||
}
|
||||
|
||||
Color LightStatus::apply(const Vector3 &normal, const SurfaceMaterial &material)
|
||||
{
|
||||
|
@ -43,3 +52,15 @@ Color LightStatus::apply(const Vector3 &normal, const SurfaceMaterial &material)
|
|||
final.a = material.base->a;
|
||||
return final;
|
||||
}
|
||||
|
||||
Color LightStatus::getSum() const
|
||||
{
|
||||
Color final = COLOR_BLACK;
|
||||
|
||||
for (auto component: components)
|
||||
{
|
||||
final = final.add(component.color);
|
||||
}
|
||||
|
||||
return final;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace software {
|
|||
class SOFTWARESHARED_EXPORT LightStatus
|
||||
{
|
||||
public:
|
||||
LightStatus(LightingManager *manager, const Vector3 &location, const Vector3 &eye);
|
||||
LightStatus(LightingManager *manager, const Vector3 &location, const Vector3 &eye, bool filtered=true);
|
||||
|
||||
inline Vector3 getLocation() const {return location;}
|
||||
|
||||
|
@ -24,11 +24,17 @@ public:
|
|||
|
||||
Color apply(const Vector3 &normal, const SurfaceMaterial &material);
|
||||
|
||||
/**
|
||||
* Return the sum of all received lights.
|
||||
*/
|
||||
Color getSum() const;
|
||||
|
||||
private:
|
||||
double max_power;
|
||||
LightingManager* manager;
|
||||
Vector3 location;
|
||||
Vector3 eye;
|
||||
bool filtered;
|
||||
std::vector<LightComponent> components;
|
||||
};
|
||||
|
||||
|
|
|
@ -37,6 +37,11 @@ void LightingManager::addStaticLight(const LightComponent &light)
|
|||
static_lights.push_back(light);
|
||||
}
|
||||
|
||||
void LightingManager::clearSources()
|
||||
{
|
||||
sources.clear();
|
||||
}
|
||||
|
||||
void LightingManager::registerSource(LightSource *source)
|
||||
{
|
||||
if (std::find(sources.begin(), sources.end(), source) == sources.end())
|
||||
|
@ -46,9 +51,17 @@ void LightingManager::registerSource(LightSource *source)
|
|||
}
|
||||
|
||||
void LightingManager::unregisterSource(LightSource *source)
|
||||
{
|
||||
if (std::find(sources.begin(), sources.end(), source) != sources.end())
|
||||
{
|
||||
sources.erase(std::find(sources.begin(), sources.end(), source));
|
||||
}
|
||||
}
|
||||
|
||||
void LightingManager::clearFilters()
|
||||
{
|
||||
filters.clear();
|
||||
}
|
||||
|
||||
void LightingManager::registerFilter(LightFilter* filter)
|
||||
{
|
||||
|
@ -59,9 +72,12 @@ void LightingManager::registerFilter(LightFilter* filter)
|
|||
}
|
||||
|
||||
void LightingManager::unregisterFilter(LightFilter *filter)
|
||||
{
|
||||
if (std::find(filters.begin(), filters.end(), filter) != filters.end())
|
||||
{
|
||||
filters.erase(std::find(filters.begin(), filters.end(), filter));
|
||||
}
|
||||
}
|
||||
|
||||
bool LightingManager::alterLight(LightComponent &component, const Vector3 &location)
|
||||
{
|
||||
|
@ -168,10 +184,8 @@ Color LightingManager::applyFinalComponent(const LightComponent &component, cons
|
|||
return result;
|
||||
}
|
||||
|
||||
Color LightingManager::apply(const Vector3 &eye, const Vector3 &location, const Vector3 &normal, const SurfaceMaterial &material)
|
||||
void LightingManager::fillStatus(LightStatus &status, const Vector3 &location) const
|
||||
{
|
||||
LightStatus status(this, location, eye);
|
||||
|
||||
for (auto &light: static_lights)
|
||||
{
|
||||
status.pushComponent(light);
|
||||
|
@ -187,6 +201,11 @@ Color LightingManager::apply(const Vector3 &eye, const Vector3 &location, const
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Color LightingManager::apply(const Vector3 &eye, const Vector3 &location, const Vector3 &normal, const SurfaceMaterial &material)
|
||||
{
|
||||
LightStatus status(this, location, eye);
|
||||
fillStatus(status, location);
|
||||
return status.apply(normal, material);
|
||||
}
|
||||
|
|
|
@ -35,6 +35,11 @@ public:
|
|||
*/
|
||||
void addStaticLight(const LightComponent &light);
|
||||
|
||||
/**
|
||||
* Remove all registered sources.
|
||||
*/
|
||||
void clearSources();
|
||||
|
||||
/**
|
||||
* Register a source of dynamic lighting.
|
||||
*/
|
||||
|
@ -45,6 +50,11 @@ public:
|
|||
*/
|
||||
void unregisterSource(LightSource *source);
|
||||
|
||||
/**
|
||||
* Remove all registered filters.
|
||||
*/
|
||||
void clearFilters();
|
||||
|
||||
/**
|
||||
* Register a filter that will receive all alterable lights.
|
||||
*/
|
||||
|
@ -72,6 +82,11 @@ public:
|
|||
*/
|
||||
Color applyFinalComponent(const LightComponent &component, const Vector3 &eye, const Vector3 &location, const Vector3 &normal, const SurfaceMaterial &material);
|
||||
|
||||
/**
|
||||
* Compute the light status at a given location.
|
||||
*/
|
||||
void fillStatus(LightStatus &status, const Vector3 &location) const;
|
||||
|
||||
/**
|
||||
* Apply lighting to a surface at a given location.
|
||||
*/
|
||||
|
|
|
@ -95,7 +95,7 @@ const Color NightSky::getColor(double altitude, const Vector3 &direction)
|
|||
|
||||
}
|
||||
|
||||
bool NightSky::getLightsAt(std::vector<LightComponent> &result, const Vector3 &location) const
|
||||
bool NightSky::getLightsAt(std::vector<LightComponent> &result, const Vector3 &) const
|
||||
{
|
||||
LightComponent moon, sky;
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "Rasterizer.h"
|
||||
#include "CanvasFragment.h"
|
||||
#include "RenderProgress.h"
|
||||
#include "GodRaysSampler.h"
|
||||
|
||||
#define SPHERE_SIZE 20000.0
|
||||
|
||||
|
@ -68,7 +69,7 @@ void SkyRasterizer::rasterizeToCanvas(CanvasPortion* canvas)
|
|||
direction.z = SPHERE_SIZE * sin(current_i) * cos(current_j + step_j);
|
||||
vertex4 = camera_location.add(direction);
|
||||
|
||||
/* TODO Triangles at poles */
|
||||
// TODO Triangles at poles
|
||||
pushQuad(canvas, vertex1, vertex4, vertex3, vertex2);
|
||||
}
|
||||
progress->add(res_i);
|
||||
|
@ -84,7 +85,7 @@ Color SkyRasterizer::shadeFragment(const CanvasFragment &fragment) const
|
|||
camera_location = renderer->getCameraLocation(location);
|
||||
direction = location.sub(camera_location);
|
||||
|
||||
/* TODO Don't compute result->color if it's fully covered by clouds */
|
||||
// TODO Don't compute sky color if it's fully covered by clouds
|
||||
result = renderer->getAtmosphereRenderer()->getSkyColor(direction.normalize()).final;
|
||||
result = renderer->getCloudsRenderer()->getColor(camera_location, camera_location.add(direction.scale(10.0)), result);
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include "NightSky.h"
|
||||
#include "LightStatus.h"
|
||||
#include "LightingManager.h"
|
||||
#include "GodRaysSampler.h"
|
||||
#include "GodRaysResult.h"
|
||||
#include "System.h"
|
||||
#include "Thread.h"
|
||||
|
||||
|
@ -37,6 +39,7 @@ SoftwareRenderer::SoftwareRenderer(Scenery* scenery):
|
|||
|
||||
fluid_medium = new FluidMediumManager(this);
|
||||
lighting = new LightingManager();
|
||||
godrays = new GodRaysSampler();
|
||||
|
||||
lighting->registerFilter(water_renderer);
|
||||
lighting->registerFilter(terrain_renderer);
|
||||
|
@ -52,6 +55,7 @@ SoftwareRenderer::~SoftwareRenderer()
|
|||
|
||||
delete fluid_medium;
|
||||
delete lighting;
|
||||
delete godrays;
|
||||
|
||||
delete nightsky_renderer;
|
||||
|
||||
|
@ -88,6 +92,7 @@ void SoftwareRenderer::prepare()
|
|||
nightsky_renderer->update();
|
||||
|
||||
// Prepare global tools
|
||||
godrays->prepare(this);
|
||||
fluid_medium->clearMedia();
|
||||
//fluid_medium->registerMedium(water_renderer);
|
||||
}
|
||||
|
@ -96,6 +101,7 @@ void SoftwareRenderer::setQuality(double quality)
|
|||
{
|
||||
terrain_renderer->setQuality(quality);
|
||||
clouds_renderer->setQuality(quality);
|
||||
godrays->setQuality(quality);
|
||||
|
||||
// TEMP compat with old code
|
||||
render_quality = (int)(quality * 9.0) + 1;
|
||||
|
|
|
@ -57,6 +57,7 @@ public:
|
|||
|
||||
inline FluidMediumManager* getFluidMediumManager() const {return fluid_medium;}
|
||||
inline LightingManager* getLightingManager() const {return lighting;}
|
||||
inline GodRaysSampler* getGodRaysSampler() const {return godrays;}
|
||||
|
||||
virtual Color applyLightingToSurface(const Vector3 &location, const Vector3 &normal, const SurfaceMaterial &material);
|
||||
virtual Color applyMediumTraversal(Vector3 location, Color color);
|
||||
|
@ -67,6 +68,7 @@ private:
|
|||
|
||||
FluidMediumManager* fluid_medium;
|
||||
LightingManager* lighting;
|
||||
GodRaysSampler* godrays;
|
||||
|
||||
BaseAtmosphereRenderer* atmosphere_renderer;
|
||||
CloudsRenderer* clouds_renderer;
|
||||
|
|
|
@ -52,7 +52,9 @@ SOURCES += SoftwareRenderer.cpp \
|
|||
clouds/CloudModelCirrus.cpp \
|
||||
clouds/CloudModelCumuloNimbus.cpp \
|
||||
RenderProgress.cpp \
|
||||
LightSource.cpp
|
||||
LightSource.cpp \
|
||||
GodRaysSampler.cpp \
|
||||
GodRaysResult.cpp
|
||||
|
||||
HEADERS += SoftwareRenderer.h\
|
||||
software_global.h \
|
||||
|
@ -94,7 +96,9 @@ HEADERS += SoftwareRenderer.h\
|
|||
clouds/CloudModelCirrus.h \
|
||||
clouds/CloudModelCumuloNimbus.h \
|
||||
RenderProgress.h \
|
||||
LightSource.h
|
||||
LightSource.h \
|
||||
GodRaysSampler.h \
|
||||
GodRaysResult.h
|
||||
|
||||
unix:!symbian {
|
||||
maemo5 {
|
||||
|
|
|
@ -49,6 +49,9 @@ namespace software {
|
|||
|
||||
class TerrainRayWalker;
|
||||
|
||||
class GodRaysSampler;
|
||||
class GodRaysResult;
|
||||
|
||||
class Canvas;
|
||||
class CanvasPortion;
|
||||
class CanvasPixel;
|
||||
|
|
149
src/tests/GodRaysSampler_Test.cpp
Normal file
149
src/tests/GodRaysSampler_Test.cpp
Normal file
|
@ -0,0 +1,149 @@
|
|||
#include "BaseTestCase.h"
|
||||
#include "GodRaysSampler.h"
|
||||
|
||||
#include "SpaceSegment.h"
|
||||
#include "Vector3.h"
|
||||
#include "LightingManager.h"
|
||||
#include "LightSource.h"
|
||||
#include "LightFilter.h"
|
||||
#include "LightComponent.h"
|
||||
#include "Color.h"
|
||||
|
||||
TEST(GodRaysSampler, getBounds)
|
||||
{
|
||||
GodRaysSampler sampler;
|
||||
|
||||
sampler.setAltitudes(-5.0, 300.0);
|
||||
sampler.setCameraLocation(Vector3(100.0, 0.0, 8000.0));
|
||||
sampler.setQuality(10.0, 500.0, 1.0);
|
||||
sampler.reset();
|
||||
|
||||
SpaceSegment bounds = sampler.getBounds();
|
||||
|
||||
EXPECT_VECTOR3_COORDS(bounds.getStart(), -400.0, -5.0, 7500.0);
|
||||
EXPECT_VECTOR3_COORDS(bounds.getEnd(), 600.0, 300.0, 8500.0);
|
||||
}
|
||||
|
||||
TEST(GodRaysSampler, getSamples)
|
||||
{
|
||||
GodRaysSampler sampler;
|
||||
|
||||
sampler.setAltitudes(-50.0, 100.0);
|
||||
sampler.setQuality(10.0, 100.0, 1.0);
|
||||
sampler.reset();
|
||||
|
||||
int x, y, z;
|
||||
sampler.getSamples(&x, &y, &z);
|
||||
|
||||
EXPECT_EQ(21, x);
|
||||
EXPECT_EQ(16, y);
|
||||
EXPECT_EQ(21, z);
|
||||
}
|
||||
|
||||
TEST(GodRaysSampler, setQuality)
|
||||
{
|
||||
GodRaysSampler sampler;
|
||||
|
||||
sampler.setQuality(0.0);
|
||||
EXPECT_DOUBLE_EQ(5.0, sampler.getSamplingStep());
|
||||
EXPECT_DOUBLE_EQ(100.0, sampler.getMaxLength());
|
||||
EXPECT_DOUBLE_EQ(5.0, sampler.getWalkStep());
|
||||
|
||||
sampler.setQuality(0.5);
|
||||
EXPECT_DOUBLE_EQ(1.0, sampler.getSamplingStep());
|
||||
EXPECT_DOUBLE_EQ(100.0, sampler.getMaxLength());
|
||||
EXPECT_DOUBLE_EQ(0.12195121951, sampler.getWalkStep());
|
||||
|
||||
sampler.setQuality(1.0);
|
||||
EXPECT_DOUBLE_EQ(0.55555555555, sampler.getSamplingStep());
|
||||
EXPECT_DOUBLE_EQ(100.0, sampler.getMaxLength());
|
||||
EXPECT_DOUBLE_EQ(0.06172839506, sampler.getWalkStep());
|
||||
}
|
||||
|
||||
class GodRayLightSource: public LightSource
|
||||
{
|
||||
virtual bool getLightsAt(std::vector<LightComponent> &result, const Vector3 &location) const override
|
||||
{
|
||||
LightComponent light;
|
||||
light.color = Color(fabs(location.x), fabs(location.y), fabs(location.z));
|
||||
result.push_back(light);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(GodRaysSampler, getRawLight)
|
||||
{
|
||||
class TestLightFilter: public LightFilter
|
||||
{
|
||||
virtual bool applyLightFilter(LightComponent &light, const Vector3 &at) override
|
||||
{
|
||||
if (at.x > 0.5)
|
||||
{
|
||||
light.color.r = 0.1;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
GodRayLightSource light_source;
|
||||
TestLightFilter filter;
|
||||
LightingManager lighting;
|
||||
lighting.registerSource(&light_source);
|
||||
lighting.registerFilter(&filter);
|
||||
|
||||
GodRaysSampler sampler;
|
||||
sampler.setLighting(&lighting);
|
||||
|
||||
EXPECT_COLOR_RGBA(sampler.getRawLight(Vector3(0.0, 0.0, 0.0), false), 0.0, 0.0, 0.0, 1.0);
|
||||
EXPECT_COLOR_RGBA(sampler.getRawLight(Vector3(1.0, 2.0, -6.0), false), 1.0, 2.0, 6.0, 1.0);
|
||||
|
||||
EXPECT_COLOR_RGBA(sampler.getRawLight(Vector3(0.0, 0.0, 0.0), true), 0.0, 0.0, 0.0, 1.0);
|
||||
EXPECT_COLOR_RGBA(sampler.getRawLight(Vector3(1.0, 2.0, -6.0), true), 0.1, 2.0, 6.0, 1.0);
|
||||
}
|
||||
|
||||
TEST(GodRaysSampler, getCachedLight)
|
||||
{
|
||||
class TestLightFilter: public LightFilter
|
||||
{
|
||||
virtual bool applyLightFilter(LightComponent &, const Vector3 &at) override
|
||||
{
|
||||
return at.x <= 10.0 or at.x >= 50.0;
|
||||
}
|
||||
};
|
||||
|
||||
GodRayLightSource light_source;
|
||||
TestLightFilter filter;
|
||||
LightingManager lighting;
|
||||
lighting.registerSource(&light_source);
|
||||
lighting.registerFilter(&filter);
|
||||
|
||||
GodRaysSampler sampler;
|
||||
|
||||
sampler.setLighting(&lighting);
|
||||
sampler.setQuality(5.0, 100.0, 1.0);
|
||||
sampler.setAltitudes(-10.0, 10.0);
|
||||
sampler.reset();
|
||||
|
||||
// outside the sampler, considered full light
|
||||
EXPECT_DOUBLE_EQ(1.0, sampler.getCachedLight(Vector3(0.0, 15.0, 0.0)));
|
||||
EXPECT_DOUBLE_EQ(1.0, sampler.getCachedLight(Vector3(0.0, -15.0, 0.0)));
|
||||
EXPECT_DOUBLE_EQ(1.0, sampler.getCachedLight(Vector3(-120.0, 0.0, 0.0)));
|
||||
EXPECT_DOUBLE_EQ(1.0, sampler.getCachedLight(Vector3(120.0, 0.0, 0.0)));
|
||||
EXPECT_DOUBLE_EQ(1.0, sampler.getCachedLight(Vector3(0.0, 0.0, -100.1)));
|
||||
EXPECT_DOUBLE_EQ(1.0, sampler.getCachedLight(Vector3(0.0, 0.0, 100.1)));
|
||||
|
||||
// at sampling point
|
||||
EXPECT_DOUBLE_EQ(1.0, sampler.getCachedLight(Vector3(0.0, 0.0, 0.0)));
|
||||
EXPECT_DOUBLE_EQ(1.0, sampler.getCachedLight(Vector3(0.0, 5.0, 10.0)));
|
||||
EXPECT_DOUBLE_EQ(1.0, sampler.getCachedLight(Vector3(5.0, 5.0, 10.0)));
|
||||
EXPECT_DOUBLE_EQ(1.0, sampler.getCachedLight(Vector3(10.0, 5.0, 10.0)));
|
||||
EXPECT_DOUBLE_EQ(0.0, sampler.getCachedLight(Vector3(15.0, 5.0, 10.0)));
|
||||
EXPECT_DOUBLE_EQ(0.0, sampler.getCachedLight(Vector3(20.0, 5.0, 10.0)));
|
||||
|
||||
// in between sampling points
|
||||
EXPECT_DOUBLE_EQ(0.5, sampler.getCachedLight(Vector3(12.5, 5.0, 10.0)));
|
||||
}
|
27
src/tests/Interpolation_Test.cpp
Normal file
27
src/tests/Interpolation_Test.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#include "BaseTestCase.h"
|
||||
#include "Interpolation.h"
|
||||
|
||||
TEST(Interpolation, trilinear)
|
||||
{
|
||||
double p[8] = {0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0};
|
||||
|
||||
EXPECT_DOUBLE_EQ(0.0, Interpolation::trilinear(p, 0.0, 0.0, 0.0));
|
||||
EXPECT_DOUBLE_EQ(1.0, Interpolation::trilinear(p, 1.0, 0.0, 0.0));
|
||||
EXPECT_DOUBLE_EQ(2.0, Interpolation::trilinear(p, 1.0, 1.0, 0.0));
|
||||
EXPECT_DOUBLE_EQ(3.0, Interpolation::trilinear(p, 0.0, 1.0, 0.0));
|
||||
EXPECT_DOUBLE_EQ(4.0, Interpolation::trilinear(p, 0.0, 0.0, 1.0));
|
||||
EXPECT_DOUBLE_EQ(5.0, Interpolation::trilinear(p, 1.0, 0.0, 1.0));
|
||||
EXPECT_DOUBLE_EQ(6.0, Interpolation::trilinear(p, 1.0, 1.0, 1.0));
|
||||
EXPECT_DOUBLE_EQ(7.0, Interpolation::trilinear(p, 0.0, 1.0, 1.0));
|
||||
|
||||
EXPECT_DOUBLE_EQ(0.5, Interpolation::trilinear(p, 0.5, 0.0, 0.0));
|
||||
EXPECT_DOUBLE_EQ(1.5, Interpolation::trilinear(p, 0.0, 0.5, 0.0));
|
||||
EXPECT_DOUBLE_EQ(2.0, Interpolation::trilinear(p, 0.0, 0.0, 0.5));
|
||||
|
||||
EXPECT_DOUBLE_EQ(1.5, Interpolation::trilinear(p, 0.5, 0.5, 0.0));
|
||||
EXPECT_DOUBLE_EQ(3.5, Interpolation::trilinear(p, 0.0, 0.5, 0.5));
|
||||
EXPECT_DOUBLE_EQ(2.5, Interpolation::trilinear(p, 0.5, 0.0, 0.5));
|
||||
|
||||
EXPECT_DOUBLE_EQ(3.5, Interpolation::trilinear(p, 0.5, 0.5, 0.5));
|
||||
}
|
||||
|
|
@ -29,7 +29,9 @@ SOURCES += main.cpp \
|
|||
ColorHSL_Test.cpp \
|
||||
RenderProgress_Test.cpp \
|
||||
IntNode_Test.cpp \
|
||||
LightingManager_Test.cpp
|
||||
LightingManager_Test.cpp \
|
||||
GodRaysSampler_Test.cpp \
|
||||
Interpolation_Test.cpp
|
||||
|
||||
HEADERS += \
|
||||
BaseTestCase.h
|
||||
|
|
Loading…
Reference in a new issue