Skip to content

Instantly share code, notes, and snippets.

@peterhellberg
Last active May 17, 2020 18:19
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save peterhellberg/71ff4dca1f4524791ec0ba1286620a8f to your computer and use it in GitHub Desktop.
Save peterhellberg/71ff4dca1f4524791ec0ba1286620a8f to your computer and use it in GitHub Desktop.
Cube software 3d renderer
package main
import (
"image/color"
"github.com/faiface/pixel"
"github.com/faiface/pixel/imdraw"
"github.com/faiface/pixel/pixelgl"
"github.com/go-gl/mathgl/mgl64"
)
var (
w, h float64 = 640, 640
fg = color.RGBA{200, 200, 200, 55}
bg = color.RGBA{27, 27, 27, 255}
)
type Camera struct {
Position mgl64.Vec3
Target mgl64.Vec3
}
type Face struct {
A, B, C int
}
type Mesh struct {
Name string
Vertices []mgl64.Vec3
Faces []Face
Position mgl64.Vec3
Rotation mgl64.Vec3
}
func NewMesh(name string, verticesCount, facesCount int) *Mesh {
return &Mesh{
Name: name,
Vertices: make([]mgl64.Vec3, verticesCount),
Faces: make([]Face, facesCount),
}
}
type Device struct {
*imdraw.IMDraw
}
func NewDevice(imd *imdraw.IMDraw) *Device {
return &Device{imd}
}
func (d *Device) Project(coord mgl64.Vec3, transMat mgl64.Mat4) pixel.Vec {
point := mgl64.TransformCoordinate(coord, transMat).Mul(50)
x := int(float64(point.X())+w/2) >> 0
y := int(float64(point.Y())+h/2) >> 0
return pixel.V(float64(x), float64(y))
}
func (d *Device) Render(camera *Camera, meshes []*Mesh) {
viewMatrix := mgl64.LookAtV(camera.Position, camera.Target, mgl64.Vec3{0, 1, 0})
projectionMatrix := mgl64.Perspective(0.90, w/h, 1, 1000)
d.Clear()
for _, mesh := range meshes {
worldMatrix := mgl64.HomogRotate3DX(mesh.Rotation.X()).
Mul4(mgl64.HomogRotate3DY(mesh.Rotation.Y())).
Mul4(mgl64.HomogRotate3DZ(mesh.Rotation.Z()))
transformMatrix := worldMatrix.Mul4(viewMatrix).Mul4(projectionMatrix)
for i, face := range mesh.Faces {
p1 := d.Project(mesh.Vertices[face.A], transformMatrix)
p2 := d.Project(mesh.Vertices[face.B], transformMatrix)
p3 := d.Project(mesh.Vertices[face.C], transformMatrix)
d.Color = color.RGBA{100 + uint8(i*32%155), uint8(int(fg.G) * i % 255), fg.B / 4, 255}
d.Push(p1, p2, p3, p1)
d.Polygon(3)
}
for _, v := range mesh.Vertices {
d.Color = color.White
d.Push(d.Project(v, transformMatrix))
d.Circle(6, 0)
}
}
}
func run() {
win, err := pixelgl.NewWindow(pixelgl.WindowConfig{
Bounds: pixel.R(0, 0, w, h),
VSync: true,
Undecorated: true,
})
if err != nil {
panic(err)
}
device := NewDevice(imdraw.New(nil))
camera := &Camera{
Position: mgl64.Vec3{0, 0, 1},
Target: mgl64.Vec3{0, 0, 0},
}
cube := newCube()
meshes := []*Mesh{cube}
for !win.Closed() {
win.SetClosed(win.JustPressed(pixelgl.KeyEscape) || win.JustPressed(pixelgl.KeyQ))
win.Clear(bg)
cube.Rotation = cube.Rotation.Add(mgl64.Vec3{0.01, 0.01, 0.0})
device.Render(camera, meshes)
device.Draw(win)
win.Update()
}
}
func main() {
pixelgl.Run(run)
}
func newCube() *Mesh {
return &Mesh{
Position: mgl64.Vec3{0, 0, 0},
Vertices: []mgl64.Vec3{
{-1, 1, 1},
{1, 1, 1},
{-1, -1, 1},
{1, -1, 1},
{-1, 1, -1},
{1, 1, -1},
{1, -1, -1},
{-1, -1, -1},
},
Faces: []Face{
// Front
{A: 0, B: 1, C: 2},
{A: 1, B: 2, C: 3},
// Back
{A: 4, B: 5, C: 6},
{A: 4, B: 6, C: 7},
// Left
{A: 1, B: 3, C: 4},
{A: 1, B: 7, C: 4},
// Right
{A: 0, B: 2, C: 5},
{A: 0, B: 6, C: 5},
// Top
{A: 3, B: 4, C: 5},
{A: 2, B: 3, C: 5},
// Bottom
{A: 0, B: 1, C: 6},
{A: 1, B: 7, C: 6},
},
}
}
@peterhellberg
Copy link
Author

3d-first
3d-circles
3d-dots
3d-transpose

@peterhellberg
Copy link
Author

polygon-20171107-041348

@peterhellberg
Copy link
Author

3d-line

@peterhellberg
Copy link
Author

peterhellberg commented Nov 21, 2017

Still not sorting the triangles

screen shot 2017-11-21 at 23 08 39

@peterhellberg
Copy link
Author

peterhellberg commented Dec 10, 2017

3d-cube-broken-sorting

package main

import (
	"image/color"
	"sort"

	"github.com/faiface/pixel"
	"github.com/faiface/pixel/imdraw"
	"github.com/faiface/pixel/pixelgl"
	"github.com/go-gl/mathgl/mgl64"
)

