Improved clouds lighting

This commit is contained in:
Michaël Lemaire 2016-02-01 20:38:29 +01:00
parent 2878f1f157
commit a54c8d5217
32 changed files with 188 additions and 134 deletions

View file

@ -22,3 +22,12 @@ double Maths::smoothstep(double edge0, double edge1, double x) {
x = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); x = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
return x * x * (3.0 - 2.0 * x); return x * x * (3.0 - 2.0 * x);
} }
double Maths::zeroPoint(double segment_length, double edge0, double edge1) {
double diff = edge1 - edge0;
if (diff == 0.0) {
return 0.0;
} else {
return -edge0 * segment_length / diff;
}
}

View file

@ -26,6 +26,13 @@ class BASICSSHARED_EXPORT Maths {
*/ */
static double smoothstep(double edge0, double edge1, double x); static double smoothstep(double edge0, double edge1, double x);
/**
* Find the length in a segment (with border values "edge0" and "edge1"), at which the value crosses 0.
*
* The length is not normalized (the middle of a segment of length 0.2 is 0.1).
*/
static double zeroPoint(double segment_length, double edge0, double edge1);
static constexpr double PI = 3.141592653589793238462643383279; static constexpr double PI = 3.141592653589793238462643383279;
static constexpr double PI_2 = PI / 2.0; static constexpr double PI_2 = PI / 2.0;
static constexpr double PI_4 = PI / 4.0; static constexpr double PI_4 = PI / 4.0;

View file

@ -27,51 +27,15 @@ typedef struct {
double w; double w;
} Grad4; } Grad4;
static Grad3 _grad3[] = {{1, 1, 0}, static Grad3 _grad3[] = {{1, 1, 0}, {-1, 1, 0}, {1, -1, 0}, {-1, -1, 0}, {1, 0, 1}, {-1, 0, 1},
{-1, 1, 0}, {1, 0, -1}, {-1, 0, -1}, {0, 1, 1}, {0, -1, 1}, {0, 1, -1}, {0, -1, -1}};
{1, -1, 0},
{-1, -1, 0},
{1, 0, 1},
{-1, 0, 1},
{1, 0, -1},
{-1, 0, -1},
{0, 1, 1},
{0, -1, 1},
{0, 1, -1},
{0, -1, -1}};
static Grad4 _grad4[] = {{0, 1, 1, 1}, static Grad4 _grad4[] = {
{0, 1, 1, -1}, {0, 1, 1, 1}, {0, 1, 1, -1}, {0, 1, -1, 1}, {0, 1, -1, -1}, {0, -1, 1, 1}, {0, -1, 1, -1}, {0, -1, -1, 1},
{0, 1, -1, 1}, {0, -1, -1, -1}, {1, 0, 1, 1}, {1, 0, 1, -1}, {1, 0, -1, 1}, {1, 0, -1, -1}, {-1, 0, 1, 1}, {-1, 0, 1, -1},
{0, 1, -1, -1}, {-1, 0, -1, 1}, {-1, 0, -1, -1}, {1, 1, 0, 1}, {1, 1, 0, -1}, {1, -1, 0, 1}, {1, -1, 0, -1}, {-1, 1, 0, 1},
{0, -1, 1, 1}, {-1, 1, 0, -1}, {-1, -1, 0, 1}, {-1, -1, 0, -1}, {1, 1, 1, 0}, {1, 1, -1, 0}, {1, -1, 1, 0}, {1, -1, -1, 0},
{0, -1, 1, -1}, {-1, 1, 1, 0}, {-1, 1, -1, 0}, {-1, -1, 1, 0}, {-1, -1, -1, 0}};
{0, -1, -1, 1},
{0, -1, -1, -1},
{1, 0, 1, 1},
{1, 0, 1, -1},
{1, 0, -1, 1},
{1, 0, -1, -1},
{-1, 0, 1, 1},
{-1, 0, 1, -1},
{-1, 0, -1, 1},
{-1, 0, -1, -1},
{1, 1, 0, 1},
{1, 1, 0, -1},
{1, -1, 0, 1},
{1, -1, 0, -1},
{-1, 1, 0, 1},
{-1, 1, 0, -1},
{-1, -1, 0, 1},
{-1, -1, 0, -1},
{1, 1, 1, 0},
{1, 1, -1, 0},
{1, -1, 1, 0},
{1, -1, -1, 0},
{-1, 1, 1, 0},
{-1, 1, -1, 0},
{-1, -1, 1, 0},
{-1, -1, -1, 0}};
static short _permutations[] = { static short _permutations[] = {
151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142,

View file

@ -45,7 +45,8 @@ class DEFINITIONSHARED_EXPORT CloudLayerDefinition : public DefinitionNode {
CUMULONIMBUS, CUMULONIMBUS,
CIRROCUMULUS, CIRROCUMULUS,
CIRROSTRATUS, CIRROSTRATUS,
CIRRUS CIRRUS,
_COUNT
} CloudsType; } CloudsType;
public: public:

View file

@ -10,10 +10,10 @@ SurfaceMaterial::SurfaceMaterial() : SurfaceMaterial(COLOR_BLACK) {
SurfaceMaterial::SurfaceMaterial(const Color &color) { SurfaceMaterial::SurfaceMaterial(const Color &color) {
base = new Color(color); base = new Color(color);
ambient = 0.0;
hardness = 0.5; hardness = 0.5;
reflection = 0.0; reflection = 0.0;
shininess = 0.0; shininess = 0.0;
receive_shadows = 1.0;
} }
SurfaceMaterial::SurfaceMaterial(const SurfaceMaterial &other) : SurfaceMaterial(COLOR_BLACK) { SurfaceMaterial::SurfaceMaterial(const SurfaceMaterial &other) : SurfaceMaterial(COLOR_BLACK) {
@ -38,29 +38,27 @@ void SurfaceMaterial::setColor(double r, double g, double b, double a) {
void SurfaceMaterial::save(PackStream *stream) const { void SurfaceMaterial::save(PackStream *stream) const {
base->save(stream); base->save(stream);
stream->write(&ambient);
stream->write(&hardness); stream->write(&hardness);
stream->write(&reflection); stream->write(&reflection);
stream->write(&shininess); stream->write(&shininess);
stream->write(&receive_shadows);
} }
void SurfaceMaterial::load(PackStream *stream) { void SurfaceMaterial::load(PackStream *stream) {
base->load(stream); base->load(stream);
stream->read(&ambient);
stream->read(&hardness); stream->read(&hardness);
stream->read(&reflection); stream->read(&reflection);
stream->read(&shininess); stream->read(&shininess);
stream->read(&receive_shadows);
} }
void SurfaceMaterial::copy(SurfaceMaterial *destination) const { void SurfaceMaterial::copy(SurfaceMaterial *destination) const {
*destination->base = *base; *destination->base = *base;
destination->ambient = ambient;
destination->hardness = hardness; destination->hardness = hardness;
destination->reflection = reflection; destination->reflection = reflection;
destination->shininess = shininess; destination->shininess = shininess;
destination->receive_shadows = receive_shadows;
destination->validate(); destination->validate();
} }

View file

@ -25,11 +25,10 @@ class DEFINITIONSHARED_EXPORT SurfaceMaterial {
public: public:
Color *base; Color *base;
double ambient;
double hardness; double hardness;
double reflection; double reflection;
double shininess; double shininess;
double receive_shadows;
}; };
} }
} }

