func ExampleBoundingBox_AddPoint() {
	box := NewBoundingBox()

	box.AddPoint(maths.NewVec3(-1, -1, -1))
	box.AddPoint(maths.NewVec3(0, 5, -0.5))
	box.AddPoint(maths.NewVec3(1, 0, 2))

	fmt.Printf("%s\n", box)

	// Output:
	// bbox[min: (-1, -1, -1), max: (1, 5, 2)]
}
func ExampleBoundingBox_Inside() {
	box := &BoundingBox{
		MinVolume: [3]float64{-1, -1, -1},
		MaxVolume: [3]float64{1, 1, 1},
	}

	fmt.Printf("0, 0, 0: %v\n", box.Inside(maths.NewVec3(0, 0, 0)))
	fmt.Printf("0, 0, 2: %v\n", box.Inside(maths.NewVec3(0, 0, 2)))

	// Output:
	// 0, 0, 0: true
	// 0, 0, 2: false
}
Exemple #3
0
func TestGetBoundingBox(t *testing.T) {
	meshData := []byte(`{
		"vertices": [
			{
				"normal": [0, 0, 1],
				"coordinates": [-1, -2, 0]
			},
			{
				"normal": [0, 0, 1],
				"coordinates": [1, 5, -8]
			},
			{
				"normal": [0, 0, 1],
				"coordinates": [1, 2, 0]
			},
			{
				"normal": [0, 0, 1],
				"coordinates": [1, 1, 0]
			}
		],
		"faces": [
			{
				"vertices": [0, 1, 2],
				"material": 42
			},
			{
				"vertices": [1, 2, 3],
				"material": 42
			}
		]
	}`)
	mesh := &Mesh{}
	err := json.Unmarshal(meshData, &mesh)
	if err != nil {
		t.Fatalf("Error reading json: %s\n", err)
		return
	}

	mesh.Init()

	bbox := mesh.GetBoundingBox()

	assertEqualVectors(t, maths.NewVec3(1, 5, 0), maths.NewVec3Array(bbox.MaxVolume))
	assertEqualVectors(t, maths.NewVec3(-1, -2, -8), maths.NewVec3Array(bbox.MinVolume))
}
Exemple #4
0
func ExamplePinholeCamera_ShootRay_weirdfocus() {
	var c Camera = &PinholeCamera{
		Focus:      *maths.NewVec3(-2, 15, 3),
		TopLeft:    *maths.NewVec3(-3, 16, 4),
		TopRight:   *maths.NewVec3(-1, 16, 4),
		BottomLeft: *maths.NewVec3(-3, 16, 2),
	}

	ray := c.ShootRay(0, 0)
	fmt.Printf("0, 0: %s\n", ray)

	ray = c.ShootRay(0.5, 0.5)
	fmt.Printf("0.5, 0.5: %s\n", ray)

	// Output:
	// 0, 0: (-2, 15, 3) -> (-0.577, 0.577, 0.577)
	// 0.5, 0.5: (-2, 15, 3) -> (0, 1, 0)
}
Exemple #5
0
// Vec3Sphere returns a random unit vector
func (r *Random) Vec3Sphere() *maths.Vec3 {
	u := r.FloatAB(-1, 1)
	theta := r.Float02Pi()
	return maths.NewVec3(
		math.Sqrt(1-u*u)*math.Cos(theta),
		math.Sqrt(1-u*u)*math.Sin(theta),
		u,
	)
}
Exemple #6
0
func TestPinholeCameraJson(t *testing.T) {
	data := []byte(`{
		"focus": [1, 2, 3],
		"top_left": [4, 5, 6],
		"top_right": [7, 8, 9],
		"bottom_left": [10, 11, 12]
	}`)
	c := &PinholeCamera{}
	err := json.Unmarshal(data, &c)
	if err != nil {
		t.Error(err)
	}

	assertEqualVectors(t, maths.NewVec3(1, 2, 3), &c.Focus)
	assertEqualVectors(t, maths.NewVec3(4, 5, 6), &c.TopLeft)
	assertEqualVectors(t, maths.NewVec3(7, 8, 9), &c.TopRight)
	assertEqualVectors(t, maths.NewVec3(10, 11, 12), &c.BottomLeft)
}
Exemple #7
0
func ExamplePinholeCamera_ShootRay() {
	var c Camera = &PinholeCamera{
		Focus:      *maths.NewVec3(0, 0, 0),
		TopLeft:    *maths.NewVec3(-1, 1, 1),
		TopRight:   *maths.NewVec3(1, 1, 1),
		BottomLeft: *maths.NewVec3(-1, 1, -1),
	}

	ray := c.ShootRay(0, 0)
	fmt.Printf("0, 0: %s\n", ray)

	ray = c.ShootRay(0.5, 0.5)
	fmt.Printf("0.5, 0.5: %s\n", ray)

	// Output:
	// 0, 0: (0, 0, 0) -> (-0.577, 0.577, 0.577)
	// 0.5, 0.5: (0, 0, 0) -> (0, 1, 0)
}
Exemple #8
0
// Init  of Mesh sets the Triangle indices in the Faces array, calculates the bounding box
// and KD tree, sets the surfaceOx and Oy and Cross Products of the sides of each triangle
func (m *Mesh) Init() {
	allIndices := make([]int, len(m.Faces))
	for i := range allIndices {
		allIndices[i] = i
	}

	m.BoundingBox = m.GetBoundingBox()
	m.tree = m.newKDtree(m.BoundingBox, allIndices, 0)
	for i := range m.Faces {
		triangle := &m.Faces[i]

		A := &m.Vertices[triangle.Vertices[0]].Coordinates
		B := &m.Vertices[triangle.Vertices[1]].Coordinates
		C := &m.Vertices[triangle.Vertices[2]].Coordinates

		AB := maths.MinusVectors(B, A)
		AC := maths.MinusVectors(C, A)

		triangle.AB = AB
		triangle.AC = AC
		triangle.ABxAC = maths.CrossProduct(AB, AC)

		surfaceA := &m.Vertices[triangle.Vertices[0]].UV
		surfaceB := &m.Vertices[triangle.Vertices[1]].UV
		surfaceC := &m.Vertices[triangle.Vertices[2]].UV

		surfaceAB := maths.MinusVectors(surfaceB, surfaceA)
		surfaceAC := maths.MinusVectors(surfaceC, surfaceA)

		// Solve using Cramer:
		// |surfaceAB.X * px + surfaceAX.X *qx + 1 = 0
		// |surfaceAB.Y * px + surfaceAX.Y *qx = 0
		// and
		// surfaceAB.X * py + surfaceAX.X *qy = 0
		// surfaceAB.X * py + surfaceAX.X *qy + 1 = 0

		px, qx := maths.SolveEquation(surfaceAB, surfaceAC, maths.NewVec3(1, 0, 0))
		py, qy := maths.SolveEquation(surfaceAB, surfaceAC, maths.NewVec3(0, 1, 0))

		triangle.surfaceOx = maths.AddVectors(AB.Scaled(px), AC.Scaled(qx))
		triangle.surfaceOy = maths.AddVectors(AB.Scaled(py), AC.Scaled(qy))
	}
}
Exemple #9
0
func TestAnyCameraJson(t *testing.T) {
	data := []byte(`{
		"type": 		"pinhole",
		"focus":		[0, 0, 0],
		"top_left": 	[-1, 1, 1],
		"top_right":	[1, 1, 1],
		"bottom_left": 	[-1, 1, -1]
	}`)
	c := &AnyCamera{}
	err := json.Unmarshal(data, &c)
	if err != nil {
		t.Error(err)
	}

	ray := c.ShootRay(0.5, 0.5)

	assertEqualVectors(t, maths.NewVec3(0, 1, 0), &ray.Direction)
	assertEqualVectors(t, maths.NewVec3(0, 0, 0), &ray.Start)
}
Exemple #10
0
func TestBoundingBoxSplit(t *testing.T) {
	box := &BoundingBox{
		MinVolume: [3]float64{-3.18, -3.96, -1.77},
		MaxVolume: [3]float64{4.58, 1.74, 4.56},
	}

	ray := &ray.Ray{
		Start:     *maths.NewVec3(4.49, -7.3, 5.53),
		Direction: *maths.NewVec3(-0.60, 0.5, -0.62),
	}
	ray.Init()

	if !box.Intersect(ray) {
		t.Error("ray should intersect big box")
	}

	left, right := box.Split(2, 1.39)

	assertEqualVectors(t, maths.NewVec3(-3.18, -3.96, -1.77), maths.NewVec3Array(left.MinVolume))
	assertEqualVectors(t, maths.NewVec3(-3.18, -3.96, 1.39), maths.NewVec3Array(right.MinVolume))
	assertEqualVectors(t, maths.NewVec3(4.58, 1.74, 1.39), maths.NewVec3Array(left.MaxVolume))
	assertEqualVectors(t, maths.NewVec3(4.58, 1.74, 4.56), maths.NewVec3Array(right.MaxVolume))

	if box.IntersectWall(2, 1.39, ray) {
		t.Error("ray shouldn't intersect the wall")
	}

	if right.Intersect(ray) {
		t.Error("ray shouldn't intersect right")
	}

	if !left.Intersect(ray) {
		t.Error("ray should intersect left")
	}
}
Exemple #11
0
// UnmarshalJSON implements the json.Unmarshaler interface
func (n *NumberSampler) UnmarshalJSON(data []byte) error {
	err := json.Unmarshal(data, &n.Fac)
	if err != nil {
		return err
	}
	n.Colour = hdrcolour.New(
		float32(n.Fac),
		float32(n.Fac),
		float32(n.Fac),
	)
	n.Vector = maths.NewVec3(n.Fac, n.Fac, n.Fac)

	return nil
}
Exemple #12
0
func ExampleBoundingBox_Intersect() {
	box := &BoundingBox{
		MinVolume: [3]float64{-1, -1, -1},
		MaxVolume: [3]float64{1, 1, 1},
	}

	ray1 := &ray.Ray{
		Start:     *maths.NewVec3(0, 0, 0),
		Direction: *maths.NewVec3(1, 0, 0),
	}

	ray2 := &ray.Ray{
		Start:     *maths.NewVec3(-5, 0, 0.5),
		Direction: *maths.NewVec3(1, 0, 0),
	}

	ray3 := &ray.Ray{
		Start:     *maths.NewVec3(-5, 0, 0.5),
		Direction: *maths.NewVec3(-1, 0, 0),
	}

	ray4 := &ray.Ray{
		Start:     *maths.NewVec3(-5, 0, 0),
		Direction: *maths.NewVec3(1, 0, 5),
	}
	ray1.Init()
	ray2.Init()
	ray3.Init()
	ray4.Init()

	fmt.Printf("ray 1: %v\n", box.Intersect(ray1))
	fmt.Printf("ray 2: %v\n", box.Intersect(ray2))
	fmt.Printf("ray 3: %v\n", box.Intersect(ray3))
	fmt.Printf("ray 4: %v\n", box.Intersect(ray4))

	// Output:
	// ray 1: true
	// ray 2: true
	// ray 3: false
	// ray 4: false
}
Exemple #13
0
// Vec3HemiCos returns a random unit vector chosen on a
// cosine-weighed hemisphere defined by normal
func (r *Random) Vec3HemiCos(normal *maths.Vec3) *maths.Vec3 {
	ox := maths.CrossProduct(maths.NewVec3(42, 56, -15), normal)
	for math.Abs(ox.Length()) < maths.Epsilon {
		ox = maths.CrossProduct(r.Vec3Sphere(), normal)
	}

	oy := maths.CrossProduct(ox, normal)
	ox.Normalise()
	oy.Normalise()

	u := r.Float01()
	radius := math.Sqrt(u)
	theta := r.Float02Pi()

	vec := normal.Scaled(math.Sqrt(math.Max(0, 1-u)))
	vec.Add(ox.Scaled(radius * math.Cos(theta)))
	vec.Add(oy.Scaled(radius * math.Sin(theta)))

	return vec
}
Exemple #14
0
func TestIntersectBoundingBoxZeroVolume(t *testing.T) {
	box := NewBoundingBox()
	box.AddPoint(maths.NewVec3(0, 0, 0))
	box.AddPoint(maths.NewVec3(1, 0, 0))
	box.AddPoint(maths.NewVec3(0, 1, 0))
	box.AddPoint(maths.NewVec3(1, 1, 0))

	ray := &ray.Ray{
		Start:     *maths.NewVec3(0.5, 0.5, 1),
		Direction: *maths.NewVec3(0, 0, -1),
	}

	if !box.Intersect(ray) {
		t.Error("ray.Ray must intersect box")
	}
}
Exemple #15
0
// GetVec3 returns the colour at (u, v)'s components as a vector
func (i *ImageTexture) GetVec3(intersection *ray.Intersection) *maths.Vec3 {
	colour := i.GetColour(intersection)
	return maths.NewVec3(float64(colour.R), float64(colour.G), float64(colour.B))
}
Exemple #16
0
func TestIntersectTwoTriangles(t *testing.T) {
	meshData := []byte(`{
		"vertices": [
			{
				"normal": [0, 0, 1],
				"coordinates": [0, 0, 0],
				"uv": [0, 0]
			},
			{
				"normal": [0, 0, 1],
				"coordinates": [1, 0, 0],
				"uv": [1, 0]
			},
			{
				"normal": [0, 0, 1],
				"coordinates": [0, 1, 0],
				"uv": [0, 1]
			},
			{
				"normal": [0, 0, -1],
				"coordinates": [0, 0, 4],
				"uv": [0, 0]
			},
			{
				"normal": [0, 0, -1],
				"coordinates": [1, 0, 4],
				"uv": [1, 0]
			},
			{
				"normal": [0, 0, -1],
				"coordinates": [0, 1, 4],
				"uv": [0, 1]
			}
		],
		"faces": [
			{
				"vertices": [3, 4, 5],
				"material": 5
			},
			{
				"vertices": [0, 1, 2],
				"material": 42
			}
		]
	}`)
	mesh := &Mesh{}
	err := json.Unmarshal(meshData, &mesh)
	if err != nil {
		t.Fatalf("Error reading json: %s\n", err)
	}

	mesh.Init()

	testRay := &ray.Ray{
		Start:     *maths.NewVec3(0.15, 0.11, 1),
		Direction: *maths.NewVec3(0, 0, -1),
	}

	var intersection *ray.Intersection
	intersection = mesh.Intersect(testRay)
	if intersection == nil {
		t.Fatal("Intersection shouldn't be nil")
	}
	if intersection.Material != 42 {
		t.Error("Intersected wrong triangle")
	}

	testRay.Direction.Z = 1

	intersection = mesh.Intersect(testRay)
	if intersection == nil {
		t.Fatal("Intersection shouldn't be nil")
	}
	if intersection.Material != 5 {
		t.Error("Intersected wrong triangle")
	}

}
Exemple #17
0
func ExampleMesh_Intersect() {
	meshData := []byte(`{
		"vertices": [
			{
				"normal": [0, 0, 1],
				"coordinates": [0, 0, 0],
				"uv": [0, 0]
			},
			{
				"normal": [0, 0, 1],
				"coordinates": [1, 0, 0],
				"uv": [1, 0]
			},
			{
				"normal": [0, 0, 1],
				"coordinates": [0, 1, 0],
				"uv": [0, 1]
			}
		],
		"faces": [
			{
				"vertices": [0, 1, 2],
				"material": 42
			}
		]
	}`)
	mesh := &Mesh{}
	err := json.Unmarshal(meshData, &mesh)
	if err != nil {
		fmt.Printf("Error reading json: %s\n", err)
		return
	}

	mesh.Init()

	testRay := &ray.Ray{
		Start:     *maths.NewVec3(0.15, 0.11, 1),
		Direction: *maths.NewVec3(0, 0, -1),
	}

	var (
		intersection *ray.Intersection
	)
	intersection = mesh.Intersect(testRay)

	if intersection == nil {
		fmt.Printf("no intersection\n")
	} else {
		fmt.Printf("intersection point: %s\n", intersection.Point)
		fmt.Printf("caused by ray: %s\n", intersection.Incoming)
		fmt.Printf("at a distance: %.3g\n", intersection.Distance)
		fmt.Printf("with surface coordinates: (%.3g, %.3g)\n",
			intersection.U, intersection.V)
		fmt.Printf("surface normal: %s\n", intersection.Normal)
		fmt.Printf("surface coordinate system: Ox: %s, Oy: %s\n",
			intersection.SurfaceOx, intersection.SurfaceOy)
		fmt.Printf("surface material: %d\n", intersection.Material)
	}

	// Output:
	// intersection point: (0.15, 0.11, 0)
	// caused by ray: (0.15, 0.11, 1) -> (0, 0, -1)
	// at a distance: 1
	// with surface coordinates: (0.15, 0.11)
	// surface normal: (0, 0, 1)
	// surface coordinate system: Ox: (1, 0, 0), Oy: (0, 1, 0)
	// surface material: 42
}