3dtrial/tinyrender3_homework/image.go

114 lines
2.7 KiB
Go
Raw Normal View History

2024-07-28 22:01:15 +00:00
package main
import (
"bytes"
"fmt"
"image/color"
"math"
"strings"
)
// Convert rgb to uint
func Col2Uint(r, g, b byte) uint {
return (uint(r) << 16) | (uint(g) << 8) | uint(b)
}
func Color2Uint(col color.Color) uint {
r, g, b, _ := col.RGBA()
return uint((r << 16) | (g << 8) | b)
}
// Convert uint to rgb (in that order)
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
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),
ZBuffer: make([]float32, width*height),
Width: width,
Height: height,
}
}
// Fill zbuffer with pixels that are max distance away
func (fb *Framebuffer) ResetZBuffer() {
for i := range fb.ZBuffer {
fb.ZBuffer[i] = math.MaxFloat32
}
}
// 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 {
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])
result.WriteString(fmt.Sprintf("%d %d %d\t", r, g, b))
}
result.WriteRune('\n')
}
return result.String()
}
func (fb *Framebuffer) ExportPPMP6() []byte {
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])
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 {
var result strings.Builder
mini := float32(math.MaxFloat32)
maxi := float32(-math.MaxFloat32)
for _, f := range fb.ZBuffer {
if f == math.MaxFloat32 {
continue
}
mini = min(f, mini)
maxi = max(f, maxi)
}
result.WriteString(fmt.Sprintf("P2\n%d %d\n255\n", fb.Width, fb.Height))
for y := range fb.Height {
for x := range fb.Width {
if fb.ZBuffer[x+y*fb.Width] == math.MaxFloat32 {
result.WriteString("0 ")
} else {
zp := byte(math.Abs(float64(255 * fb.ZBuffer[x+y*fb.Width] / (maxi - mini))))
result.WriteString(fmt.Sprintf("%d ", zp))
}
}
result.WriteRune('\n')
}
return result.String()
}