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)
}
// Color is in ARGB (alpha not used right now)
type Framebuffer struct {
Data []uint
// A simple buffer where you can set pixels
type Framebuffer interface {
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
Width uint
Height uint
}
// Create a new framebuffer for the given width and height.
func NewFramebuffer(width uint, height uint) Framebuffer {
return Framebuffer{
Data: make([]uint, width*height),
func NewRenderbuffer(d Framebuffer, width uint, height uint) RenderBuffer {
return RenderBuffer{
Data: d,
ZBuffer: make([]float32, width*height),
Width: width,
Height: height,
}
}
func NewTexture(texture image.Image, skip int) Framebuffer {
func NewTexture(texture image.Image, skip int) *SimpleFramebuffer {
bounds := texture.Bounds()
width := bounds.Dx() / skip
height := bounds.Dy() / skip
result := Framebuffer{
Data: make([]uint, width*height),
Width: uint(width),
Height: uint(height),
result := NewSimpleFramebuffer(uint(width), uint(height))
wlog := math.Log2(float64(width))
hlog := math.Log2(float64(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 x := bounds.Min.X; x < bounds.Max.X; x += skip {
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
}
// Fill zbuffer with pixels that are max distance away
func (fb *Framebuffer) ResetZBuffer() {
func (fb *RenderBuffer) ResetZBuffer() {
for i := range fb.ZBuffer {
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
func (fb *Framebuffer) ExportPPM() string {
func (fb *RenderBuffer) ExportPPM() string {
log.Printf("ExportPPM called for framebuffer %dx%d", fb.Width, fb.Height)
var result strings.Builder
result.WriteString(fmt.Sprintf("P3\n%d %d\n255\n", fb.Width, fb.Height))
for y := range fb.Height {
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.WriteRune('\n')
@ -102,20 +134,22 @@ func (fb *Framebuffer) ExportPPM() 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)
var result bytes.Buffer
result.WriteString(fmt.Sprintf("P6\n%d %d\n255\n", fb.Width, fb.Height))
for i := range fb.Data {
r, g, b := Uint2Col(fb.Data[i])
for y := range fb.Height {
for x := range fb.Width {
r, g, b := fb.Data.Get(x, y)
result.Write([]byte{r, g, b})
}
//result.WriteString(fmt.Sprintf("%d %d %d\t", r, g, b))
}
//result.WriteRune('\n')
return result.Bytes()
}
func (fb *Framebuffer) ZBuffer_ExportPPM() string {
func (fb *RenderBuffer) ZBuffer_ExportPPM() string {
var result strings.Builder
mini := 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)
}
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()
v1 := v1f.ToVec2i()
v2 := v2f.ToVec2i()
r, g, b := Uint2Col(color)
boundsTL, boundsBR := ComputeBoundingBox(v0, v1, v2)
if 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] {
//log.Print(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)))
}
@ -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()
v1 := v1v.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
if pz < fb.ZBuffer[x+y*fb.Width] {
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.Y + w1a*v1v.Tex.Y + w2a*v2v.Tex.Y),
)
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())
fb.Data.Set(x, y, byte(float32(r)*intensity), byte(float32(g)*intensity), byte(float32(b)*intensity))
}
}
w0 += w0_xi

View File

@ -2,11 +2,14 @@ package main
import (
"flag"
"image"
"log"
"os"
"renderer1/hrend"
"runtime/pprof" // For performance profiling (unnecessary)
_ "image/jpeg"
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
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
@ -51,6 +73,14 @@ func main() {
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")
defer rl.CloseWindow()
@ -62,6 +92,30 @@ func main() {
for !rl.WindowShouldClose() {
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.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
}