Added god rays manager (initial implementation)
This commit is contained in:
parent
6f2d23d960
commit
b045b731ad
28 changed files with 812 additions and 33 deletions
1
TODO
1
TODO
|
@ -3,6 +3,7 @@ Technlology Preview 2 :
|
||||||
- Streamline the definition system, with undo and events.
|
- Streamline the definition system, with undo and events.
|
||||||
- Implement copy-on-write on definitions (to avoid copying whole heightmaps for instance).
|
- Implement copy-on-write on definitions (to avoid copying whole heightmaps for instance).
|
||||||
- Add clouds to OpenGL with 3d textures.
|
- 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 potential holes in land rendering (OpenGL and software).
|
||||||
- Fix polygon culling near the camera in low-res renders (automatic tessellation ?).
|
- Fix polygon culling near the camera in low-res renders (automatic tessellation ?).
|
||||||
- Fix sun size not being consistent between opengl and software
|
- 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);
|
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
|
class BASICSSHARED_EXPORT Interpolation
|
||||||
{
|
{
|
||||||
public:
|
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)
|
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])));
|
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 getStart() const {return start;}
|
||||||
inline Vector3 getEnd() const {return end;}
|
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.
|
* \brief Keep only the intersection with a slice orthogonal to the Y axis.
|
||||||
* \return true if a segment remains
|
* \return true if a segment remains
|
||||||
|
|
|
@ -10,6 +10,11 @@
|
||||||
#include "SurfaceMaterial.h"
|
#include "SurfaceMaterial.h"
|
||||||
#include "FloatNode.h"
|
#include "FloatNode.h"
|
||||||
#include "SkyRasterizer.h"
|
#include "SkyRasterizer.h"
|
||||||
|
#include "CloudsDefinition.h"
|
||||||
|
#include "LightComponent.h"
|
||||||
|
#include "LightingManager.h"
|
||||||
|
#include "LightFilter.h"
|
||||||
|
#include "GodRaysSampler.h"
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
@ -103,10 +108,68 @@ 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);
|
||||||
|
for (int i = 0; i < 6; i++)
|
||||||
|
{
|
||||||
|
renderer.setQuality((double)i / 5.0);
|
||||||
|
rasterizer->setQuality(0.2);
|
||||||
|
startTestRender(&renderer, "god_rays", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void runTestSuite()
|
void runTestSuite()
|
||||||
{
|
{
|
||||||
testGroundShadowQuality();
|
testGroundShadowQuality();
|
||||||
testRasterizationQuality();
|
testRasterizationQuality();
|
||||||
testCloudQuality();
|
testCloudQuality();
|
||||||
|
testGodRays();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "CloudsRenderer.h"
|
#include "CloudsRenderer.h"
|
||||||
#include "Scenery.h"
|
#include "Scenery.h"
|
||||||
#include "LightingManager.h"
|
#include "LightingManager.h"
|
||||||
|
#include "GodRaysSampler.h"
|
||||||
#include "Logs.h"
|
#include "Logs.h"
|
||||||
#include "Vector3.h"
|
#include "Vector3.h"
|
||||||
|
|
||||||
|
@ -71,6 +72,7 @@ void OpenGLRenderer::initialize()
|
||||||
|
|
||||||
getCloudsRenderer()->setEnabled(false);
|
getCloudsRenderer()->setEnabled(false);
|
||||||
getLightingManager()->setSpecularity(false);
|
getLightingManager()->setSpecularity(false);
|
||||||
|
getGodRaysSampler()->setEnabled(false);
|
||||||
|
|
||||||
skybox->initialize();
|
skybox->initialize();
|
||||||
skybox->updateScenery();
|
skybox->updateScenery();
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "AtmosphereDefinition.h"
|
#include "AtmosphereDefinition.h"
|
||||||
#include "AtmosphereModelBruneton.h"
|
#include "AtmosphereModelBruneton.h"
|
||||||
#include "AtmosphereResult.h"
|
#include "AtmosphereResult.h"
|
||||||
|
#include "GodRaysSampler.h"
|
||||||
#include "LightComponent.h"
|
#include "LightComponent.h"
|
||||||
#include "LightStatus.h"
|
#include "LightStatus.h"
|
||||||
#include "Scenery.h"
|
#include "Scenery.h"
|
||||||
|
@ -98,7 +99,7 @@ AtmosphereResult BaseAtmosphereRenderer::getSkyColor(Vector3)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 BaseAtmosphereRenderer::getSunDirection(bool cache) const
|
Vector3 BaseAtmosphereRenderer::getSunDirection(bool) const
|
||||||
{
|
{
|
||||||
AtmosphereDefinition* atmosphere = getDefinition();
|
AtmosphereDefinition* atmosphere = getDefinition();
|
||||||
double sun_angle = (atmosphere->propDayTime()->getValue() + 0.75) * M_PI * 2.0;
|
double sun_angle = (atmosphere->propDayTime()->getValue() + 0.75) * M_PI * 2.0;
|
||||||
|
@ -131,7 +132,7 @@ AtmosphereResult SoftwareBrunetonAtmosphereRenderer::applyAerialPerspective(Vect
|
||||||
AtmosphereDefinition* definition = getDefinition();
|
AtmosphereDefinition* definition = getDefinition();
|
||||||
AtmosphereResult result;
|
AtmosphereResult result;
|
||||||
|
|
||||||
/* Get base perspective */
|
// Get base perspective
|
||||||
switch (definition->model)
|
switch (definition->model)
|
||||||
{
|
{
|
||||||
case AtmosphereDefinition::ATMOSPHERE_MODEL_BRUNETON:
|
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);
|
_applyWeatherEffects(definition, &result);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -162,10 +166,10 @@ AtmosphereResult SoftwareBrunetonAtmosphereRenderer::getSkyColor(Vector3 directi
|
||||||
|
|
||||||
base = COLOR_BLACK;
|
base = COLOR_BLACK;
|
||||||
|
|
||||||
/* Get night sky */
|
// Get night sky
|
||||||
base = base.add(parent->getNightSky()->getColor(camera_location.y, direction));
|
base = base.add(parent->getNightSky()->getColor(camera_location.y, direction));
|
||||||
|
|
||||||
/* Get sun shape */
|
// Get sun shape
|
||||||
/*if (v3Dot(sun_direction, direction) >= 0)
|
/*if (v3Dot(sun_direction, direction) >= 0)
|
||||||
{
|
{
|
||||||
double sun_radius = definition->sun_radius * SUN_RADIUS_SCALED * 5.0; // FIXME Why should we multiply by 5 ?
|
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;
|
AtmosphereResult result;
|
||||||
Vector3 location = camera_location.add(direction.scale(6421.0));
|
Vector3 location = camera_location.add(direction.scale(6421.0));
|
||||||
switch (definition->model)
|
switch (definition->model)
|
||||||
|
@ -202,7 +206,10 @@ AtmosphereResult SoftwareBrunetonAtmosphereRenderer::getSkyColor(Vector3 directi
|
||||||
result = BaseAtmosphereRenderer::applyAerialPerspective(location, result.base);
|
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);
|
_applyWeatherEffects(definition, &result);
|
||||||
|
|
||||||
return 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;
|
factor = 1.0 - (1.0 - miminum_light) * factor;
|
||||||
|
|
||||||
light->color.r *= factor;
|
light->color.r *= factor;
|
||||||
|
|
|
@ -198,3 +198,23 @@ bool CloudsRenderer::applyLightFilter(LightComponent &light, const Vector3 &at)
|
||||||
|
|
||||||
return true;
|
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.
|
* Return true if the light was altered.
|
||||||
*/
|
*/
|
||||||
virtual bool applyLightFilter(LightComponent &light, const Vector3 &at) override;
|
virtual bool applyLightFilter(LightComponent &light, const Vector3 &at) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the highest altitude of all layers.
|
||||||
|
*/
|
||||||
|
double getHighestAltitude();
|
||||||
private:
|
private:
|
||||||
double quality;
|
double quality;
|
||||||
|
|
||||||
|
|
36
src/render/software/GodRaysResult.cpp
Normal file
36
src/render/software/GodRaysResult.cpp
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#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)
|
||||||
|
{
|
||||||
|
if (inside_length == 0.0)
|
||||||
|
{
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
else if (inside_length < full_length)
|
||||||
|
{
|
||||||
|
double diff = full_length - inside_length;
|
||||||
|
double factor = 1.0 - 0.01 * diff;
|
||||||
|
if (factor < 0.3)
|
||||||
|
{
|
||||||
|
factor = 0.3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
factor = pow((factor - 0.3) / 0.7, 8.0) * 0.7 + 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
33
src/render/software/GodRaysResult.h
Normal file
33
src/render/software/GodRaysResult.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#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:
|
||||||
|
GodRaysResult() = default;
|
||||||
|
GodRaysResult(double inside_length, double full_length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the result on a base color.
|
||||||
|
*/
|
||||||
|
Color apply(const Color &raw, const Color &atmosphered);
|
||||||
|
|
||||||
|
private:
|
||||||
|
double inside_length;
|
||||||
|
double full_length;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // GODRAYSRESULT_H
|
208
src/render/software/GodRaysSampler.cpp
Normal file
208
src/render/software/GodRaysSampler.cpp
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
#include "GodRaysSampler.h"
|
||||||
|
|
||||||
|
#include "SoftwareRenderer.h"
|
||||||
|
#include "Vector3.h"
|
||||||
|
#include "SpaceSegment.h"
|
||||||
|
#include "GodRaysResult.h"
|
||||||
|
#include "LightingManager.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();
|
||||||
|
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 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());
|
||||||
|
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));
|
||||||
|
return result.apply(raw, atmosphered);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
122
src/render/software/GodRaysSampler.h
Normal file
122
src/render/software/GodRaysSampler.h
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
#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;
|
||||||
|
|
||||||
|
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"
|
#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
|
class SOFTWARESHARED_EXPORT LightComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
//LightComponent();
|
LightComponent() = default;
|
||||||
|
LightComponent(const Color &color, const Vector3 &direction, double reflection = 0.0, bool altered = true);
|
||||||
|
|
||||||
Color color; // Light power
|
Color color; // Light power
|
||||||
Vector3 direction; // Direction the light is travelling
|
Vector3 direction; // Direction the light is travelling
|
||||||
|
|
|
@ -1,19 +1,23 @@
|
||||||
#include "LightStatus.h"
|
#include "LightStatus.h"
|
||||||
|
|
||||||
#include "LightingManager.h"
|
#include "LightingManager.h"
|
||||||
|
#include "LightComponent.h"
|
||||||
#include "Color.h"
|
#include "Color.h"
|
||||||
#include "SurfaceMaterial.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->max_power = 0.0;
|
||||||
this->manager = manager;
|
this->manager = manager;
|
||||||
this->location = location;
|
this->location = location;
|
||||||
this->eye = eye;
|
this->eye = eye;
|
||||||
|
this->filtered = filtered;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LightStatus::pushComponent(LightComponent component)
|
void LightStatus::pushComponent(LightComponent component)
|
||||||
{
|
{
|
||||||
|
if (filtered)
|
||||||
|
{
|
||||||
double power = component.color.getPower();
|
double power = component.color.getPower();
|
||||||
if (component.altered && (power < max_power * 0.05 || power < 0.001))
|
if (component.altered && (power < max_power * 0.05 || power < 0.001))
|
||||||
{
|
{
|
||||||
|
@ -29,6 +33,11 @@ void LightStatus::pushComponent(LightComponent component)
|
||||||
}
|
}
|
||||||
components.push_back(component);
|
components.push_back(component);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
components.push_back(component);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Color LightStatus::apply(const Vector3 &normal, const SurfaceMaterial &material)
|
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;
|
final.a = material.base->a;
|
||||||
return final;
|
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
|
class SOFTWARESHARED_EXPORT LightStatus
|
||||||
{
|
{
|
||||||
public:
|
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;}
|
inline Vector3 getLocation() const {return location;}
|
||||||
|
|
||||||
|
@ -24,11 +24,17 @@ public:
|
||||||
|
|
||||||
Color apply(const Vector3 &normal, const SurfaceMaterial &material);
|
Color apply(const Vector3 &normal, const SurfaceMaterial &material);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the sum of all received lights.
|
||||||
|
*/
|
||||||
|
Color getSum() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
double max_power;
|
double max_power;
|
||||||
LightingManager* manager;
|
LightingManager* manager;
|
||||||
Vector3 location;
|
Vector3 location;
|
||||||
Vector3 eye;
|
Vector3 eye;
|
||||||
|
bool filtered;
|
||||||
std::vector<LightComponent> components;
|
std::vector<LightComponent> components;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,11 @@ void LightingManager::addStaticLight(const LightComponent &light)
|
||||||
static_lights.push_back(light);
|
static_lights.push_back(light);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LightingManager::clearSources()
|
||||||
|
{
|
||||||
|
sources.clear();
|
||||||
|
}
|
||||||
|
|
||||||
void LightingManager::registerSource(LightSource *source)
|
void LightingManager::registerSource(LightSource *source)
|
||||||
{
|
{
|
||||||
if (std::find(sources.begin(), sources.end(), source) == sources.end())
|
if (std::find(sources.begin(), sources.end(), source) == sources.end())
|
||||||
|
@ -47,7 +52,15 @@ void LightingManager::registerSource(LightSource *source)
|
||||||
|
|
||||||
void LightingManager::unregisterSource(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));
|
sources.erase(std::find(sources.begin(), sources.end(), source));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LightingManager::clearFilters()
|
||||||
|
{
|
||||||
|
filters.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LightingManager::registerFilter(LightFilter* filter)
|
void LightingManager::registerFilter(LightFilter* filter)
|
||||||
|
@ -60,7 +73,10 @@ void LightingManager::registerFilter(LightFilter* filter)
|
||||||
|
|
||||||
void LightingManager::unregisterFilter(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));
|
filters.erase(std::find(filters.begin(), filters.end(), filter));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LightingManager::alterLight(LightComponent &component, const Vector3 &location)
|
bool LightingManager::alterLight(LightComponent &component, const Vector3 &location)
|
||||||
|
@ -168,10 +184,8 @@ Color LightingManager::applyFinalComponent(const LightComponent &component, cons
|
||||||
return result;
|
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)
|
for (auto &light: static_lights)
|
||||||
{
|
{
|
||||||
status.pushComponent(light);
|
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);
|
return status.apply(normal, material);
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,11 @@ public:
|
||||||
*/
|
*/
|
||||||
void addStaticLight(const LightComponent &light);
|
void addStaticLight(const LightComponent &light);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all registered sources.
|
||||||
|
*/
|
||||||
|
void clearSources();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a source of dynamic lighting.
|
* Register a source of dynamic lighting.
|
||||||
*/
|
*/
|
||||||
|
@ -45,6 +50,11 @@ public:
|
||||||
*/
|
*/
|
||||||
void unregisterSource(LightSource *source);
|
void unregisterSource(LightSource *source);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all registered filters.
|
||||||
|
*/
|
||||||
|
void clearFilters();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a filter that will receive all alterable lights.
|
* 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);
|
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.
|
* Apply lighting to a surface at a given location.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "Rasterizer.h"
|
#include "Rasterizer.h"
|
||||||
#include "CanvasFragment.h"
|
#include "CanvasFragment.h"
|
||||||
#include "RenderProgress.h"
|
#include "RenderProgress.h"
|
||||||
|
#include "GodRaysSampler.h"
|
||||||
|
|
||||||
#define SPHERE_SIZE 20000.0
|
#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);
|
direction.z = SPHERE_SIZE * sin(current_i) * cos(current_j + step_j);
|
||||||
vertex4 = camera_location.add(direction);
|
vertex4 = camera_location.add(direction);
|
||||||
|
|
||||||
/* TODO Triangles at poles */
|
// TODO Triangles at poles
|
||||||
pushQuad(canvas, vertex1, vertex4, vertex3, vertex2);
|
pushQuad(canvas, vertex1, vertex4, vertex3, vertex2);
|
||||||
}
|
}
|
||||||
progress->add(res_i);
|
progress->add(res_i);
|
||||||
|
@ -84,7 +85,7 @@ Color SkyRasterizer::shadeFragment(const CanvasFragment &fragment) const
|
||||||
camera_location = renderer->getCameraLocation(location);
|
camera_location = renderer->getCameraLocation(location);
|
||||||
direction = location.sub(camera_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->getAtmosphereRenderer()->getSkyColor(direction.normalize()).final;
|
||||||
result = renderer->getCloudsRenderer()->getColor(camera_location, camera_location.add(direction.scale(10.0)), result);
|
result = renderer->getCloudsRenderer()->getColor(camera_location, camera_location.add(direction.scale(10.0)), result);
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
#include "NightSky.h"
|
#include "NightSky.h"
|
||||||
#include "LightStatus.h"
|
#include "LightStatus.h"
|
||||||
#include "LightingManager.h"
|
#include "LightingManager.h"
|
||||||
|
#include "GodRaysSampler.h"
|
||||||
|
#include "GodRaysResult.h"
|
||||||
#include "System.h"
|
#include "System.h"
|
||||||
#include "Thread.h"
|
#include "Thread.h"
|
||||||
|
|
||||||
|
@ -37,6 +39,7 @@ SoftwareRenderer::SoftwareRenderer(Scenery* scenery):
|
||||||
|
|
||||||
fluid_medium = new FluidMediumManager(this);
|
fluid_medium = new FluidMediumManager(this);
|
||||||
lighting = new LightingManager();
|
lighting = new LightingManager();
|
||||||
|
godrays = new GodRaysSampler();
|
||||||
|
|
||||||
lighting->registerFilter(water_renderer);
|
lighting->registerFilter(water_renderer);
|
||||||
lighting->registerFilter(terrain_renderer);
|
lighting->registerFilter(terrain_renderer);
|
||||||
|
@ -52,6 +55,7 @@ SoftwareRenderer::~SoftwareRenderer()
|
||||||
|
|
||||||
delete fluid_medium;
|
delete fluid_medium;
|
||||||
delete lighting;
|
delete lighting;
|
||||||
|
delete godrays;
|
||||||
|
|
||||||
delete nightsky_renderer;
|
delete nightsky_renderer;
|
||||||
|
|
||||||
|
@ -88,6 +92,7 @@ void SoftwareRenderer::prepare()
|
||||||
nightsky_renderer->update();
|
nightsky_renderer->update();
|
||||||
|
|
||||||
// Prepare global tools
|
// Prepare global tools
|
||||||
|
godrays->prepare(this);
|
||||||
fluid_medium->clearMedia();
|
fluid_medium->clearMedia();
|
||||||
//fluid_medium->registerMedium(water_renderer);
|
//fluid_medium->registerMedium(water_renderer);
|
||||||
}
|
}
|
||||||
|
@ -96,6 +101,7 @@ void SoftwareRenderer::setQuality(double quality)
|
||||||
{
|
{
|
||||||
terrain_renderer->setQuality(quality);
|
terrain_renderer->setQuality(quality);
|
||||||
clouds_renderer->setQuality(quality);
|
clouds_renderer->setQuality(quality);
|
||||||
|
godrays->setQuality(quality);
|
||||||
|
|
||||||
// TEMP compat with old code
|
// TEMP compat with old code
|
||||||
render_quality = (int)(quality * 9.0) + 1;
|
render_quality = (int)(quality * 9.0) + 1;
|
||||||
|
|
|
@ -57,6 +57,7 @@ public:
|
||||||
|
|
||||||
inline FluidMediumManager* getFluidMediumManager() const {return fluid_medium;}
|
inline FluidMediumManager* getFluidMediumManager() const {return fluid_medium;}
|
||||||
inline LightingManager* getLightingManager() const {return lighting;}
|
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 applyLightingToSurface(const Vector3 &location, const Vector3 &normal, const SurfaceMaterial &material);
|
||||||
virtual Color applyMediumTraversal(Vector3 location, Color color);
|
virtual Color applyMediumTraversal(Vector3 location, Color color);
|
||||||
|
@ -67,6 +68,7 @@ private:
|
||||||
|
|
||||||
FluidMediumManager* fluid_medium;
|
FluidMediumManager* fluid_medium;
|
||||||
LightingManager* lighting;
|
LightingManager* lighting;
|
||||||
|
GodRaysSampler* godrays;
|
||||||
|
|
||||||
BaseAtmosphereRenderer* atmosphere_renderer;
|
BaseAtmosphereRenderer* atmosphere_renderer;
|
||||||
CloudsRenderer* clouds_renderer;
|
CloudsRenderer* clouds_renderer;
|
||||||
|
|
|
@ -52,7 +52,9 @@ SOURCES += SoftwareRenderer.cpp \
|
||||||
clouds/CloudModelCirrus.cpp \
|
clouds/CloudModelCirrus.cpp \
|
||||||
clouds/CloudModelCumuloNimbus.cpp \
|
clouds/CloudModelCumuloNimbus.cpp \
|
||||||
RenderProgress.cpp \
|
RenderProgress.cpp \
|
||||||
LightSource.cpp
|
LightSource.cpp \
|
||||||
|
GodRaysSampler.cpp \
|
||||||
|
GodRaysResult.cpp
|
||||||
|
|
||||||
HEADERS += SoftwareRenderer.h\
|
HEADERS += SoftwareRenderer.h\
|
||||||
software_global.h \
|
software_global.h \
|
||||||
|
@ -94,7 +96,9 @@ HEADERS += SoftwareRenderer.h\
|
||||||
clouds/CloudModelCirrus.h \
|
clouds/CloudModelCirrus.h \
|
||||||
clouds/CloudModelCumuloNimbus.h \
|
clouds/CloudModelCumuloNimbus.h \
|
||||||
RenderProgress.h \
|
RenderProgress.h \
|
||||||
LightSource.h
|
LightSource.h \
|
||||||
|
GodRaysSampler.h \
|
||||||
|
GodRaysResult.h
|
||||||
|
|
||||||
unix:!symbian {
|
unix:!symbian {
|
||||||
maemo5 {
|
maemo5 {
|
||||||
|
|
|
@ -49,6 +49,9 @@ namespace software {
|
||||||
|
|
||||||
class TerrainRayWalker;
|
class TerrainRayWalker;
|
||||||
|
|
||||||
|
class GodRaysSampler;
|
||||||
|
class GodRaysResult;
|
||||||
|
|
||||||
class Canvas;
|
class Canvas;
|
||||||
class CanvasPortion;
|
class CanvasPortion;
|
||||||
class CanvasPixel;
|
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 \
|
ColorHSL_Test.cpp \
|
||||||
RenderProgress_Test.cpp \
|
RenderProgress_Test.cpp \
|
||||||
IntNode_Test.cpp \
|
IntNode_Test.cpp \
|
||||||
LightingManager_Test.cpp
|
LightingManager_Test.cpp \
|
||||||
|
GodRaysSampler_Test.cpp \
|
||||||
|
Interpolation_Test.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
BaseTestCase.h
|
BaseTestCase.h
|
||||||
|
|
Loading…
Reference in a new issue