paysages3d/src/render/software/WaterRenderer.cpp

278 lines
7.4 KiB
C++

#include "WaterRenderer.h"
#include "SoftwareRenderer.h"
#include "TerrainRenderer.h"
#include "WaterDefinition.h"
#include "NoiseGenerator.h"
#include "LightComponent.h"
#include "Scenery.h"
#include "SurfaceMaterial.h"
#include "NoiseFunctionSimplex.h"
WaterRenderer::WaterRenderer(SoftwareRenderer* parent):
parent(parent)
{
noise = new NoiseFunctionSimplex();
}
WaterRenderer::~WaterRenderer()
{
delete noise;
}
void WaterRenderer::update()
{
WaterDefinition* definition = parent->getScenery()->getWater();
noise->setState(*definition->noise_state);
noise->setScaling(definition->scaling * 0.3, definition->waves_height * 0.05);
noise->setStep(0.3);
}
static inline double _getHeight(FractalNoise* noise, double x, double z)
{
return noise->get2d(0.00001, x, z);
}
static inline Vector3 _getNormal(FractalNoise* noise, Vector3 base, double detail)
{
Vector3 back, right;
double x, z;
x = base.x;
z = base.z;
back.x = x;
back.y = _getHeight(noise, x, z + detail);
back.z = z + detail;
back = back.sub(base);
right.x = x + detail;
right.y = _getHeight(noise, x + detail, z);
right.z = z;
right = right.sub(base);
return back.crossProduct(right).normalize();
}
static inline Vector3 _reflectRay(Vector3 incoming, Vector3 normal)
{
double c;
c = normal.dotProduct(incoming.scale(-1.0));
return incoming.add(normal.scale(2.0 * c));
}
static inline Vector3 _refractRay(Vector3 incoming, Vector3 normal)
{
double c1, c2, f;
f = 1.0 / 1.33;
c1 = normal.dotProduct(incoming.scale(-1.0));
c2 = sqrt(1.0 - pow(f, 2.0) * (1.0 - pow(c1, 2.0)));
if (c1 >= 0.0)
{
return incoming.scale(f).add(normal.scale(f * c1 - c2));
}
else
{
return incoming.scale(f).add(normal.scale(c2 - f * c1));
}
}
static inline Color _getFoamMask(SoftwareRenderer* renderer, WaterDefinition* definition, FractalNoise* noise, Vector3 location, Vector3 normal, double detail)
{
Color result;
double foam_factor, normal_diff, location_offset;
location_offset = 2.0 * detail;
foam_factor = 0.0;
location.x += location_offset;
normal_diff = 1.0 - normal.dotProduct(_getNormal(noise, location, detail));
if (normal_diff > foam_factor)
{
foam_factor = normal_diff;
}
location.x -= location_offset * 2.0;
normal_diff = 1.0 - normal.dotProduct(_getNormal(noise, location, detail));
if (normal_diff > foam_factor)
{
foam_factor = normal_diff;
}
location.x += location_offset;
location.z -= location_offset;
normal_diff = 1.0 - normal.dotProduct(_getNormal(noise, location, detail));
if (normal_diff > foam_factor)
{
foam_factor = normal_diff;
}
location.z += location_offset * 2.0;
normal_diff = 1.0 - normal.dotProduct(_getNormal(noise, location, detail));
if (normal_diff > foam_factor)
{
foam_factor = normal_diff;
}
foam_factor *= 10.0;
if (foam_factor > 1.0)
{
foam_factor = 1.0;
}
if (foam_factor <= 1.0 - definition->foam_coverage)
{
return COLOR_TRANSPARENT;
}
foam_factor = (foam_factor - (1.0 - definition->foam_coverage)) * definition->foam_coverage;
/* TODO Re-use base lighting status */
result = renderer->applyLightingToSurface(location, normal, *definition->foam_material);
result.r *= 2.0;
result.g *= 2.0;
result.b *= 2.0;
/* TODO This should be configurable */
if (foam_factor > 0.2)
{
result.a = 0.8;
}
else
{
result.a = 0.8 * (foam_factor / 0.2);
}
return result;
}
HeightInfo WaterRenderer::getHeightInfo()
{
HeightInfo info;
double noise_minvalue, noise_maxvalue;
info.base_height = 0.0;
// TODO
//noise->getRange(&noise_minvalue, &noise_maxvalue);
noise_minvalue = noise_maxvalue = 0.0;
info.min_height = info.base_height + noise_minvalue;
info.max_height = info.base_height + noise_maxvalue;
return info;
}
double WaterRenderer::getHeight(double x, double z)
{
return _getHeight(noise, x, z);
}
WaterRenderer::WaterResult WaterRenderer::getResult(double x, double z)
{
WaterDefinition* definition = parent->getScenery()->getWater();
WaterResult result;
RayCastingResult refracted;
Vector3 location, normal, look_direction;
Color color, foam;
double detail, depth;
location.x = x;
location.y = _getHeight(noise, x, z);
location.z = z;
result.location = location;
detail = parent->getPrecision(location) * 0.1;
if (detail < 0.00001)
{
detail = 0.00001;
}
normal = _getNormal(noise, location, detail);
look_direction = location.sub(parent->getCameraLocation(location)).normalize();
/* Reflection */
if (definition->reflection == 0.0)
{
result.reflected = COLOR_BLACK;
}
else
{
result.reflected = parent->rayWalking(location, _reflectRay(look_direction, normal), 1, 0, 1, 1).hit_color;
}
/* Transparency/refraction */
if (definition->transparency == 0.0)
{
result.refracted = COLOR_BLACK;
}
else
{
Color depth_color = *definition->depth_color;
refracted = parent->rayWalking(location, _refractRay(look_direction, normal), 1, 0, 1, 1);
depth = location.sub(refracted.hit_location).getNorm();
depth_color.limitPower(refracted.hit_color.getPower());
if (depth > definition->transparency_depth)
{
result.refracted = depth_color;
}
else
{
depth /= definition->transparency_depth;
result.refracted.r = refracted.hit_color.r * (1.0 - depth) + depth_color.r * depth;
result.refracted.g = refracted.hit_color.g * (1.0 - depth) + depth_color.g * depth;
result.refracted.b = refracted.hit_color.b * (1.0 - depth) + depth_color.b * depth;
result.refracted.a = 1.0;
}
}
/* Lighting from environment */
color = parent->applyLightingToSurface(location, normal, *definition->material);
color.r += result.reflected.r * definition->reflection + result.refracted.r * definition->transparency;
color.g += result.reflected.g * definition->reflection + result.refracted.g * definition->transparency;
color.b += result.reflected.b * definition->reflection + result.refracted.b * definition->transparency;
/* Merge with foam */
foam = _getFoamMask(parent, definition, noise, location, normal, detail);
color.mask(foam);
/* Bring color to the camera */
color = parent->applyMediumTraversal(location, color);
result.base = definition->material->_rgb;
result.final = color;
return result;
}
bool WaterRenderer::applyLightFilter(LightComponent &light, const Vector3 &at)
{
WaterDefinition* definition = parent->getScenery()->getWater();
double factor;
if (at.y < 0.0)
{
if (light.direction.y <= -0.00001)
{
factor = -at.y / (-light.direction.y * definition->lighting_depth);
if (factor > 1.0)
{
factor = 1.0;
}
factor = 1.0 - factor;
light.color.r *= factor;
light.color.g *= factor;
light.color.b *= factor;
light.reflection *= factor;
return true;
}
else
{
light.color = COLOR_BLACK;
return false;
}
}
else
{
return true;
}
}