var w, h float64 = 640, 640

type Camera struct {
	Position mgl64.Vec3
	Target   mgl64.Vec3
}

type Triangle struct {
	Vectors []pixel.Vec
	Color   color.RGBA
	AvgZ    float64
}

type Face struct {
	A, B, C int
	Color   color.RGBA
}

type Mesh struct {
	Name     string
	Vertices []mgl64.Vec3
	Faces    []Face
	Position mgl64.Vec3
	Rotation mgl64.Vec3
}

func NewMesh(name string, verticesCount, facesCount int) *Mesh {
	return &Mesh{
		Name:     name,
		Vertices: make([]mgl64.Vec3, verticesCount),
		Faces:    make([]Face, facesCount),
	}
}

type Device struct {
	*imdraw.IMDraw
}

func NewDevice(imd *imdraw.IMDraw) *Device {
	return &Device{imd}
}

func (d *Device) TransformAndProject(coord mgl64.Vec3, transMat mgl64.Mat4) (mgl64.Vec3, pixel.Vec) {
	t := mgl64.TransformCoordinate(coord, transMat).Mul(50)

	return t, pixel.V(
		float64(int(float64(t.X())+w/2)>>0),
		float64(int(float64(t.Y())+h/2)>>0),
	)
}

func (d *Device) Render(camera *Camera, meshes []*Mesh) {
	viewMatrix := mgl64.LookAtV(camera.Position, camera.Target, mgl64.Vec3{0, 1, 0})
	projectionMatrix := mgl64.Perspective(0.90, w/h, 1, 1000)

	d.Clear()

	for _, mesh := range meshes {
		worldMatrix := mgl64.HomogRotate3DX(mesh.Rotation.X()).
			Mul4(mgl64.HomogRotate3DY(mesh.Rotation.Y())).
			Mul4(mgl64.HomogRotate3DZ(mesh.Rotation.Z()))

		transformMatrix := worldMatrix.Mul4(viewMatrix).Mul4(projectionMatrix)

		var triangles = make([]Triangle, len(mesh.Faces))

		for i, face := range mesh.Faces {
			t1, p1 := d.TransformAndProject(mesh.Vertices[face.A], transformMatrix)
			t2, p2 := d.TransformAndProject(mesh.Vertices[face.B], transformMatrix)
			t3, p3 := d.TransformAndProject(mesh.Vertices[face.C], transformMatrix)

			triangles[i] = Triangle{
				Vectors: []pixel.Vec{p1, p2, p3, p1},
				Color:   face.Color,
				AvgZ:    (t1.Z() + t2.Z() + t3.Z()) / 3,
			}
		}

		sort.Slice(triangles, func(i, j int) bool {
			return triangles[i].AvgZ > triangles[j].AvgZ
		})

		for _, t := range triangles {
			d.Color = t.Color
			d.Push(t.Vectors...)
			d.Polygon(0)

			for _, v := range t.Vectors {
				d.Color = color.White
				d.Push(v)
				d.Circle(4, 0)
			}
		}
	}
}

func run() {
	win, err := pixelgl.NewWindow(pixelgl.WindowConfig{
		Bounds:      pixel.R(0, 0, w, h),
		VSync:       true,
		Undecorated: true,
	})
	if err != nil {
		panic(err)
	}

	device := NewDevice(imdraw.New(nil))

	camera := &Camera{
		Position: mgl64.Vec3{0, 0, 1},
		Target:   mgl64.Vec3{0, 0, 0},
	}

	cube := newCube()

	meshes := []*Mesh{cube}

	for !win.Closed() {
		cube.Rotation = cube.Rotation.Add(mgl64.Vec3{0.01, 0.01, 0.0})

		win.SetClosed(win.JustPressed(pixelgl.KeyEscape) || win.JustPressed(pixelgl.KeyQ))
		win.Clear(color.RGBA{27, 27, 27, 255})

		device.Render(camera, meshes)
		device.Draw(win)

		win.Update()
	}
}

func main() {
	pixelgl.Run(run)
}

func newCube() *Mesh {
	return &Mesh{
		Position: mgl64.Vec3{0, 0, 0},
		Vertices: []mgl64.Vec3{
			{-1, 1, 1},
			{1, 1, 1},
			{-1, -1, 1},
			{1, -1, 1},
			{-1, 1, -1},
			{1, 1, -1},
			{1, -1, -1},
			{-1, -1, -1},
		},
		Faces: []Face{
			// Front
			{A: 0, B: 1, C: 2, Color: color.RGBA{255, 0, 0, 255}},
			{A: 1, B: 2, C: 3, Color: color.RGBA{127, 0, 0, 255}},

			// Back
			{A: 4, B: 5, C: 6, Color: color.RGBA{0, 255, 0, 255}},
			{A: 4, B: 6, C: 7, Color: color.RGBA{0, 127, 0, 255}},

			// Left
			{A: 1, B: 3, C: 4, Color: color.RGBA{0, 0, 255, 255}},
			{A: 1, B: 7, C: 4, Color: color.RGBA{0, 0, 127, 255}},

			// Right
			{A: 0, B: 2, C: 5, Color: color.RGBA{255, 255, 0, 255}},
			{A: 0, B: 6, C: 5, Color: color.RGBA{127, 127, 0, 255}},

			// Top
			{A: 3, B: 4, C: 5, Color: color.RGBA{0, 255, 255, 255}},
			{A: 2, B: 3, C: 5, Color: color.RGBA{0, 127, 127, 255}},

			// Bottom
			{A: 0, B: 1, C: 6, Color: color.RGBA{255, 0, 255, 255}},
			{A: 1, B: 7, C: 6, Color: color.RGBA{127, 0, 127, 255}},
		},
	}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment