Refactored textures renderer

This commit is contained in:
Michaël Lemaire 2016-01-10 14:27:32 +01:00
parent 6cf607a557
commit 3b27d3be3e
15 changed files with 342 additions and 203 deletions

View file

@ -111,6 +111,24 @@ VectorSpherical Vector3::toSpherical() const {
return result;
}
Vector3 Vector3::getNormal3(const Vector3 &posx, const Vector3 &posy) const {
return posx.sub(*this).crossProduct(posy.sub(*this)).normalize();
}
Vector3 Vector3::getNormal5(const Vector3 &posx, const Vector3 &negx, const Vector3 &posy, const Vector3 &negy) const {
Vector3 dnegx = negx.sub(*this);
Vector3 dposy = posy.sub(*this);
Vector3 dposx = posx.sub(*this);
Vector3 dnegy = negy.sub(*this);
Vector3 normal = dposy.crossProduct(dnegx);
normal = normal.add(dposx.crossProduct(dposy));
normal = normal.add(dnegy.crossProduct(dposx));
normal = normal.add(dnegx.crossProduct(dnegy));
return normal.normalize();
}
Vector3 Vector3::midPointTo(const Vector3 &other) const {
return Vector3((other.x + x) * 0.5, (other.y + y) * 0.5, (other.z + z) * 0.5);
}

View file

