Moving fast and probably breaking everything
This commit is contained in:
parent
61bcbcb823
commit
123ebc83df
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
46
renderer1/raybuffer.go
Normal 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user