clouds_walking: Started using walker in rendering

This commit is contained in:
Michaël Lemaire 2013-06-01 16:57:23 +02:00
parent aefc3cacdd
commit 1ede3de8d5
6 changed files with 183 additions and 35 deletions

2
TODO
View file

@ -1,4 +1,5 @@
Technology Preview 2 : Technology Preview 2 :
- Implement perspective correction for coordinate mapping of rasterized polygons.
- Finalize terrain editor. - Finalize terrain editor.
=> Add a generation dialog for base noise (overwriting changes). => Add a generation dialog for base noise (overwriting changes).
- Get rid of noise dialogs, for simpler settings. - Get rid of noise dialogs, for simpler settings.
@ -15,6 +16,7 @@ Technology Preview 2 :
Technlogy Preview 3 : Technlogy Preview 3 :
- Start an undo/redo system ? - Start an undo/redo system ?
- Alter aerial perspective using estimation of the amount of light left after cloud layers traversal.
- Add a map preview to terrain editor. - Add a map preview to terrain editor.
- Better time selection widget for atmosphere. - Better time selection widget for atmosphere.
- Clouds should keep distance to ground. - Clouds should keep distance to ground.

View file

@ -1,5 +1,6 @@
#include "private.h" #include "private.h"
#include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include "../tools.h" #include "../tools.h"
#include "../renderer.h" #include "../renderer.h"
@ -26,32 +27,117 @@ static Color _fakeGetColor(Renderer* renderer, Color base, Vector3 start, Vector
} }
/******************** Real ********************/ /******************** Real ********************/
typedef struct
{
double light_power;
double out_scattering; /* Amount of light scattered away by heavy particles */
} AccumulatedLightData;
static void _walkerFilterCallback(CloudsWalker* walker)
{
CloudWalkerStepInfo* segment = cloudsWalkerGetLastSegment(walker);
AccumulatedLightData* data = (AccumulatedLightData*)segment->data;
assert(data != NULL);
double density_integral = segment->length * (segment->start.global_density + segment->end.global_density) / 2.0;
data->out_scattering += 0.3 * density_integral;
if (data->out_scattering > data->light_power)
{
cloudsWalkerOrderStop(walker);
}
}
static int _alterLight(Renderer* renderer, LightDefinition* light, Vector3 location) static int _alterLight(Renderer* renderer, LightDefinition* light, Vector3 location)
{ {
#if 0
CloudsDefinition* definition = renderer->clouds->definition; CloudsDefinition* definition = renderer->clouds->definition;
int i, n; int i, n;
AccumulatedLightData data;
data.out_scattering = 0.0;
data.light_power = colorGetPower(&light->color);
/* TODO Iter layers in sorted order */ /* TODO Iter layers in sorted order */
n = layersCount(definition->layers); n = layersCount(definition->layers);
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
{ {
light->color = cloudsLayerFilterLight(layersGetLayer(definition->layers, i), renderer, light->color, location, v3Add(location, v3Scale(light->direction, -10000.0)), v3Scale(light->direction, -1.0)); CloudsLayerDefinition* layer = (CloudsLayerDefinition*)layersGetLayer(renderer->clouds->definition->layers, i);
/* TODO Reduce light->reflection too */ Vector3 ostart, oend;
ostart = location;
oend = v3Add(location, v3Scale(light->direction, -10000.0));
if (!cloudsOptimizeWalkingBounds(layer, &ostart, &oend))
{
continue;
} }
return n > 0; else
#endif {
return 0; CloudsWalker* walker;
walker = cloudsCreateWalker(renderer, layer, ostart, oend);
cloudsWalkerSetStepSize(walker, -1.0);
cloudsStartWalking(walker, _walkerFilterCallback, &data);
cloudsDeleteWalker(walker);
}
}
double max_power = colorGetPower(&light->color) - data.out_scattering;
if (max_power < 0.0)
{
light->color = COLOR_BLACK;
}
else
{
colorLimitPower(&light->color, max_power);
}
return data.out_scattering > 0.0;
} }
typedef struct typedef struct
{ {
Color result; double out_scattering; /* Amount of light scattered away by heavy particles */
Color in_scattering; /* Amount of light redirected toward the viewer */
} AccumulatedMaterialData; } AccumulatedMaterialData;
static inline void _applyOutScattering(Color* col, double out_scattering)
{
if (out_scattering >= 1.0)
{
col->r = col->g = col->b = 0.0;
}
else
{
col->r *= (1.0 - out_scattering);
col->g *= (1.0 - out_scattering);
col->b *= (1.0 - out_scattering);
}
}
static void _walkerMaterialCallback(CloudsWalker* walker) static void _walkerMaterialCallback(CloudsWalker* walker)
{ {
/*AccumulatedMaterialData* data = (AccumulatedMaterialData*)segment->data;*/ CloudWalkerStepInfo* segment = cloudsWalkerGetLastSegment(walker);
AccumulatedMaterialData* data = (AccumulatedMaterialData*)segment->data;
Renderer* renderer = segment->renderer;
CloudsLayerDefinition* layer = segment->layer;
assert(data != NULL);
double density_integral = segment->length * (segment->start.global_density + segment->end.global_density) / 2.0;
data->out_scattering += 0.5 * density_integral;
Color in_scattering = renderer->applyLightingToSurface(renderer, segment->start.location, VECTOR_UP, &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;
} }
static Color _getColor(Renderer* renderer, Color base, Vector3 start, Vector3 end) static Color _getColor(Renderer* renderer, Color base, Vector3 start, Vector3 end)
@ -81,13 +167,25 @@ static Color _getColor(Renderer* renderer, Color base, Vector3 start, Vector3 en
{ {
CloudsWalker* walker; CloudsWalker* walker;
AccumulatedMaterialData data; AccumulatedMaterialData data;
data.result = COLOR_TRANSPARENT; data.out_scattering = 0.0;
data.in_scattering = COLOR_BLACK;
walker = cloudsCreateWalker(renderer, layer, start, end); walker = cloudsCreateWalker(renderer, layer, ostart, oend);
cloudsWalkerSetStepSize(walker, -1.0);
cloudsStartWalking(walker, _walkerMaterialCallback, &data); cloudsStartWalking(walker, _walkerMaterialCallback, &data);
cloudsDeleteWalker(walker); cloudsDeleteWalker(walker);
colorMask(&base, &data.result); /* Apply final out_scattering to base */
_applyOutScattering(&base, data.out_scattering);
/* Apply in_scattering */
base.r += data.in_scattering.r;
base.g += data.in_scattering.g;
base.b += data.in_scattering.b;
/* Apply aerial perspective approximation */
/* TODO This should be done at cloud entry */
base = renderer->applyMediumTraversal(renderer, ostart, base);
} }
} }