View file

@ -127,8 +127,7 @@ int main(int argc, char **argv) {
scenery->autoPreset(); scenery->autoPreset();
} }
Logs::debug("CommandLine") << "Rendered scenery :" << endl Logs::debug("CommandLine") << "Rendered scenery :" << endl << scenery->toString() << endl;
<< scenery->toString() << endl;
TimeManager time; TimeManager time;
time.setWind(conf_wind_x, conf_wind_z); time.setWind(conf_wind_x, conf_wind_z);

View file

@ -152,6 +152,7 @@ static void testCloudQuality() {
SoftwareCanvasRenderer renderer(&scenery); SoftwareCanvasRenderer renderer(&scenery);
renderer.setSize(600, 800); renderer.setSize(600, 800);
renderer.getGodRaysSampler()->setEnabled(false);
SkyRasterizer rasterizer(&renderer, renderer.getProgressHelper(), 0); SkyRasterizer rasterizer(&renderer, renderer.getProgressHelper(), 0);
renderer.setSoloRasterizer(&rasterizer); renderer.setSoloRasterizer(&rasterizer);
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
@ -463,14 +464,11 @@ static void testCloudsLighting() {
FakeModel(CloudLayerDefinition *layer, double scale) : BaseCloudsModel(layer), scale(scale) { FakeModel(CloudLayerDefinition *layer, double scale) : BaseCloudsModel(layer), scale(scale) {
} }
virtual void getAltitudeRange(double *min_altitude, double *max_altitude) const override { virtual void getAltitudeRange(double *min_altitude, double *max_altitude) const override {
*min_altitude = -scale; *min_altitude = 10.0 - scale;
*max_altitude = scale; *max_altitude = 10.0 + scale;
} }
virtual void getDetailRange(double *min_step, double *max_step) const override { virtual double getDensity(const Vector3 &location, double) const override {
*min_step = *max_step = scale * 0.01; return 1.0 - location.sub(Vector3(0.0, 10.0, 0.0)).getNorm() / scale;
}
virtual double getDensity(const Vector3 &location) const override {
return Maths::smoothstep(0.0, 0.2, 1.0 - location.getNorm() / scale);
} }
double scale; double scale;
}; };
@ -482,8 +480,8 @@ static void testCloudsLighting() {
virtual Color processPixel(int, int, double relx, double rely) const override { virtual Color processPixel(int, int, double relx, double rely) const override {
auto cloud_renderer = renderer->getCloudsRenderer(); auto cloud_renderer = renderer->getCloudsRenderer();
auto atmo_renderer = renderer->getAtmosphereRenderer(); auto atmo_renderer = renderer->getAtmosphereRenderer();
return cloud_renderer->getColor(Vector3(relx * scale * 1.2, rely * scale * 1.2, scale), return cloud_renderer->getColor(Vector3(relx * scale * 1.2, 10.0 + rely * scale * 1.2, scale),
Vector3(relx * scale * 1.2, rely * scale * 1.2, -scale), Vector3(relx * scale * 1.2, 10.0 + rely * scale * 1.2, -scale),
atmo_renderer->getSkyColor(Vector3(relx, rely, 1.0).normalize()).final); atmo_renderer->getSkyColor(Vector3(relx, rely, 1.0).normalize()).final);
} }
virtual int prepareRasterization() override { virtual int prepareRasterization() override {
@ -500,15 +498,16 @@ static void testCloudsLighting() {
Scenery scenery; Scenery scenery;
scenery.autoPreset(1); scenery.autoPreset(1);
scenery.getTerrain()->propHeightNoise()->setConfig(0.0);
scenery.getCamera()->setTarget(VECTOR_ZERO); scenery.getCamera()->setTarget(VECTOR_ZERO);
scenery.getCamera()->setLocation(Vector3(0.0, 0.0, 5.0)); scenery.getCamera()->setLocation(Vector3(0.0, 10.0, 11.0));
CloudLayerDefinition layer(NULL, "test"); CloudLayerDefinition layer(NULL, "test");
scenery.getClouds()->clear(); scenery.getClouds()->clear();
scenery.getClouds()->addLayer(layer); scenery.getClouds()->addLayer(layer);
SoftwareCanvasRenderer renderer(&scenery); SoftwareCanvasRenderer renderer(&scenery);
FakeRasterizer rasterizer(&renderer, 10.0); FakeRasterizer rasterizer(&renderer, 5.0);
renderer.setSize(800, 800); renderer.setSize(800, 800);
renderer.setSoloRasterizer(&rasterizer); renderer.setSoloRasterizer(&rasterizer);
renderer.getGodRaysSampler()->setEnabled(false); renderer.getGodRaysSampler()->setEnabled(false);
@ -522,6 +521,36 @@ static void testCloudsLighting() {
startTestRender(&renderer, "clouds_lighting_night"); startTestRender(&renderer, "clouds_lighting_night");
} }
static void testCloudModels() {
Scenery scenery;
scenery.autoPreset(1);
scenery.getTerrain()->propHeightNoise()->setConfig(0.0);
CloudLayerDefinition layer(NULL, "test");
layer.altitude = 1.0;
scenery.getClouds()->clear();
scenery.getClouds()->addLayer(layer);
SoftwareCanvasRenderer renderer(&scenery);
renderer.setSize(800, 600);
renderer.getGodRaysSampler()->setEnabled(false);
for (int i = CloudLayerDefinition::STRATOCUMULUS; i < CloudLayerDefinition::_COUNT; i++) {
// FIXME Test all
layer.type = static_cast<CloudLayerDefinition::CloudsType>(i);
layer.validate();
renderer.getCloudsRenderer()->update();
auto model = renderer.getCloudsRenderer()->getLayerModel(0);
double minalt, maxalt;
model->getAltitudeRange(&minalt, &maxalt);
double offset = 8.0;
scenery.getCamera()->setLocation(Vector3(0.0, minalt - offset, 0.0));
scenery.getCamera()->setTarget(Vector3(0.0, minalt, offset));
startTestRender(&renderer, "clouds_model", i);
}
}
void runTestSuite() { void runTestSuite() {
testNoise(); testNoise();
testTextures(); testTextures();

View file

@ -141,8 +141,7 @@ void MainModelerWindow::keyReleaseEvent(QKeyEvent *event) {
} else if (event->key() == Qt::Key_F6) { } else if (event->key() == Qt::Key_F6) {
render_process->showPreviousRender(); render_process->showPreviousRender();
} else if (event->key() == Qt::Key_F12) { } else if (event->key() == Qt::Key_F12) {
Logs::warning("UI") << "Current scenery dump:" << endl Logs::warning("UI") << "Current scenery dump:" << endl << scenery->toString() << endl;
<< scenery->toString() << endl;
} else if (event->key() == Qt::Key_N) { } else if (event->key() == Qt::Key_N) {
if (event->modifiers() & Qt::ControlModifier) { if (event->modifiers() & Qt::ControlModifier) {
newFile(); newFile();

View file

@ -19,7 +19,7 @@ class OpenGLView : public QQuickItem {
void handleResize(); void handleResize();
void handleSceneGraphReady(); void handleSceneGraphReady();
signals: signals:
void stopped(); void stopped();
protected: protected:

View file

@ -8,7 +8,7 @@ class PropertyBind : public QObject {
public: public:
explicit PropertyBind(QObject *parent = 0); explicit PropertyBind(QObject *parent = 0);
signals: signals:
public slots: public slots:
}; };

View file

@ -128,11 +128,11 @@ void RenderProcess::startQuickRender() {
} }
void RenderProcess::startMediumRender() { void RenderProcess::startMediumRender() {
startRender(window->getScenery(), RenderConfig(800, 450, 1, 5)); startRender(window->getScenery(), RenderConfig(800, 450, 1, 7));
} }
void RenderProcess::startFinalRender() { void RenderProcess::startFinalRender() {
startRender(window->getScenery(), RenderConfig(1920, 1080, 4, 8)); startRender(window->getScenery(), RenderConfig(1920, 1080, 4, 10));
} }
void RenderProcess::showPreviousRender() { void RenderProcess::showPreviousRender() {

View file

@ -253,7 +253,7 @@ void OpenGLRenderer::cameraChangeEvent(CameraDefinition *camera) {
projection.perspective(perspective.yfov * 180.0 / Maths::PI, perspective.xratio, perspective.znear, projection.perspective(perspective.yfov * 180.0 / Maths::PI, perspective.xratio, perspective.znear,
perspective.zfar); perspective.zfar);
*view_matrix = projection *transform; *view_matrix = projection * transform;
// Set in shaders // Set in shaders
shared_state->set("cameraLocation", location); shared_state->set("cameraLocation", location);

View file

@ -266,8 +266,8 @@ static Color _transmittance3(double r, double mu, double d) {
} }
static void _getIrradianceRMuS(double x, double y, double *r, double *muS) { static void _getIrradianceRMuS(double x, double y, double *r, double *muS) {
*r = Rg + y *(Rt - Rg); *r = Rg + y * (Rt - Rg);
*muS = -0.2 + x *(1.0 + 0.2); *muS = -0.2 + x * (1.0 + 0.2);
} }
/* nearest intersection of ray r,mu with ground or top atmosphere boundary /* nearest intersection of ray r,mu with ground or top atmosphere boundary

View file

@ -117,8 +117,7 @@ AtmosphereResult SoftwareBrunetonAtmosphereRenderer::applyAerialPerspective(cons
case AtmosphereDefinition::ATMOSPHERE_MODEL_BRUNETON: case AtmosphereDefinition::ATMOSPHERE_MODEL_BRUNETON:
result = model->applyAerialPerspective(location, base); result = model->applyAerialPerspective(location, base);
break; break;
default: default:;
;
} }
// Apply god rays ponderation // Apply god rays ponderation

View file

@ -13,6 +13,7 @@
#include "clouds/BaseCloudsModel.h" #include "clouds/BaseCloudsModel.h"
#include "SurfaceMaterial.h" #include "SurfaceMaterial.h"
#include "Logs.h" #include "Logs.h"
#include "Maths.h"
#include "FloatNode.h" #include "FloatNode.h"
struct CloudSegment { struct CloudSegment {
@ -24,10 +25,6 @@ struct CloudSegment {
CloudBasicLayerRenderer::CloudBasicLayerRenderer(SoftwareRenderer *parent) : BaseCloudLayerRenderer(parent) { CloudBasicLayerRenderer::CloudBasicLayerRenderer(SoftwareRenderer *parent) : BaseCloudLayerRenderer(parent) {
} }
static inline double _getDistanceToBorder(BaseCloudsModel *model, const Vector3 &position) {
return model->getDensity(position);
}
/** /**
* Go through the cloud layer to find segments (parts of the lookup that are inside the cloud). * Go through the cloud layer to find segments (parts of the lookup that are inside the cloud).
* *
@ -47,13 +44,14 @@ static inline double _getDistanceToBorder(BaseCloudsModel *model, const Vector3
*/ */
int CloudBasicLayerRenderer::findSegments(BaseCloudsModel *model, const Vector3 &start, const Vector3 &direction, int CloudBasicLayerRenderer::findSegments(BaseCloudsModel *model, const Vector3 &start, const Vector3 &direction,
int max_segments, double max_inside_length, double max_total_length, int max_segments, double max_inside_length, double max_total_length,
double *inside_length, double *total_length, CloudSegment *out_segments) { double *inside_length, double *total_length, CloudSegment *out_segments,
double base_detail) {
double ymin, ymax; double ymin, ymax;
int inside, segment_count; int inside, segment_count;
double current_total_length, current_inside_length; double current_total_length, current_inside_length;
double step_length, segment_length; double step_length, segment_length;
double min_step, max_step; double min_step, max_step;
double noise_distance; double noise_distance, previous_noise_distance;
Vector3 walker, step, segment_start, offset; Vector3 walker, step, segment_start, offset;
double render_precision; double render_precision;
@ -68,20 +66,30 @@ int CloudBasicLayerRenderer::findSegments(BaseCloudsModel *model, const Vector3
double distance = parent->getCameraLocation().sub(start).getNorm(); double distance = parent->getCameraLocation().sub(start).getNorm();
render_precision = min_step + (max_step - min_step) * min(distance / (quality + 0.1), 100.0) * 0.01; render_precision = min_step + (max_step - min_step) * min(distance / (quality + 0.1), 100.0) * 0.01;
/*double verticality = fabs(direction.y);
if (verticality > 0.5) {
render_precision *= 1.0 - 1.8 * (verticality - 0.5);
}*/
segment_count = 0; segment_count = 0;
current_total_length = 0.0; current_total_length = 0.0;
current_inside_length = 0.0; current_inside_length = 0.0;
segment_length = 0.0; segment_length = 0.0;
walker = start; walker = start;
offset = Vector3(model->getLayer()->propXOffset()->getValue(), 0.0, model->getLayer()->propZOffset()->getValue()); offset = Vector3(model->getLayer()->propXOffset()->getValue(), 0.0, model->getLayer()->propZOffset()->getValue());
noise_distance = _getDistanceToBorder(model, start.add(offset)) * render_precision; noise_distance = model->getDensity(start.add(offset), base_detail);
inside = 0; previous_noise_distance = noise_distance;
inside = noise_distance > 0.0;
if (inside) {
segment_start = start;
}
step = direction.scale(render_precision); step = direction.scale(render_precision);
bool stop = false;
do { do {
walker = walker.add(step); walker = walker.add(step);
step_length = step.getNorm(); step_length = step.getNorm();
noise_distance = _getDistanceToBorder(model, walker.add(offset)) * render_precision; noise_distance = stop ? 0.0 : model->getDensity(walker.add(offset), base_detail);
current_total_length += step_length; current_total_length += step_length;
if (noise_distance > 0.0) { if (noise_distance > 0.0) {
@ -89,23 +97,26 @@ int CloudBasicLayerRenderer::findSegments(BaseCloudsModel *model, const Vector3
// inside the cloud // inside the cloud
segment_length += step_length; segment_length += step_length;
current_inside_length += step_length; current_inside_length += step_length;
step = direction.scale((noise_distance < render_precision) ? render_precision : noise_distance); step = direction.scale((noise_distance < 1.0) ? render_precision : (noise_distance * render_precision));
} else { } else {
// entering the cloud // entering the cloud
inside = 1; inside = 1;
segment_length = 0.0; segment_length = step_length - Maths::zeroPoint(step_length, previous_noise_distance, noise_distance);
segment_start = walker; assert(segment_length >= 0.0 && segment_length <= step_length);
segment_start = walker.add(direction.scale(-segment_length));
current_inside_length += segment_length; current_inside_length += segment_length;
step = direction.scale(render_precision); step = direction.scale(render_precision);
} }
} else { } else {
if (inside) { if (inside) {
// exiting the cloud // exiting the cloud
segment_length += step_length; double exit_length = Maths::zeroPoint(step_length, previous_noise_distance, noise_distance);
current_inside_length += step_length; assert(exit_length >= 0.0);
segment_length += exit_length;
current_inside_length += exit_length;
out_segments->start = segment_start; out_segments->start = segment_start;
out_segments->end = walker; out_segments->end = walker.add(direction.scale(exit_length - step_length));
out_segments->length = segment_length; out_segments->length = segment_length;
out_segments++; out_segments++;
if (++segment_count >= max_segments) { if (++segment_count >= max_segments) {
@ -116,19 +127,30 @@ int CloudBasicLayerRenderer::findSegments(BaseCloudsModel *model, const Vector3
step = direction.scale(render_precision); step = direction.scale(render_precision);
} else { } else {
// searching for a cloud // searching for a cloud
step = direction.scale((noise_distance > -render_precision) ? render_precision : -noise_distance); step =
direction.scale((noise_distance > -1.0) ? render_precision : (-noise_distance * render_precision));
} }
} }
render_precision *= 1.0 + 0.001 / (quality + 0.1); render_precision *= 1.0 + 0.001 / (quality + 0.1);
} while (inside || (walker.y >= ymin - 0.001 && walker.y <= ymax + 0.001 && previous_noise_distance = noise_distance;
current_total_length < max_total_length && current_inside_length < max_inside_length)); stop = not(walker.y >= ymin - 0.001 && walker.y <= ymax + 0.001 && current_total_length < max_total_length &&
current_inside_length < max_inside_length);
} while (inside or not stop);
*total_length = current_total_length; *total_length = current_total_length;
*inside_length = current_inside_length; *inside_length = current_inside_length;
return segment_count; return segment_count;
} }
static inline Vector3 _getPseudoNormal(const BaseCloudsModel *model, const Vector3 &base, const Vector3 &direction) {
double precision = 0.3;
double base_density = model->getDensity(base, precision); // TODO keep
double density = model->getDensity(base.add(direction.scale(precision * 10.0)), precision);
double diff = base_density - density;
return direction.scale(diff > 0.0 ? diff : 0.0);
}
Color CloudBasicLayerRenderer::getColor(BaseCloudsModel *model, const Vector3 &eye, const Vector3 &location) { Color CloudBasicLayerRenderer::getColor(BaseCloudsModel *model, const Vector3 &eye, const Vector3 &location) {
int i, segment_count; int i, segment_count;
double max_length, total_length, inside_length; double max_length, total_length, inside_length;
@ -151,27 +173,32 @@ Color CloudBasicLayerRenderer::getColor(BaseCloudsModel *model, const Vector3 &e
model->getAltitudeRange(&ymin, &ymax); model->getAltitudeRange(&ymin, &ymax);
double transparency_depth = (ymax - ymin); double transparency_depth = (ymax - ymin);
SurfaceMaterial material(COLOR_WHITE.scaled(5.0)); SurfaceMaterial material(COLOR_WHITE.scaled(8.0));
material.hardness = 1.0; material.ambient = 0.8;
material.reflection = 0.0; material.hardness = 0.0;
material.shininess = 0.0; material.reflection = 0.2;
material.shininess = 3.0;
material.validate(); material.validate();
segment_count = findSegments(model, start, direction, 30, transparency_depth, max_length, &inside_length, segment_count = findSegments(model, start, direction, 30, transparency_depth, max_length, &inside_length,
&total_length, segments); &total_length, segments, 0.001);
for (i = segment_count - 1; i >= 0; i--) { for (i = segment_count - 1; i >= 0; i--) {
col = parent->applyLightingToSurface(segments[i].start, VECTOR_UP, material); Vector3 normal = VECTOR_ZERO;
const Vector3 &location = segments[i].start;
normal = normal.add(_getPseudoNormal(model, location, VECTOR_UP));
normal = normal.add(_getPseudoNormal(model, location, VECTOR_DOWN));
normal = normal.add(_getPseudoNormal(model, location, VECTOR_EAST));
normal = normal.add(_getPseudoNormal(model, location, VECTOR_WEST));
normal = normal.add(_getPseudoNormal(model, location, VECTOR_NORTH));
normal = normal.add(_getPseudoNormal(model, location, VECTOR_SOUTH));
col = parent->applyLightingToSurface(location, normal.normalize(), material);
col.a = (segments[i].length >= transparency_depth) ? 1.0 : (segments[i].length / transparency_depth); col.a = (segments[i].length >= transparency_depth) ? 1.0 : (segments[i].length / transparency_depth);
result.mask(col); result.mask(col);
} }
// Opacify when hitting inside_length limit // Opacity
if (inside_length >= transparency_depth) { result.a = Maths::smoothstep(0.0, transparency_depth, inside_length);
result.a = 1.0;
} else if (inside_length >= transparency_depth * 0.8) {
result.a += (1.0 - result.a) * ((inside_length - transparency_depth * 0.8) / (transparency_depth * 0.2));
}
// Apply aerial perspective // Apply aerial perspective
if (result.a > 0.00001) { if (result.a > 0.00001) {
@ -196,14 +223,14 @@ bool CloudBasicLayerRenderer::alterLight(BaseCloudsModel *model, LightComponent
direction = light->direction.scale(-1.0); direction = light->direction.scale(-1.0);
end = location.add(direction.scale(10000.0)); end = location.add(direction.scale(10000.0));
if (not optimizeSearchLimits(model, &start, &end)) { if (not optimizeSearchLimits(model, &start, &end)) {
return false; return true;
} }
double ymin, ymax; double ymin, ymax;
model->getAltitudeRange(&ymin, &ymax); model->getAltitudeRange(&ymin, &ymax);
double light_traversal = (ymax - ymin) * 0.8 * light->color.getPower(); double light_traversal = (ymax - ymin) * 0.8 * light->color.getPower();
findSegments(model, start, direction, 30, light_traversal, end.sub(start).getNorm(), &inside_depth, &total_depth, findSegments(model, start, direction, 30, light_traversal, end.sub(start).getNorm(), &inside_depth, &total_depth,
segments); segments, 0.1);
if (light_traversal < 0.0001) { if (light_traversal < 0.0001) {
factor = 0.0; factor = 0.0;

View file

@ -27,7 +27,7 @@ class SOFTWARESHARED_EXPORT CloudBasicLayerRenderer : public BaseCloudLayerRende
private: private:
int findSegments(BaseCloudsModel *model, const Vector3 &start, const Vector3 &direction, int max_segments, int findSegments(BaseCloudsModel *model, const Vector3 &start, const Vector3 &direction, int max_segments,
double max_inside_length, double max_total_length, double *inside_length, double *total_length, double max_inside_length, double max_total_length, double *inside_length, double *total_length,
CloudSegment *out_segments); CloudSegment *out_segments, double base_detail);
}; };
} }
} }

View file

@ -85,6 +85,7 @@ void CloudsRenderer::update() {
case CloudLayerDefinition::ALTOSTRATUS: case CloudLayerDefinition::ALTOSTRATUS:
case CloudLayerDefinition::CIRROCUMULUS: case CloudLayerDefinition::CIRROCUMULUS:
case CloudLayerDefinition::CIRROSTRATUS: case CloudLayerDefinition::CIRROSTRATUS:
case CloudLayerDefinition::_COUNT:
model = new BaseCloudsModel(layer); model = new BaseCloudsModel(layer);
break; break;
} }

View file

@ -119,9 +119,14 @@ double GodRaysSampler::getCachedLight(const Vector3 &location) {
} }
// Hit cache // Hit cache
double p[8] = {getCache(ix, iy, iz), getCache(ix + 1, iy, iz), getCache(ix + 1, iy + 1, iz), double p[8] = {getCache(ix, iy, iz),
getCache(ix, iy + 1, iz), getCache(ix, iy, iz + 1), getCache(ix + 1, iy, iz + 1), getCache(ix + 1, iy, iz),
getCache(ix + 1, iy + 1, iz + 1), getCache(ix, iy + 1, iz + 1)}; 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, return Interpolation::trilinear(p, (x - sampling_step * double(ix)) / sampling_step,
(y - sampling_step * double(iy)) / sampling_step, (y - sampling_step * double(iy)) / sampling_step,
(z - sampling_step * double(iz)) / sampling_step); (z - sampling_step * double(iz)) / sampling_step);

View file

@ -7,6 +7,7 @@
#include "LightSource.h" #include "LightSource.h"
#include "Color.h" #include "Color.h"
#include "SurfaceMaterial.h" #include "SurfaceMaterial.h"
#include "Logs.h"
LightingManager::LightingManager() { LightingManager::LightingManager() {
specularity = true; specularity = true;
@ -92,21 +93,16 @@ void LightingManager::setFiltering(bool enabled) {
Color LightingManager::applyFinalComponent(const LightComponent &component, const Vector3 &eye, const Vector3 &location, Color LightingManager::applyFinalComponent(const LightComponent &component, const Vector3 &eye, const Vector3 &location,
const Vector3 &normal, const SurfaceMaterial &material) { const Vector3 &normal, const SurfaceMaterial &material) {
Color result, light_color; Color result, light_color;
double normal_norm;
Vector3 direction_inv; Vector3 direction_inv;
light_color = component.color; light_color = component.color;
direction_inv = component.direction.normalize().scale(-1.0); direction_inv = component.direction.normalize().scale(-1.0);
normal.normalize();
normal_norm = normal.getNorm();
if (normal_norm > 1.0) {
normal_norm = 1.0;
}
result = COLOR_BLACK; result = COLOR_BLACK;
/* diffused light */ /* diffused light */
double diffuse = direction_inv.dotProduct(normal.normalize()); double diffuse = direction_inv.dotProduct(normal);
double sign = (diffuse < 0.0) ? -1.0 : 1.0; double sign = (diffuse < 0.0) ? -1.0 : 1.0;
if (material.hardness <= 0.5) { if (material.hardness <= 0.5) {
double hardness = material.hardness * 2.0; double hardness = material.hardness * 2.0;
@ -115,7 +111,9 @@ Color LightingManager::applyFinalComponent(const LightComponent &component, cons
double hardness = (material.hardness - 0.5) * 2.0; double hardness = (material.hardness - 0.5) * 2.0;
diffuse = (1.0 - hardness) * diffuse + hardness * sign * sqrt(fabs(diffuse)); diffuse = (1.0 - hardness) * diffuse + hardness * sign * sqrt(fabs(diffuse));
} }
diffuse = (diffuse + (1.0 - normal_norm)) / (1.0 + (1.0 - normal_norm)); if (material.ambient > 0.0) {
diffuse = material.ambient + (1.0 - material.ambient) * diffuse;
}
if (diffuse > 0.0) { if (diffuse > 0.0) {
result.r += diffuse * material.base->r * light_color.r; result.r += diffuse * material.base->r * light_color.r;
result.g += diffuse * material.base->g * light_color.g; result.g += diffuse * material.base->g * light_color.g;
@ -123,12 +121,12 @@ Color LightingManager::applyFinalComponent(const LightComponent &component, cons
} }
/* specular reflection */ /* specular reflection */
if (material.shininess > 0.0 && material.reflection > 0.0 && component.reflection > 0.0) { if (sign > 0.0 && material.shininess > 0.0 && material.reflection > 0.0 && component.reflection > 0.0) {
Vector3 view = location.sub(eye).normalize(); Vector3 view = location.sub(eye).normalize();
Vector3 reflect = direction_inv.sub(normal.scale(2.0 * direction_inv.dotProduct(normal))); Vector3 reflect = direction_inv.sub(normal.scale(2.0 * direction_inv.dotProduct(normal)));
double specular = reflect.dotProduct(view); double specular = reflect.dotProduct(view);
if (specular > 0.0) { if (specular > 0.0) {
specular = pow(specular, material.shininess) * material.reflection * component.reflection * normal_norm; specular = pow(specular, material.shininess) * material.reflection * component.reflection;
if (specular > 0.0) { if (specular > 0.0) {
result.r += specular * light_color.r; result.r += specular * light_color.r;
result.g += specular * light_color.g; result.g += specular * light_color.g;

View file

@ -33,6 +33,7 @@ SoftwareCanvasRenderer::SoftwareCanvasRenderer(Scenery *scenery) : SoftwareRende
new VegetationRasterizer(this, progress, RASTERIZER_CLIENT_VEGETATION)); new VegetationRasterizer(this, progress, RASTERIZER_CLIENT_VEGETATION));
current_work = NULL; current_work = NULL;
setQuality(0.5);
} }
SoftwareCanvasRenderer::~SoftwareCanvasRenderer() { SoftwareCanvasRenderer::~SoftwareCanvasRenderer() {

View file

@ -29,7 +29,7 @@ double BaseCloudsModel::getProbability(const Vector3 &, double) const {
return 1.0; return 1.0;
} }
double BaseCloudsModel::getDensity(const Vector3 &) const { double BaseCloudsModel::getDensity(const Vector3 &, double) const {
return 0.0; return 0.0;
} }

View file

@ -21,7 +21,7 @@ class SOFTWARESHARED_EXPORT BaseCloudsModel {
virtual void getAltitudeRange(double *min_altitude, double *max_altitude) const; virtual void getAltitudeRange(double *min_altitude, double *max_altitude) const;
virtual void getDetailRange(double *min_step, double *max_step) const; virtual void getDetailRange(double *min_step, double *max_step) const;
virtual double getProbability(const Vector3 &location, double radius) const; virtual double getProbability(const Vector3 &location, double radius) const;
virtual double getDensity(const Vector3 &location) const; virtual double getDensity(const Vector3 &location, double precision) const;
virtual Color filterLight(const Color &light, double length, double density) const; virtual Color filterLight(const Color &light, double length, double density) const;
virtual Color applyLightExit(const Color &light, const Vector3 &light_direction, virtual Color applyLightExit(const Color &light, const Vector3 &light_direction,
const Vector3 &direction_to_eye) const; const Vector3 &direction_to_eye) const;

View file

@ -32,7 +32,7 @@ void CloudModelAltoCumulus::getAltitudeRange(double *min_altitude, double *max_a
*max_altitude = *min_altitude + 18.0 * layer->scaling; *max_altitude = *min_altitude + 18.0 * layer->scaling;
} }
double CloudModelAltoCumulus::getDensity(const Vector3 &location) const { double CloudModelAltoCumulus::getDensity(const Vector3 &location, double) const {
double val; double val;
double min_altitude, max_altitude; double min_altitude, max_altitude;
double noise_scaling = 18.0 * layer->scaling; double noise_scaling = 18.0 * layer->scaling;

View file

@ -16,7 +16,7 @@ class CloudModelAltoCumulus : public BaseCloudsModel {
virtual void update() override; virtual void update() override;
virtual void getAltitudeRange(double *min_altitude, double *max_altitude) const override; virtual void getAltitudeRange(double *min_altitude, double *max_altitude) const override;
virtual double getDensity(const Vector3 &location) const override; virtual double getDensity(const Vector3 &location, double precision) const override;
private: private:
NoiseGenerator *noise; NoiseGenerator *noise;

View file

@ -30,7 +30,7 @@ void CloudModelCirrus::getAltitudeRange(double *min_altitude, double *max_altitu
*max_altitude = *min_altitude + 20.0 * layer->scaling; *max_altitude = *min_altitude + 20.0 * layer->scaling;
} }
double CloudModelCirrus::getDensity(const Vector3 &location) const { double CloudModelCirrus::getDensity(const Vector3 &location, double) const {
double val; double val;
double min_altitude, max_altitude; double min_altitude, max_altitude;
double noise_scaling = 30.0 * layer->scaling; double noise_scaling = 30.0 * layer->scaling;

View file

@ -16,7 +16,7 @@ class CloudModelCirrus : public BaseCloudsModel {
virtual void update() override; virtual void update() override;
virtual void getAltitudeRange(double *min_altitude, double *max_altitude) const override; virtual void getAltitudeRange(double *min_altitude, double *max_altitude) const override;
virtual double getDensity(const Vector3 &location) const override; virtual double getDensity(const Vector3 &location, double precision) const override;
private: private:
NoiseGenerator *noise; NoiseGenerator *noise;

View file

@ -37,7 +37,7 @@ void CloudModelCumuloNimbus::getAltitudeRange(double *min_altitude, double *max_
*max_altitude = *min_altitude + 50.0 + 50.0 * layer->scaling; *max_altitude = *min_altitude + 50.0 + 50.0 * layer->scaling;
} }
double CloudModelCumuloNimbus::getDensity(const Vector3 &location) const { double CloudModelCumuloNimbus::getDensity(const Vector3 &location, double) const {
double val; double val;
double min_altitude, max_altitude; double min_altitude, max_altitude;
double noise_scaling = 60.0 * layer->scaling; double noise_scaling = 60.0 * layer->scaling;

View file

@ -16,7 +16,7 @@ class CloudModelCumuloNimbus : public BaseCloudsModel {
virtual void update() override; virtual void update() override;
virtual void getAltitudeRange(double *min_altitude, double *max_altitude) const override; virtual void getAltitudeRange(double *min_altitude, double *max_altitude) const override;
virtual double getDensity(const Vector3 &location) const override; virtual double getDensity(const Vector3 &location, double precision) const override;
private: private:
NoiseGenerator *noise; NoiseGenerator *noise;

View file

@ -37,7 +37,7 @@ void CloudModelStratoCumulus::getAltitudeRange(double *min_altitude, double *max
*max_altitude = *min_altitude + 11.0 * layer->scaling; *max_altitude = *min_altitude + 11.0 * layer->scaling;
} }
double CloudModelStratoCumulus::getDensity(const Vector3 &location) const { double CloudModelStratoCumulus::getDensity(const Vector3 &location, double precision) const {
double val; double val;
double min_altitude, max_altitude; double min_altitude, max_altitude;
double noise_scaling = 30.0 * layer->scaling; double noise_scaling = 30.0 * layer->scaling;
@ -55,7 +55,7 @@ double CloudModelStratoCumulus::getDensity(const Vector3 &location) const {
// layer->scaling); // layer->scaling);
double coverage = layer->coverage; double coverage = layer->coverage;
val = 0.5 * noise->get3DTotal(x, y, z); val = 0.5 * noise->get3DDetail(x, y, z, precision);
return val - 0.9 + coverage; return val - 0.9 + coverage;
} }
} }

View file

@ -16,7 +16,7 @@ class CloudModelStratoCumulus : public BaseCloudsModel {
virtual void update() override; virtual void update() override;
virtual void getAltitudeRange(double *min_altitude, double *max_altitude) const override; virtual void getAltitudeRange(double *min_altitude, double *max_altitude) const override;
virtual double getDensity(const Vector3 &location) const override; virtual double getDensity(const Vector3 &location, double precision) const override;
private: private:
NoiseGenerator *noise; NoiseGenerator *noise;

View file

@ -11,3 +11,21 @@ TEST(Maths, modInRange) {
EXPECT_DOUBLE_EQ(5.2831853071795862, Maths::modInRange(-1.0, 0, Maths::TWOPI)); EXPECT_DOUBLE_EQ(5.2831853071795862, Maths::modInRange(-1.0, 0, Maths::TWOPI));
EXPECT_DOUBLE_EQ(-1.183185307179586, Maths::modInRange(5.1, -Maths::PI, Maths::PI)); EXPECT_DOUBLE_EQ(-1.183185307179586, Maths::modInRange(5.1, -Maths::PI, Maths::PI));
} }
TEST(Maths, zeroPoint) {
EXPECT_DOUBLE_EQ(0.5, Maths::zeroPoint(1.0, 1.0, -1.0));
EXPECT_DOUBLE_EQ(4.0, Maths::zeroPoint(2.0, 1.0, 0.5));
EXPECT_DOUBLE_EQ(-1.5, Maths::zeroPoint(1.5, -1.0, -2.0));
EXPECT_DOUBLE_EQ(0.0, Maths::zeroPoint(0.0, 1.0, 1.0));
}
TEST(Maths, smoothstep) {
EXPECT_DOUBLE_EQ(0.0, Maths::smoothstep(2.0, 5.0, -1.0));
EXPECT_DOUBLE_EQ(0.0, Maths::smoothstep(2.0, 5.0, 1.0));
EXPECT_DOUBLE_EQ(0.0, Maths::smoothstep(2.0, 5.0, 2.0));
EXPECT_DOUBLE_EQ(0.07407407407407407, Maths::smoothstep(2.0, 5.0, 2.5));
EXPECT_DOUBLE_EQ(0.25925925925925924, Maths::smoothstep(2.0, 5.0, 3.0));
EXPECT_DOUBLE_EQ(0.5, Maths::smoothstep(2.0, 5.0, 3.5));
EXPECT_DOUBLE_EQ(1.0, Maths::smoothstep(2.0, 5.0, 5.0));
EXPECT_DOUBLE_EQ(1.0, Maths::smoothstep(2.0, 5.0, 10.0));
}