Moving fast and probably breaking everything

This commit is contained in:
Carlos Sanchez 2024-07-30 23:17:00 -04:00
parent 61bcbcb823
commit 123ebc83df
5 changed files with 6535 additions and 44 deletions

6357
head.obj Normal file

File diff suppressed because it is too large Load Diff

View File

@ -26,75 +26,107 @@ func Uint2Col(col uint) (byte, byte, byte) {
return byte((col >> 16) & 0xFF), byte((col >> 8) & 0xFF), byte(col & 0xFF) return byte((col >> 16) & 0xFF), byte((col >> 8) & 0xFF), byte(col & 0xFF)
} }
// Color is in ARGB (alpha not used right now) // A simple buffer where you can set pixels
type Framebuffer struct { type Framebuffer interface {
Data []uint Set(x uint, y uint, r byte, g byte, b byte)
Get(x uint, y uint) (byte, byte, byte)
GetUv(u float32, v float32) (byte, byte, byte)
}
// Color is in RGB (alpha not used right now)
type SimpleFramebuffer struct {
Data []byte
Width uint
Height uint
}
// Sure hope this gets inlined...
func (fb *SimpleFramebuffer) Set(x uint, y uint, r byte, g byte, b byte) {
if x >= fb.Width || y >= fb.Height {
return
}
fb.Data[(x+y*fb.Width)*3] = r
fb.Data[(x+y*fb.Width)*3+1] = g
fb.Data[(x+y*fb.Width)*3+2] = b
}
func (fb *SimpleFramebuffer) Get(x uint, y uint) (byte, byte, byte) {
if x >= fb.Width || y >= fb.Height {
return 0, 0, 0
}
return fb.Data[(x+y*fb.Width)*3],
fb.Data[(x+y*fb.Width)*3+1],
fb.Data[(x+y*fb.Width)*3+2]
}
func (fb *SimpleFramebuffer) GetUv(u float32, v float32) (byte, byte, byte) {
x := uint(float32(fb.Width)*u) & (fb.Width - 1)
y := uint(float32(fb.Height)*(1-v)) & (fb.Height - 1)
return fb.Data[(x+y*fb.Width)*3],
fb.Data[(x+y*fb.Width)*3+1],
fb.Data[(x+y*fb.Width)*3+2]
}
func NewSimpleFramebuffer(width uint, height uint) *SimpleFramebuffer {
return &SimpleFramebuffer{
Data: make([]byte, width*height*3),
Width: width,
Height: height,
}
}
type RenderBuffer struct {
Data Framebuffer
ZBuffer []float32 //uint16 // Apparently 16 bit z-buffers are used ZBuffer []float32 //uint16 // Apparently 16 bit z-buffers are used
Width uint Width uint
Height uint Height uint
} }
// Create a new framebuffer for the given width and height. // Create a new framebuffer for the given width and height.
func NewFramebuffer(width uint, height uint) Framebuffer { func NewRenderbuffer(d Framebuffer, width uint, height uint) RenderBuffer {
return Framebuffer{ return RenderBuffer{
Data: make([]uint, width*height), Data: d,
ZBuffer: make([]float32, width*height), ZBuffer: make([]float32, width*height),
Width: width, Width: width,
Height: height, Height: height,
} }
} }
func NewTexture(texture image.Image, skip int) Framebuffer { func NewTexture(texture image.Image, skip int) *SimpleFramebuffer {
bounds := texture.Bounds() bounds := texture.Bounds()
width := bounds.Dx() / skip width := bounds.Dx() / skip
height := bounds.Dy() / skip height := bounds.Dy() / skip
result := Framebuffer{ result := NewSimpleFramebuffer(uint(width), uint(height))
Data: make([]uint, width*height), wlog := math.Log2(float64(width))
Width: uint(width), hlog := math.Log2(float64(height))
Height: uint(height), if wlog != math.Floor(wlog) || hlog != math.Floor(hlog) {
panic("Texture must be power of two")
} }
for y := bounds.Min.Y; y < bounds.Max.Y; y += skip { for y := bounds.Min.Y; y < bounds.Max.Y; y += skip {
for x := bounds.Min.X; x < bounds.Max.X; x += skip { for x := bounds.Min.X; x < bounds.Max.X; x += skip {
col := texture.At(x, y) col := texture.At(x, y)
result.Set(uint(x/skip), uint(y/skip), Color2Uint(col)) r, g, b, _ := col.RGBA()
result.Set(uint(x/skip), uint(y/skip), byte(r>>8), byte(g>>8), byte(b>>8))
} }
} }
return result return result
} }
// Fill zbuffer with pixels that are max distance away // Fill zbuffer with pixels that are max distance away
func (fb *Framebuffer) ResetZBuffer() { func (fb *RenderBuffer) ResetZBuffer() {
for i := range fb.ZBuffer { for i := range fb.ZBuffer {
fb.ZBuffer[i] = 65535 //math.MaxFloat32 fb.ZBuffer[i] = 65535 //math.MaxFloat32
} }
} }
func (fb *Framebuffer) GetUv(u float32, v float32) uint {
x := uint(float32(fb.Width)*u) & (fb.Width - 1)
y := uint(float32(fb.Height)*(1-v)) & (fb.Height - 1)
return fb.Data[x+y*fb.Width]
}
// Sure hope this gets inlined...
func (fb *Framebuffer) Set(x uint, y uint, color uint) {
fb.Data[x+y*fb.Width] = color
}
func (fb *Framebuffer) SetSafe(x uint, y uint, color uint) {
if x >= fb.Width || y >= fb.Height {
return
}
fb.Data[x+y*fb.Width] = color
}
// Given some image data, return a string that is the ppm of it // Given some image data, return a string that is the ppm of it
func (fb *Framebuffer) ExportPPM() string { func (fb *RenderBuffer) ExportPPM() string {
log.Printf("ExportPPM called for framebuffer %dx%d", fb.Width, fb.Height) log.Printf("ExportPPM called for framebuffer %dx%d", fb.Width, fb.Height)
var result strings.Builder var result strings.Builder
result.WriteString(fmt.Sprintf("P3\n%d %d\n255\n", fb.Width, fb.Height)) result.WriteString(fmt.Sprintf("P3\n%d %d\n255\n", fb.Width, fb.Height))
for y := range fb.Height { for y := range fb.Height {
for x := range fb.Width { for x := range fb.Width {
r, g, b := Uint2Col(fb.Data[x+y*fb.Width]) r, g, b := fb.Data.Get(x, y)
result.WriteString(fmt.Sprintf("%d %d %d\t", r, g, b)) result.WriteString(fmt.Sprintf("%d %d %d\t", r, g, b))
} }
result.WriteRune('\n') result.WriteRune('\n')
@ -102,20 +134,22 @@ func (fb *Framebuffer) ExportPPM() string {
return result.String() return result.String()
} }
func (fb *Framebuffer) ExportPPMP6() []byte { func (fb *RenderBuffer) ExportPPMP6() []byte {
log.Printf("ExportPPM6 called for framebuffer %dx%d", fb.Width, fb.Height) log.Printf("ExportPPM6 called for framebuffer %dx%d", fb.Width, fb.Height)
var result bytes.Buffer var result bytes.Buffer
result.WriteString(fmt.Sprintf("P6\n%d %d\n255\n", fb.Width, fb.Height)) result.WriteString(fmt.Sprintf("P6\n%d %d\n255\n", fb.Width, fb.Height))
for i := range fb.Data { for y := range fb.Height {
r, g, b := Uint2Col(fb.Data[i]) for x := range fb.Width {
r, g, b := fb.Data.Get(x, y)
result.Write([]byte{r, g, b}) result.Write([]byte{r, g, b})
}
//result.WriteString(fmt.Sprintf("%d %d %d\t", r, g, b)) //result.WriteString(fmt.Sprintf("%d %d %d\t", r, g, b))
} }
//result.WriteRune('\n') //result.WriteRune('\n')
return result.Bytes() return result.Bytes()
} }
func (fb *Framebuffer) ZBuffer_ExportPPM() string { func (fb *RenderBuffer) ZBuffer_ExportPPM() string {
var result strings.Builder var result strings.Builder
mini := float32(math.MaxFloat32) mini := float32(math.MaxFloat32)
maxi := float32(-math.MaxFloat32) maxi := float32(-math.MaxFloat32)

View File

@ -36,10 +36,11 @@ func EdgeIncrementi(v1, v2 Vec2i) (int, int) {
return (v2.Y - v1.Y), -(v2.X - v1.X) return (v2.Y - v1.Y), -(v2.X - v1.X)
} }
func TriangleFlat(fb *Framebuffer, color uint, v0f Vec3f, v1f Vec3f, v2f Vec3f) { func TriangleFlat(fb *RenderBuffer, color uint, v0f Vec3f, v1f Vec3f, v2f Vec3f) {
v0 := v0f.ToVec2i() v0 := v0f.ToVec2i()
v1 := v1f.ToVec2i() v1 := v1f.ToVec2i()
v2 := v2f.ToVec2i() v2 := v2f.ToVec2i()
r, g, b := Uint2Col(color)
boundsTL, boundsBR := ComputeBoundingBox(v0, v1, v2) boundsTL, boundsBR := ComputeBoundingBox(v0, v1, v2)
if boundsTL.Y < 0 { if boundsTL.Y < 0 {
boundsTL.Y = 0 boundsTL.Y = 0
@ -84,7 +85,7 @@ func TriangleFlat(fb *Framebuffer, color uint, v0f Vec3f, v1f Vec3f, v2f Vec3f)
if pz < fb.ZBuffer[x+y*fb.Width] { if pz < fb.ZBuffer[x+y*fb.Width] {
//log.Print(pz) //log.Print(pz)
fb.ZBuffer[x+y*fb.Width] = pz fb.ZBuffer[x+y*fb.Width] = pz
fb.Set(x, y, color) fb.Data.Set(x, y, r, g, b)
} }
// 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)))
} }
@ -98,7 +99,7 @@ func TriangleFlat(fb *Framebuffer, color uint, v0f Vec3f, v1f Vec3f, v2f Vec3f)
} }
} }
func TriangleTextured(fb *Framebuffer, texture *Framebuffer, intensity float32, v0v Vertex, v1v Vertex, v2v Vertex) { func TriangleTextured(fb *RenderBuffer, texture Framebuffer, intensity float32, v0v Vertex, v1v Vertex, v2v Vertex) {
v0 := v0v.Pos.ToVec2i() v0 := v0v.Pos.ToVec2i()
v1 := v1v.Pos.ToVec2i() v1 := v1v.Pos.ToVec2i()
v2 := v2v.Pos.ToVec2i() v2 := v2v.Pos.ToVec2i()
@ -144,12 +145,11 @@ func TriangleTextured(fb *Framebuffer, texture *Framebuffer, intensity float32,
pz := w0a*v0v.Pos.Z + w1a*v1v.Pos.Z + w2a*v2v.Pos.Z pz := w0a*v0v.Pos.Z + w1a*v1v.Pos.Z + w2a*v2v.Pos.Z
if pz < fb.ZBuffer[x+y*fb.Width] { if pz < fb.ZBuffer[x+y*fb.Width] {
fb.ZBuffer[x+y*fb.Width] = pz fb.ZBuffer[x+y*fb.Width] = pz
col := texture.GetUv( r, g, b := texture.GetUv(
(w0a*v0v.Tex.X + w1a*v1v.Tex.X + w2a*v2v.Tex.X), (w0a*v0v.Tex.X + w1a*v1v.Tex.X + w2a*v2v.Tex.X),
(w0a*v0v.Tex.Y + w1a*v1v.Tex.Y + w2a*v2v.Tex.Y), (w0a*v0v.Tex.Y + w1a*v1v.Tex.Y + w2a*v2v.Tex.Y),
) )
r, g, b := Uint2Col(col) fb.Data.Set(x, y, byte(float32(r)*intensity), byte(float32(g)*intensity), byte(float32(b)*intensity))
fb.Set(x, y, Col2Uint(byte(float32(r)*intensity), byte(float32(g)*intensity), byte(float32(b)*intensity))) //uint(texture.Bounds().Dx())
} }
} }
w0 += w0_xi w0 += w0_xi

View File

@ -2,11 +2,14 @@ package main
import ( import (
"flag" "flag"
"image"
"log" "log"
"os" "os"
"renderer1/hrend" "renderer1/hrend"
"runtime/pprof" // For performance profiling (unnecessary) "runtime/pprof" // For performance profiling (unnecessary)
_ "image/jpeg"
rl "github.com/gen2brain/raylib-go/raylib" rl "github.com/gen2brain/raylib-go/raylib"
) )
@ -27,6 +30,25 @@ func must(err error) {
} }
} }
func loadDefault() (*hrend.ObjModel, hrend.Framebuffer) {
log.Printf("Loading obj %s, texture %s", ObjectFile, TextureFile)
of, err := os.Open(ObjectFile)
must(err)
defer of.Close()
o, err := hrend.ParseObj(of)
must(err)
jf, err := os.Open(TextureFile)
must(err)
defer jf.Close()
timg, _, err := image.Decode(jf)
must(err)
texture := hrend.NewTexture(timg, 4)
return o, texture
}
// However flag works... idk // However flag works... idk
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
@ -51,6 +73,14 @@ func main() {
defer pprof.StopCPUProfile() defer pprof.StopCPUProfile()
} }
fb := NewRaylibBuffer(Width, Height)
rb := hrend.NewRenderbuffer(fb, Width, Height)
o, texture := loadDefault()
light := hrend.Vec3f{0, 0, -1}
var projection, worldToCamera, viewport hrend.Mat44f
rl.InitWindow(Width, Height, "Simple renderer with raylib") rl.InitWindow(Width, Height, "Simple renderer with raylib")
defer rl.CloseWindow() defer rl.CloseWindow()
@ -62,6 +92,30 @@ func main() {
for !rl.WindowShouldClose() { for !rl.WindowShouldClose() {
rl.BeginDrawing() rl.BeginDrawing()
screenmat := worldToCamera.Multiply(&projection)
screenmat = screenmat.Multiply(&viewport)
var sc [3]hrend.Vertex
rb.ResetZBuffer()
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]hrend.Vec3f
for i := range 3 { // Triangles, bro
sc[i] = f[i]
sc[i].Pos = screenmat.MultiplyPoint3(f[i].Pos)
fpt[i] = worldToCamera.MultiplyPoint3(f[i].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 {
hrend.TriangleTextured(&rb, texture, intensity, sc[0], sc[1], sc[2])
}
}
rl.ClearBackground(rl.RayWhite) rl.ClearBackground(rl.RayWhite)
rl.DrawText("Congrats! You created your first window!", 190, 200, 20, rl.LightGray) rl.DrawText("Congrats! You created your first window!", 190, 200, 20, rl.LightGray)

46
renderer1/raybuffer.go Normal file
View File

@ -0,0 +1,46 @@
package main
import (
rl "github.com/gen2brain/raylib-go/raylib"
)
type RaylibBuffer struct {
Data []rl.Color
Width uint
Height uint
}
func NewRaylibBuffer(width uint, height uint) *RaylibBuffer {
return &RaylibBuffer{
Data: make([]rl.Color, width*height),
Width: width,
Height: height,
}
}
// Sure hope this gets inlined...
func (fb *RaylibBuffer) Set(x uint, y uint, r byte, g byte, b byte) {
if x >= fb.Width || y >= fb.Height {
return
}
fb.Data[x+y*fb.Width].R = r
fb.Data[x+y*fb.Width].G = g
fb.Data[x+y*fb.Width].B = b
}
func (fb *RaylibBuffer) Get(x uint, y uint) (byte, byte, byte) {
if x >= fb.Width || y >= fb.Height {
return 0, 0, 0
}
return fb.Data[x+y*fb.Width].R,
fb.Data[x+y*fb.Width].G,
fb.Data[x+y*fb.Width].B
}
func (fb *RaylibBuffer) GetUv(u float32, v float32) (byte, byte, byte) {
x := uint(float32(fb.Width)*u) & (fb.Width - 1)
y := uint(float32(fb.Height)*(1-v)) & (fb.Height - 1)
return fb.Data[x+y*fb.Width].R,
fb.Data[x+y*fb.Width].G,
fb.Data[x+y*fb.Width].B
}