Improved clouds lighting
This commit is contained in:
parent
2878f1f157
commit
a54c8d5217
32 changed files with 188 additions and 134 deletions
|
@ -22,3 +22,12 @@ double Maths::smoothstep(double edge0, double edge1, double x) {
|
|||
x = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
|
||||
return x * x * (3.0 - 2.0 * x);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,13 @@ class BASICSSHARED_EXPORT Maths {
|
|||
*/
|
||||
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_2 = PI / 2.0;
|
||||
static constexpr double PI_4 = PI / 4.0;
|
||||
|
|
|
@ -27,51 +27,15 @@ typedef struct {
|
|||
double w;
|
||||
} Grad4;
|
||||
|
||||
static Grad3 _grad3[] = {{1, 1, 0},
|
||||
{-1, 1, 0},
|
||||
{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 Grad3 _grad3[] = {{1, 1, 0}, {-1, 1, 0}, {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},
|
||||
{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, -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 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}, {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[] = {
|
||||
151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142,
|
||||
|
|
|
@ -45,7 +45,8 @@ class DEFINITIONSHARED_EXPORT CloudLayerDefinition : public DefinitionNode {
|
|||
CUMULONIMBUS,
|
||||
CIRROCUMULUS,
|
||||
CIRROSTRATUS,
|
||||
CIRRUS
|
||||
CIRRUS,
|
||||
_COUNT
|
||||
} CloudsType;
|
||||
|
||||
public:
|
||||
|
|
|
@ -10,10 +10,10 @@ SurfaceMaterial::SurfaceMaterial() : SurfaceMaterial(COLOR_BLACK) {
|
|||
|
||||
SurfaceMaterial::SurfaceMaterial(const Color &color) {
|
||||
base = new Color(color);
|
||||
ambient = 0.0;
|
||||
hardness = 0.5;
|
||||
reflection = 0.0;
|
||||
shininess = 0.0;
|
||||
receive_shadows = 1.0;
|
||||
}
|
||||
|
||||
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 {
|
||||
base->save(stream);
|
||||
|
||||
stream->write(&ambient);
|
||||
stream->write(&hardness);
|
||||
stream->write(&reflection);
|
||||
stream->write(&shininess);
|
||||
|
||||
stream->write(&receive_shadows);
|
||||
}
|
||||
|
||||
void SurfaceMaterial::load(PackStream *stream) {
|
||||
base->load(stream);
|
||||
|
||||
stream->read(&ambient);
|
||||
stream->read(&hardness);
|
||||
stream->read(&reflection);
|
||||
stream->read(&shininess);
|
||||
|
||||
stream->read(&receive_shadows);
|
||||
}
|
||||
|
||||
void SurfaceMaterial::copy(SurfaceMaterial *destination) const {
|
||||
*destination->base = *base;
|
||||
destination->ambient = ambient;
|
||||
destination->hardness = hardness;
|
||||
destination->reflection = reflection;
|
||||
destination->shininess = shininess;
|
||||
destination->receive_shadows = receive_shadows;
|
||||
destination->validate();
|
||||
}
|
||||
|
||||
|
|
|
@ -25,11 +25,10 @@ class DEFINITIONSHARED_EXPORT SurfaceMaterial {
|
|||
public:
|
||||
Color *base;
|
||||
|
||||
double ambient;
|
||||
double hardness;
|
||||
double reflection;
|
||||
double shininess;
|
||||
|
||||
double receive_shadows;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,8 +127,7 @@ int main(int argc, char **argv) {
|
|||
scenery->autoPreset();
|
||||
}
|
||||
|
||||
Logs::debug("CommandLine") << "Rendered scenery :" << endl
|
||||
<< scenery->toString() << endl;
|
||||
Logs::debug("CommandLine") << "Rendered scenery :" << endl << scenery->toString() << endl;
|
||||
|
||||
TimeManager time;
|
||||
time.setWind(conf_wind_x, conf_wind_z);
|
||||
|
|
|
@ -152,6 +152,7 @@ static void testCloudQuality() {
|
|||
|
||||
SoftwareCanvasRenderer renderer(&scenery);
|
||||
renderer.setSize(600, 800);
|
||||
renderer.getGodRaysSampler()->setEnabled(false);
|
||||
SkyRasterizer rasterizer(&renderer, renderer.getProgressHelper(), 0);
|
||||
renderer.setSoloRasterizer(&rasterizer);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
|
@ -463,14 +464,11 @@ static void testCloudsLighting() {
|
|||
FakeModel(CloudLayerDefinition *layer, double scale) : BaseCloudsModel(layer), scale(scale) {
|
||||
}
|
||||
virtual void getAltitudeRange(double *min_altitude, double *max_altitude) const override {
|
||||
*min_altitude = -scale;
|
||||
*max_altitude = scale;
|
||||
*min_altitude = 10.0 - scale;
|
||||
*max_altitude = 10.0 + scale;
|
||||
}
|
||||
virtual void getDetailRange(double *min_step, double *max_step) const override {
|
||||
*min_step = *max_step = scale * 0.01;
|
||||
}
|
||||
virtual double getDensity(const Vector3 &location) const override {
|
||||
return Maths::smoothstep(0.0, 0.2, 1.0 - location.getNorm() / scale);
|
||||
virtual double getDensity(const Vector3 &location, double) const override {
|
||||
return 1.0 - location.sub(Vector3(0.0, 10.0, 0.0)).getNorm() / scale;
|
||||
}
|
||||
double scale;
|
||||
};
|
||||
|
@ -482,8 +480,8 @@ static void testCloudsLighting() {
|
|||
virtual Color processPixel(int, int, double relx, double rely) const override {
|
||||
auto cloud_renderer = renderer->getCloudsRenderer();
|
||||
auto atmo_renderer = renderer->getAtmosphereRenderer();
|
||||
return cloud_renderer->getColor(Vector3(relx * scale * 1.2, rely * scale * 1.2, scale),
|
||||
Vector3(relx * scale * 1.2, rely * scale * 1.2, -scale),
|
||||
return cloud_renderer->getColor(Vector3(relx * scale * 1.2, 10.0 + 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);
|
||||
}
|
||||
virtual int prepareRasterization() override {
|
||||
|
@ -500,15 +498,16 @@ static void testCloudsLighting() {
|
|||
|
||||
Scenery scenery;
|
||||
scenery.autoPreset(1);
|
||||
scenery.getTerrain()->propHeightNoise()->setConfig(0.0);
|
||||
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");
|
||||
scenery.getClouds()->clear();
|
||||
scenery.getClouds()->addLayer(layer);
|
||||
|
||||
SoftwareCanvasRenderer renderer(&scenery);
|
||||
FakeRasterizer rasterizer(&renderer, 10.0);
|
||||
FakeRasterizer rasterizer(&renderer, 5.0);
|
||||
renderer.setSize(800, 800);
|
||||
renderer.setSoloRasterizer(&rasterizer);
|
||||
renderer.getGodRaysSampler()->setEnabled(false);
|
||||
|
@ -522,6 +521,36 @@ static void testCloudsLighting() {
|
|||
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() {
|
||||
testNoise();
|
||||
testTextures();
|
||||
|
|
|
@ -141,8 +141,7 @@ void MainModelerWindow::keyReleaseEvent(QKeyEvent *event) {
|
|||
} else if (event->key() == Qt::Key_F6) {
|
||||
render_process->showPreviousRender();
|
||||
} else if (event->key() == Qt::Key_F12) {
|
||||
Logs::warning("UI") << "Current scenery dump:" << endl
|
||||
<< scenery->toString() << endl;
|
||||
Logs::warning("UI") << "Current scenery dump:" << endl << scenery->toString() << endl;
|
||||
} else if (event->key() == Qt::Key_N) {
|
||||
if (event->modifiers() & Qt::ControlModifier) {
|
||||
newFile();
|
||||
|
|
|
@ -19,7 +19,7 @@ class OpenGLView : public QQuickItem {
|
|||
void handleResize();
|
||||
void handleSceneGraphReady();
|
||||
|
||||
signals:
|
||||
signals:
|
||||
void stopped();
|
||||
|
||||
protected:
|
||||
|
|
|
@ -8,7 +8,7 @@ class PropertyBind : public QObject {
|
|||
public:
|
||||
explicit PropertyBind(QObject *parent = 0);
|
||||
|
||||
signals:
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
};
|
||||
|
|
|
@ -128,11 +128,11 @@ void RenderProcess::startQuickRender() {
|
|||
}
|
||||
|
||||
void RenderProcess::startMediumRender() {
|
||||
startRender(window->getScenery(), RenderConfig(800, 450, 1, 5));
|
||||
startRender(window->getScenery(), RenderConfig(800, 450, 1, 7));
|
||||
}
|
||||
|
||||
void RenderProcess::startFinalRender() {
|
||||
startRender(window->getScenery(), RenderConfig(1920, 1080, 4, 8));
|
||||
startRender(window->getScenery(), RenderConfig(1920, 1080, 4, 10));
|
||||
}
|
||||
|
||||
void RenderProcess::showPreviousRender() {
|
||||
|
|
|
@ -253,7 +253,7 @@ void OpenGLRenderer::cameraChangeEvent(CameraDefinition *camera) {
|
|||
projection.perspective(perspective.yfov * 180.0 / Maths::PI, perspective.xratio, perspective.znear,
|
||||
perspective.zfar);
|
||||
|
||||
*view_matrix = projection *transform;
|
||||
*view_matrix = projection * transform;
|
||||
|
||||
// Set in shaders
|
||||
shared_state->set("cameraLocation", location);
|
||||
|
|
|
@ -266,8 +266,8 @@ static Color _transmittance3(double r, double mu, double d) {
|
|||
}
|
||||
|
||||
static void _getIrradianceRMuS(double x, double y, double *r, double *muS) {
|
||||
*r = Rg + y *(Rt - Rg);
|
||||
*muS = -0.2 + x *(1.0 + 0.2);
|
||||
*r = Rg + y * (Rt - Rg);
|
||||
*muS = -0.2 + x * (1.0 + 0.2);
|
||||
}
|
||||
|
||||
/* nearest intersection of ray r,mu with ground or top atmosphere boundary
|
||||
|
|
|
@ -117,8 +117,7 @@ AtmosphereResult SoftwareBrunetonAtmosphereRenderer::applyAerialPerspective(cons
|
|||
case AtmosphereDefinition::ATMOSPHERE_MODEL_BRUNETON:
|
||||
result = model->applyAerialPerspective(location, base);
|
||||
break;
|
||||
default:
|
||||
;
|
||||
default:;
|
||||
}
|
||||
|
||||
// Apply god rays ponderation
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "clouds/BaseCloudsModel.h"
|
||||
#include "SurfaceMaterial.h"
|
||||
#include "Logs.h"
|
||||
#include "Maths.h"
|
||||
#include "FloatNode.h"
|
||||
|
||||
struct CloudSegment {
|
||||
|
@ -24,10 +25,6 @@ struct CloudSegment {
|
|||
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).
|
||||
*
|
||||
|
@ -47,13 +44,14 @@ static inline double _getDistanceToBorder(BaseCloudsModel *model, const Vector3
|
|||
*/
|
||||
int CloudBasicLayerRenderer::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, CloudSegment *out_segments) {
|
||||
double *inside_length, double *total_length, CloudSegment *out_segments,
|
||||
double base_detail) {
|
||||
double ymin, ymax;
|
||||
int inside, segment_count;
|
||||
double current_total_length, current_inside_length;
|
||||
double step_length, segment_length;
|
||||
double min_step, max_step;
|
||||
double noise_distance;
|
||||
double noise_distance, previous_noise_distance;
|
||||
Vector3 walker, step, segment_start, offset;
|
||||
double render_precision;
|
||||
|
||||
|
@ -68,20 +66,30 @@ int CloudBasicLayerRenderer::findSegments(BaseCloudsModel *model, const Vector3
|
|||
double distance = parent->getCameraLocation().sub(start).getNorm();
|
||||
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;
|
||||
current_total_length = 0.0;
|
||||
current_inside_length = 0.0;
|
||||
segment_length = 0.0;
|
||||
walker = start;
|
||||
offset = Vector3(model->getLayer()->propXOffset()->getValue(), 0.0, model->getLayer()->propZOffset()->getValue());
|
||||
noise_distance = _getDistanceToBorder(model, start.add(offset)) * render_precision;
|
||||
inside = 0;
|
||||
noise_distance = model->getDensity(start.add(offset), base_detail);
|
||||
previous_noise_distance = noise_distance;
|
||||
inside = noise_distance > 0.0;
|
||||
if (inside) {
|
||||
segment_start = start;
|
||||
}
|
||||
step = direction.scale(render_precision);
|
||||
bool stop = false;
|
||||
|
||||
do {
|
||||
walker = walker.add(step);
|
||||
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;
|
||||
|
||||
if (noise_distance > 0.0) {
|
||||
|
@ -89,23 +97,26 @@ int CloudBasicLayerRenderer::findSegments(BaseCloudsModel *model, const Vector3
|
|||
// inside the cloud
|
||||
segment_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 {
|
||||
// entering the cloud
|
||||
inside = 1;
|
||||
segment_length = 0.0;
|
||||
segment_start = walker;
|
||||
segment_length = step_length - Maths::zeroPoint(step_length, previous_noise_distance, noise_distance);
|
||||
assert(segment_length >= 0.0 && segment_length <= step_length);
|
||||
segment_start = walker.add(direction.scale(-segment_length));
|
||||
current_inside_length += segment_length;
|
||||
step = direction.scale(render_precision);
|
||||
}
|
||||
} else {
|
||||
if (inside) {
|
||||
// exiting the cloud
|
||||
segment_length += step_length;
|
||||
current_inside_length += step_length;
|
||||
double exit_length = Maths::zeroPoint(step_length, previous_noise_distance, noise_distance);
|
||||
assert(exit_length >= 0.0);
|
||||
segment_length += exit_length;
|
||||
current_inside_length += exit_length;
|
||||
|
||||
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++;
|
||||
if (++segment_count >= max_segments) {
|
||||
|
@ -116,19 +127,30 @@ int CloudBasicLayerRenderer::findSegments(BaseCloudsModel *model, const Vector3
|
|||
step = direction.scale(render_precision);
|
||||
} else {
|
||||
// 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);
|
||||
} while (inside || (walker.y >= ymin - 0.001 && walker.y <= ymax + 0.001 &&
|
||||
current_total_length < max_total_length && current_inside_length < max_inside_length));
|
||||
previous_noise_distance = noise_distance;
|
||||
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;
|
||||
*inside_length = current_inside_length;
|
||||
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) {
|
||||
int i, segment_count;
|
||||
double max_length, total_length, inside_length;
|
||||
|
@ -151,27 +173,32 @@ Color CloudBasicLayerRenderer::getColor(BaseCloudsModel *model, const Vector3 &e
|
|||
model->getAltitudeRange(&ymin, &ymax);
|
||||
double transparency_depth = (ymax - ymin);
|
||||
|
||||
SurfaceMaterial material(COLOR_WHITE.scaled(5.0));
|
||||
material.hardness = 1.0;
|
||||
material.reflection = 0.0;
|
||||
material.shininess = 0.0;
|
||||
SurfaceMaterial material(COLOR_WHITE.scaled(8.0));
|
||||
material.ambient = 0.8;
|
||||
material.hardness = 0.0;
|
||||
material.reflection = 0.2;
|
||||
material.shininess = 3.0;
|
||||
material.validate();
|
||||
|
||||
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--) {
|
||||
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);
|
||||
result.mask(col);
|
||||
}
|
||||
|
||||
// Opacify when hitting inside_length limit
|
||||
if (inside_length >= transparency_depth) {
|
||||
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));
|
||||
}
|
||||
// Opacity
|
||||
result.a = Maths::smoothstep(0.0, transparency_depth, inside_length);
|
||||
|
||||
// Apply aerial perspective
|
||||
if (result.a > 0.00001) {
|
||||
|
@ -196,14 +223,14 @@ bool CloudBasicLayerRenderer::alterLight(BaseCloudsModel *model, LightComponent
|
|||
direction = light->direction.scale(-1.0);
|
||||
end = location.add(direction.scale(10000.0));
|
||||
if (not optimizeSearchLimits(model, &start, &end)) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
double ymin, ymax;
|
||||
model->getAltitudeRange(&ymin, &ymax);
|
||||
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,
|
||||
segments);
|
||||
segments, 0.1);
|
||||
|
||||
if (light_traversal < 0.0001) {
|
||||
factor = 0.0;
|
||||
|
|
|
@ -27,7 +27,7 @@ class SOFTWARESHARED_EXPORT CloudBasicLayerRenderer : public BaseCloudLayerRende
|
|||
private:
|
||||
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,
|
||||
CloudSegment *out_segments);
|
||||
CloudSegment *out_segments, double base_detail);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,6 +85,7 @@ void CloudsRenderer::update() {
|
|||
case CloudLayerDefinition::ALTOSTRATUS:
|
||||
case CloudLayerDefinition::CIRROCUMULUS:
|
||||
case CloudLayerDefinition::CIRROSTRATUS:
|
||||
case CloudLayerDefinition::_COUNT:
|
||||
model = new BaseCloudsModel(layer);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -119,9 +119,14 @@ double GodRaysSampler::getCachedLight(const Vector3 &location) {
|
|||
}
|
||||
|
||||
// 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)};
|
||||
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);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "LightSource.h"
|
||||
#include "Color.h"
|
||||
#include "SurfaceMaterial.h"
|
||||
#include "Logs.h"
|
||||
|
||||
LightingManager::LightingManager() {
|
||||
specularity = true;
|
||||
|
@ -92,21 +93,16 @@ void LightingManager::setFiltering(bool enabled) {
|
|||
Color LightingManager::applyFinalComponent(const LightComponent &component, const Vector3 &eye, const Vector3 &location,
|
||||
const Vector3 &normal, const SurfaceMaterial &material) {
|
||||
Color result, light_color;
|
||||
double normal_norm;
|
||||
Vector3 direction_inv;
|
||||
|
||||
light_color = component.color;
|
||||
direction_inv = component.direction.normalize().scale(-1.0);
|
||||
|
||||
normal_norm = normal.getNorm();
|
||||
if (normal_norm > 1.0) {
|
||||
normal_norm = 1.0;
|
||||
}
|
||||
normal.normalize();
|
||||
|
||||
result = COLOR_BLACK;
|
||||
|
||||
/* diffused light */
|
||||
double diffuse = direction_inv.dotProduct(normal.normalize());
|
||||
double diffuse = direction_inv.dotProduct(normal);
|
||||
double sign = (diffuse < 0.0) ? -1.0 : 1.0;
|
||||
if (material.hardness <= 0.5) {
|
||||
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;
|
||||
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) {
|
||||
result.r += diffuse * material.base->r * light_color.r;
|
||||
result.g += diffuse * material.base->g * light_color.g;
|
||||
|
@ -123,12 +121,12 @@ Color LightingManager::applyFinalComponent(const LightComponent &component, cons
|
|||
}
|
||||
|
||||
/* 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 reflect = direction_inv.sub(normal.scale(2.0 * direction_inv.dotProduct(normal)));
|
||||
double specular = reflect.dotProduct(view);
|
||||
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) {
|
||||
result.r += specular * light_color.r;
|
||||
result.g += specular * light_color.g;
|
||||
|
|
|
@ -33,6 +33,7 @@ SoftwareCanvasRenderer::SoftwareCanvasRenderer(Scenery *scenery) : SoftwareRende
|
|||
new VegetationRasterizer(this, progress, RASTERIZER_CLIENT_VEGETATION));
|
||||
|
||||
current_work = NULL;
|
||||
setQuality(0.5);
|
||||
}
|
||||
|
||||
SoftwareCanvasRenderer::~SoftwareCanvasRenderer() {
|
||||
|
|
|
@ -29,7 +29,7 @@ double BaseCloudsModel::getProbability(const Vector3 &, double) const {
|
|||
return 1.0;
|
||||
}
|
||||
|
||||
double BaseCloudsModel::getDensity(const Vector3 &) const {
|
||||
double BaseCloudsModel::getDensity(const Vector3 &, double) const {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ class SOFTWARESHARED_EXPORT BaseCloudsModel {
|
|||
virtual void getAltitudeRange(double *min_altitude, double *max_altitude) const;
|
||||
virtual void getDetailRange(double *min_step, double *max_step) 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 applyLightExit(const Color &light, const Vector3 &light_direction,
|
||||
const Vector3 &direction_to_eye) const;
|
||||
|
|
|
@ -32,7 +32,7 @@ void CloudModelAltoCumulus::getAltitudeRange(double *min_altitude, double *max_a
|
|||
*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 min_altitude, max_altitude;
|
||||
double noise_scaling = 18.0 * layer->scaling;
|
||||
|
|
|
@ -16,7 +16,7 @@ class CloudModelAltoCumulus : public BaseCloudsModel {
|
|||
virtual void update() 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:
|
||||
NoiseGenerator *noise;
|
||||
|
|
|
@ -30,7 +30,7 @@ void CloudModelCirrus::getAltitudeRange(double *min_altitude, double *max_altitu
|
|||
*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 min_altitude, max_altitude;
|
||||
double noise_scaling = 30.0 * layer->scaling;
|
||||
|
|
|
@ -16,7 +16,7 @@ class CloudModelCirrus : public BaseCloudsModel {
|
|||
virtual void update() 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:
|
||||
NoiseGenerator *noise;
|
||||
|
|
|
@ -37,7 +37,7 @@ void CloudModelCumuloNimbus::getAltitudeRange(double *min_altitude, double *max_
|
|||
*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 min_altitude, max_altitude;
|
||||
double noise_scaling = 60.0 * layer->scaling;
|
||||
|
|
|
@ -16,7 +16,7 @@ class CloudModelCumuloNimbus : public BaseCloudsModel {
|
|||
virtual void update() 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:
|
||||
NoiseGenerator *noise;
|
||||
|
|
|
@ -37,7 +37,7 @@ void CloudModelStratoCumulus::getAltitudeRange(double *min_altitude, double *max
|
|||
*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 min_altitude, max_altitude;
|
||||
double noise_scaling = 30.0 * layer->scaling;
|
||||
|
@ -55,7 +55,7 @@ double CloudModelStratoCumulus::getDensity(const Vector3 &location) const {
|
|||
// layer->scaling);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ class CloudModelStratoCumulus : public BaseCloudsModel {
|
|||
virtual void update() 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:
|
||||
NoiseGenerator *noise;
|
||||
|
|
|
@ -11,3 +11,21 @@ TEST(Maths, modInRange) {
|
|||
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));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue