Add fog support to software rasterizer (as if it wasn't slow enough already)

This commit is contained in:
Henrik Rydgard committed 2015-06-11 16:01:17 +02:00
1 parent 7e2f37abc1
commit 842817c91d
5 files changed
+61 -30

No files matched your search

+1
View File
@@ -196,6 +196,7 @@ void ProcessRect(const VertexData& v0, const VertexData& v1)
buf[0].color0 = buf[1].color0 = buf[2].color0 = buf[3].color0;
buf[0].color1 = buf[1].color1 = buf[2].color1 = buf[3].color1;
buf[0].clippos.w = buf[1].clippos.w = buf[2].clippos.w = buf[3].clippos.w = 1.0f;
buf[0].fogdepth = buf[1].fogdepth = buf[2].fogdepth = buf[3].fogdepth = 1.0f;
VertexData* topleft = &buf[0];
VertexData* topright = &buf[1];
+48 -27
View File
@@ -103,6 +103,15 @@ static inline u32 LookupColor(unsigned int index, unsigned int level)
}
}
static inline u8 ClampFogDepth(float fogdepth) {
if (fogdepth <= 0.0f)
return 0;
else if (fogdepth >= 1.0f)
return 255;
else
return (u8)(u32)(fogdepth * 255.0f);
}
static inline void GetTexelCoordinates(int level, float s, float t, int& out_u, int& out_v)
{
int width = 1 << (gstate.texsize[level] & 0xf);
@@ -936,7 +945,7 @@ static inline Vec3<int> AlphaBlendingResult(const Vec4<int> &source, const Vec4<
}
template <bool clearMode>
inline void DrawSinglePixel(const DrawingCoords &p, u16 z, const Vec4<int> &color_in) {
inline void DrawSinglePixel(const DrawingCoords &p, u16 z, u8 fog, const Vec4<int> &color_in) {
Vec4<int> prim_color = color_in;
// Depth range test
// TODO: Clear mode?
@@ -955,7 +964,7 @@ inline void DrawSinglePixel(const DrawingCoords &p, u16 z, const Vec4<int> &colo
// In clear mode, it uses the alpha color as stencil.
u8 stencil = clearMode ? prim_color.a() : GetPixelStencil(p.x, p.y);
// TODO: Is it safe to ignore gstate.isDepthTestEnabled() when clear mode is enabled?
// TODO: Is it safe to ignore gstate.isDepthTestEnabled() when clear mode is enabled? Probably yes
if (!clearMode && (gstate.isStencilTestEnabled() || gstate.isDepthTestEnabled())) {
if (gstate.isStencilTestEnabled() && !StencilTestPassed(stencil)) {
stencil = ApplyStencilOp(gstate.getStencilOpSFail(), p.x, p.y);
@@ -989,6 +998,14 @@ inline void DrawSinglePixel(const DrawingCoords &p, u16 z, const Vec4<int> &colo
prim_color.b() <<= 1;
}
if (gstate.isFogEnabled() && !gstate.isModeThrough() && !clearMode) {
Vec3<int> fogColor = Vec3<int>::FromRGB(gstate.fogcolor);
fogColor = (prim_color.rgb() * (int)fog + fogColor * (255 - (int)fog)) / 255;
prim_color.r() = fogColor.r();
prim_color.g() = fogColor.g();
prim_color.b() = fogColor.b();
}
const u32 old_color = GetPixelColor(p.x, p.y);
u32 new_color;
@@ -1200,6 +1217,7 @@ void DrawTriangleSlice(
int w1_base = orient2d(v2.screenpos, v0.screenpos, pprime);
int w2_base = orient2d(v0.screenpos, v1.screenpos, pprime);
// Step forward to y1 (slice..)
w0_base += orient2dIncY(d12.x) * 16 * y1;
w1_base += orient2dIncY(-d02.x) * 16 * y1;
w2_base += orient2dIncY(d01.x) * 16 * y1;
@@ -1226,20 +1244,18 @@ void DrawTriangleSlice(
p.x = (p.x + 1) & 0x3FF) {
// If p is on or inside all edges, render pixel
// TODO: Should we render if the pixel is both on the left and the right side? (i.e. degenerated triangle)
if (w0 + bias0 >= 0 && w1 + bias1 >= 0 && w2 + bias2 >= 0) {
// TODO: Check if this check is still necessary
if (w0 == 0 && w1 == 0 && w2 == 0)
int wsum = w0 + w1 + w2;
if (wsum == 0.0f)
continue;
float wsum = 1.0f / (w0 + w1 + w2);
float wsum_recip = 1.0f / (float)wsum;
Vec4<int> prim_color;
Vec3<int> sec_color;
if (gstate.getShadeMode() == GE_SHADE_GOURAUD && !clearMode) {
// TODO: Is that the correct way to interpolate?
prim_color = Interpolate(v0.color0, v1.color0, v2.color0, w0, w1, w2, wsum);
sec_color = Interpolate(v0.color1, v1.color1, v2.color1, w0, w1, w2, wsum);
// Does the PSP do perspective-correct color interpolation? The GC doesn't.
prim_color = Interpolate(v0.color0, v1.color0, v2.color0, w0, w1, w2, wsum_recip);
sec_color = Interpolate(v0.color1, v1.color1, v2.color1, w0, w1, w2, wsum_recip);
} else {
prim_color = v2.color0;
sec_color = v2.color1;
@@ -1247,8 +1263,8 @@ void DrawTriangleSlice(
if (gstate.isTextureMapEnabled() && !clearMode) {
if (gstate.isModeThrough()) {
// TODO: Is it really this simple?
Vec2<float> texcoords = Interpolate(v0.texturecoords, v1.texturecoords, v2.texturecoords, w0, w1, w2, wsum);
// Texture coordinate interpolation must definitely be perspective-correct.
Vec2<float> texcoords = Interpolate(v0.texturecoords, v1.texturecoords, v2.texturecoords, w0, w1, w2, wsum_recip);
ApplyTexturing(prim_color, texcoords.s(), texcoords.t(), maxTexLevel, magFilt, texptr, texbufwidthbits);
} else {
float s = 0, t = 0;
@@ -1269,15 +1285,18 @@ void DrawTriangleSlice(
#endif
}
// TODO: Fogging
int fog = 255;
if (gstate.isFogEnabled() && !clearMode) {
fog = ClampFogDepth(((float)v0.fogdepth * w0 + (float)v1.fogdepth * w1 + (float)v2.fogdepth * w2) * wsum_recip);
}
u16 z = v2.screenpos.z;
// TODO: Is that the correct way to interpolate?
// Without the (u32), this causes an ICE in some versions of gcc.
if (!flatZ)
z = (u16)(u32)(((float)v0.screenpos.z * w0 + (float)v1.screenpos.z * w1 + (float)v2.screenpos.z * w2) * wsum);
z = (u16)(u32)(((float)v0.screenpos.z * w0 + (float)v1.screenpos.z * w1 + (float)v2.screenpos.z * w2) * wsum_recip);
DrawSinglePixel<clearMode>(p, z, prim_color);
DrawSinglePixel<clearMode>(p, z, fog, prim_color);
}
}
}
@@ -1296,11 +1315,10 @@ void DrawTriangle(const VertexData& v0, const VertexData& v1, const VertexData&
if (d01.x * d02.y - d01.y * d02.x < 0)
return;
// Is the division and multiplication just &= ~0xF ?
int minX = std::min(std::min(v0.screenpos.x, v1.screenpos.x), v2.screenpos.x) / 16 * 16;
int minY = std::min(std::min(v0.screenpos.y, v1.screenpos.y), v2.screenpos.y) / 16 * 16;
int maxX = std::max(std::max(v0.screenpos.x, v1.screenpos.x), v2.screenpos.x) / 16 * 16;
int maxY = std::max(std::max(v0.screenpos.y, v1.screenpos.y), v2.screenpos.y) / 16 * 16;
int minX = std::min(std::min(v0.screenpos.x, v1.screenpos.x), v2.screenpos.x) & ~0xF;
int minY = std::min(std::min(v0.screenpos.y, v1.screenpos.y), v2.screenpos.y) & ~0xF;
int maxX = std::max(std::max(v0.screenpos.x, v1.screenpos.x), v2.screenpos.x) & ~0xF;
int maxY = std::max(std::max(v0.screenpos.y, v1.screenpos.y), v2.screenpos.y) & ~0xF;
DrawingCoords scissorTL(gstate.getScissorX1(), gstate.getScissorY1(), 0);
DrawingCoords scissorBR(gstate.getScissorX2(), gstate.getScissorY2(), 0);
@@ -1400,14 +1418,15 @@ void DrawPoint(const VertexData &v0)
ScreenCoords pprime = pos;
// TODO: Fogging
DrawingCoords p = TransformUnit::ScreenToDrawing(pprime);
u16 z = pos.z;
u8 fog = ClampFogDepth(v0.fogdepth);
if (clearMode) {
DrawSinglePixel<true>(p, z, prim_color);
DrawSinglePixel<true>(p, z, v0.fogdepth, prim_color);
} else {
DrawSinglePixel<false>(p, z, prim_color);
DrawSinglePixel<false>(p, z, v0.fogdepth, prim_color);
}
}
@@ -1476,11 +1495,15 @@ void DrawLine(const VertexData &v0, const VertexData &v1)
if (x < scissorTL.x || y < scissorTL.y || x >= scissorBR.x || y >= scissorBR.y)
continue;
// Interpolate between the two points.
Vec4<int> c0 = (v0.color0 * (steps - i) + v1.color0 * i) / steps1;
Vec3<int> sec_color = (v0.color1 * (steps - i) + v1.color1 * i) / steps1;
// TODO: UVGenMode?
Vec2<float> tc = (v0.texturecoords * (float)(steps - i) + v1.texturecoords * (float)i) / steps1;
Vec4<int> prim_color = c0;
// TODO: Interpolate fog as well
float s = tc.s();
float t = tc.t();
@@ -1500,13 +1523,11 @@ void DrawLine(const VertexData &v0, const VertexData &v1)
ScreenCoords pprime = ScreenCoords(x, y, z);
// TODO: Fogging
DrawingCoords p = TransformUnit::ScreenToDrawing(pprime);
if (clearMode) {
DrawSinglePixel<true>(p, z, prim_color);
DrawSinglePixel<true>(p, z, v0.fogdepth, prim_color);
} else {
DrawSinglePixel<false>(p, z, prim_color);
DrawSinglePixel<false>(p, z, v0.fogdepth, prim_color);
}
x = x + xinc;
+8 -1
View File
@@ -172,7 +172,13 @@ VertexData TransformUnit::ReadVertex(VertexReader& vreader)
if (!gstate.isModeThrough()) {
vertex.modelpos = ModelCoords(pos[0], pos[1], pos[2]);
vertex.worldpos = WorldCoords(TransformUnit::ModelToWorld(vertex.modelpos));
vertex.clippos = ClipCoords(TransformUnit::ViewToClip(TransformUnit::WorldToView(vertex.worldpos)));
ModelCoords viewpos = TransformUnit::WorldToView(vertex.worldpos);
vertex.clippos = ClipCoords(TransformUnit::ViewToClip(viewpos));
if (gstate.isFogEnabled()) {
vertex.fogdepth = (viewpos.z + getFloat24(gstate.fog1)) * getFloat24(gstate.fog2);
} else {
vertex.fogdepth = 1.0f;
}
vertex.screenpos = ClipToScreenInternal(vertex.clippos, &outside_range_flag);
if (vreader.hasNormal()) {
@@ -187,6 +193,7 @@ VertexData TransformUnit::ReadVertex(VertexReader& vreader)
vertex.screenpos.y = (u32)pos[1] * 16 + gstate.getOffsetY16();
vertex.screenpos.z = pos[2];
vertex.clippos.w = 1.f;
vertex.fogdepth = 1.f;
}
return vertex;
+3 -1
View File
@@ -93,8 +93,9 @@ struct VertexData
screenpos = ::Lerp(a.screenpos, b.screenpos, t); // TODO: Should use a LerpInt (?)
texturecoords = ::Lerp(a.texturecoords, b.texturecoords, t);
normal = ::Lerp(a.normal, b.normal, t);
fogdepth = ::Lerp(a.fogdepth, b.fogdepth, t);
u16 t_int =(u16)(t*256);
u16 t_int = (u16)(t*256);
color0 = LerpInt<Vec4<int>,256>(a.color0, b.color0, t_int);
color1 = LerpInt<Vec3<int>,256>(a.color1, b.color1, t_int);
}
@@ -108,6 +109,7 @@ struct VertexData
WorldCoords worldnormal;
Vec4<int> color0;
Vec3<int> color1;
float fogdepth;
};
class VertexReader;
+1 -1
Submodule native updated: 93a7d0c67e...316d6ab84d.