diff --git a/tinyrender4/main.go b/tinyrender4/main.go index 5de7c1f..cca8fec 100644 --- a/tinyrender4/main.go +++ b/tinyrender4/main.go @@ -84,6 +84,8 @@ func main() { // Premultiply all the translation/etc matrices. Why do we do world to camera THEN // projection? I guess that makes sense actually, oops... projection is the last step. screenmat := worldToCamera.Multiply(&projection) + // light = worldToCamera.MultiplyPoint3(light) + // light = light.Normalize() halfwidth := float32(fb.Width / 2) halfheight := float32(fb.Height / 2) @@ -94,8 +96,10 @@ func main() { for _, f := range o.Faces { // Precompute perspective for vertices to save time. Notice Z // is not considered: is this orthographic projection? Yeah probably... + var fpt [3]Vec3f for i := range 3 { // Triangles, bro fp := screenmat.MultiplyPoint3(f[i].Pos) + fpt[i] = worldToCamera.MultiplyPoint3(f[i].Pos) sc[i] = f[i] sc[i].Pos.X = (fp.X + 1) * halfwidth sc[i].Pos.Y = hi - (fp.Y+1)*halfheight @@ -105,15 +109,12 @@ func main() { // sc[i].Pos.Z = -fp.Z // Pull Z value directly. This is fine, our z-buffer is currently float32 } - l1 := f[2].Pos.Sub(f[0].Pos) - n := l1.CrossProduct(f[1].Pos.Sub(f[0].Pos)) + l1 := fpt[2].Sub(fpt[0]) + n := l1.CrossProduct(fpt[1].Sub(fpt[0])) n = n.Normalize() intensity := n.MultSimp(&light) if intensity > 0 { Triangle3t(&fb, &texture, intensity, sc[0], sc[1], sc[2]) - //Triangle3(&fb, uint(rand.Int()), sc[0], sc[1], sc[2]) - //Triangle1(&fb, uint(rand.Int()), sc[0].ToVec2i(), sc[1].ToVec2i(), sc[2].ToVec2i()) - //Triangle2(&fb, 0xFFFFFF, sc[0], sc[1], sc[2]) } } } diff --git a/tinyrender4/render.go b/tinyrender4/render.go index 6216fc1..38722bf 100644 --- a/tinyrender4/render.go +++ b/tinyrender4/render.go @@ -1,120 +1,9 @@ package main import ( - //"log" - "math" +// "log" ) -func Bresenham2(fb *Framebuffer, color uint, x0 int, y0 int, x1 int, y1 int) { - dx := int(math.Abs(float64(x1 - x0))) - sx := -1 - if x0 < x1 { - sx = 1 - } - dy := -int(math.Abs(float64(y1 - y0))) - sy := -1 - if y0 < y1 { - sy = 1 - } - err := dx + dy - - for { - fb.SetSafe(uint(x0), uint(y0), color) - if x0 == x1 && y0 == y1 { - break - } - e2 := 2 * err - if e2 >= dy { - if x0 == x1 { - break - } - err += dy - x0 += sx - } - if e2 <= dx { - if y0 == y1 { - break - } - err += dx - y0 += sy - } - } -} - -func line(fb *Framebuffer, color uint, v0 Vec2i, v1 Vec2i) { - Bresenham2(fb, color, v0.X, v0.Y, v1.X, v1.Y) -} - -/*func LineSweep(fb *Framebuffer, color uint, v0 Vec2i, v1 Vec2i, v2 Vec2i) { - -}*/ - -func Triangle1(fb *Framebuffer, color uint, v0 Vec2i, v1 Vec2i, v2 Vec2i) { - // The dude gets rid of "degenerate" triangles so... we do too? - if v2.Y == v1.Y && v1.Y == v0.Y { - return - } - // Very silly manual sorting by Y - if v2.Y < v0.Y { - v0, v2 = v2, v0 - } - if v1.Y < v0.Y { - v0, v1 = v1, v0 - } - if v2.Y < v1.Y { - v1, v2 = v2, v1 - } - - var v02step, v01step, v12step, xlong, xshort float32 - - xlong = float32(v0.X) - xshort = xlong - - // The first and last Y CAN'T be equal because sorting!! - if v1.Y == v0.Y { - xshort = float32(v1.X) - } - - // We can check just for greater than because we sorted the vertices - // Assume 02 is on the right(?) and 01 on the left - v02step = (float32(v2.X - v0.X)) / (float32(v2.Y-v0.Y) + 0.001) // long side always - v01step = (float32(v1.X - v0.X)) / (float32(v1.Y-v0.Y) + 0.001) // first short side - v12step = (float32(v2.X - v1.X)) / (float32(v2.Y-v1.Y) + 0.001) // second short side - - for y := v0.Y; y <= v2.Y; y++ { - xleft := int(xshort) - xright := int(xlong) - if xleft > xright { - xleft, xright = xright, xleft - } - if xleft < 0 || xright >= int(fb.Width) { - continue - } - // Draw a horizontal line from left to right - for x := xleft; x <= xright; x++ { - fb.SetSafe(uint(x), uint(y), color) - } - xlong += v02step - if y < v1.Y { - xshort += v01step - } else { - xshort += v12step - } - } -} - -// How does this work? Compare with your -// other barycentric function (in a different repo). In the original -// cpp code, they used an overloaded operator ^ to mean cross product -func Barycentric(v0, v1, v2, p Vec2i) Vec3f { - // WARN: Just not doing this one - u := Vec3f{} - if math.Abs(float64(u.Z)) < 1 { - return Vec3f{-1, 1, 1} - } - return Vec3f{1 - (u.X+u.Y)/u.Z, u.Y / u.Z, u.X / u.Z} -} - // Figure out the minimum bounding box for a triangle defined by // these vertices. Returns the top left and bottom right points, // inclusive @@ -147,67 +36,6 @@ func EdgeIncrementi(v1, v2 Vec2i) (int, int) { return (v2.Y - v1.Y), -(v2.X - v1.X) } -func Triangle2(fb *Framebuffer, color uint, v0 Vec2i, v1 Vec2i, v2 Vec2i) { - boundsTL, boundsBR := ComputeBoundingBox(v0, v1, v2) - if boundsTL.Y < 0 { - boundsTL.Y = 0 - } - if boundsTL.X < 0 { - boundsTL.X = 0 - } - if boundsBR.Y >= int(fb.Height) { - boundsBR.Y = int(fb.Height - 1) - } - if boundsBR.X >= int(fb.Width) { - boundsBR.X = int(fb.Width - 1) - } - // Where to start our scanning - pstart := Vec2i{boundsTL.X, boundsTL.Y} - //log.Print(boundsTL, boundsBR) - // v0f := v0.ToF() - // v1f := v1.ToF() - // v2f := v2.ToF() - // parea := EdgeFunction(v0f, v1f, v2f) - // invarea := 1 / parea - w0_y := EdgeFunctioni(v1, v2, pstart) - w1_y := EdgeFunctioni(v2, v0, pstart) - w2_y := EdgeFunctioni(v0, v1, pstart) - w0_xi, w0_yi := EdgeIncrementi(v1, v2) - w1_xi, w1_yi := EdgeIncrementi(v2, v0) - w2_xi, w2_yi := EdgeIncrementi(v0, v1) - //dyi := int(fb.Width) - //dy := boundsTL.X + dyi*boundsTL.Y - - for y := uint(boundsTL.Y); y <= uint(boundsBR.Y); y++ { - w0 := w0_y - w1 := w1_y - w2 := w2_y - //di := dy - //done := false - for x := uint(boundsTL.X); x <= uint(boundsBR.X); x++ { - if (w0 | w1 | w2) >= 0 { - //fb.Data[di] = color - fb.Set(x, y, color) - //done = true - // w0a := w0 * invarea - // w1a := w1 * invarea - // w2a := w2 * invarea - // fb.Set(x, y, Col2Uint(byte(255*w0a), byte(255*w1a), byte(255*w2a))) - } /*else if done { - break - }*/ - //di += 1 - w0 += w0_xi - w1 += w1_xi - w2 += w2_xi - } - //dy += dyi - w0_y += w0_yi - w1_y += w1_yi - w2_y += w2_yi - } -} - func Triangle3(fb *Framebuffer, color uint, v0f Vec3f, v1f Vec3f, v2f Vec3f) { v0 := v0f.ToVec2i() v1 := v1f.ToVec2i() @@ -324,9 +152,7 @@ func Triangle3t(fb *Framebuffer, texture *Framebuffer, intensity float32, v0v Ve ) r, g, b := Uint2Col(col) fb.Set(x, y, Col2Uint(byte(float32(r)*intensity), byte(float32(g)*intensity), byte(float32(b)*intensity))) //uint(texture.Bounds().Dx()) - //0xF) // fb.Set(x, y, Col2Uint(byte(255*w0a), byte(255*w1a), byte(255*w2a))) } - // fb.Set(x, y, Col2Uint(byte(255*w0a), byte(255*w1a), byte(255*w2a))) } w0 += w0_xi w1 += w1_xi diff --git a/tinyrender4/render_less.go b/tinyrender4/render_less.go new file mode 100644 index 0000000..5d41ad4 --- /dev/null +++ b/tinyrender4/render_less.go @@ -0,0 +1,177 @@ +package main + +import ( + //"log" + "math" +) + +func Bresenham2(fb *Framebuffer, color uint, x0 int, y0 int, x1 int, y1 int) { + dx := int(math.Abs(float64(x1 - x0))) + sx := -1 + if x0 < x1 { + sx = 1 + } + dy := -int(math.Abs(float64(y1 - y0))) + sy := -1 + if y0 < y1 { + sy = 1 + } + err := dx + dy + + for { + fb.SetSafe(uint(x0), uint(y0), color) + if x0 == x1 && y0 == y1 { + break + } + e2 := 2 * err + if e2 >= dy { + if x0 == x1 { + break + } + err += dy + x0 += sx + } + if e2 <= dx { + if y0 == y1 { + break + } + err += dx + y0 += sy + } + } +} + +func line(fb *Framebuffer, color uint, v0 Vec2i, v1 Vec2i) { + Bresenham2(fb, color, v0.X, v0.Y, v1.X, v1.Y) +} + +/*func LineSweep(fb *Framebuffer, color uint, v0 Vec2i, v1 Vec2i, v2 Vec2i) { + +}*/ + +func Triangle1(fb *Framebuffer, color uint, v0 Vec2i, v1 Vec2i, v2 Vec2i) { + // The dude gets rid of "degenerate" triangles so... we do too? + if v2.Y == v1.Y && v1.Y == v0.Y { + return + } + // Very silly manual sorting by Y + if v2.Y < v0.Y { + v0, v2 = v2, v0 + } + if v1.Y < v0.Y { + v0, v1 = v1, v0 + } + if v2.Y < v1.Y { + v1, v2 = v2, v1 + } + + var v02step, v01step, v12step, xlong, xshort float32 + + xlong = float32(v0.X) + xshort = xlong + + // The first and last Y CAN'T be equal because sorting!! + if v1.Y == v0.Y { + xshort = float32(v1.X) + } + + // We can check just for greater than because we sorted the vertices + // Assume 02 is on the right(?) and 01 on the left + v02step = (float32(v2.X - v0.X)) / (float32(v2.Y-v0.Y) + 0.001) // long side always + v01step = (float32(v1.X - v0.X)) / (float32(v1.Y-v0.Y) + 0.001) // first short side + v12step = (float32(v2.X - v1.X)) / (float32(v2.Y-v1.Y) + 0.001) // second short side + + for y := v0.Y; y <= v2.Y; y++ { + xleft := int(xshort) + xright := int(xlong) + if xleft > xright { + xleft, xright = xright, xleft + } + if xleft < 0 || xright >= int(fb.Width) { + continue + } + // Draw a horizontal line from left to right + for x := xleft; x <= xright; x++ { + fb.SetSafe(uint(x), uint(y), color) + } + xlong += v02step + if y < v1.Y { + xshort += v01step + } else { + xshort += v12step + } + } +} + +// How does this work? Compare with your +// other barycentric function (in a different repo). In the original +// cpp code, they used an overloaded operator ^ to mean cross product +func Barycentric(v0, v1, v2, p Vec2i) Vec3f { + // WARN: Just not doing this one + u := Vec3f{} + if math.Abs(float64(u.Z)) < 1 { + return Vec3f{-1, 1, 1} + } + return Vec3f{1 - (u.X+u.Y)/u.Z, u.Y / u.Z, u.X / u.Z} +} + +func Triangle2(fb *Framebuffer, color uint, v0 Vec2i, v1 Vec2i, v2 Vec2i) { + boundsTL, boundsBR := ComputeBoundingBox(v0, v1, v2) + if boundsTL.Y < 0 { + boundsTL.Y = 0 + } + if boundsTL.X < 0 { + boundsTL.X = 0 + } + if boundsBR.Y >= int(fb.Height) { + boundsBR.Y = int(fb.Height - 1) + } + if boundsBR.X >= int(fb.Width) { + boundsBR.X = int(fb.Width - 1) + } + // Where to start our scanning + pstart := Vec2i{boundsTL.X, boundsTL.Y} + //log.Print(boundsTL, boundsBR) + // v0f := v0.ToF() + // v1f := v1.ToF() + // v2f := v2.ToF() + // parea := EdgeFunction(v0f, v1f, v2f) + // invarea := 1 / parea + w0_y := EdgeFunctioni(v1, v2, pstart) + w1_y := EdgeFunctioni(v2, v0, pstart) + w2_y := EdgeFunctioni(v0, v1, pstart) + w0_xi, w0_yi := EdgeIncrementi(v1, v2) + w1_xi, w1_yi := EdgeIncrementi(v2, v0) + w2_xi, w2_yi := EdgeIncrementi(v0, v1) + //dyi := int(fb.Width) + //dy := boundsTL.X + dyi*boundsTL.Y + + for y := uint(boundsTL.Y); y <= uint(boundsBR.Y); y++ { + w0 := w0_y + w1 := w1_y + w2 := w2_y + //di := dy + //done := false + for x := uint(boundsTL.X); x <= uint(boundsBR.X); x++ { + if (w0 | w1 | w2) >= 0 { + //fb.Data[di] = color + fb.Set(x, y, color) + //done = true + // w0a := w0 * invarea + // w1a := w1 * invarea + // w2a := w2 * invarea + // fb.Set(x, y, Col2Uint(byte(255*w0a), byte(255*w1a), byte(255*w2a))) + } /*else if done { + break + }*/ + //di += 1 + w0 += w0_xi + w1 += w1_xi + w2 += w2_xi + } + //dy += dyi + w0_y += w0_yi + w1_y += w1_yi + w2_y += w2_yi + } +}