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:
Michaël Lemaire 2015-10-16 00:51:46 +02:00
parent ac1b6a909b
commit 52bad18d26
18 changed files with 168 additions and 51 deletions

View file

@ -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;
} }

View file

@ -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;

View file

@ -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,10 +68,13 @@ void CanvasPixel::pushFragment(const CanvasFragment &fragment)
} }
fragments[0] = fragment; fragments[0] = fragment;
} }
else if (i < count) else
{
// Transparent pixel
if (i < count)
{ {
// Need to make room for the incoming fragment // Need to make room for the incoming fragment
if (count < MAX_FRAGMENT_COUNT) if (count < MAX_FRAGMENTS_PER_PIXEL)
{ {
memmove(fragments + i + 1, fragments + i, sizeof(CanvasFragment) * (count - i)); memmove(fragments + i + 1, fragments + i, sizeof(CanvasFragment) * (count - i));
fragments[i] = fragment; fragments[i] = fragment;
@ -74,7 +83,7 @@ void CanvasPixel::pushFragment(const CanvasFragment &fragment)
} }
else else
{ {
if (count == MAX_FRAGMENT_COUNT) if (count == MAX_FRAGMENTS_PER_PIXEL)
{ {
// Replace nearest fragment // Replace nearest fragment
fragments[count - 1] = fragment; fragments[count - 1] = fragment;
@ -87,6 +96,7 @@ void CanvasPixel::pushFragment(const CanvasFragment &fragment)
} }
} }
} }
}
updateComposite(); updateComposite();
} }

View file

@ -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;
}; };

View file

@ -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

View file

@ -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, &current); scanInterpolate(renderer->render_camera, &down, &diff, fy / dy, &current);
} }
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);

View file

@ -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;
}; };
} }

View file

@ -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;

View file

@ -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;

View file

@ -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;
} }

View file

@ -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));

View file

@ -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:
/** /**

View file

@ -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;

View file

@ -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;

View 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);
}

View file

@ -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));

View file

@ -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;
} }

View file

@ -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 \