From 330ac54ac96b97b90268f9a1de49961e61eaa559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Tue, 13 Aug 2013 17:11:39 +0200 Subject: [PATCH] clouds: Added edge density --- src/rendering/clouds/clo_definition.c | 5 ++- src/rendering/clouds/clo_density.c | 37 ++++++++++++++++++ src/rendering/clouds/clo_density.h | 9 ++++- src/rendering/clouds/clo_rendering.c | 15 ++++++- src/rendering/clouds/clo_walking.c | 18 ++++++++- src/rendering/clouds/clo_walking.h | 11 ++++++ src/rendering/clouds/public.h | 2 + src/testing/test_clouds.c | 56 ++++++++++++++++++++++++++- 8 files changed, 147 insertions(+), 6 deletions(-) diff --git a/src/rendering/clouds/clo_definition.c b/src/rendering/clouds/clo_definition.c index 845c3d9..a1c6767 100644 --- a/src/rendering/clouds/clo_definition.c +++ b/src/rendering/clouds/clo_definition.c @@ -87,6 +87,8 @@ void cloudsLayerValidateDefinition(CloudsLayerDefinition* definition) curveQuickAddPoint(definition->_coverage_by_altitude, 1.0, 0.0); noiseAddLevelsSimple(definition->_shape_noise, 7, 1.0, 0.0, 1.0, 0.5); noiseSetFunctionParams(definition->_shape_noise, NOISE_FUNCTION_SIMPLEX, 0.4, 0.0); + noiseAddLevelsSimple(definition->_edge_noise, 2, 1.0, -0.5, 0.5, 0.5); + noiseSetFunctionParams(definition->_edge_noise, NOISE_FUNCTION_SIMPLEX, 0.8, 0.0); break; case CLOUDS_TYPE_STRATOCUMULUS: curveQuickAddPoint(definition->_coverage_by_altitude, 0.0, 0.0); @@ -95,7 +97,7 @@ void cloudsLayerValidateDefinition(CloudsLayerDefinition* definition) curveQuickAddPoint(definition->_coverage_by_altitude, 1.0, 0.0); noiseAddLevelsSimple(definition->_shape_noise, 2, 1.0, 0.0, 1.0, 0.5); noiseSetFunctionParams(definition->_shape_noise, NOISE_FUNCTION_SIMPLEX, 0.3, 0.0); - noiseAddLevelsSimple(definition->_edge_noise, 8, 1.0, -0.5, 0.5, 0.5); + noiseAddLevelsSimple(definition->_edge_noise, 6, 1.0, -0.5, 0.5, 0.5); noiseSetFunctionParams(definition->_edge_noise, NOISE_FUNCTION_SIMPLEX, 0.5, 0.0); break; case CLOUDS_TYPE_STRATUS: @@ -114,6 +116,7 @@ void cloudsLayerValidateDefinition(CloudsLayerDefinition* definition) noiseNormalizeAmplitude(definition->_coverage_noise, -1.0, 3.0, 0); noiseNormalizeAmplitude(definition->_shape_noise, -0.5, 0.5, 0); + noiseNormalizeAmplitude(definition->_edge_noise, -0.5, 0.5, 0); } CloudsLayerDefinition* cloudsLayerCreateDefinition() diff --git a/src/rendering/clouds/clo_density.c b/src/rendering/clouds/clo_density.c index f967ae0..fbd767c 100644 --- a/src/rendering/clouds/clo_density.c +++ b/src/rendering/clouds/clo_density.c @@ -37,6 +37,24 @@ double cloudsGetLayerDensity(CloudsLayerDefinition* layer, Vector3 location, dou } } +double cloudsGetEdgeDensity(CloudsLayerDefinition* layer, Vector3 location, double layer_density) +{ + if (layer_density == 0.0) + { + return 0.0; + } + else if (layer_density == 1.0) + { + return 1.0; + } + else + { + double density = noiseGet3DTotal(layer->_edge_noise, location.x / layer->edge_scaling, location.y / layer->edge_scaling, location.z / layer->edge_scaling); + density -= (0.5 - layer_density); + return (density <= 0.0) ? 0.0 : density; + } +} + static double _fakeGetDensity(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 location) { UNUSED(layer); @@ -46,9 +64,20 @@ static double _fakeGetDensity(Renderer* renderer, CloudsLayerDefinition* layer, return 0.0; } +static double _fakeGetEdgeDensity(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 location, double layer_density) +{ + UNUSED(layer); + UNUSED(renderer); + UNUSED(location); + UNUSED(layer_density); + + return 0.0; +} + void cloudsBindFakeDensityToRenderer(CloudsRenderer* renderer) { renderer->getLayerDensity = _fakeGetDensity; + renderer->getEdgeDensity = _fakeGetEdgeDensity; } static double _realGetDensity(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 location) @@ -60,7 +89,15 @@ static double _realGetDensity(Renderer* renderer, CloudsLayerDefinition* layer, return cloudsGetLayerDensity(layer, location, coverage); } +static double _realGetEdgeDensity(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 location, double layer_density) +{ + UNUSED(renderer); + + return cloudsGetEdgeDensity(layer, location, layer_density); +} + void cloudsBindRealDensityToRenderer(CloudsRenderer* renderer) { renderer->getLayerDensity = _realGetDensity; + renderer->getEdgeDensity = _realGetEdgeDensity; } diff --git a/src/rendering/clouds/clo_density.h b/src/rendering/clouds/clo_density.h index afdf125..9117881 100644 --- a/src/rendering/clouds/clo_density.h +++ b/src/rendering/clouds/clo_density.h @@ -13,7 +13,7 @@ extern "C" #endif /** - * Get the local coverage of a cloud layer [0.0;1.0] + * Get the coverage of a cloud layer [0.0;1.0] * * 0.0 means no cloud is present. * 1.0 means full layer. @@ -21,13 +21,18 @@ extern "C" double cloudsGetLayerCoverage(CloudsLayerDefinition* layer, Vector3 location); /** - * Get the local density of a cloud layer at a given point [0.0;1.0]. + * Get the global density of a cloud layer at a given point [0.0;1.0]. * * 0.0 means no cloud is present. * 1.0 means full density (deep inside cloud). */ double cloudsGetLayerDensity(CloudsLayerDefinition* layer, Vector3 location, double coverage); +/** + * Get the local density of a cloud layer at a given point inside an edge [0.0;1.0]. + */ +double cloudsGetEdgeDensity(CloudsLayerDefinition* layer, Vector3 location, double layer_density); + /* * Bind fake density functions to a renderer. */ diff --git a/src/rendering/clouds/clo_rendering.c b/src/rendering/clouds/clo_rendering.c index 27c0274..494c0ba 100644 --- a/src/rendering/clouds/clo_rendering.c +++ b/src/rendering/clouds/clo_rendering.c @@ -125,7 +125,20 @@ static void _walkerMaterialCallback(CloudsWalker* walker) assert(data != NULL); - double density_integral = segment->length * (segment->start.global_density + segment->end.global_density) / 2.0; + double density_integral; + if (segment->subdivided) + { + density_integral = segment->length * (segment->start.local_density + segment->end.local_density) / 2.0; + } + else + { + if ((segment->start.global_density > 0.0 || segment->end.global_density > 0.0) && (segment->start.global_density < 1.0 || segment->end.global_density < 1.0)) + { + cloudsWalkerOrderSubdivide(walker, renderer->render_quality + 3); + return; + } + density_integral = segment->length * (segment->start.global_density + segment->end.global_density) / 2.0; + } data->out_scattering += 0.5 * density_integral; diff --git a/src/rendering/clouds/clo_walking.c b/src/rendering/clouds/clo_walking.c index c2ba795..7941029 100644 --- a/src/rendering/clouds/clo_walking.c +++ b/src/rendering/clouds/clo_walking.c @@ -36,6 +36,7 @@ struct CloudsWalker double max_length; double step_size; int skip_void; + int local_density; int started; CloudWalkerStepInfo last_segment; @@ -113,6 +114,7 @@ CloudsWalker* cloudsCreateWalker(Renderer* renderer, CloudsLayerDefinition* laye result->cursor = 0.0; result->step_size = 1.0; result->skip_void = 0; + result->local_density = -1; result->started = 0; result->subdivision_count = 0; @@ -138,10 +140,15 @@ void cloudsWalkerSetStepSize(CloudsWalker* walker, double step) else { /* TODO Automatic settings (using rendering quality and cloud feature size) */ - walker->step_size = 1.0; + walker->step_size = 5.0 / (double)walker->last_segment.renderer->render_quality; } } +void cloudsWalkerToggleLocalDensity(CloudsWalker* walker, int enabled) +{ + walker->local_density = enabled; +} + void cloudsWalkerSetVoidSkipping(CloudsWalker* walker, int enabled) { walker->skip_void = enabled; @@ -155,6 +162,15 @@ static void _getPoint(CloudsWalker* walker, double cursor, CloudWalkerPoint* out Renderer* renderer = walker->last_segment.renderer; CloudsLayerDefinition* layer = walker->last_segment.layer; out_point->global_density = renderer->clouds->getLayerDensity(renderer, layer, out_point->location); + + if (walker->local_density > 0 || (walker->local_density < 0 && walker->subdivision_count > 0)) + { + out_point->local_density = renderer->clouds->getEdgeDensity(renderer, layer, out_point->location, out_point->global_density); + } + else + { + out_point->local_density = 0.0; + } } static void _refineSegment(CloudsWalker* walker, double start_cursor, double start_density, double end_cursor, double end_density, double precision, CloudWalkerPoint* result) diff --git a/src/rendering/clouds/clo_walking.h b/src/rendering/clouds/clo_walking.h index a3c56e7..e8c89fb 100644 --- a/src/rendering/clouds/clo_walking.h +++ b/src/rendering/clouds/clo_walking.h @@ -18,6 +18,7 @@ typedef struct double distance_from_start; Vector3 location; double global_density; + double local_density; } CloudWalkerPoint; /** @@ -86,6 +87,16 @@ void cloudsWalkerSetStepSize(CloudsWalker* walker, double step); */ void cloudsWalkerSetVoidSkipping(CloudsWalker* walker, int enabled); +/** + * Toggle the local density computing. + * + * When this option is set, the CloudWalkerStepInfo will contain information about local density. + * The automatic setting will set to 1 on subdivided steps, and 0 elsewhere. + * @param walker The walker to configure + * @param enabled 1 to enable local density, 0 to disable it, -1 for automatic setting. + */ +void cloudsWalkerToggleLocalDensity(CloudsWalker* walker, int enabled); + /** * Perform a single step. * diff --git a/src/rendering/clouds/public.h b/src/rendering/clouds/public.h index 283ac27..82ebee2 100644 --- a/src/rendering/clouds/public.h +++ b/src/rendering/clouds/public.h @@ -61,6 +61,7 @@ typedef struct typedef Color (*FuncCloudsGetColor)(Renderer* renderer, Color base, Vector3 start, Vector3 end); typedef double (*FuncCloudsGetLayerDensity)(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 location); +typedef double (*FuncCloudsGetEdgeDensity)(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 location, double layer_density); typedef struct { @@ -69,6 +70,7 @@ typedef struct FuncCloudsGetColor getColor; FuncLightingAlterLight alterLight; FuncCloudsGetLayerDensity getLayerDensity; + FuncCloudsGetEdgeDensity getEdgeDensity; } CloudsRenderer; diff --git a/src/testing/test_clouds.c b/src/testing/test_clouds.c index 5e276a2..d76fe93 100644 --- a/src/testing/test_clouds.c +++ b/src/testing/test_clouds.c @@ -200,6 +200,15 @@ static double _getLayerDensitySinX(Renderer* renderer, CloudsLayerDefinition* la return (density > 0.0) ? density : 0.0; } +static double _getEdgeDensitySquared(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 location, double edge_density) +{ + UNUSED(renderer); + UNUSED(layer); + UNUSED(location); + return edge_density * edge_density; +} + + START_TEST(test_clouds_walking) { /* Init */ @@ -414,7 +423,52 @@ START_TEST(test_clouds_walking) } END_TEST +START_TEST(test_clouds_walking_local) +{ + /* Init */ + CloudsLayerDefinition* layer; + layer = cloudsGetLayerType().callback_create(); + layer->lower_altitude = -1.0; + layer->thickness = 2.0; + cloudsGetLayerType().callback_validate(layer); + + Renderer* renderer; + renderer = rendererCreate(); + + renderer->render_quality = 8; + renderer->clouds->getLayerDensity = _getLayerDensitySinX; + renderer->clouds->getEdgeDensity = _getEdgeDensitySquared; + + CloudsWalker* walker = cloudsCreateWalker(renderer, layer, v3(0.0, 0.0, 0.0), v3(1.0, 0.0, 0.0)); + CloudWalkerStepInfo* segment; + int result; + + /* Test that local density is off by default */ + cloudsWalkerSetStepSize(walker, 0.3); + result = cloudsWalkerPerformStep(walker); + segment = cloudsWalkerGetLastSegment(walker); + ck_assert_int_eq(result, 1); + ck_assert_double_eq(segment->length, 0.3); + ck_assert_double_eq(segment->start.global_density, 0.0); + ck_assert_double_eq(segment->start.local_density, 0.0); + ck_assert_double_eq(segment->end.global_density, 0.951056516295); + ck_assert_double_eq(segment->end.local_density, 0.0); + + /* Test it's automatically enabled on subdivision */ + cloudsWalkerOrderSubdivide(walker, 2); + result = cloudsWalkerPerformStep(walker); + segment = cloudsWalkerGetLastSegment(walker); + ck_assert_int_eq(result, 1); + ck_assert_double_eq(segment->length, 0.15); + ck_assert_double_eq(segment->start.global_density, 0.0); + ck_assert_double_eq(segment->start.local_density, 0.0); + ck_assert_double_eq(segment->end.global_density, 0.809016994375); + ck_assert_double_eq(segment->end.local_density, 0.654508497187); +} +END_TEST + TEST_CASE(clouds, test_clouds_density, + test_clouds_walking, test_clouds_walking_boundaries, - test_clouds_walking) + test_clouds_walking_local)