@ -64,8 +64,24 @@ class BASICSSHARED_EXPORT Vector3 {
*/
Vector3 midPointTo(const Vector3 &other) const;
/**
* Get the spherical coordinates corresponding to this vector.
*/
VectorSpherical toSpherical() const;
/**
* Get a normal vector, using "posx-this" as X axis and "posy-this" as Y axis, to produce the normal along the Z
* axis.
*/
Vector3 getNormal3(const Vector3 &posx, const Vector3 &posy) const;
/**
* Same as "getNormal3", but using also negative axis for more precision.
*
* As the 4 vectors used may not be in the same place, an average is done.
*/
Vector3 getNormal5(const Vector3 &posx, const Vector3 &negx, const Vector3 &posy, const Vector3 &negy) const;
/**
* Produce a random vector in a sphere domain.
*

View file

@ -9,6 +9,7 @@
#include "ColorProfile.h"
#include "CameraDefinition.h"
#include "OpenGLRenderer.h"
#include "TerrainRenderer.h"
#include "TexturesRenderer.h"
#include "Scenery.h"
#include "TerrainDefinition.h"
@ -97,7 +98,7 @@ bool OpenGLTerrainChunk::maintain() {
if (_texture_current_size <= 1 || i % 2 != 0 || j % 2 != 0) {
double x = _startx + factor * to_double(i);
double z = _startz + factor * to_double(j);
Color color = _renderer->getTexturesRenderer()->applyToTerrain(x, z, 0.001).final_color;
Color color = _renderer->getTerrainRenderer()->getFinalColor(x, z, 0.001);
new_image->setPixel(i, j, Color(color.r * 0.2, color.g * 0.2, color.b * 0.2).normalized().to32BitRGBA());
}
}

View file

@ -32,7 +32,7 @@ SoftwareRenderer::SoftwareRenderer(Scenery *scenery) : scenery(scenery) {
atmosphere_renderer = new BaseAtmosphereRenderer(this);
clouds_renderer = new CloudsRenderer(this);
terrain_renderer = new TerrainRenderer(this);
textures_renderer = new TexturesRenderer(this);
textures_renderer = new TexturesRenderer();
vegetation_renderer = new VegetationRenderer(this);
water_renderer = new WaterRenderer(this);
@ -86,7 +86,6 @@ void SoftwareRenderer::prepare() {
clouds_renderer->update();
terrain_renderer->update();
water_renderer->update();
textures_renderer->update();
nightsky_renderer->update();

View file

@ -56,19 +56,19 @@ void TerrainRasterizer::renderQuad(CanvasPortion *canvas, double x, double z, do
ov1.x = x;
ov1.z = z;
dv1 = renderer->getTerrainRenderer()->getResult(x, z, true, true).location;
dv1 = renderer->getTerrainRenderer()->getDisplaced(x, z, true);
ov2.x = x;
ov2.z = z + size;
dv2 = renderer->getTerrainRenderer()->getResult(x, z + size, true, true).location;
dv2 = renderer->getTerrainRenderer()->getDisplaced(x, z + size, true);
ov3.x = x + size;
ov3.z = z + size;
dv3 = renderer->getTerrainRenderer()->getResult(x + size, z + size, true, true).location;
dv3 = renderer->getTerrainRenderer()->getDisplaced(x + size, z + size, true);
ov4.x = x + size;
ov4.z = z;
dv4 = renderer->getTerrainRenderer()->getResult(x + size, z, true, true).location;
dv4 = renderer->getTerrainRenderer()->getDisplaced(x + size, z, true);
if (yoffset != 0.0) {
dv1.y += yoffset;
@ -87,11 +87,11 @@ void TerrainRasterizer::setYOffset(double offset) {
}
void TerrainRasterizer::getChunk(SoftwareRenderer *renderer, TerrainRasterizer::TerrainChunkInfo *chunk, double x,
double z, double size, int displaced) {
chunk->point_nw = renderer->getTerrainRenderer()->getResult(x, z, true, displaced).location;
chunk->point_sw = renderer->getTerrainRenderer()->getResult(x, z + size, true, displaced).location;
chunk->point_se = renderer->getTerrainRenderer()->getResult(x + size, z + size, true, displaced).location;
chunk->point_ne = renderer->getTerrainRenderer()->getResult(x + size, z, true, displaced).location;
double z, double size) {
chunk->point_nw = renderer->getTerrainRenderer()->getResult(x, z, true).location;
chunk->point_sw = renderer->getTerrainRenderer()->getResult(x, z + size, true).location;
chunk->point_se = renderer->getTerrainRenderer()->getResult(x + size, z + size, true).location;
chunk->point_ne = renderer->getTerrainRenderer()->getResult(x + size, z, true).location;
if (yoffset != 0.0) {
chunk->point_nw.y += yoffset;
@ -100,13 +100,8 @@ void TerrainRasterizer::getChunk(SoftwareRenderer *renderer, TerrainRasterizer::
chunk->point_ne.y += yoffset;
}
double displacement_power;
if (displaced) {
displacement_power = 0.0;
} else {
displacement_power =
double displacement_power =
renderer->getTexturesRenderer()->getMaximalDisplacement(renderer->getScenery()->getTextures());
}
BoundingBox box;
if (displacement_power > 0.0) {
@ -137,7 +132,7 @@ void TerrainRasterizer::getChunk(SoftwareRenderer *renderer, TerrainRasterizer::
}
}
int TerrainRasterizer::performTessellation(CanvasPortion *canvas, bool displaced) {
int TerrainRasterizer::performTessellation(CanvasPortion *canvas) {
TerrainChunkInfo chunk;
int chunk_factor, chunk_count, i, result;
Vector3 cam = renderer->getCameraLocation(VECTOR_ZERO);
@ -156,7 +151,7 @@ int TerrainRasterizer::performTessellation(CanvasPortion *canvas, bool displaced
while (radius_int < 20000.0) {
for (i = 0; i < chunk_count - 1; i++) {
getChunk(renderer, &chunk, cx - radius_ext + chunk_size * i, cz - radius_ext, chunk_size, displaced);
getChunk(renderer, &chunk, cx - radius_ext + chunk_size * i, cz - radius_ext, chunk_size);
if (chunk.detail_hint > 0) {
result += chunk.detail_hint * chunk.detail_hint;
if (canvas) {
@ -167,7 +162,7 @@ int TerrainRasterizer::performTessellation(CanvasPortion *canvas, bool displaced
return result;
}
getChunk(renderer, &chunk, cx + radius_int, cz - radius_ext + chunk_size * i, chunk_size, displaced);
getChunk(renderer, &chunk, cx + radius_int, cz - radius_ext + chunk_size * i, chunk_size);
if (chunk.detail_hint > 0) {
result += chunk.detail_hint * chunk.detail_hint;
if (canvas) {
@ -178,7 +173,7 @@ int TerrainRasterizer::performTessellation(CanvasPortion *canvas, bool displaced
return result;
}
getChunk(renderer, &chunk, cx + radius_int - chunk_size * i, cz + radius_int, chunk_size, displaced);
getChunk(renderer, &chunk, cx + radius_int - chunk_size * i, cz + radius_int, chunk_size);
if (chunk.detail_hint > 0) {
result += chunk.detail_hint * chunk.detail_hint;
if (canvas) {
@ -189,7 +184,7 @@ int TerrainRasterizer::performTessellation(CanvasPortion *canvas, bool displaced
return result;
}
getChunk(renderer, &chunk, cx - radius_ext, cz + radius_int - chunk_size * i, chunk_size, displaced);
getChunk(renderer, &chunk, cx - radius_ext, cz + radius_int - chunk_size * i, chunk_size);
if (chunk.detail_hint > 0) {
result += chunk.detail_hint * chunk.detail_hint;
if (canvas) {
@ -220,15 +215,15 @@ void TerrainRasterizer::processChunk(CanvasPortion *canvas, TerrainChunkInfo *ch
int TerrainRasterizer::prepareRasterization() {
// TODO Chunks could be saved and reused in rasterizeToCanvas
return performTessellation(NULL, false);
return performTessellation(NULL);
}
void TerrainRasterizer::rasterizeToCanvas(CanvasPortion *canvas) {
performTessellation(canvas, false);
performTessellation(canvas);
}
Color TerrainRasterizer::shadeFragment(const CanvasFragment &fragment, const CanvasFragment *) const {
Vector3 point = fragment.getLocation();
double precision = renderer->getPrecision(_getPoint(renderer, point.x, point.z));
return renderer->getTerrainRenderer()->getFinalColor(point, precision);
return renderer->getTerrainRenderer()->getFinalColor(point.x, point.z, precision);
}

View file

@ -59,7 +59,7 @@ class SOFTWARESHARED_EXPORT TerrainRasterizer : public Rasterizer {
*
* 'canvas' may be NULL to only simulate the tessellation.
*/
int performTessellation(CanvasPortion *canvas, bool displaced);
int performTessellation(CanvasPortion *canvas);
/**
* Tessellate a terrain chunk, pushing the quads in the render area.
@ -69,7 +69,7 @@ class SOFTWARESHARED_EXPORT TerrainRasterizer : public Rasterizer {
void renderQuad(CanvasPortion *canvas, double x, double z, double size, double water_height);
void getChunk(SoftwareRenderer *renderer, TerrainRasterizer::TerrainChunkInfo *chunk, double x, double z,
double size, int displaced);
double size);
private:
double yoffset;

View file

@ -50,8 +50,9 @@ static inline Vector3 _getShiftAxis(const Vector3 &direction) {
bool TerrainRayWalker::startWalking(const Vector3 &start, Vector3 direction, double escape_angle,
TerrainHitResult &result) {
TerrainRenderer *terrain_renderer = renderer->getTerrainRenderer();
TexturesRenderer *textures_renderer = renderer->getTexturesRenderer();
auto terrain_renderer = renderer->getTerrainRenderer();
auto textures_renderer = renderer->getTexturesRenderer();
auto textures_definition = renderer->getScenery()->getTextures();
TerrainRenderer::TerrainResult terrain_result;
Vector3 cursor, displaced;
double diff;
@ -75,7 +76,7 @@ bool TerrainRayWalker::startWalking(const Vector3 &start, Vector3 direction, dou
cursor = previous_cursor.add(direction.scale(step_length));
// Get the terrain info at end (without textures displacement)
terrain_result = terrain_renderer->getResult(cursor.x, cursor.z, true, false);
terrain_result = terrain_renderer->getResult(cursor.x, cursor.z, true);
diff = cursor.y - terrain_result.location.y;
// If we are very under the terrain, consider a hit
@ -85,7 +86,8 @@ bool TerrainRayWalker::startWalking(const Vector3 &start, Vector3 direction, dou
// If we are close enough to the terrain, apply displacement
else if (diff < displacement_base * displacement_safety) {
displaced = textures_renderer->displaceTerrain(terrain_result);
displaced =
textures_renderer->displaceTerrain(textures_definition, terrain_result.location, terrain_result.normal);
diff = cursor.y - displaced.y;
hit = diff < 0.0;
}

View file

@ -5,6 +5,7 @@
#include "Scenery.h"
#include "TerrainDefinition.h"
#include "TexturesRenderer.h"
#include "TexturesDefinition.h"
#include "LightComponent.h"
#include "TerrainRayWalker.h"
#include "RayCastingResult.h"
@ -40,27 +41,7 @@ double TerrainRenderer::getHeight(double x, double z, bool with_painting, bool w
return parent->getScenery()->getTerrain()->getInterpolatedHeight(x, z, true, with_painting, water_offset);
}
static inline Vector3 _getNormal4(Vector3 center, Vector3 north, Vector3 east, Vector3 south, Vector3 west) {
Vector3 dnorth, deast, dsouth, dwest, normal;
dnorth = north.sub(center);
deast = east.sub(center);
dsouth = south.sub(center);
dwest = west.sub(center);
normal = deast.crossProduct(dnorth);
normal = normal.add(dsouth.crossProduct(deast));
normal = normal.add(dwest.crossProduct(dsouth));
normal = normal.add(dnorth.crossProduct(dwest));
return normal.normalize();
}
static inline Vector3 _getNormal2(Vector3 center, Vector3 east, Vector3 south) {
return south.sub(center).crossProduct(east.sub(center)).normalize();
}
TerrainRenderer::TerrainResult TerrainRenderer::getResult(double x, double z, bool with_painting, bool with_textures) {
TerrainRenderer::TerrainResult TerrainRenderer::getResult(double x, double z, bool with_painting) {
TerrainResult result;
double offset = 0.001;
@ -88,43 +69,65 @@ TerrainRenderer::TerrainResult TerrainRenderer::getResult(double x, double z, bo
north.z = z - offset;
north.y = getHeight(north.x, north.z, with_painting);
result.normal = _getNormal4(center, north, east, south, west);
result.normal = center.getNormal5(south, north, east, west);
} else {
result.normal = _getNormal2(center, east, south);
result.normal = center.getNormal3(south, east);
}
/* Location */
result.location = center;
/* Texture displacement */
if (with_textures) {
center = parent->getTexturesRenderer()->displaceTerrain(result);
result.location = center;
/* Recompute normal */
if (parent->render_quality > 6) {
/* Use 5 points on displaced terrain */
east = parent->getTexturesRenderer()->displaceTerrain(getResult(east.x, east.z, with_painting, 0));
south = parent->getTexturesRenderer()->displaceTerrain(getResult(south.x, south.z, with_painting, 0));
west = parent->getTexturesRenderer()->displaceTerrain(getResult(west.x, west.z, with_painting, 0));
north = parent->getTexturesRenderer()->displaceTerrain(getResult(north.x, north.z, with_painting, 0));
result.normal = _getNormal4(center, north, east, south, west);
} else {
/* Use 3 points on displaced terrain */
east = parent->getTexturesRenderer()->displaceTerrain(getResult(east.x, east.z, with_painting, 0));
south = parent->getTexturesRenderer()->displaceTerrain(getResult(south.x, south.z, with_painting, 0));
result.normal = _getNormal2(center, east, south);
}
}
return result;
}
Color TerrainRenderer::getFinalColor(const Vector3 &location, double precision) {
TexturesRenderer::TexturesResult textures = parent->getTexturesRenderer()->applyToTerrain(location.x, location.z, precision);
return parent->applyMediumTraversal(textures.final_location, textures.final_color);
Vector3 TerrainRenderer::getDisplaced(double x, double z, bool with_painting) {
auto terrain = getResult(x, z, with_painting);
auto textures = parent->getScenery()->getTextures();
return parent->getTexturesRenderer()->displaceTerrain(textures, terrain.location, terrain.normal);
}
static inline pair<vector<double>, vector<Vector3>> _getTexturesInfo(TerrainRenderer *terrain_renderer,
TexturesRenderer *textures_renderer, double x,
double z,
TexturesDefinition *textures_definition) {
auto terrain = terrain_renderer->getResult(x, z, true);
auto presence = textures_renderer->getLayersPresence(textures_definition, terrain.location, terrain.normal);
auto displaced =
textures_renderer->getLayersDisplacement(textures_definition, terrain.location, terrain.normal, presence);
return pair<vector<double>, vector<Vector3>>(presence, displaced);
}
Color TerrainRenderer::getFinalColor(double x, double z, double precision) {
auto textures_renderer = parent->getTexturesRenderer();
auto textures_definition = parent->getScenery()->getTextures();
if (textures_definition->getLayerCount() == 0) {
return COLOR_BLACK;
} else {
auto current = _getTexturesInfo(this, textures_renderer, x, z, textures_definition);
auto top_location = current.second.back();
vector<Vector3> normal;
int i = 0;
// TODO Use getNormal5 on high-quality renders
double offset = 0.0001;
auto east = _getTexturesInfo(this, textures_renderer, x + offset, z, textures_definition);
auto south = _getTexturesInfo(this, textures_renderer, x, z + offset, textures_definition);
for (auto layer_presence : current.first) {
if (layer_presence > 0.0) {
normal.push_back(current.second[i].getNormal3(south.second[i], east.second[i]));
} else {
normal.push_back(VECTOR_ZERO);
}
i++;
}
auto color = textures_renderer->getFinalComposition(textures_definition, parent->getLightingManager(),
current.first, current.second, normal, precision,
parent->getCameraLocation(top_location));
return parent->applyMediumTraversal(top_location, color);
}
}
RayCastingResult TerrainRenderer::castRay(const Vector3 &start, const Vector3 &direction) {
@ -133,7 +136,8 @@ RayCastingResult TerrainRenderer::castRay(const Vector3 &start, const Vector3 &d
if (walker_ray->startWalking(start, direction.normalize(), 0.0, walk_result)) {
result.hit = true;
result.hit_location = walk_result.hit_location;
result.hit_color = getFinalColor(walk_result.hit_location, parent->getPrecision(walk_result.hit_location));
result.hit_color = getFinalColor(walk_result.hit_location.x, walk_result.hit_location.z,
parent->getPrecision(walk_result.hit_location));
} else {
result.hit = false;
}

View file

@ -5,8 +5,8 @@
#include "LightFilter.h"
#include <vector>
#include "Vector3.h"
#include "Color.h"
namespace paysages {
namespace software {
@ -28,8 +28,8 @@ class SOFTWARESHARED_EXPORT TerrainRenderer : public LightFilter {
virtual RayCastingResult castRay(const Vector3 &start, const Vector3 &direction);
virtual double getHeight(double x, double z, bool with_painting, bool water_offset = true);
virtual TerrainResult getResult(double x, double z, bool with_painting, bool with_textures);
virtual Color getFinalColor(const Vector3 &location, double precision);
virtual TerrainResult getResult(double x, double z, bool with_painting);
Vector3 getDisplaced(double x, double z, bool with_painting);
virtual bool applyLightFilter(LightComponent &light, const Vector3 &at) override;
/**
@ -37,6 +37,13 @@ class SOFTWARESHARED_EXPORT TerrainRenderer : public LightFilter {
*/
void estimateMinMaxHeight(double x1, double z1, double x2, double z2, double *ymin, double *ymax);
/**
* Get the final color at a given terrain location.
*
* Textures will be applied (with displacement and detail), and medium traversal will be performed at the location.
*/
virtual Color getFinalColor(double x, double z, double precision);
private:
SoftwareRenderer *parent;
TerrainRayWalker *walker_ray;

View file

@ -1,33 +1,33 @@
#include "TexturesRenderer.h"
#include <cassert>
#include <cmath>
#include "Scenery.h"
#include "SoftwareRenderer.h"
#include "TextureLayerDefinition.h"
#include "TexturesDefinition.h"
#include "Zone.h"
#include "LightingManager.h"
#include "NoiseNode.h"
#include "FractalNoise.h"
#include "NoiseGenerator.h"
TexturesRenderer::TexturesRenderer(SoftwareRenderer *parent) : parent(parent) {
TexturesRenderer::TexturesRenderer() {
setQualityFactor(0.5);
}
TexturesRenderer::~TexturesRenderer() {
}
void TexturesRenderer::update() {
void TexturesRenderer::setQuality(bool normal5) {
quality_normal5 = normal5;
}
/*
* Get the base presence factor of a layer, not accounting for other layers.
*/
double TexturesRenderer::getLayerBasePresence(TextureLayerDefinition *layer,
const TerrainRenderer::TerrainResult &terrain) {
return layer->terrain_zone->getValue(terrain.location, terrain.normal);
void TexturesRenderer::setQualityFactor(double factor) {
setQuality(factor > 0.6);
}
double TexturesRenderer::getMaximalDisplacement(TexturesDefinition *textures) {
double TexturesRenderer::getMaximalDisplacement(TexturesDefinition *textures) const {
int i, n;
double disp = 0.0;
n = textures->getLayerCount();
@ -41,28 +41,8 @@ double TexturesRenderer::getMaximalDisplacement(TexturesDefinition *textures) {
return disp;
}
static inline Vector3 _getNormal4(Vector3 center, Vector3 north, Vector3 east, Vector3 south, Vector3 west) {
Vector3 dnorth, deast, dsouth, dwest, normal;
dnorth = north.sub(center);
deast = east.sub(center);
dsouth = south.sub(center);
dwest = west.sub(center);
normal = deast.crossProduct(dnorth);
normal = normal.add(dsouth.crossProduct(deast));
normal = normal.add(dwest.crossProduct(dsouth));
normal = normal.add(dnorth.crossProduct(dwest));
return normal.normalize();
}
static inline Vector3 _getNormal2(Vector3 center, Vector3 east, Vector3 south) {
return south.sub(center).crossProduct(east.sub(center)).normalize();
}
static Vector3 _getDetailNormal(SoftwareRenderer *renderer, Vector3 base_location, Vector3 base_normal,
TextureLayerDefinition *layer, double precision) {
static Vector3 _getDetailNormal(Vector3 base_location, Vector3 base_normal, TextureLayerDefinition *layer,
double precision, bool normal5) {
Vector3 result;
/* Find guiding vectors in the appoximated local plane */
@ -90,16 +70,16 @@ static Vector3 _getDetailNormal(SoftwareRenderer *renderer, Vector3 base_locatio
south = base_location.add(dy.scale(offset));
south = south.add(base_normal.scale(detail_noise->getTriplanar(detail, south, base_normal)));
if (renderer->render_quality > 6) {
if (normal5) {
west = base_location.add(dx.scale(-offset));
west = west.add(base_normal.scale(detail_noise->getTriplanar(detail, west, base_normal)));
north = base_location.add(dy.scale(-offset));
north = north.add(base_normal.scale(detail_noise->getTriplanar(detail, north, base_normal)));
result = _getNormal4(center, north, east, south, west);
result = center.getNormal5(south, north, east, west);
} else {
result = _getNormal2(center, east, south);
result = center.getNormal3(south, east);
}
if (result.dotProduct(base_normal) < 0.0) {
@ -108,8 +88,24 @@ static Vector3 _getDetailNormal(SoftwareRenderer *renderer, Vector3 base_locatio
return result;
}
Vector3 TexturesRenderer::displaceTerrain(const TerrainRenderer::TerrainResult &terrain) {
TexturesDefinition *textures = parent->getScenery()->getTextures();
static inline double _getLayerPresence(TextureLayerDefinition *layer, const Vector3 &location, const Vector3 &normal) {
return layer->terrain_zone->getValue(location, normal);
}
static inline double _getLayerDisplacement(TextureLayerDefinition *layer, const Vector3 &location,
const Vector3 &normal, double presence) {
auto noise = layer->propDisplacementNoise()->getGenerator();
return noise->getTriplanar(0.001, location, normal) * presence;
}
static inline double _getLayerDisplacement(TextureLayerDefinition *layer, const Vector3 &location,
const Vector3 &normal) {
double presence = _getLayerPresence(layer, location, normal);
return _getLayerDisplacement(layer, location, normal, presence);
}
Vector3 TexturesRenderer::displaceTerrain(const TexturesDefinition *textures, const Vector3 &location,
const Vector3 &normal) const {
double offset = 0.0;
int i, n;
@ -118,64 +114,68 @@ Vector3 TexturesRenderer::displaceTerrain(const TerrainRenderer::TerrainResult &
TextureLayerDefinition *layer = textures->getTextureLayer(i);
if (layer->hasDisplacement()) {
double presence = getLayerBasePresence(layer, terrain);
auto noise = layer->propDisplacementNoise()->getGenerator();
offset += noise->getTriplanar(0.001, terrain.location, terrain.normal) * presence;
offset += _getLayerDisplacement(layer, location, normal);
}
}
return terrain.location.add(terrain.normal.normalize().scale(offset));
return location.add(normal.scale(offset));
}
double TexturesRenderer::getBasePresence(int layer, const TerrainRenderer::TerrainResult &terrain) {
TextureLayerDefinition *layerdef = parent->getScenery()->getTextures()->getTextureLayer(layer);
return getLayerBasePresence(layerdef, terrain);
}
TexturesRenderer::TexturesResult TexturesRenderer::applyToTerrain(double x, double z, double precision) {
TexturesDefinition *textures = parent->getScenery()->getTextures();
TexturesResult result;
// Displacement
// FIXME
TerrainRenderer::TerrainResult raw_terrain = parent->getTerrainRenderer()->getResult(x, z, true, false);
TerrainRenderer::TerrainResult terrain = parent->getTerrainRenderer()->getResult(x, z, true, true);
// TODO Displaced textures had their presence already computed before, store that result and use it
// Find presence of each layer
vector<double> TexturesRenderer::getLayersPresence(const TexturesDefinition *textures, const Vector3 &location,
const Vector3 &normal) const {
int n = textures->getLayerCount();
int start = 0;
vector<double> result;
for (int i = 0; i < n; i++) {
TexturesLayerResult &layer = result.layers[i];
layer.definition = textures->getTextureLayer(i);
layer.presence = getBasePresence(i, raw_terrain);
if (layer.presence > 0.9999) {
start = i;
TextureLayerDefinition *layer = textures->getTextureLayer(i);
double presence = _getLayerPresence(layer, location, normal);
result.push_back(presence);
}
return result;
}
vector<Vector3> TexturesRenderer::getLayersDisplacement(const TexturesDefinition *textures, const Vector3 &location,
const Vector3 &normal, const vector<double> &presence) const {
int n = textures->getLayerCount();
assert(presence.size() == to_size(n));
vector<Vector3> result;
Vector3 displaced = location;
for (int i = 0; i < n; i++) {
double layer_presence = presence[i];
if (layer_presence > 0.0) {
TextureLayerDefinition *layer = textures->getTextureLayer(i);
double displacement = _getLayerDisplacement(layer, location, normal, layer_presence);
displaced = displaced.add(normal.scale(displacement * layer_presence));
}
result.push_back(displaced);
}
return result;
}
Color TexturesRenderer::getFinalComposition(const TexturesDefinition *textures, LightingManager *lighting,
const vector<double> &presence, const vector<Vector3> &location,
const vector<Vector3> &normal, double precision, const Vector3 &eye) const {
int n = textures->getLayerCount();
assert(presence.size() == to_size(n));
assert(location.size() == to_size(n));
assert(normal.size() == to_size(n));
Color result = COLOR_BLACK;
// TODO share the same lighting status (no need to recompute shadows)
int i;
for (i = n - 1; i > 0; i--) {
// Start at the top-most covering layer (layers underneath are only important for displacement, not color)
if (presence[i] > 0.99999) {
break;
}
}
for (i = 0; i < n; i++) {
double layer_presence = presence[i];
if (layer_presence > 0.0) {
TextureLayerDefinition *layer = textures->getTextureLayer(i);
auto detail_normal = _getDetailNormal(location[i], normal[i], layer, precision, quality_normal5);
Color layer_color = lighting->apply(eye, location[i], detail_normal, *layer->material);
layer_color.a *= layer_presence;
result.mask(layer_color);
}
}
}
result.layer_count = n;
result.base_location = terrain.location;
result.base_normal = terrain.normal;
result.final_location = terrain.location;
result.final_color = COLOR_GREEN;
// Compute and merge colors of visible layers
for (int i = start; i < n; i++) {
TexturesLayerResult &layer = result.layers[i];
if (layer.presence > 0.0) {
Vector3 normal = _getDetailNormal(parent, terrain.location, terrain.normal, layer.definition, precision);
Vector3 location(x, terrain.location.y, z);
layer.color = parent->applyLightingToSurface(location, normal, *layer.definition->material);
layer.color.a = layer.presence;
result.final_color.mask(layer.color);
} else {
layer.color = COLOR_TRANSPARENT;
}
}
return result;
}

View file

@ -3,45 +3,66 @@
#include "software_global.h"
#include "TerrainRenderer.h"
#define TEXTURES_MAX_LAYERS 50
#include <vector>
namespace paysages {
namespace software {
class SOFTWARESHARED_EXPORT TexturesRenderer {
public:
typedef struct {
TextureLayerDefinition *definition;
double presence;
Color color;
} TexturesLayerResult;
typedef struct {
Vector3 base_location;
Vector3 base_normal;
int layer_count;
TexturesLayerResult layers[TEXTURES_MAX_LAYERS];
Vector3 final_location;
Color final_color;
} TexturesResult;
public:
TexturesRenderer(SoftwareRenderer *parent);
TexturesRenderer();
virtual ~TexturesRenderer();
virtual void update();
/**
* Set the quality parameters.
*
* "normal5" can be set to true to use balanced 5 points instead of unbalanced 3 points for normal computations.
*/
void setQuality(bool normal5);
virtual double getMaximalDisplacement(TexturesDefinition *textures);
virtual double getLayerBasePresence(TextureLayerDefinition *layer, const TerrainRenderer::TerrainResult &terrain);
/**
* Set an automated quality factor.
*/
void setQualityFactor(double factor);
virtual Vector3 displaceTerrain(const TerrainRenderer::TerrainResult &terrain);
virtual double getBasePresence(int layer, const TerrainRenderer::TerrainResult &terrain);
virtual TexturesResult applyToTerrain(double x, double z, double precision);
/**
* Get the maximal displacement offset all combined textures can make.
*/
double getMaximalDisplacement(TexturesDefinition *textures) const;
/**
* Get the fully displaced terrain location (applying all textures).
*/
Vector3 displaceTerrain(const TexturesDefinition *textures, const Vector3 &location, const Vector3 &normal) const;
/**
* Get the presence of each texture layer at a given terrain location.
*/
vector<double> getLayersPresence(const TexturesDefinition *textures, const Vector3 &location,
const Vector3 &normal) const;
/**
* Get the displaced location of each texture layer at a given terrain location.
*
* 'presence' is the result of 'getLayersPresence'.
*/
vector<Vector3> getLayersDisplacement(const TexturesDefinition *textures, const Vector3 &location,
const Vector3 &normal, const vector<double> &presence) const;
/**
* Get the final lighted texture composition.
*
* 'presence' is the result of 'getLayersPresence'.
* 'location' is the result of 'getLayersDisplacement'.
* 'normal' is the normal vector (taking only displacement into account, not detail) at each texture's 'location'.
* 'precision' is the level of detail needed for the composition (minimal height of the detail noise).
*/
Color getFinalComposition(const TexturesDefinition *textures, LightingManager *lighting,
const vector<double> &presence, const vector<Vector3> &location,
const vector<Vector3> &normal, double precision, const Vector3 &eye) const;
private:
SoftwareRenderer *parent;
bool quality_normal5;
};
}
}

View file

@ -55,9 +55,10 @@ RayCastingResult VegetationRenderer::renderInstance(const SpaceSegment &segment,
bool only_hit, bool displaced) {
if (!displaced) {
// Recursive call on displaced instance
const Vector3 &base = instance.getBase();
TerrainRenderer::TerrainResult terrain = parent->getTerrainRenderer()->getResult(base.x, base.z, true, true);
VegetationInstance displaced_instance = instance.displace(terrain.location, terrain.normal);
auto base = instance.getBase();
auto terrain = parent->getTerrainRenderer()->getResult(base.x, base.z, true);
auto displaced = parent->getTerrainRenderer()->getDisplaced(base.x, base.z, true);
auto displaced_instance = instance.displace(displaced, terrain.normal);
return renderInstance(segment, displaced_instance, only_hit, true);
}

25
src/tests/TestToolNoise.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef TESTTOOLNOISE_H
#define TESTTOOLNOISE_H
#include "FractalNoise.h"
namespace {
/**
* Fractal noise that produces the same value anywhere.
*/
class ConstantFractalNoise : public FractalNoise {
public:
ConstantFractalNoise(double value) : value(value) {
// The noise will yield its value at first iteration, then its height will collapse to 0
setScaling(1.0, 0.0);
}
virtual double getBase3d(double, double, double) const {
return value;
}
private:
double value;
};
}
#endif // TESTTOOLNOISE_H

View file

@ -0,0 +1,42 @@
#include "BaseTestCase.h"
#include "TexturesRenderer.h"
#include "Vector3.h"
#include "TexturesDefinition.h"
#include "TextureLayerDefinition.h"
#include "Zone.h"
TEST(TexturesRenderer, getLayersPresence) {
TexturesRenderer renderer;
TexturesDefinition textures(NULL);
vector<double> result;
result = renderer.getLayersPresence(&textures, VECTOR_ZERO, VECTOR_UP);
ASSERT_EQ(0u, result.size());
TextureLayerDefinition layer1(NULL, "t1");
textures.addLayer(layer1);
result = renderer.getLayersPresence(&textures, VECTOR_ZERO, VECTOR_UP);
ASSERT_EQ(1u, result.size());
EXPECT_DOUBLE_EQ(1.0, result[0]);
TextureLayerDefinition layer2(NULL, "t2");
layer2.terrain_zone->addHeightRangeQuick(0.8, 0.0, 1.0, 2.0, 3.0);
textures.addLayer(layer2);
result = renderer.getLayersPresence(&textures, VECTOR_ZERO, VECTOR_UP);
ASSERT_EQ(2u, result.size());
EXPECT_DOUBLE_EQ(1.0, result[0]);
EXPECT_DOUBLE_EQ(0.0, result[1]);
result = renderer.getLayersPresence(&textures, VECTOR_UP.scale(0.5), VECTOR_UP);
ASSERT_EQ(2u, result.size());
EXPECT_DOUBLE_EQ(1.0, result[0]);
EXPECT_DOUBLE_EQ(0.4, result[1]);
result = renderer.getLayersPresence(&textures, VECTOR_UP, VECTOR_UP);
ASSERT_EQ(2u, result.size());
EXPECT_DOUBLE_EQ(1.0, result[0]);
EXPECT_DOUBLE_EQ(0.8, result[1]);
}

View file

@ -20,3 +20,11 @@ TEST(Vector3, randomInSphere) {
v = Vector3::randomInSphere(0.5, true);
EXPECT_DOUBLE_EQ(v.getNorm(), 0.5);
}
TEST(Vector3, getNormal3) {
EXPECT_VECTOR3_COORDS(VECTOR_ZERO.getNormal3(VECTOR_SOUTH, VECTOR_EAST), 0.0, 1.0, 0.0);
}
TEST(Vector3, getNormal5) {
EXPECT_VECTOR3_COORDS(VECTOR_ZERO.getNormal5(VECTOR_SOUTH, VECTOR_NORTH, VECTOR_EAST, VECTOR_WEST), 0.0, 1.0, 0.0);
}