clouds: Added edge density

This commit is contained in:
Michaël Lemaire 2013-08-13 17:11:39 +02:00
parent 359c6b5902
commit 330ac54ac9
8 changed files with 147 additions and 6 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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