-
Notifications
You must be signed in to change notification settings - Fork 0
/
texture.go
129 lines (109 loc) · 3.16 KB
/
texture.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package gotrace
import (
"image"
"image/draw"
"log"
"math"
"os"
"github.com/ojrac/opensimplex-go"
"github.com/teobouvard/gotrace/util"
)
/*
Texture interface
Value
@in
u, v : coordinates of the point
@out
Vec3 : color at the given coordinates
*/
type Texture interface {
Value(u, v float64, pos Vec3) Vec3
}
// ConstantTexture is a uniform texture with a single color
type ConstantTexture struct {
color Vec3
}
// Value implements the texture interface for a ConstantTexture
func (t ConstantTexture) Value(u, v float64, pos Vec3) Vec3 {
return t.color
}
// CheckerTexture is a checkboard-like texture
type CheckerTexture struct {
odd Texture
even Texture
freq float64
}
// Value implements the texture interface for a CheckerTexture
func (t CheckerTexture) Value(u, v float64, pos Vec3) Vec3 {
sines := math.Sin(t.freq*pos.X) * math.Sin(t.freq*pos.Y) * math.Sin(t.freq*pos.Z)
if sines < 0.0 {
return t.odd.Value(u, v, pos)
}
return t.even.Value(u, v, pos)
}
// Noise is an opensimplex noise
type Noise struct {
noise opensimplex.Noise
frequency float64
}
// Value implements the texture interface for a Noise Texture
func (t Noise) Value(u, v float64, pos Vec3) Vec3 {
scaled := pos.Scale(t.frequency)
sample := t.noise.Eval3(scaled.X, scaled.Y, scaled.Z)
return WHITE.Scale(0.5 * (1.0 + sample))
}
// Marble is a marble-like texture
type Marble struct {
noise opensimplex.Noise
depth int
turbulence float64
scale float64
}
// genTurbulence creates a turbulence effect by summing noise at different frequencies
func (t Marble) genTurbulence(pos Vec3) float64 {
sum := 0.0
freq := pos
weight := 1.0
for i := 0; i < t.depth; i++ {
sum += weight * t.noise.Eval3(t.scale*freq.X, t.scale*freq.Y, t.scale*freq.Z)
weight *= 0.5
freq = freq.Scale(2)
}
return math.Abs(sum)
}
// Value implements the texture interface for a Marble texture
func (t Marble) Value(u, v float64, pos Vec3) Vec3 {
turbulence := t.genTurbulence(pos)
return WHITE.Scale(0.5 * (1.0 + math.Sin(t.scale*pos.Y+t.turbulence*turbulence)))
}
// Image is a texture mapped to an image file
type Image struct {
data image.Image
xoffset float64
yoffset float64
}
// NewImage creates an image texture from the path to the image, and an offset on the x axis
// The offset is given as a percentage of the width
func NewImage(file string, xoffset, yoffset float64) Image {
f, err := os.Open(file)
src, _, err := image.Decode(f)
if err != nil {
log.Fatal(err)
}
bounds := src.Bounds()
img := image.NewRGBA(image.Rect(0, 0, bounds.Dx(), bounds.Dy()))
draw.Draw(img, img.Bounds(), src, bounds.Min, draw.Src)
return Image{img, xoffset / 100.0, yoffset / 100.0}
}
// Value implements the texture interface for an Image texture
func (t Image) Value(u, v float64, pos Vec3) Vec3 {
width := t.data.Bounds().Max.X - 1
height := t.data.Bounds().Max.Y - 1
x := int(util.Map(u, 0, 1, 0, float64(width)))
y := int(util.Map(v, 0, 1, 0, float64(height)))
x = int(float64(x)+t.xoffset*float64(width)) % width
y = int(float64(y)+t.yoffset*float64(height)) % height
color := t.data.At(x, height-y)
r, g, b, _ := color.RGBA()
return Vec3{float64(r) / 65535, float64(g) / 65535, float64(b) / 65535}
}