Merge branch god_rays

This commit is contained in:
Michaël Lemaire 2015-10-08 09:45:39 +02:00
commit 8d9e3fbc94
35 changed files with 913 additions and 36 deletions

1
TODO
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View 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

View file

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

View file

@ -30,6 +30,7 @@ namespace definition {
class CloudsDefinition;
class CloudLayerDefinition;
class AtmosphereDefinition;
class GodRaysDefinition;
class TexturesDefinition;
class TextureLayerDefinition;
class TerrainDefinition;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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 &params)
{
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;
}
}

View 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 &params);
private:
double inside_length;
double full_length;
};
}
}
#endif // GODRAYSRESULT_H

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

View 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

View file

@ -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)
{
}*/
}

View file

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

View file

@ -1,32 +1,41 @@
#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)
{
double power = component.color.getPower();
if (component.altered && (power < max_power * 0.05 || power < 0.001))
if (filtered)
{
// Exclude filtered lights that are owerpowered by a previous one
return;
}
if (manager->alterLight(component, location))
{
if (power > max_power)
double power = component.color.getPower();
if (component.altered && (power < max_power * 0.05 || power < 0.001))
{
max_power = power;
// Exclude filtered lights that are owerpowered by a previous one
return;
}
if (manager->alterLight(component, location))
{
if (power > max_power)
{
max_power = power;
}
components.push_back(component);
}
}
else
{
components.push_back(component);
}
}
@ -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;
}

View file

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

View file

@ -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())
@ -47,7 +52,15 @@ void LightingManager::registerSource(LightSource *source)
void LightingManager::unregisterSource(LightSource *source)
{
sources.erase(std::find(sources.begin(), sources.end(), 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)
@ -60,7 +73,10 @@ void LightingManager::registerFilter(LightFilter* filter)
void LightingManager::unregisterFilter(LightFilter *filter)
{
filters.erase(std::find(filters.begin(), filters.end(), 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);
}

View file

@ -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.
*/

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -49,6 +49,9 @@ namespace software {
class TerrainRayWalker;
class GodRaysSampler;
class GodRaysResult;
class Canvas;
class CanvasPortion;
class CanvasPixel;

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

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

View file

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