clouds: Added edge density
This commit is contained in:
parent
359c6b5902
commit
330ac54ac9
8 changed files with 147 additions and 6 deletions
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue