Use the new terrain walker for improved shadows
This commit is contained in:
parent
eabe68b77d
commit
220fe89b05
6 changed files with 87 additions and 79 deletions
|
@ -17,6 +17,8 @@ OpenGLRenderer::OpenGLRenderer(Scenery* scenery):
|
||||||
{
|
{
|
||||||
ready = false;
|
ready = false;
|
||||||
|
|
||||||
|
render_quality = 3;
|
||||||
|
|
||||||
functions = new OpenGLFunctions();
|
functions = new OpenGLFunctions();
|
||||||
shared_state = new OpenGLSharedState();
|
shared_state = new OpenGLSharedState();
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ SceneryTopDownPreviewRenderer::SceneryTopDownPreviewRenderer(Scenery* scenery):
|
||||||
scenery(scenery)
|
scenery(scenery)
|
||||||
{
|
{
|
||||||
clouds_enabled = true;
|
clouds_enabled = true;
|
||||||
|
render_quality = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneryTopDownPreviewRenderer::bindEvent(BasePreview* preview)
|
void SceneryTopDownPreviewRenderer::bindEvent(BasePreview* preview)
|
||||||
|
|
|
@ -134,16 +134,16 @@ void SoftwareRenderer::disableAtmosphere()
|
||||||
light.color.g = 1.5;
|
light.color.g = 1.5;
|
||||||
light.color.b = 1.5;
|
light.color.b = 1.5;
|
||||||
light.direction.x = -1.0;
|
light.direction.x = -1.0;
|
||||||
light.direction.y = -0.5;
|
light.direction.y = -0.3;
|
||||||
light.direction.z = 1.0;
|
light.direction.z = 1.0;
|
||||||
light.direction = light.direction.normalize();
|
light.direction = light.direction.normalize();
|
||||||
light.altered = 1;
|
light.altered = 1;
|
||||||
light.reflection = 0.0;
|
light.reflection = 0.0;
|
||||||
lights.push_back(light);
|
lights.push_back(light);
|
||||||
|
|
||||||
light.color.r = 0.4;
|
light.color.r = 0.2;
|
||||||
light.color.g = 0.4;
|
light.color.g = 0.2;
|
||||||
light.color.b = 0.42;
|
light.color.b = 0.21;
|
||||||
light.direction.x = 1.0;
|
light.direction.x = 1.0;
|
||||||
light.direction.y = -0.5;
|
light.direction.y = -0.5;
|
||||||
light.direction.z = -1.0;
|
light.direction.z = -1.0;
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "TexturesDefinition.h"
|
#include "TexturesDefinition.h"
|
||||||
#include "TerrainRenderer.h"
|
#include "TerrainRenderer.h"
|
||||||
#include "TexturesRenderer.h"
|
#include "TexturesRenderer.h"
|
||||||
|
#include "Matrix4.h"
|
||||||
|
|
||||||
TerrainRayWalker::TerrainRayWalker(SoftwareRenderer* renderer):
|
TerrainRayWalker::TerrainRayWalker(SoftwareRenderer* renderer):
|
||||||
renderer(renderer)
|
renderer(renderer)
|
||||||
|
@ -23,33 +24,57 @@ void TerrainRayWalker::update()
|
||||||
ymin = info.min_height - disp;
|
ymin = info.min_height - disp;
|
||||||
ymax = info.max_height + disp;
|
ymax = info.max_height + disp;
|
||||||
|
|
||||||
ydispmin = -disp;
|
ydispmax = disp * (0.5 + (double)renderer->render_quality * 0.1);
|
||||||
ydispmax = disp;
|
ydispmin = -ydispmax;
|
||||||
|
|
||||||
minstep = 0.01 * terrain->scaling / (double)renderer->render_quality;
|
minstep = 1.0 * terrain->scaling / (double)(renderer->render_quality * renderer->render_quality);
|
||||||
maxstep = 1.0 * terrain->scaling;
|
maxstep = 10.0 * terrain->scaling / (double)renderer->render_quality;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TerrainRayWalker::startWalking(Vector3 start, const Vector3 &direction, double escaping_factor, double max_length, TerrainHitResult &result)
|
static inline Vector3 _getShiftAxis(const Vector3 &direction)
|
||||||
|
{
|
||||||
|
if (fabs(direction.y) > 0.99)
|
||||||
|
{
|
||||||
|
// When the ray is vertical, we choose an arbitrary shift axis
|
||||||
|
return VECTOR_NORTH;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return VECTOR_UP.crossProduct(direction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TerrainRayWalker::startWalking(const Vector3 &start, Vector3 direction, double escape_angle, double max_length, TerrainHitResult &result)
|
||||||
{
|
{
|
||||||
TerrainRenderer* terrain_renderer = renderer->getTerrainRenderer();
|
TerrainRenderer* terrain_renderer = renderer->getTerrainRenderer();
|
||||||
TexturesRenderer* textures_renderer = renderer->getTexturesRenderer();
|
TexturesRenderer* textures_renderer = renderer->getTexturesRenderer();
|
||||||
TerrainRenderer::TerrainResult terrain_result;
|
TerrainRenderer::TerrainResult terrain_result;
|
||||||
Vector3 end, displaced;
|
Vector3 cursor, displaced;
|
||||||
bool hit;
|
|
||||||
double diff;
|
double diff;
|
||||||
|
Matrix4 shift_matrix;
|
||||||
|
double shift_step = 0.0;
|
||||||
|
|
||||||
|
Vector3 previous_cursor = start;
|
||||||
|
bool hit = false;
|
||||||
double step_length = minstep;
|
double step_length = minstep;
|
||||||
double walked_length = 0.0;
|
double walked_length = 0.0;
|
||||||
|
|
||||||
|
result.escape_angle = 0.0;
|
||||||
|
if (escape_angle != 0.0)
|
||||||
|
{
|
||||||
|
// Prepare escape
|
||||||
|
shift_step = escape_angle / (double)(renderer->render_quality * renderer->render_quality);
|
||||||
|
shift_matrix = Matrix4::newRotateAxis(-shift_step, _getShiftAxis(direction));
|
||||||
|
}
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
// Perform a step
|
// Perform a step
|
||||||
end = start.add(direction.scale(step_length));
|
cursor = previous_cursor.add(direction.scale(step_length));
|
||||||
|
|
||||||
// Get the terrain info at end (without textures displacement)
|
// Get the terrain info at end (without textures displacement)
|
||||||
terrain_result = terrain_renderer->getResult(end.x, end.z, true, false);
|
terrain_result = terrain_renderer->getResult(cursor.x, cursor.z, true, false);
|
||||||
diff = end.y - terrain_result.location.y;
|
diff = cursor.y - terrain_result.location.y;
|
||||||
|
|
||||||
// If we are very under the terrain, consider a hit
|
// If we are very under the terrain, consider a hit
|
||||||
if (diff < ydispmin)
|
if (diff < ydispmin)
|
||||||
|
@ -61,33 +86,42 @@ bool TerrainRayWalker::startWalking(Vector3 start, const Vector3 &direction, dou
|
||||||
else if (diff < ydispmax)
|
else if (diff < ydispmax)
|
||||||
{
|
{
|
||||||
displaced = textures_renderer->displaceTerrain(terrain_result);
|
displaced = textures_renderer->displaceTerrain(terrain_result);
|
||||||
diff = end.y - displaced.y;
|
diff = cursor.y - displaced.y;
|
||||||
hit = (diff < 0.0);
|
hit = diff < 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hit)
|
if (hit)
|
||||||
{
|
{
|
||||||
// Refine the hit with dichotomy at high quality
|
// TODO Refine the hit with dichotomy at high quality
|
||||||
/*if (renderer->render_quality > 7)
|
/*if (renderer->render_quality > 7)
|
||||||
{
|
{
|
||||||
end = refineHit(start, end, step_length);
|
cursor = refineHit(previous_cursor, cursor, step_length);
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
// Find an escape
|
// Shift ray to escape terrain
|
||||||
/*if (escaping_factor != 0.0)
|
if (escape_angle != 0.0)
|
||||||
{
|
{
|
||||||
result.escape_length = findEscape(end, walked_length, escaping_factor, max_length);
|
result.escape_angle += shift_step;
|
||||||
}*/
|
if (result.escape_angle > escape_angle)
|
||||||
|
{
|
||||||
|
// Too much shifted to escape, make it a hit
|
||||||
|
result.escape_angle = 0.0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
hit = false;
|
||||||
|
direction = shift_matrix.multPoint(direction);
|
||||||
|
previous_cursor = start.add(shift_matrix.multPoint(previous_cursor.sub(start)));
|
||||||
|
}
|
||||||
|
|
||||||
result.hit_location = end;
|
result.hit_location = cursor;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Prepare next step
|
// Prepare next step
|
||||||
start = end;
|
previous_cursor = cursor;
|
||||||
walked_length += step_length;
|
walked_length += step_length;
|
||||||
|
|
||||||
step_length = diff / (double)renderer->render_quality;
|
step_length = diff * 3.0 / (double)renderer->render_quality;
|
||||||
if (step_length < minstep)
|
if (step_length < minstep)
|
||||||
{
|
{
|
||||||
step_length = minstep;
|
step_length = minstep;
|
||||||
|
@ -97,7 +131,7 @@ bool TerrainRayWalker::startWalking(Vector3 start, const Vector3 &direction, dou
|
||||||
step_length = maxstep;
|
step_length = maxstep;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (not hit and start.y < ymax and walked_length < max_length);
|
} while (not hit and cursor.y < ymax and walked_length < max_length);
|
||||||
|
|
||||||
return hit;
|
return hit or result.escape_angle > 0.0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ class SOFTWARESHARED_EXPORT TerrainRayWalker
|
||||||
public:
|
public:
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Vector3 hit_location; // Location of the hit
|
Vector3 hit_location; // Location of the hit
|
||||||
double escape_length; // Length walked before terrain was escaped after hit (0.0 if no escape)
|
double escape_angle; // Angle used to shift the ray to escape the terrain (0.0 if no escape was possible)
|
||||||
} TerrainHitResult;
|
} TerrainHitResult;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -36,12 +36,12 @@ public:
|
||||||
*
|
*
|
||||||
* \param start Point of origin of the ray
|
* \param start Point of origin of the ray
|
||||||
* \param direction Ray direction (normalized vector)
|
* \param direction Ray direction (normalized vector)
|
||||||
* \param escaping_factor Factor used to escape the terrain on hit (mainly for shadows computing)
|
* \param escape_angle Maximal angle allowed to escape the terrain on hit (mainly for shadows computing)
|
||||||
* \param max_length Maximum length to walk before considering no hit
|
* \param max_length Maximum length to walk before considering no hit
|
||||||
* \param result Object to store the results info
|
* \param result Object to store the results info
|
||||||
* \return true if there was a hit
|
* \return true if there was a hit
|
||||||
*/
|
*/
|
||||||
bool startWalking(Vector3 start, const Vector3 &direction, double escaping_factor, double max_length, TerrainHitResult &result);
|
bool startWalking(const Vector3 &start, Vector3 direction, double escape_angle, double max_length, TerrainHitResult &result);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SoftwareRenderer* renderer;
|
SoftwareRenderer* renderer;
|
||||||
|
|
|
@ -151,16 +151,16 @@ RayCastingResult TerrainRenderer::castRay(const Vector3 &start, const Vector3 &d
|
||||||
bool TerrainRenderer::applyLightFilter(LightComponent &light, const Vector3 &at)
|
bool TerrainRenderer::applyLightFilter(LightComponent &light, const Vector3 &at)
|
||||||
{
|
{
|
||||||
TerrainDefinition* definition = parent->getScenery()->getTerrain();
|
TerrainDefinition* definition = parent->getScenery()->getTerrain();
|
||||||
Vector3 inc_vector, direction_to_light, cursor;
|
TerrainRayWalker::TerrainHitResult walk_result;
|
||||||
double inc_value, inc_base, inc_factor, height, diff, light_factor, smoothing, length;
|
|
||||||
|
|
||||||
|
// If location is above terrain, don't bother
|
||||||
if (at.y > definition->getHeightInfo().max_height)
|
if (at.y > definition->getHeightInfo().max_height)
|
||||||
{
|
{
|
||||||
// Location is above terrain, don't bother
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
direction_to_light = light.direction.scale(-1.0);
|
// Handle sun below horizon
|
||||||
|
Vector3 direction_to_light = light.direction.scale(-1.0);
|
||||||
if (direction_to_light.y < -0.05)
|
if (direction_to_light.y < -0.05)
|
||||||
{
|
{
|
||||||
light.color = COLOR_BLACK;
|
light.color = COLOR_BLACK;
|
||||||
|
@ -173,60 +173,31 @@ bool TerrainRenderer::applyLightFilter(LightComponent &light, const Vector3 &at)
|
||||||
light.color.b *= (0.05 + direction_to_light.y) / 0.05;
|
light.color.b *= (0.05 + direction_to_light.y) / 0.05;
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor = at;
|
// Walk to find an intersection
|
||||||
inc_factor = (double)parent->render_quality;
|
double escape_angle = definition->shadow_smoothing;
|
||||||
inc_base = definition->height / definition->scaling;
|
if (walker->startWalking(at, direction_to_light, escape_angle, 100.0, walk_result))
|
||||||
inc_value = inc_base / inc_factor;
|
|
||||||
smoothing = definition->shadow_smoothing;
|
|
||||||
|
|
||||||
light_factor = 1.0;
|
|
||||||
length = 0.0;
|
|
||||||
diff = 0.0;
|
|
||||||
do
|
|
||||||
{
|
{
|
||||||
inc_vector = direction_to_light.scale(inc_value);
|
if (walk_result.escape_angle == 0.0)
|
||||||
length += inc_vector.getNorm();
|
|
||||||
cursor = cursor.add(inc_vector);
|
|
||||||
height = parent->getTerrainRenderer()->getResult(cursor.x, cursor.z, 1, 1).location.y;
|
|
||||||
diff = cursor.y - height;
|
|
||||||
if (diff < 0.0)
|
|
||||||
{
|
{
|
||||||
if (length * smoothing > 0.000001)
|
// Hit, with no escape, cancelling the light
|
||||||
{
|
light.color = COLOR_BLACK;
|
||||||
light_factor += diff * inc_vector.getNorm() / (length * smoothing);
|
return false;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
light_factor = 0.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (diff < inc_base / inc_factor)
|
|
||||||
{
|
|
||||||
inc_value = inc_base / inc_factor;
|
|
||||||
}
|
|
||||||
else if (diff > inc_base)
|
|
||||||
{
|
|
||||||
inc_value = inc_base;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
inc_value = diff;
|
// Hit, with an escape
|
||||||
}
|
double light_factor = 1.0 - (walk_result.escape_angle / escape_angle);
|
||||||
}
|
|
||||||
while (light_factor > 0.0 && length < (10.0 * inc_factor) && cursor.y <= definition->_max_height);
|
|
||||||
|
|
||||||
if (light_factor <= 0.0)
|
light.color.r *= light_factor;
|
||||||
{
|
light.color.g *= light_factor;
|
||||||
light.color = COLOR_BLACK;
|
light.color.b *= light_factor;
|
||||||
return false;
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
light.color.r *= light_factor;
|
// No hit, leave light alone
|
||||||
light.color.g *= light_factor;
|
|
||||||
light.color.b *= light_factor;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue