Added "backface culling" and "previous fragment" in rasterizers
Backface culling speeds up rasterization Previous fragment will be used later by vegetation rasterizer
This commit is contained in:
parent
ac1b6a909b
commit
52bad18d26
18 changed files with 168 additions and 51 deletions
|
@ -1,7 +1,7 @@
|
||||||
#include "CanvasFragment.h"
|
#include "CanvasFragment.h"
|
||||||
|
|
||||||
CanvasFragment::CanvasFragment(double z, const Vector3 &location, int client, bool opaque):
|
CanvasFragment::CanvasFragment(bool front_facing, const Vector3 &pixel, const Vector3 &location, int client, bool opaque):
|
||||||
opaque(opaque), z(z), location(location), client(client)
|
opaque(opaque), front_facing(front_facing), pixel(pixel), location(location), client(client)
|
||||||
{
|
{
|
||||||
color = COLOR_WHITE;
|
color = COLOR_WHITE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,19 +16,22 @@ class SOFTWARESHARED_EXPORT CanvasFragment
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CanvasFragment() = default;
|
CanvasFragment() = default;
|
||||||
CanvasFragment(double z, const Vector3 &location, int client=0, bool opaque=true);
|
CanvasFragment(bool front_facing, const Vector3 &pixel, const Vector3 &location, int client=0, bool opaque=true);
|
||||||
|
|
||||||
void setColor(const Color &col);
|
void setColor(const Color &col);
|
||||||
|
|
||||||
inline bool getOpaque() const {return opaque;}
|
inline bool getOpaque() const {return opaque;}
|
||||||
inline double getZ() const {return z;}
|
inline bool isFrontFacing() const {return front_facing;}
|
||||||
|
inline double getZ() const {return pixel.z;}
|
||||||
inline const Vector3 &getLocation() const {return location;}
|
inline const Vector3 &getLocation() const {return location;}
|
||||||
|
inline const Vector3 &getPixel() const {return pixel;}
|
||||||
inline int getClient() const {return client;}
|
inline int getClient() const {return client;}
|
||||||
inline const Color &getColor() const {return color;}
|
inline const Color &getColor() const {return color;}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool opaque;
|
bool opaque;
|
||||||
double z;
|
bool front_facing;
|
||||||
|
Vector3 pixel;
|
||||||
Vector3 location;
|
Vector3 location;
|
||||||
int client;
|
int client;
|
||||||
Color color;
|
Color color;
|
||||||
|
|
|
@ -35,7 +35,7 @@ void CanvasPixel::pushFragment(const CanvasFragment &fragment)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (fragments[0].getOpaque() and fragment.getZ() < fragments[0].getZ())
|
if (fragments[0].getOpaque() and fragment.getZ() <= fragments[0].getZ())
|
||||||
{
|
{
|
||||||
// behind opaque fragment, don't bother
|
// behind opaque fragment, don't bother
|
||||||
return;
|
return;
|
||||||
|
@ -43,11 +43,17 @@ void CanvasPixel::pushFragment(const CanvasFragment &fragment)
|
||||||
|
|
||||||
// find expected position
|
// find expected position
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (i < count and fragment.getZ() > fragments[i].getZ())
|
while (i < count and fragment.getZ() >= fragments[i].getZ())
|
||||||
{
|
{
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (i > 0 and fragments[i - 1].getZ() == fragment.getZ() and fragments[i - 1].getClient() == fragment.getClient())
|
||||||
|
{
|
||||||
|
// Pixel already pushed by same client, don't do anything
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (fragment.getOpaque())
|
if (fragment.getOpaque())
|
||||||
{
|
{
|
||||||
// Discard fragments masked by the incoming opaque one
|
// Discard fragments masked by the incoming opaque one
|
||||||
|
@ -62,28 +68,32 @@ void CanvasPixel::pushFragment(const CanvasFragment &fragment)
|
||||||
}
|
}
|
||||||
fragments[0] = fragment;
|
fragments[0] = fragment;
|
||||||
}
|
}
|
||||||
else if (i < count)
|
|
||||||
{
|
|
||||||
// Need to make room for the incoming fragment
|
|
||||||
if (count < MAX_FRAGMENT_COUNT)
|
|
||||||
{
|
|
||||||
memmove(fragments + i + 1, fragments + i, sizeof(CanvasFragment) * (count - i));
|
|
||||||
fragments[i] = fragment;
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (count == MAX_FRAGMENT_COUNT)
|
// Transparent pixel
|
||||||
|
if (i < count)
|
||||||
{
|
{
|
||||||
// Replace nearest fragment
|
// Need to make room for the incoming fragment
|
||||||
fragments[count - 1] = fragment;
|
if (count < MAX_FRAGMENTS_PER_PIXEL)
|
||||||
|
{
|
||||||
|
memmove(fragments + i + 1, fragments + i, sizeof(CanvasFragment) * (count - i));
|
||||||
|
fragments[i] = fragment;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Append
|
if (count == MAX_FRAGMENTS_PER_PIXEL)
|
||||||
fragments[count] = fragment;
|
{
|
||||||
count++;
|
// Replace nearest fragment
|
||||||
|
fragments[count - 1] = fragment;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Append
|
||||||
|
fragments[count] = fragment;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
#include "CanvasFragment.h"
|
#include "CanvasFragment.h"
|
||||||
|
|
||||||
const int MAX_FRAGMENT_COUNT = 2;
|
const int MAX_FRAGMENTS_PER_PIXEL = 2;
|
||||||
|
|
||||||
namespace paysages {
|
namespace paysages {
|
||||||
namespace software {
|
namespace software {
|
||||||
|
@ -32,7 +32,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int count;
|
int count;
|
||||||
CanvasFragment fragments[MAX_FRAGMENT_COUNT];
|
CanvasFragment fragments[MAX_FRAGMENTS_PER_PIXEL];
|
||||||
Color composite;
|
Color composite;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -45,11 +45,13 @@ void CanvasPixelShader::processParallelUnit(int unit)
|
||||||
const CanvasPixel &pixel = portion->at(base_x + x, base_y + y);
|
const CanvasPixel &pixel = portion->at(base_x + x, base_y + y);
|
||||||
int n = pixel.getFragmentCount();
|
int n = pixel.getFragmentCount();
|
||||||
Color composite = COLOR_BLACK;
|
Color composite = COLOR_BLACK;
|
||||||
|
const CanvasFragment *previous = NULL;
|
||||||
for (int i = 0; i < n; i++)
|
for (int i = 0; i < n; i++)
|
||||||
{
|
{
|
||||||
const CanvasFragment &fragment = pixel.getFragment(i);
|
const CanvasFragment &fragment = pixel.getFragment(i);
|
||||||
const Rasterizer &rasterizer = renderer.getRasterizer(fragment.getClient());
|
const Rasterizer &rasterizer = renderer.getRasterizer(fragment.getClient());
|
||||||
composite.mask(rasterizer.shadeFragment(fragment));
|
composite.mask(rasterizer.shadeFragment(fragment, previous));
|
||||||
|
previous = &fragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill the square area
|
// Fill the square area
|
||||||
|
|
|
@ -22,6 +22,7 @@ struct paysages::software::ScanPoint
|
||||||
double z;
|
double z;
|
||||||
} location;
|
} location;
|
||||||
int client;
|
int client;
|
||||||
|
bool front_facing;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct paysages::software::RenderScanlines
|
struct paysages::software::RenderScanlines
|
||||||
|
@ -38,6 +39,7 @@ Rasterizer::Rasterizer(SoftwareRenderer* renderer, RenderProgress *progress, int
|
||||||
this->color = new Color(color);
|
this->color = new Color(color);
|
||||||
|
|
||||||
interrupted = false;
|
interrupted = false;
|
||||||
|
backface_culling = false;
|
||||||
triangle_count = 0;
|
triangle_count = 0;
|
||||||
auto_cut_limit = 0.01;
|
auto_cut_limit = 0.01;
|
||||||
|
|
||||||
|
@ -58,6 +60,16 @@ void Rasterizer::setQuality(double)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Rasterizer::setColor(const Color &color)
|
||||||
|
{
|
||||||
|
*this->color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rasterizer::setBackFaceCulling(bool cull)
|
||||||
|
{
|
||||||
|
this->backface_culling = cull;
|
||||||
|
}
|
||||||
|
|
||||||
void Rasterizer::setAutoCutLimit(double limit)
|
void Rasterizer::setAutoCutLimit(double limit)
|
||||||
{
|
{
|
||||||
this->auto_cut_limit = limit;
|
this->auto_cut_limit = limit;
|
||||||
|
@ -92,6 +104,14 @@ bool Rasterizer::pushProjectedTriangle(CanvasPortion *canvas, const Vector3 &pix
|
||||||
return location1.sub(location2).getNorm() > auto_cut_limit && location2.sub(location3).getNorm() > auto_cut_limit && location3.sub(location1).getNorm() > auto_cut_limit;
|
return location1.sub(location2).getNorm() > auto_cut_limit && location2.sub(location3).getNorm() > auto_cut_limit && location3.sub(location1).getNorm() > auto_cut_limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check the poylgon's facing (front-face or back-face)
|
||||||
|
Vector3 normal = dpixel2.sub(dpixel1).crossProduct(dpixel3.sub(dpixel1));
|
||||||
|
bool front_facing = (normal.z >= 0.0);
|
||||||
|
if (backface_culling and not front_facing)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare vertices
|
// Prepare vertices
|
||||||
point1.pixel.x = dpixel1.x;
|
point1.pixel.x = dpixel1.x;
|
||||||
point1.pixel.y = dpixel1.y;
|
point1.pixel.y = dpixel1.y;
|
||||||
|
@ -100,6 +120,7 @@ bool Rasterizer::pushProjectedTriangle(CanvasPortion *canvas, const Vector3 &pix
|
||||||
point1.location.y = location1.y;
|
point1.location.y = location1.y;
|
||||||
point1.location.z = location1.z;
|
point1.location.z = location1.z;
|
||||||
point1.client = client_id;
|
point1.client = client_id;
|
||||||
|
point1.front_facing = front_facing;
|
||||||
|
|
||||||
point2.pixel.x = dpixel2.x;
|
point2.pixel.x = dpixel2.x;
|
||||||
point2.pixel.y = dpixel2.y;
|
point2.pixel.y = dpixel2.y;
|
||||||
|
@ -108,6 +129,7 @@ bool Rasterizer::pushProjectedTriangle(CanvasPortion *canvas, const Vector3 &pix
|
||||||
point2.location.y = location2.y;
|
point2.location.y = location2.y;
|
||||||
point2.location.z = location2.z;
|
point2.location.z = location2.z;
|
||||||
point2.client = client_id;
|
point2.client = client_id;
|
||||||
|
point2.front_facing = front_facing;
|
||||||
|
|
||||||
point3.pixel.x = dpixel3.x;
|
point3.pixel.x = dpixel3.x;
|
||||||
point3.pixel.y = dpixel3.y;
|
point3.pixel.y = dpixel3.y;
|
||||||
|
@ -116,6 +138,7 @@ bool Rasterizer::pushProjectedTriangle(CanvasPortion *canvas, const Vector3 &pix
|
||||||
point3.location.y = location3.y;
|
point3.location.y = location3.y;
|
||||||
point3.location.z = location3.z;
|
point3.location.z = location3.z;
|
||||||
point3.client = client_id;
|
point3.client = client_id;
|
||||||
|
point3.front_facing = front_facing;
|
||||||
|
|
||||||
// Prepare scanlines
|
// Prepare scanlines
|
||||||
// TODO Don't create scanlines for each triangles (one by thread is more appropriate)
|
// TODO Don't create scanlines for each triangles (one by thread is more appropriate)
|
||||||
|
@ -167,6 +190,7 @@ void Rasterizer::pushDisplacedTriangle(CanvasPortion *canvas, const Vector3 &v1,
|
||||||
{
|
{
|
||||||
Vector3 p1, p2, p3;
|
Vector3 p1, p2, p3;
|
||||||
|
|
||||||
|
// TODO v1, v2 and v3 are lost, but may be useful (avoid need to unproject)
|
||||||
p1 = getRenderer()->projectPoint(v1);
|
p1 = getRenderer()->projectPoint(v1);
|
||||||
p2 = getRenderer()->projectPoint(v2);
|
p2 = getRenderer()->projectPoint(v2);
|
||||||
p3 = getRenderer()->projectPoint(v3);
|
p3 = getRenderer()->projectPoint(v3);
|
||||||
|
@ -225,6 +249,7 @@ void Rasterizer::scanInterpolate(CameraDefinition* camera, ScanPoint* v1, ScanPo
|
||||||
result->location.y = ((1.0 - value) * (v1->location.y * v1depth) + value * (v1->location.y + diff->location.y) * v2depth) * factor;
|
result->location.y = ((1.0 - value) * (v1->location.y * v1depth) + value * (v1->location.y + diff->location.y) * v2depth) * factor;
|
||||||
result->location.z = ((1.0 - value) * (v1->location.z * v1depth) + value * (v1->location.z + diff->location.z) * v2depth) * factor;
|
result->location.z = ((1.0 - value) * (v1->location.z * v1depth) + value * (v1->location.z + diff->location.z) * v2depth) * factor;
|
||||||
result->client = v1->client;
|
result->client = v1->client;
|
||||||
|
result->front_facing = v1->front_facing;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rasterizer::pushScanPoint(CanvasPortion* canvas, RenderScanlines* scanlines, ScanPoint* point)
|
void Rasterizer::pushScanPoint(CanvasPortion* canvas, RenderScanlines* scanlines, ScanPoint* point)
|
||||||
|
@ -397,13 +422,17 @@ void Rasterizer::renderScanLines(CanvasPortion *canvas, RenderScanlines* scanlin
|
||||||
scanInterpolate(renderer->render_camera, &down, &diff, fy / dy, ¤t);
|
scanInterpolate(renderer->render_camera, &down, &diff, fy / dy, ¤t);
|
||||||
}
|
}
|
||||||
|
|
||||||
CanvasFragment fragment(current.pixel.z, Vector3(current.location.x, current.location.y, current.location.z), current.client);
|
Vector3 pixel(current.pixel.x + canvas->getXOffset(), current.pixel.y + canvas->getYOffset(), current.pixel.z);
|
||||||
|
Vector3 location(current.location.x, current.location.y, current.location.z);
|
||||||
|
CanvasFragment fragment(current.front_facing, pixel, location, current.client, color->a == 1.0);
|
||||||
|
|
||||||
Color frag_color = *color;
|
Color frag_color = *color;
|
||||||
|
frag_color.a = 1.0;
|
||||||
if (cury == starty || cury == endy)
|
if (cury == starty || cury == endy)
|
||||||
{
|
{
|
||||||
frag_color.mask(Color(0.0, 0.0, 0.0, 0.3));
|
frag_color.mask(Color(0.0, 0.0, 0.0, 0.3));
|
||||||
}
|
}
|
||||||
|
frag_color.a = color->a;
|
||||||
fragment.setColor(frag_color);
|
fragment.setColor(frag_color);
|
||||||
|
|
||||||
canvas->pushFragment(current.x, current.y, fragment);
|
canvas->pushFragment(current.x, current.y, fragment);
|
||||||
|
|
|
@ -6,6 +6,10 @@
|
||||||
namespace paysages {
|
namespace paysages {
|
||||||
namespace software {
|
namespace software {
|
||||||
|
|
||||||
|
const int RASTERIZER_CLIENT_SKY = 0;
|
||||||
|
const int RASTERIZER_CLIENT_WATER = 1;
|
||||||
|
const int RASTERIZER_CLIENT_TERRAIN = 2;
|
||||||
|
|
||||||
typedef struct ScanPoint ScanPoint;
|
typedef struct ScanPoint ScanPoint;
|
||||||
typedef struct RenderScanlines RenderScanlines;
|
typedef struct RenderScanlines RenderScanlines;
|
||||||
|
|
||||||
|
@ -21,9 +25,6 @@ public:
|
||||||
inline SoftwareRenderer *getRenderer() const {return renderer;}
|
inline SoftwareRenderer *getRenderer() const {return renderer;}
|
||||||
inline int getTriangleCount() const {return triangle_count;}
|
inline int getTriangleCount() const {return triangle_count;}
|
||||||
|
|
||||||
virtual Color shadeFragment(const CanvasFragment &fragment) const = 0;
|
|
||||||
virtual void interrupt();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the rasterization quality factor.
|
* Set the rasterization quality factor.
|
||||||
*/
|
*/
|
||||||
|
@ -34,20 +35,34 @@ public:
|
||||||
*/
|
*/
|
||||||
void setAutoCutLimit(double limit);
|
void setAutoCutLimit(double limit);
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset the internal triangle counter to 0.
|
|
||||||
*/
|
|
||||||
void resetTriangleCount();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract method to prepare for the rasterization process, and return the estimated progress count.
|
* Abstract method to prepare for the rasterization process, and return the estimated progress count.
|
||||||
*/
|
*/
|
||||||
virtual int prepareRasterization() = 0;
|
virtual int prepareRasterization() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract method to effectively do the rasterization on a canvas.
|
* Abstract method to effectively do the rasterization on a canvas.
|
||||||
*/
|
*/
|
||||||
virtual void rasterizeToCanvas(CanvasPortion *canvas) = 0;
|
virtual void rasterizeToCanvas(CanvasPortion *canvas) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract method to render a fragment stored on a canvas, to a color.
|
||||||
|
*/
|
||||||
|
virtual Color shadeFragment(const CanvasFragment &fragment, const CanvasFragment *previous) const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ask for an interrupt in rasterization process.
|
||||||
|
*/
|
||||||
|
virtual void interrupt();
|
||||||
|
|
||||||
|
void setColor(const Color &color);
|
||||||
|
void setBackFaceCulling(bool cull);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the internal triangle counter to 0.
|
||||||
|
*/
|
||||||
|
void resetTriangleCount();
|
||||||
|
|
||||||
void pushTriangle(CanvasPortion *canvas, const Vector3 &v1, const Vector3 &v2, const Vector3 &v3);
|
void pushTriangle(CanvasPortion *canvas, const Vector3 &v1, const Vector3 &v2, const Vector3 &v3);
|
||||||
void pushDisplacedTriangle(CanvasPortion *canvas, const Vector3 &v1, const Vector3 &v2, const Vector3 &v3, const Vector3 &ov1, const Vector3 &ov2, const Vector3 &ov3);
|
void pushDisplacedTriangle(CanvasPortion *canvas, const Vector3 &v1, const Vector3 &v2, const Vector3 &v3, const Vector3 &ov1, const Vector3 &ov2, const Vector3 &ov3);
|
||||||
|
|
||||||
|
@ -72,6 +87,7 @@ private:
|
||||||
|
|
||||||
int triangle_count;
|
int triangle_count;
|
||||||
double auto_cut_limit;
|
double auto_cut_limit;
|
||||||
|
bool backface_culling;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,7 @@ void SkyRasterizer::rasterizeToCanvas(CanvasPortion* canvas)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Color SkyRasterizer::shadeFragment(const CanvasFragment &fragment) const
|
Color SkyRasterizer::shadeFragment(const CanvasFragment &fragment, const CanvasFragment *) const
|
||||||
{
|
{
|
||||||
Vector3 location = fragment.getLocation();
|
Vector3 location = fragment.getLocation();
|
||||||
Vector3 camera_location, direction;
|
Vector3 camera_location, direction;
|
||||||
|
|
|
@ -15,7 +15,7 @@ public:
|
||||||
|
|
||||||
virtual int prepareRasterization() override;
|
virtual int prepareRasterization() override;
|
||||||
virtual void rasterizeToCanvas(CanvasPortion* canvas) override;
|
virtual void rasterizeToCanvas(CanvasPortion* canvas) override;
|
||||||
virtual Color shadeFragment(const CanvasFragment &fragment) const override;
|
virtual Color shadeFragment(const CanvasFragment &fragment, const CanvasFragment *previous) const override;
|
||||||
|
|
||||||
void setQuality(int res_i, int res_j);
|
void setQuality(int res_i, int res_j);
|
||||||
virtual void setQuality(double factor) override;
|
virtual void setQuality(double factor) override;
|
||||||
|
|
|
@ -27,9 +27,9 @@ SoftwareCanvasRenderer::SoftwareCanvasRenderer(Scenery *scenery):
|
||||||
|
|
||||||
postprocess_enabled = true;
|
postprocess_enabled = true;
|
||||||
|
|
||||||
rasterizers.push_back(new SkyRasterizer(this, progress, 0));
|
rasterizers.push_back(new SkyRasterizer(this, progress, RASTERIZER_CLIENT_SKY));
|
||||||
rasterizers.push_back(new WaterRasterizer(this, progress, 1));
|
rasterizers.push_back(new WaterRasterizer(this, progress, RASTERIZER_CLIENT_WATER));
|
||||||
rasterizers.push_back(new TerrainRasterizer(this, progress, 2));
|
rasterizers.push_back(new TerrainRasterizer(this, progress, RASTERIZER_CLIENT_TERRAIN));
|
||||||
|
|
||||||
current_work = NULL;
|
current_work = NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -245,7 +245,7 @@ void TerrainRasterizer::rasterizeToCanvas(CanvasPortion *canvas)
|
||||||
performTessellation(canvas, false);
|
performTessellation(canvas, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Color TerrainRasterizer::shadeFragment(const CanvasFragment &fragment) const
|
Color TerrainRasterizer::shadeFragment(const CanvasFragment &fragment, const CanvasFragment *) const
|
||||||
{
|
{
|
||||||
Vector3 point = fragment.getLocation();
|
Vector3 point = fragment.getLocation();
|
||||||
double precision = renderer->getPrecision(_getPoint(renderer, point.x, point.z));
|
double precision = renderer->getPrecision(_getPoint(renderer, point.x, point.z));
|
||||||
|
|
|
@ -36,7 +36,7 @@ public:
|
||||||
|
|
||||||
virtual int prepareRasterization() override;
|
virtual int prepareRasterization() override;
|
||||||
virtual void rasterizeToCanvas(CanvasPortion* canvas) override;
|
virtual void rasterizeToCanvas(CanvasPortion* canvas) override;
|
||||||
virtual Color shadeFragment(const CanvasFragment &fragment) const override;
|
virtual Color shadeFragment(const CanvasFragment &fragment, const CanvasFragment *previous) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -43,7 +43,7 @@ void WaterRasterizer::rasterizeToCanvas(CanvasPortion *canvas)
|
||||||
performTessellation(canvas);
|
performTessellation(canvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
Color WaterRasterizer::shadeFragment(const CanvasFragment &fragment) const
|
Color WaterRasterizer::shadeFragment(const CanvasFragment &fragment, const CanvasFragment *) const
|
||||||
{
|
{
|
||||||
Vector3 location = fragment.getLocation();
|
Vector3 location = fragment.getLocation();
|
||||||
return renderer->getWaterRenderer()->getResult(location.x, location.z).final;
|
return renderer->getWaterRenderer()->getResult(location.x, location.z).final;
|
||||||
|
|
|
@ -17,7 +17,7 @@ public:
|
||||||
|
|
||||||
virtual int prepareRasterization() override;
|
virtual int prepareRasterization() override;
|
||||||
virtual void rasterizeToCanvas(CanvasPortion* canvas) override;
|
virtual void rasterizeToCanvas(CanvasPortion* canvas) override;
|
||||||
virtual Color shadeFragment(const CanvasFragment &fragment) const override;
|
virtual Color shadeFragment(const CanvasFragment &fragment, const CanvasFragment *previous) const override;
|
||||||
|
|
||||||
virtual void setQuality(double factor) override;
|
virtual void setQuality(double factor) override;
|
||||||
|
|
||||||
|
|
56
src/tests/CanvasPixel_Test.cpp
Normal file
56
src/tests/CanvasPixel_Test.cpp
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#include "BaseTestCase.h"
|
||||||
|
|
||||||
|
#include "CanvasPixel.h"
|
||||||
|
|
||||||
|
TEST(CanvasPixel, MaxFragments)
|
||||||
|
{
|
||||||
|
CanvasPixel pixel;
|
||||||
|
|
||||||
|
// One opaque fragment
|
||||||
|
pixel.pushFragment(CanvasFragment(true, Vector3(0.0, 0.0, 0.0), Vector3(), 0, true));
|
||||||
|
|
||||||
|
// Overflow max fragment count with transparent fragments
|
||||||
|
for (int i = 0; i < MAX_FRAGMENTS_PER_PIXEL * 2; i++)
|
||||||
|
{
|
||||||
|
pixel.pushFragment(CanvasFragment(true, Vector3(0.0, 0.0, (double)i), Vector3(), 0, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(MAX_FRAGMENTS_PER_PIXEL, pixel.getFragmentCount());
|
||||||
|
EXPECT_DOUBLE_EQ(pixel.getFragment(0).getZ(), 0.0);
|
||||||
|
EXPECT_EQ(pixel.getFragment(0).getOpaque(), true);
|
||||||
|
EXPECT_DOUBLE_EQ(pixel.getFragment(MAX_FRAGMENTS_PER_PIXEL - 1).getZ(), (double)(MAX_FRAGMENTS_PER_PIXEL * 2 - 1));
|
||||||
|
EXPECT_EQ(pixel.getFragment(MAX_FRAGMENTS_PER_PIXEL - 1).getOpaque(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CanvasPixel, SameTransparent)
|
||||||
|
{
|
||||||
|
CanvasPixel pixel;
|
||||||
|
|
||||||
|
pixel.pushFragment(CanvasFragment(true, Vector3(0.0, 0.0, 1.4), Vector3(), 0, false));
|
||||||
|
pixel.pushFragment(CanvasFragment(true, Vector3(0.0, 0.0, 1.4), Vector3(), 0, false));
|
||||||
|
|
||||||
|
ASSERT_EQ(1, pixel.getFragmentCount());
|
||||||
|
|
||||||
|
pixel.pushFragment(CanvasFragment(true, Vector3(0.0, 0.0, 1.4), Vector3(), 1, false));
|
||||||
|
|
||||||
|
ASSERT_EQ(2, pixel.getFragmentCount());
|
||||||
|
|
||||||
|
EXPECT_EQ(pixel.getFragment(0).getClient(), 0);
|
||||||
|
EXPECT_EQ(pixel.getFragment(1).getClient(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CanvasPixel, SameOpaque)
|
||||||
|
{
|
||||||
|
CanvasPixel pixel;
|
||||||
|
|
||||||
|
pixel.pushFragment(CanvasFragment(true, Vector3(0.0, 0.0, 1.4), Vector3(), 0, true));
|
||||||
|
pixel.pushFragment(CanvasFragment(true, Vector3(0.0, 0.0, 1.4), Vector3(), 0, true));
|
||||||
|
|
||||||
|
ASSERT_EQ(1, pixel.getFragmentCount());
|
||||||
|
|
||||||
|
pixel.pushFragment(CanvasFragment(true, Vector3(0.0, 0.0, 1.4), Vector3(), 1, true));
|
||||||
|
|
||||||
|
ASSERT_EQ(1, pixel.getFragmentCount());
|
||||||
|
|
||||||
|
EXPECT_EQ(pixel.getFragment(0).getClient(), 0);
|
||||||
|
}
|
|
@ -44,19 +44,19 @@ TEST(CanvasPortion, pushFragment_opaque)
|
||||||
portion.setSize(10, 10);
|
portion.setSize(10, 10);
|
||||||
portion.preparePixels();
|
portion.preparePixels();
|
||||||
|
|
||||||
pushed = CanvasFragment(2.0, VECTOR_ZERO, 0);
|
pushed = CanvasFragment(true, Vector3(0.0, 0.0, 2.0), VECTOR_ZERO, 0);
|
||||||
portion.pushFragment(2, 2, pushed);
|
portion.pushFragment(2, 2, pushed);
|
||||||
|
|
||||||
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
|
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
|
||||||
EXPECT_DOUBLE_EQ(2.0, portion.getFrontFragment(2, 2)->getZ());
|
EXPECT_DOUBLE_EQ(2.0, portion.getFrontFragment(2, 2)->getZ());
|
||||||
|
|
||||||
pushed = CanvasFragment(1.0, VECTOR_ZERO, 0);
|
pushed = CanvasFragment(true, Vector3(0.0, 0.0, 1.0), VECTOR_ZERO, 0);
|
||||||
portion.pushFragment(2, 2, pushed);
|
portion.pushFragment(2, 2, pushed);
|
||||||
|
|
||||||
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
|
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
|
||||||
EXPECT_DOUBLE_EQ(2.0, portion.getFrontFragment(2, 2)->getZ());
|
EXPECT_DOUBLE_EQ(2.0, portion.getFrontFragment(2, 2)->getZ());
|
||||||
|
|
||||||
pushed = CanvasFragment(4.0, VECTOR_ZERO, 0);
|
pushed = CanvasFragment(true, Vector3(0.0, 0.0, 4.0), VECTOR_ZERO, 0);
|
||||||
portion.pushFragment(2, 2, pushed);
|
portion.pushFragment(2, 2, pushed);
|
||||||
|
|
||||||
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
|
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
|
||||||
|
@ -71,19 +71,19 @@ TEST(CanvasPortion, pushFragment_transparent)
|
||||||
portion.setSize(10, 10);
|
portion.setSize(10, 10);
|
||||||
portion.preparePixels();
|
portion.preparePixels();
|
||||||
|
|
||||||
pushed = CanvasFragment(2.0, VECTOR_ZERO, 0, false);
|
pushed = CanvasFragment(true, Vector3(0.0, 0.0, 2.0), VECTOR_ZERO, 0, false);
|
||||||
portion.pushFragment(2, 2, pushed);
|
portion.pushFragment(2, 2, pushed);
|
||||||
|
|
||||||
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
|
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
|
||||||
EXPECT_DOUBLE_EQ(2.0, portion.getFrontFragment(2, 2)->getZ());
|
EXPECT_DOUBLE_EQ(2.0, portion.getFrontFragment(2, 2)->getZ());
|
||||||
|
|
||||||
pushed = CanvasFragment(3.0, VECTOR_ZERO, 0, true);
|
pushed = CanvasFragment(true, Vector3(0.0, 0.0, 3.0), VECTOR_ZERO, 0, true);
|
||||||
portion.pushFragment(2, 2, pushed);
|
portion.pushFragment(2, 2, pushed);
|
||||||
|
|
||||||
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
|
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
|
||||||
EXPECT_DOUBLE_EQ(3.0, portion.getFrontFragment(2, 2)->getZ());
|
EXPECT_DOUBLE_EQ(3.0, portion.getFrontFragment(2, 2)->getZ());
|
||||||
|
|
||||||
pushed = CanvasFragment(4.0, VECTOR_ZERO, 0, false);
|
pushed = CanvasFragment(true, Vector3(0.0, 0.0, 4.0), VECTOR_ZERO, 0, false);
|
||||||
portion.pushFragment(2, 2, pushed);
|
portion.pushFragment(2, 2, pushed);
|
||||||
|
|
||||||
ASSERT_EQ(2, portion.getFragmentCount(2, 2));
|
ASSERT_EQ(2, portion.getFragmentCount(2, 2));
|
||||||
|
|
|
@ -14,7 +14,7 @@ public:
|
||||||
FakeRasterizer(SoftwareRenderer *renderer): Rasterizer(renderer, NULL, 0, COLOR_WHITE)
|
FakeRasterizer(SoftwareRenderer *renderer): Rasterizer(renderer, NULL, 0, COLOR_WHITE)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
virtual Color shadeFragment(const CanvasFragment &) const override
|
virtual Color shadeFragment(const CanvasFragment &, const CanvasFragment *) const override
|
||||||
{
|
{
|
||||||
return COLOR_RED;
|
return COLOR_RED;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ SOURCES += main.cpp \
|
||||||
SpaceSegment_Test.cpp \
|
SpaceSegment_Test.cpp \
|
||||||
Canvas_Test.cpp \
|
Canvas_Test.cpp \
|
||||||
CanvasPortion_Test.cpp \
|
CanvasPortion_Test.cpp \
|
||||||
|
CanvasPixel_Test.cpp \
|
||||||
CanvasPreview_Test.cpp \
|
CanvasPreview_Test.cpp \
|
||||||
AtmosphereDefinition_Test.cpp \
|
AtmosphereDefinition_Test.cpp \
|
||||||
Scenery_Test.cpp \
|
Scenery_Test.cpp \
|
||||||
|
|
Loading…
Reference in a new issue