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"
CanvasFragment::CanvasFragment(double z, const Vector3 &location, int client, bool opaque):
opaque(opaque), z(z), location(location), client(client)
CanvasFragment::CanvasFragment(bool front_facing, const Vector3 &pixel, const Vector3 &location, int client, bool opaque):
opaque(opaque), front_facing(front_facing), pixel(pixel), location(location), client(client)
{
color = COLOR_WHITE;
}

View file

@ -16,19 +16,22 @@ class SOFTWARESHARED_EXPORT CanvasFragment
{
public:
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);
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 &getPixel() const {return pixel;}
inline int getClient() const {return client;}
inline const Color &getColor() const {return color;}
private:
bool opaque;
double z;
bool front_facing;
Vector3 pixel;
Vector3 location;
int client;
Color color;

View file

@ -35,7 +35,7 @@ void CanvasPixel::pushFragment(const CanvasFragment &fragment)
}
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
return;
@ -43,11 +43,17 @@ void CanvasPixel::pushFragment(const CanvasFragment &fragment)
// find expected position
int i = 0;
while (i < count and fragment.getZ() > fragments[i].getZ())
while (i < count and fragment.getZ() >= fragments[i].getZ())
{
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())
{
// Discard fragments masked by the incoming opaque one
@ -62,28 +68,32 @@ void CanvasPixel::pushFragment(const CanvasFragment &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
{
if (count == MAX_FRAGMENT_COUNT)
// Transparent pixel
if (i < count)
{
// Replace nearest fragment
fragments[count - 1] = fragment;
// Need to make room for the incoming fragment
if (count < MAX_FRAGMENTS_PER_PIXEL)
{
memmove(fragments + i + 1, fragments + i, sizeof(CanvasFragment) * (count - i));
fragments[i] = fragment;
count++;
}
}
else
{
// Append
fragments[count] = fragment;
count++;
if (count == MAX_FRAGMENTS_PER_PIXEL)
{
// Replace nearest fragment
fragments[count - 1] = fragment;
}
else
{
// Append
fragments[count] = fragment;
count++;
}
}
}
}

View file

@ -5,7 +5,7 @@
#include "CanvasFragment.h"
const int MAX_FRAGMENT_COUNT = 2;
const int MAX_FRAGMENTS_PER_PIXEL = 2;
namespace paysages {
namespace software {
@ -32,7 +32,7 @@ public:
private:
int count;
CanvasFragment fragments[MAX_FRAGMENT_COUNT];
CanvasFragment fragments[MAX_FRAGMENTS_PER_PIXEL];
Color composite;
};

View file

@ -45,11 +45,13 @@ void CanvasPixelShader::processParallelUnit(int unit)
const CanvasPixel &pixel = portion->at(base_x + x, base_y + y);
int n = pixel.getFragmentCount();
Color composite = COLOR_BLACK;
const CanvasFragment *previous = NULL;
for (int i = 0; i < n; i++)
{
const CanvasFragment &fragment = pixel.getFragment(i);
const Rasterizer &rasterizer = renderer.getRasterizer(fragment.getClient());
composite.mask(rasterizer.shadeFragment(fragment));
composite.mask(rasterizer.shadeFragment(fragment, previous));
previous = &fragment;
}
// Fill the square area

View file

@ -22,6 +22,7 @@ struct paysages::software::ScanPoint
double z;
} location;
int client;
bool front_facing;
};
struct paysages::software::RenderScanlines
@ -38,6 +39,7 @@ Rasterizer::Rasterizer(SoftwareRenderer* renderer, RenderProgress *progress, int
this->color = new Color(color);
interrupted = false;
backface_culling = false;
triangle_count = 0;
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)
{
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;
}
// 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
point1.pixel.x = dpixel1.x;
point1.pixel.y = dpixel1.y;
@ -100,6 +120,7 @@ bool Rasterizer::pushProjectedTriangle(CanvasPortion *canvas, const Vector3 &pix
point1.location.y = location1.y;
point1.location.z = location1.z;
point1.client = client_id;
point1.front_facing = front_facing;
point2.pixel.x = dpixel2.x;
point2.pixel.y = dpixel2.y;
@ -108,6 +129,7 @@ bool Rasterizer::pushProjectedTriangle(CanvasPortion *canvas, const Vector3 &pix
point2.location.y = location2.y;
point2.location.z = location2.z;
point2.client = client_id;
point2.front_facing = front_facing;
point3.pixel.x = dpixel3.x;
point3.pixel.y = dpixel3.y;
@ -116,6 +138,7 @@ bool Rasterizer::pushProjectedTriangle(CanvasPortion *canvas, const Vector3 &pix
point3.location.y = location3.y;
point3.location.z = location3.z;
point3.client = client_id;
point3.front_facing = front_facing;
// Prepare scanlines
// 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;
// TODO v1, v2 and v3 are lost, but may be useful (avoid need to unproject)
p1 = getRenderer()->projectPoint(v1);
p2 = getRenderer()->projectPoint(v2);
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.z = ((1.0 - value) * (v1->location.z * v1depth) + value * (v1->location.z + diff->location.z) * v2depth) * factor;
result->client = v1->client;
result->front_facing = v1->front_facing;
}
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);
}
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;
frag_color.a = 1.0;
if (cury == starty || cury == endy)
{
frag_color.mask(Color(0.0, 0.0, 0.0, 0.3));
}
frag_color.a = color->a;
fragment.setColor(frag_color);
canvas->pushFragment(current.x, current.y, fragment);

