diff --git a/src/rendering/clouds/clo_definition.c b/src/rendering/clouds/clo_definition.c index a1c6767..7ebcc37 100644 --- a/src/rendering/clouds/clo_definition.c +++ b/src/rendering/clouds/clo_definition.c @@ -87,7 +87,7 @@ 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); + noiseAddLevelsSimple(definition->_edge_noise, 4, 1.0, -0.5, 0.5, 0.5); noiseSetFunctionParams(definition->_edge_noise, NOISE_FUNCTION_SIMPLEX, 0.8, 0.0); break; case CLOUDS_TYPE_STRATOCUMULUS: @@ -95,7 +95,7 @@ void cloudsLayerValidateDefinition(CloudsLayerDefinition* definition) curveQuickAddPoint(definition->_coverage_by_altitude, 0.2, 1.0); curveQuickAddPoint(definition->_coverage_by_altitude, 0.5, 1.0); curveQuickAddPoint(definition->_coverage_by_altitude, 1.0, 0.0); - noiseAddLevelsSimple(definition->_shape_noise, 2, 1.0, 0.0, 1.0, 0.5); + noiseAddLevelsSimple(definition->_shape_noise, 4, 1.0, 0.0, 1.0, 0.5); noiseSetFunctionParams(definition->_shape_noise, NOISE_FUNCTION_SIMPLEX, 0.3, 0.0); noiseAddLevelsSimple(definition->_edge_noise, 6, 1.0, -0.5, 0.5, 0.5); noiseSetFunctionParams(definition->_edge_noise, NOISE_FUNCTION_SIMPLEX, 0.5, 0.0); diff --git a/src/rendering/clouds/clo_density.c b/src/rendering/clouds/clo_density.c index fbd767c..2daec4f 100644 --- a/src/rendering/clouds/clo_density.c +++ b/src/rendering/clouds/clo_density.c @@ -4,28 +4,39 @@ double cloudsGetLayerCoverage(CloudsLayerDefinition* layer, Vector3 location) { - if (layer->base_coverage == 0.0) + if (layer->base_coverage <= 0.0) { return 0.0; } else { - double coverage = noiseGet2DTotal(layer->_coverage_noise, location.x / layer->shape_scaling, location.z / layer->shape_scaling); + double coverage = 0.5 + noiseGet2DTotal(layer->_coverage_noise, location.x / layer->shape_scaling, location.z / layer->shape_scaling); coverage -= (1.0 - layer->base_coverage); coverage *= curveGetValue(layer->_coverage_by_altitude, (location.y - layer->lower_altitude) / layer->thickness); - return (coverage <= 0.0) ? 0.0 : coverage; + if (coverage < 0.0) + { + return 0.0; + } + else if (coverage >= 1.0) + { + return 1.0; + } + else + { + return coverage; + } } } double cloudsGetLayerDensity(CloudsLayerDefinition* layer, Vector3 location, double coverage) { - if (coverage == 0.0) + if (coverage <= 0.0) { return 0.0; } - else if (coverage == 1.0) + else if (coverage >= 1.0) { return 1.0; } @@ -39,11 +50,11 @@ double cloudsGetLayerDensity(CloudsLayerDefinition* layer, Vector3 location, dou double cloudsGetEdgeDensity(CloudsLayerDefinition* layer, Vector3 location, double layer_density) { - if (layer_density == 0.0) + if (layer_density <= 0.0) { return 0.0; } - else if (layer_density == 1.0) + else if (layer_density >= 1.0) { return 1.0; } diff --git a/src/rendering/clouds/clo_rendering.c b/src/rendering/clouds/clo_rendering.c index 494c0ba..ffbb885 100644 --- a/src/rendering/clouds/clo_rendering.c +++ b/src/rendering/clouds/clo_rendering.c @@ -36,11 +36,30 @@ typedef struct static void _walkerFilterCallback(CloudsWalker* walker) { CloudWalkerStepInfo* segment = cloudsWalkerGetLastSegment(walker); + Renderer* renderer = segment->renderer; AccumulatedLightData* data = (AccumulatedLightData*)segment->data; 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->refined && segment->start.global_density == 0.0 && segment->end.global_density > 0.0) + { + cloudsWalkerOrderRefine(walker, 1.0 / (double)renderer->render_quality); + return; + } + 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 + 1); + return; + } + density_integral = segment->length * (segment->start.global_density + segment->end.global_density) / 2.0; + } data->out_scattering += 0.3 * density_integral; @@ -78,12 +97,13 @@ static int _alterLight(Renderer* renderer, LightDefinition* light, Vector3 locat walker = cloudsCreateWalker(renderer, layer, ostart, oend); cloudsWalkerSetStepSize(walker, -1.0); + cloudsWalkerSetVoidSkipping(walker, 1); cloudsStartWalking(walker, _walkerFilterCallback, &data); cloudsDeleteWalker(walker); } } - double max_power = colorGetPower(&light->color) - data.out_scattering; + double max_power = data.light_power - data.out_scattering; if (max_power < 0.0) { light->color = COLOR_BLACK; @@ -100,6 +120,8 @@ typedef struct { double out_scattering; /* Amount of light scattered away by heavy particles */ Color in_scattering; /* Amount of light redirected toward the viewer */ + int entry_found; /* 1 if a cloud entry has been found */ + Vector3 entry; /* Point of entry in the clouds */ } AccumulatedMaterialData; static inline void _applyOutScattering(Color* col, double out_scattering) @@ -132,7 +154,12 @@ static void _walkerMaterialCallback(CloudsWalker* walker) } 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)) + if (!segment->refined && segment->start.global_density == 0.0 && segment->end.global_density > 0.0) + { + cloudsWalkerOrderRefine(walker, 1.0 / (double)(renderer->render_quality * renderer->render_quality)); + return; + } + 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; @@ -140,17 +167,26 @@ static void _walkerMaterialCallback(CloudsWalker* walker) density_integral = segment->length * (segment->start.global_density + segment->end.global_density) / 2.0; } - data->out_scattering += 0.5 * density_integral; + if (density_integral > 0.0) + { + data->out_scattering += 0.3 * density_integral; - Color in_scattering = renderer->applyLightingToSurface(renderer, segment->start.location, VECTOR_ZERO, &layer->material); - in_scattering.r *= density_integral * 5.0; - in_scattering.g *= density_integral * 5.0; - in_scattering.b *= density_integral * 5.0; - _applyOutScattering(&in_scattering, data->out_scattering); + Color in_scattering = renderer->applyLightingToSurface(renderer, segment->start.location, VECTOR_ZERO, &layer->material); + in_scattering.r *= density_integral * 5.0; + in_scattering.g *= density_integral * 5.0; + in_scattering.b *= density_integral * 5.0; + _applyOutScattering(&in_scattering, data->out_scattering); - data->in_scattering.r += in_scattering.r; - data->in_scattering.g += in_scattering.g; - data->in_scattering.b += in_scattering.b; + data->in_scattering.r += in_scattering.r; + data->in_scattering.g += in_scattering.g; + data->in_scattering.b += in_scattering.b; + + if (!data->entry_found) + { + data->entry_found = 1; + data->entry = segment->start.location; + } + } } static Color _getColor(Renderer* renderer, Color base, Vector3 start, Vector3 end) @@ -182,9 +218,12 @@ static Color _getColor(Renderer* renderer, Color base, Vector3 start, Vector3 en AccumulatedMaterialData data; data.out_scattering = 0.0; data.in_scattering = COLOR_BLACK; + data.entry = ostart; + data.entry_found = 0; walker = cloudsCreateWalker(renderer, layer, ostart, oend); cloudsWalkerSetStepSize(walker, -1.0); + cloudsWalkerSetVoidSkipping(walker, 1); cloudsStartWalking(walker, _walkerMaterialCallback, &data); cloudsDeleteWalker(walker); @@ -197,8 +236,7 @@ static Color _getColor(Renderer* renderer, Color base, Vector3 start, Vector3 en base.b += data.in_scattering.b; /* Apply aerial perspective approximation */ - /* TODO This should be done at cloud entry */ - base = renderer->atmosphere->applyAerialPerspective(renderer, ostart, base).final; + base = renderer->atmosphere->applyAerialPerspective(renderer, data.entry, base).final; } }