View file

@ -122,11 +122,18 @@ void cloudsDeleteWalker(CloudsWalker* walker)
free(walker); free(walker);
} }
void cloudsSetStepSize(CloudsWalker* walker, double step) void cloudsWalkerSetStepSize(CloudsWalker* walker, double step)
{
if (step > 0.0)
{ {
/* TODO Negative step => automatic */
walker->step_size = step; walker->step_size = step;
} }
else
{
/* TODO Automatic settings (using rendering quality and cloud feature size) */
walker->step_size = 1.0;
}
}
static void _getPoint(CloudsWalker* walker, double cursor, CloudWalkerPoint* out_point) static void _getPoint(CloudsWalker* walker, double cursor, CloudWalkerPoint* out_point)
{ {
@ -147,33 +154,33 @@ static void _refineSegment(CloudsWalker* walker, double start_cursor, double sta
if (start_density == 0.0) if (start_density == 0.0)
{ {
/* Looking for entry */ /* Looking for entry */
if (middle.global_density == 0.0) if (middle.distance_from_start - start_cursor < precision)
{
*result = middle;
}
else if (middle.global_density == 0.0)
{ {
_refineSegment(walker, middle.distance_from_start, middle.global_density, end_cursor, end_density, precision, result); _refineSegment(walker, middle.distance_from_start, middle.global_density, end_cursor, end_density, precision, result);
} }
else if (middle.distance_from_start - start_cursor > precision)
{
_refineSegment(walker, start_cursor, start_density, middle.distance_from_start, middle.global_density, precision, result);
}
else else
{ {
*result = middle; _refineSegment(walker, start_cursor, start_density, middle.distance_from_start, middle.global_density, precision, result);
} }
} }
else else
{ {
/* Looking for exit */ /* Looking for exit */
if (middle.global_density == 0.0) if (end_cursor - middle.distance_from_start < precision)
{
*result = middle;
}
else if (middle.global_density == 0.0)
{ {
_refineSegment(walker, start_cursor, start_density, middle.distance_from_start, middle.global_density, precision, result); _refineSegment(walker, start_cursor, start_density, middle.distance_from_start, middle.global_density, precision, result);
} }
else if (end_cursor - middle.distance_from_start > precision)
{
_refineSegment(walker, middle.distance_from_start, middle.global_density, end_cursor, end_density, precision, result);
}
else else
{ {
*result = middle; _refineSegment(walker, middle.distance_from_start, middle.global_density, end_cursor, end_density, precision, result);
} }
} }
} }
@ -200,6 +207,7 @@ int cloudsWalkerPerformStep(CloudsWalker* walker)
_getPoint(walker, walker->cursor, &walker->last_segment.end); _getPoint(walker, walker->cursor, &walker->last_segment.end);
walker->last_segment.length = walker->step_size; walker->last_segment.length = walker->step_size;
walker->last_segment.refined = 0;
return 1; return 1;
} }
@ -212,9 +220,12 @@ int cloudsWalkerPerformStep(CloudsWalker* walker)
walker->last_segment.end.distance_from_start, walker->last_segment.end.distance_from_start,
walker->last_segment.end.global_density, walker->last_segment.end.global_density,
walker->next_action.precision, walker->next_action.precision,
&walker->last_segment.start); (walker->last_segment.start.global_density == 0.0) ? (&walker->last_segment.start) : (&walker->last_segment.end));
walker->last_segment.length = walker->last_segment.end.distance_from_start - walker->last_segment.start.distance_from_start; walker->last_segment.length = walker->last_segment.end.distance_from_start - walker->last_segment.start.distance_from_start;
walker->last_segment.refined = 1;
walker->next_action.order = CLOUD_WALKING_CONTINUE; walker->next_action.order = CLOUD_WALKING_CONTINUE;
return 1; return 1;
} }
else else
@ -248,6 +259,7 @@ CloudWalkerStepInfo* cloudsWalkerGetLastSegment(CloudsWalker* walker)
void cloudsStartWalking(CloudsWalker* walker, FuncCloudsWalkingCallback callback, void* data) void cloudsStartWalking(CloudsWalker* walker, FuncCloudsWalkingCallback callback, void* data)
{ {
walker->last_segment.data = data;
while (cloudsWalkerPerformStep(walker)) while (cloudsWalkerPerformStep(walker))
{ {
callback(walker); callback(walker);

View file

@ -32,8 +32,8 @@ typedef struct
CloudWalkerPoint end; CloudWalkerPoint end;
double length; double length;
/*int refined; int refined;
int subdivision_level; /*int subdivision_level;
double precision_asked;*/ double precision_asked;*/
void* data; void* data;
@ -77,7 +77,7 @@ void cloudsDeleteWalker(CloudsWalker* walker);
* @param walker The walker to configure * @param walker The walker to configure
* @param step The step length, negative for automatic * @param step The step length, negative for automatic
*/ */
void cloudsSetStepSize(CloudsWalker* walker, double step); void cloudsWalkerSetStepSize(CloudsWalker* walker, double step);
/** /**
* Perform a single step. * Perform a single step.

View file

@ -27,6 +27,11 @@ static inline void _add_methods_to_case(TCase* tc, ...)
suite_add_tcase(s, tc); \ suite_add_tcase(s, tc); \
} }
/***** Boolean assertions *****/
#define ck_assert_true(_X_) ck_assert_int_ne((_X_), 0)
#define ck_assert_false(_X_) ck_assert_int_eq((_X_), 0)
/***** Floating point assertions *****/
static inline int _double_equals(double x, double y) static inline int _double_equals(double x, double y)
{ {
return fabs(x - y) < 0.00000000001; return fabs(x - y) < 0.00000000001;

View file

@ -220,10 +220,11 @@ START_TEST(test_clouds_walking)
int result; int result;
/* First step */ /* First step */
cloudsSetStepSize(walker, 0.3); cloudsWalkerSetStepSize(walker, 0.3);
result = cloudsWalkerPerformStep(walker); result = cloudsWalkerPerformStep(walker);
segment = cloudsWalkerGetLastSegment(walker); segment = cloudsWalkerGetLastSegment(walker);
ck_assert_int_eq(result, 1); ck_assert_int_eq(result, 1);
ck_assert_false(segment->refined);
ck_assert_double_eq(segment->length, 0.3); ck_assert_double_eq(segment->length, 0.3);
ck_assert_double_eq(segment->start.distance_from_start, 0.0); ck_assert_double_eq(segment->start.distance_from_start, 0.0);
ck_assert_vector_values(segment->start.location, -0.4, 0.0, 0.0); ck_assert_vector_values(segment->start.location, -0.4, 0.0, 0.0);
@ -236,6 +237,7 @@ START_TEST(test_clouds_walking)
result = cloudsWalkerPerformStep(walker); result = cloudsWalkerPerformStep(walker);
segment = cloudsWalkerGetLastSegment(walker); segment = cloudsWalkerGetLastSegment(walker);
ck_assert_int_eq(result, 1); ck_assert_int_eq(result, 1);
ck_assert_false(segment->refined);
ck_assert_double_eq(segment->length, 0.3); ck_assert_double_eq(segment->length, 0.3);
ck_assert_double_eq(segment->start.distance_from_start, 0.3); ck_assert_double_eq(segment->start.distance_from_start, 0.3);
ck_assert_vector_values(segment->start.location, -0.1, 0.0, 0.0); ck_assert_vector_values(segment->start.location, -0.1, 0.0, 0.0);
@ -249,14 +251,43 @@ START_TEST(test_clouds_walking)
result = cloudsWalkerPerformStep(walker); result = cloudsWalkerPerformStep(walker);
segment = cloudsWalkerGetLastSegment(walker); segment = cloudsWalkerGetLastSegment(walker);
ck_assert_int_eq(result, 1); ck_assert_int_eq(result, 1);
ck_assert_double_in_range(segment->length, 0.19, 0.21); ck_assert_true(segment->refined);
ck_assert_double_in_range(segment->start.distance_from_start, 0.39, 0.41); ck_assert_double_in_range(segment->length, 0.19, 0.20);
ck_assert_double_in_range(segment->start.location.x, -0.01, 0.01); ck_assert_double_in_range(segment->start.distance_from_start, 0.40, 0.41);
/* TODO Check segment->start.global_density */ ck_assert_double_in_range(segment->start.location.x, 0.0, 0.01);
ck_assert_double_gt(segment->start.global_density, 0.0);
ck_assert_double_eq(segment->end.distance_from_start, 0.6); ck_assert_double_eq(segment->end.distance_from_start, 0.6);
ck_assert_vector_values(segment->end.location, 0.2, 0.0, 0.0); ck_assert_vector_values(segment->end.location, 0.2, 0.0, 0.0);
ck_assert_double_gt(segment->end.global_density, 0.9); ck_assert_double_gt(segment->end.global_density, 0.9);
/* Third step, change step size */
cloudsWalkerSetStepSize(walker, 0.4);
result = cloudsWalkerPerformStep(walker);
segment = cloudsWalkerGetLastSegment(walker);
ck_assert_int_eq(result, 1);
ck_assert_false(segment->refined);
ck_assert_double_eq(segment->length, 0.4);
ck_assert_double_eq(segment->start.distance_from_start, 0.6);
ck_assert_vector_values(segment->start.location, 0.2, 0.0, 0.0);
ck_assert_double_gt(segment->start.global_density, 0.9);
ck_assert_double_eq(segment->end.distance_from_start, 1.0);
ck_assert_vector_values(segment->end.location, 0.6, 0.0, 0.0);
ck_assert_double_eq(segment->end.global_density, 0.0);
/* Refine exit point */
cloudsWalkerOrderRefine(walker, 0.001);
result = cloudsWalkerPerformStep(walker);
segment = cloudsWalkerGetLastSegment(walker);
ck_assert_int_eq(result, 1);
ck_assert_true(segment->refined);
ck_assert_double_in_range(segment->length, 0.3, 0.301);
ck_assert_double_eq(segment->start.distance_from_start, 0.6);
ck_assert_vector_values(segment->start.location, 0.2, 0.0, 0.0);
ck_assert_double_gt(segment->start.global_density, 0.9);
ck_assert_double_in_range(segment->end.distance_from_start, 0.9, 0.901);
ck_assert_double_in_range(segment->end.location.x, 0.5, 0.501);
ck_assert_double_lt(segment->end.global_density, 0.1);
/* Clean up */ /* Clean up */
cloudsDeleteWalker(walker); cloudsDeleteWalker(walker);