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)
|
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 {
|
||||||
result.Write([]byte{r, g, b})
|
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.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)
|
||||||
|
@ -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
|
||||||
|
@ -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
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