View file

@ -6,6 +6,10 @@
namespace paysages {
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 RenderScanlines RenderScanlines;
@ -21,9 +25,6 @@ public:
inline SoftwareRenderer *getRenderer() const {return renderer;}
inline int getTriangleCount() const {return triangle_count;}
virtual Color shadeFragment(const CanvasFragment &fragment) const = 0;
virtual void interrupt();
/**
* Set the rasterization quality factor.
*/
@ -34,20 +35,34 @@ public:
*/
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.
*/
virtual int prepareRasterization() = 0;
/**
* Abstract method to effectively do the rasterization on a canvas.
*/
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 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;
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 camera_location, direction;

View file

@ -15,7 +15,7 @@ public:
virtual int prepareRasterization() 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);
virtual void setQuality(double factor) override;

View file

@ -27,9 +27,9 @@ SoftwareCanvasRenderer::SoftwareCanvasRenderer(Scenery *scenery):
postprocess_enabled = true;
rasterizers.push_back(new SkyRasterizer(this, progress, 0));
rasterizers.push_back(new WaterRasterizer(this, progress, 1));
rasterizers.push_back(new TerrainRasterizer(this, progress, 2));
rasterizers.push_back(new SkyRasterizer(this, progress, RASTERIZER_CLIENT_SKY));
rasterizers.push_back(new WaterRasterizer(this, progress, RASTERIZER_CLIENT_WATER));
rasterizers.push_back(new TerrainRasterizer(this, progress, RASTERIZER_CLIENT_TERRAIN));
current_work = NULL;
}

View file

@ -245,7 +245,7 @@ void TerrainRasterizer::rasterizeToCanvas(CanvasPortion *canvas)
performTessellation(canvas, false);
}
Color TerrainRasterizer::shadeFragment(const CanvasFragment &fragment) const
Color TerrainRasterizer::shadeFragment(const CanvasFragment &fragment, const CanvasFragment *) const
{
Vector3 point = fragment.getLocation();
double precision = renderer->getPrecision(_getPoint(renderer, point.x, point.z));

View file

@ -36,7 +36,7 @@ public:
virtual int prepareRasterization() 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:
/**

View file

@ -43,7 +43,7 @@ void WaterRasterizer::rasterizeToCanvas(CanvasPortion *canvas)
performTessellation(canvas);
}
Color WaterRasterizer::shadeFragment(const CanvasFragment &fragment) const
Color WaterRasterizer::shadeFragment(const CanvasFragment &fragment, const CanvasFragment *) const
{
Vector3 location = fragment.getLocation();
return renderer->getWaterRenderer()->getResult(location.x, location.z).final;

View file

@ -17,7 +17,7 @@ public:
virtual int prepareRasterization() 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;

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.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);
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
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);
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
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);
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
@ -71,19 +71,19 @@ TEST(CanvasPortion, pushFragment_transparent)
portion.setSize(10, 10);
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);
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
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);
ASSERT_EQ(1, portion.getFragmentCount(2, 2));
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);
ASSERT_EQ(2, portion.getFragmentCount(2, 2));

View file

@ -14,7 +14,7 @@ public:
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;
}

View file

@ -21,6 +21,7 @@ SOURCES += main.cpp \
SpaceSegment_Test.cpp \
Canvas_Test.cpp \
CanvasPortion_Test.cpp \
CanvasPixel_Test.cpp \
CanvasPreview_Test.cpp \
AtmosphereDefinition_Test.cpp \
Scenery_Test.cpp \