1. package main
  2. import (
  3. "encoding/binary"
  4. "fmt"
  5. "math"
  6. "math/rand"
  7. "os"
  8. "runtime"
  9. //"runtime/pprof"
  10. // "sync/atomic"
  11. "time"
  12. )
  13. const (
  14. EPSILON = math.SmallestNonzeroFloat32
  15. RAY_BIAS = 0.0005
  16. RESOLUTION = 128
  17. SPP = 16 * 16 // samples per pixel
  18. MIN_BOUNCES = 4
  19. MAX_BOUNCES = 8
  20. DIRECT_ILLUMINATION = true
  21. DIFFUSE = iota
  22. GLOSSY
  23. MIRROR
  24. MAX_MATERIAL_TYPE
  25. )
  26. type rfloat float64
  27. type Vector struct {
  28. x, y, z rfloat
  29. }
  30. type Material struct {
  31. materialType uint8
  32. diffuse Vector
  33. emissive Vector
  34. specular Vector
  35. exp rfloat
  36. }
  37. type Sphere struct {
  38. radius rfloat
  39. center Vector
  40. material *Material
  41. radiusSqr rfloat
  42. }
  43. type Ray struct {
  44. origin Vector
  45. dir Vector
  46. }
  47. type Plane struct {
  48. normal Vector
  49. d rfloat
  50. }
  51. type Camera struct {
  52. right, up, forward Vector
  53. fovScale rfloat
  54. }
  55. type Scene struct {
  56. objects []Sphere
  57. lights []int
  58. camera Camera
  59. }
  60. type FrameBuffer struct {
  61. w, h int
  62. buf []uint32
  63. }
  64. type Context struct {
  65. channelQuit chan bool
  66. channelJob chan WorkChunk
  67. channelJoin chan bool
  68. samples [SPP * 2]rfloat
  69. scene *Scene
  70. buffer *FrameBuffer
  71. progress int32
  72. }
  73. type WorkChunk struct {
  74. x0, y0, w, h int
  75. }
  76. func Rand01() rfloat {
  77. return rfloat(rand.Float32())
  78. }
  79. func (mat *Material) IsLight() bool {
  80. return mat.emissive.x > EPSILON || mat.emissive.y > EPSILON || mat.emissive.z > EPSILON
  81. }
  82. func (scene *Scene) CollectLights() {
  83. for index, s := range scene.objects {
  84. if s.material.IsLight() {
  85. scene.lights = append(scene.lights, index)
  86. }
  87. }
  88. }
  89. func (scene *Scene) Initialize() {
  90. for index := range scene.objects {
  91. s := &scene.objects[index]
  92. s.radiusSqr = s.radius * s.radius
  93. }
  94. }
  95. func (ray *Ray) CalcIntersectionPoint(t rfloat, intersectionPoint *Vector) {
  96. *intersectionPoint = ray.origin
  97. Madd(intersectionPoint, intersectionPoint, &ray.dir, t)
  98. }
  99. func (v *Vector) MaxComponent() rfloat {
  100. maxC := v.x
  101. if maxC < v.y {
  102. maxC = v.y
  103. }
  104. if maxC < v.z {
  105. maxC = v.z
  106. }
  107. return maxC
  108. }
  109. func InitializeSamplesUniform(samples *[SPP * 2]rfloat) {
  110. for i := 0; i < SPP*2; i++ {
  111. samples[i] = Rand01()
  112. }
  113. }
  114. func InitializeSamples(samples *[SPP * 2]rfloat) {
  115. xstrata := sqrtf(SPP)
  116. ystrata := rfloat(SPP) / xstrata
  117. is := 0
  118. for ystep := 0; ystep < int(ystrata); ystep++ {
  119. for xstep := 0; xstep < int(xstrata); xstep++ {
  120. fx := (rfloat(xstep) + Rand01()) / xstrata
  121. fy := (rfloat(ystep) + Rand01()) / ystrata
  122. samples[is] = fx
  123. samples[is+1] = fy
  124. is += 2
  125. }
  126. }
  127. }
  128. // t, object index
  129. func (scene *Scene) Intersect(ray *Ray) (rfloat, int) {
  130. var minT rfloat = math.MaxFloat32
  131. var iSphere = -1
  132. for i := range scene.objects {
  133. s := &scene.objects[i]
  134. t := SphereIntersect(s, ray)
  135. if t > 0 && t < minT {
  136. minT = t
  137. iSphere = i
  138. }
  139. }
  140. if iSphere < 0 {
  141. minT = -1.0
  142. }
  143. return minT, iSphere
  144. }
  145. func Clamp01(x rfloat) rfloat {
  146. if x < 0 {
  147. return 0
  148. }
  149. if x > 1 {
  150. return 1
  151. }
  152. return x
  153. }
  154. func GetColor(color *Vector) (uint32, uint32, uint32) {
  155. color.x = Clamp01(color.x)
  156. color.y = Clamp01(color.y)
  157. color.z = Clamp01(color.z)
  158. r := uint32(powf(color.x, 0.45)*255.0 + 0.5)
  159. g := uint32(powf(color.y, 0.45)*255.0 + 0.5)
  160. b := uint32(powf(color.z, 0.45)*255.0 + 0.5)
  161. return r, g, b
  162. }
  163. func SampleHemisphere_Uniform(u1, u2 rfloat, dir *Vector) {
  164. r := sqrtf(1.0 - u1*u1)
  165. phi := 2.0 * math.Pi * u2
  166. Set(dir, cosf(phi)*r, sinf(phi)*r, u1)
  167. }
  168. func SampleHemisphere_Cosine(u1, u2 rfloat, dir *Vector) {
  169. phi := 2.0 * math.Pi * u1
  170. r := sqrtf(u2)
  171. sx := cosf(phi) * r
  172. sy := sinf(phi) * r
  173. Set(dir, sx, sy, sqrtf(1.0 - sx*sx - sy*sy))
  174. }
  175. func SampleHemisphere_Spec(u1, u2, exp rfloat, dir *Vector) {
  176. phi := 2.0 * math.Pi * u1
  177. cosTheta := powf(1.0 - u2, 1.0 / (exp + 1.0))
  178. sinTheta := sqrtf(1.0 - cosTheta*cosTheta)
  179. Set(dir, cosf(phi) * sinTheta, sinf(phi) * sinTheta, cosTheta)
  180. }
  181. func fabsf(x rfloat) rfloat {
  182. return rfloat(math.Abs(float64(x)))
  183. }
  184. func BuildBasis(v1, v2, v3 *Vector) {
  185. if fabsf(v1.x) > fabsf(v2.y) {
  186. ooLen := 1.0 / sqrtf(v1.x*v1.x+v1.z*v1.z)
  187. Set(v2, -v1.z*ooLen, 0.0, v1.x*ooLen)
  188. } else {
  189. ooLen := 1.0 / sqrtf(v1.y*v1.y+v1.z*v1.z)
  190. Set(v2, 0.0, v1.z*ooLen, -v1.y*ooLen)
  191. }
  192. Cross(v3, v1, v2)
  193. }
  194. func TransformToBasis(vin, vx, vy, vz, vout *Vector) {
  195. vout.x = vx.x*vin.x + vy.x*vin.y + vz.x*vin.z
  196. vout.y = vx.y*vin.x + vy.y*vin.y + vz.y*vin.z
  197. vout.z = vx.z*vin.x + vy.z*vin.y + vz.z*vin.z
  198. }
  199. func Reflect(dir, n *Vector) Vector {
  200. h := *n
  201. Scale(&h, 2.0*Dot(dir, n))
  202. var reflected Vector
  203. Sub(&reflected, &h, dir)
  204. return reflected
  205. }
  206. func SampleLights(scene *Scene, intersectionPoint *Vector, normal, rayDir *Vector, material *Material) Vector {
  207. result := Vector{0, 0, 0}
  208. var toLight Vector
  209. for _, lightIndex := range scene.lights {
  210. light := scene.objects[lightIndex]
  211. Sub(&toLight, &light.center, intersectionPoint)
  212. lightDistSqr := Dot(&toLight, &toLight)
  213. Normalize(&toLight)
  214. d := Dot(normal, &toLight)
  215. if d < 0 {
  216. d = 0
  217. }
  218. var shadowRay Ray
  219. shadowRay.origin = *intersectionPoint
  220. shadowRay.dir = toLight
  221. t, objIndex := scene.Intersect(&shadowRay)
  222. if t > 0 && objIndex == lightIndex {
  223. sinAlphaMaxSqr := light.radiusSqr/lightDistSqr
  224. cosAlphaMax := sqrtf(1.0 - sinAlphaMaxSqr)
  225. omega := 2.0 * (1.0 - cosAlphaMax)
  226. d *= omega
  227. c := VecMul(&material.diffuse, &light.material.emissive)
  228. Madd(&result, &result, &c, d)
  229. // Specular part
  230. if material.materialType != DIFFUSE {
  231. reflected := Reflect(&toLight, normal)
  232. d = -Dot(&reflected, rayDir)
  233. if d < 0 {
  234. d = 0.0
  235. }
  236. s := material.specular
  237. smul := powf(d, material.exp)
  238. Madd(&result, &result, &s, smul)
  239. }
  240. }
  241. }
  242. return result
  243. }
  244. func InterreflectDiffuse(normal, intersectionPoint *Vector, u1, u2 rfloat, newRay *Ray) {
  245. var v2, v3 Vector
  246. BuildBasis(normal, &v2, &v3)
  247. var sampledDir Vector
  248. SampleHemisphere_Cosine(u1, u2, &sampledDir)
  249. newRay.origin = *intersectionPoint
  250. TransformToBasis(&sampledDir, &v2, &v3, normal, &newRay.dir)
  251. }
  252. func InterreflectSpecular(normal, intersectionPoint *Vector, u1, u2 rfloat, material *Material, newRay *Ray) {
  253. view := newRay.dir
  254. Negate(&view)
  255. reflected := Reflect(&view, normal)
  256. Normalize(&reflected)
  257. var v2, v3 Vector
  258. BuildBasis(&reflected, &v2, &v3)
  259. var sampledDir Vector
  260. SampleHemisphere_Spec(u1, u2, material.exp, &sampledDir)
  261. newRay.origin = *intersectionPoint
  262. TransformToBasis(&sampledDir, &v2, &v3, &reflected, &newRay.dir)
  263. }
  264. func Trace(ray *Ray, context *Context, u1, u2 rfloat) Vector {
  265. scene := context.scene
  266. result := Vector{0, 0, 0}
  267. rrScale := Vector{1, 1, 1}
  268. direct := true
  269. for bounce := 0; bounce < MAX_BOUNCES; bounce++ {
  270. t, objectIndex := scene.Intersect(ray)
  271. if t <= 0 {
  272. break
  273. }
  274. hitObject := &scene.objects[objectIndex]
  275. material := hitObject.material
  276. if !DIRECT_ILLUMINATION || direct {
  277. e := VecMul(&rrScale, &material.emissive)
  278. Add(&result, &result, &e)
  279. }
  280. // Russian roulette (termination test)
  281. diffuse := material.diffuse
  282. maxDiffuse := diffuse.MaxComponent()
  283. if bounce >= MIN_BOUNCES || maxDiffuse < EPSILON {
  284. if Rand01() > maxDiffuse {
  285. break
  286. }
  287. Scale(&diffuse, 1.0 / maxDiffuse)
  288. }
  289. var intersectionPoint Vector
  290. ray.CalcIntersectionPoint(t, &intersectionPoint)
  291. normal := intersectionPoint
  292. Sub(&normal, &normal, &hitObject.center)
  293. Scale(&normal, 1.0 / hitObject.radius)
  294. if Dot(&normal, &ray.dir) >= 0 {
  295. Negate(&normal)
  296. }
  297. if material.materialType == DIFFUSE {
  298. direct = false
  299. if DIRECT_ILLUMINATION {
  300. directLight := SampleLights(scene, &intersectionPoint, &normal, &ray.dir, material)
  301. directLight = VecMul(&directLight, &rrScale)
  302. Add(&result, &result, &directLight)
  303. }
  304. InterreflectDiffuse(&normal, &intersectionPoint, u1, u2, ray)
  305. rrScale = VecMul(&rrScale, &diffuse)
  306. } else if material.materialType == GLOSSY {
  307. direct = false
  308. if DIRECT_ILLUMINATION {
  309. directLight := SampleLights(scene, &intersectionPoint, &normal, &ray.dir, material)
  310. directLight = VecMul(&directLight, &rrScale)
  311. Add(&result, &result, &directLight)
  312. }
  313. // Specular/diffuse Russian roulette
  314. maxSpec := material.specular.MaxComponent()
  315. p := maxSpec / (maxSpec + maxDiffuse)
  316. smult := 1.0 / p
  317. if Rand01() > p { // diffuse
  318. InterreflectDiffuse(&normal, &intersectionPoint, u1, u2, ray)
  319. color := diffuse
  320. Scale(&color, 1.0 / (1.0 - 1.0/smult))
  321. rrScale = VecMul(&rrScale, &color)
  322. } else {
  323. InterreflectSpecular(&normal, &intersectionPoint, u1, u2, material, ray)
  324. color := material.specular
  325. Scale(&color, smult)
  326. rrScale = VecMul(&rrScale, &color)
  327. }
  328. } else {
  329. view := ray.dir
  330. Negate(&view)
  331. reflected := Reflect(&view, &normal)
  332. Normalize(&reflected)
  333. ray.origin = intersectionPoint
  334. ray.dir = reflected
  335. rrScale = VecMul(&rrScale, &diffuse)
  336. }
  337. sampleIdx := rand.Intn(SPP)
  338. u1 = context.samples[sampleIdx*2]
  339. u2 = context.samples[sampleIdx*2+1]
  340. }
  341. return result
  342. }
  343. func ApplyTentFilter(samples []rfloat, numSamples int) {
  344. for i := 0; i < numSamples; i++ {
  345. x := samples[i*2+0]
  346. y := samples[i*2+1]
  347. if x < 0.5 {
  348. samples[i*2] = sqrtf(2.0*x) - 1.0
  349. } else {
  350. samples[i*2] = 1.0 - sqrtf(2.0-2.0*x)
  351. }
  352. if y < 0.5 {
  353. samples[i*2+1] = sqrtf(2.0 * y)
  354. } else {
  355. samples[i*2+1] = 1.0 - sqrtf(2.0-2.0*y)
  356. }
  357. }
  358. }
  359. func ProcessChunk(context *Context, chunk *WorkChunk) {
  360. endY := chunk.y0 + chunk.h
  361. endX := chunk.x0 + chunk.w
  362. var res rfloat = RESOLUTION
  363. camera := &context.scene.camera
  364. var viewRay Ray
  365. cx := Vector{camera.fovScale, 0, 0}
  366. var cy Vector
  367. Cross(&cy, &cx, &camera.forward)
  368. Normalize(&cy)
  369. Scale(&cy, camera.fovScale)
  370. invSPP := 1.0 / rfloat(SPP)
  371. var chunkSamples [SPP * 2]rfloat
  372. var sphereSamples [SPP * 2]rfloat
  373. InitializeSamples(&chunkSamples)
  374. ApplyTentFilter(chunkSamples[0:], SPP)
  375. for y := chunk.y0; y < endY; y++ {
  376. yoffset := y * context.buffer.w
  377. scanline := context.buffer.buf[yoffset:]
  378. for x := chunk.x0; x < endX; x++ {
  379. //if x > RESOLUTION / 2 {
  380. // InitializeSamplesUniform(&sphereSamples)
  381. //} else {
  382. InitializeSamples(&sphereSamples)
  383. //}
  384. cr := Vector{0, 0, 0}
  385. for aa := 0; aa < 4; aa++ {
  386. aax := rfloat(aa & 0x1)
  387. aay := rfloat(aa >> 1)
  388. pr := Vector{0, 0, 0}
  389. for s := 0; s < SPP; s++ {
  390. dx := chunkSamples[s*2]
  391. dy := chunkSamples[s*2+1]
  392. px := ((aax+0.5+dx)/2.0+rfloat(x))/res - 0.5
  393. py := -(((aay+0.5+dy)/2.0+rfloat(y))/res - 0.5)
  394. Set(&viewRay.origin, 50, 52, 295.6)
  395. Set(&viewRay.dir, 0, 0, 0)
  396. ccx := cx
  397. ccy := cy
  398. Scale(&ccx, px)
  399. Scale(&ccy, py)
  400. Add(&viewRay.dir, &ccx, &ccy)
  401. Add(&viewRay.dir, &viewRay.dir, &camera.forward)
  402. Normalize(&viewRay.dir)
  403. //scaledDir := viewRay.dir
  404. //Scale(&scaledDir, 136.0)
  405. Madd(&viewRay.origin, &viewRay.origin, &viewRay.dir, 136.0)
  406. //Add(&viewRay.origin, &viewRay.origin, &scaledDir)
  407. u1 := sphereSamples[s*2]
  408. u2 := sphereSamples[s*2+1]
  409. r := Trace(&viewRay, context, u1, u2)
  410. Madd(&pr, &pr, &r, invSPP)
  411. }
  412. Madd(&cr, &cr, &pr, 0.25)
  413. }
  414. r, g, b := GetColor(&cr)
  415. scanline[x] = 0xFF000000 | (r << 16) | (g << 8) | b
  416. }
  417. }
  418. // progress := atomic.AddInt32(&context.progress, 1)
  419. // fmt.Printf("%v\n", progress)
  420. }
  421. func workerFunc(ctx *Context) {
  422. channelJob := ctx.channelJob
  423. for {
  424. select {
  425. case chunk := <-channelJob:
  426. ProcessChunk(ctx, &chunk)
  427. case <-ctx.channelQuit:
  428. ctx.channelJoin <- true
  429. return
  430. }
  431. }
  432. }
  433. func Set(v *Vector, x rfloat, y rfloat, z rfloat) {
  434. v.x = x
  435. v.y = y
  436. v.z = z
  437. }
  438. func Add(result, a, b *Vector) {
  439. result.x = a.x + b.x
  440. result.y = a.y + b.y
  441. result.z = a.z + b.z
  442. }
  443. func Sub(result, a, b *Vector) {
  444. result.x = a.x - b.x
  445. result.y = a.y - b.y
  446. result.z = a.z - b.z
  447. }
  448. func VecMul(a, b *Vector) Vector {
  449. return Vector{a.x * b.x, a.y * b.y, a.z * b.z}
  450. }
  451. func Scale(v *Vector, s rfloat) {
  452. v.x *= s
  453. v.y *= s
  454. v.z *= s
  455. }
  456. func Negate(v *Vector) {
  457. v.x = -v.x
  458. v.y = -v.y
  459. v.z = -v.z
  460. }
  461. // result = a + b*s
  462. func Madd(result, a, b *Vector, s rfloat) {
  463. result.x = a.x + b.x * s
  464. result.y = a.y + b.y * s
  465. result.z = a.z + b.z * s
  466. }
  467. func Dot(a, b *Vector) rfloat {
  468. return a.x*b.x + a.y*b.y + a.z*b.z
  469. }
  470. func Cross(result, a, b *Vector) {
  471. result.x = a.y*b.z - a.z*b.y
  472. result.y = a.z*b.x - a.x*b.z
  473. result.z = a.x*b.y - a.y*b.x
  474. }
  475. func Normalize(v *Vector) {
  476. lenSqr := Dot(v, v)
  477. if lenSqr > EPSILON {
  478. len := rfloat(math.Sqrt(float64(lenSqr)))
  479. v.x /= len
  480. v.y /= len
  481. v.z /= len
  482. }
  483. }
  484. func powf(x, y rfloat) rfloat {
  485. return rfloat(math.Pow(float64(x), float64(y)))
  486. }
  487. func tanf(x rfloat) rfloat {
  488. return rfloat(math.Tan(float64(x)))
  489. }
  490. func sqrtf(x rfloat) rfloat {
  491. return rfloat(math.Sqrt(float64(x)))
  492. }
  493. func sinf(x rfloat) rfloat {
  494. return rfloat(math.Sin(float64(x)))
  495. }
  496. func cosf(x rfloat) rfloat {
  497. return rfloat(math.Cos(float64(x)))
  498. }
  499. func SphereIntersect(s *Sphere, r *Ray) rfloat {
  500. var op Vector
  501. Sub(&op, &s.center, &r.origin)
  502. b := Dot(&op, &r.dir)
  503. d := b*b - Dot(&op, &op) + s.radiusSqr
  504. if d < 0 {
  505. return 0
  506. }
  507. d = rfloat(math.Sqrt(float64(d)))
  508. t := b - d
  509. if t > RAY_BIAS {
  510. return t
  511. }
  512. t = b + d
  513. if t > RAY_BIAS {
  514. return t
  515. }
  516. return 0
  517. }
  518. func Put16(buffer []uint8, v uint16) {
  519. buffer[0] = (uint8)(v & 0xFF)
  520. buffer[1] = (uint8)(v >> 8)
  521. }
  522. func WriteTGAHeader(f *os.File, width uint16, height uint16) {
  523. var header [18]uint8
  524. header[2] = 2 // 32-bit
  525. Put16(header[12:], width)
  526. Put16(header[14:], height)
  527. header[16] = 32 // BPP
  528. header[17] = 0x20 // top down, non interlaced
  529. binary.Write(f, binary.LittleEndian, header)
  530. }
  531. func WriteTGA(fname string, pixels []uint32, width uint16, height uint16) {
  532. f, err := os.Create(fname)
  533. if err != nil {
  534. return
  535. }
  536. defer f.Close()
  537. WriteTGAHeader(f, width, height)
  538. binary.Write(f, binary.LittleEndian, pixels)
  539. }
  540. func main() {
  541. rand.Seed(time.Now().UnixNano())
  542. var camera Camera
  543. Set(&camera.right, 1, 0, 0)
  544. Set(&camera.up, 0, 1, 0)
  545. Set(&camera.forward, 0, -0.042612, -1)
  546. Normalize(&camera.forward)
  547. camera.fovScale = tanf((55.0 * math.Pi / 180.0) * 0.5)
  548. var scene Scene
  549. diffuseGrey := Material{materialType: DIFFUSE, diffuse: Vector{.75, .75, .75}}
  550. diffuseRed := Material{materialType: DIFFUSE, diffuse: Vector{.95, .15, .15}}
  551. diffuseBlue := Material{materialType: DIFFUSE, diffuse: Vector{.25, .25, .75}}
  552. diffuseBlack := Material{materialType: DIFFUSE}
  553. //glossyGreen := Material{materialType: GLOSSY, diffuse: Vector{0.0, 0.55, 14.0 / 255.0}, specular: Vector{1, 1, 1}, exp: 3.0}
  554. diffuseGreen := Material{materialType: DIFFUSE, diffuse: Vector{0.0, 0.55, 14.0 / 255.0}}
  555. diffuseWhite := Material{materialType: DIFFUSE, diffuse: Vector{.99, .99, .99}}
  556. glossyWhite := Material{materialType: GLOSSY, diffuse: Vector{.3, .05, .05}, specular: Vector{0.69, 0.69, 0.69}, exp: 45.0}
  557. whiteLight := Material{materialType: DIFFUSE, emissive: Vector{400, 400, 400}}
  558. mirror := Material{materialType: MIRROR, diffuse: Vector{0.999, 0.999, 0.999}}
  559. scene.camera = camera
  560. scene.objects = []Sphere{
  561. Sphere{1e5, Vector{1e5 + 1, 40.8, 81.6}, &diffuseRed, 0},
  562. Sphere{1e5, Vector{-1e5 + 99, 40.8, 81.6}, &diffuseBlue, 0},
  563. Sphere{1e5, Vector{50, 40.8, 1e5}, &diffuseGrey, 0},
  564. Sphere{1e5, Vector{50, 40.8, -1e5 + 170}, &diffuseBlack, 0},
  565. Sphere{1e5, Vector{50, 1e5, 81.6}, &diffuseGrey, 0},
  566. Sphere{1e5, Vector{50, -1e5 + 81.6, 81.6}, &diffuseGrey, 0},
  567. Sphere{16.5, Vector{27, 16.5, 57}, &mirror, 0},
  568. Sphere{10.5, Vector{17, 10.5, 97}, &diffuseGreen, 0},
  569. Sphere{16.5, Vector{76, 16.5, 78}, &glossyWhite, 0},
  570. Sphere{8.5, Vector{82, 8.5, 108}, &diffuseWhite, 0},
  571. //Sphere{600, Vector{50, 681.6-.27, 81.6}, &whiteLight, 0}}
  572. Sphere{1.5, Vector{50, 81.6 - 16.5, 81.6}, &whiteLight, 0}}
  573. scene.Initialize()
  574. scene.CollectLights()
  575. var fb FrameBuffer
  576. var context Context
  577. fb.w = RESOLUTION
  578. fb.h = RESOLUTION
  579. fb.buf = make([]uint32, fb.w*fb.h)
  580. context.buffer = &fb
  581. context.scene = &scene
  582. context.channelQuit = make(chan bool)
  583. context.channelJoin = make(chan bool)
  584. context.channelJob = make(chan WorkChunk)
  585. InitializeSamples(&context.samples)
  586. chunkSize := 16
  587. chunksPerLine := RESOLUTION / chunkSize
  588. numWorkers := chunksPerLine * chunksPerLine
  589. runtime.GOMAXPROCS(runtime.NumCPU())
  590. fmt.Printf("Num workers: %v, GOMAXPROCS=%v\n", numWorkers, runtime.GOMAXPROCS(0))
  591. tstart := time.Now()
  592. for w := 0; w < numWorkers; w++ {
  593. go workerFunc(&context)
  594. }
  595. /*
  596. f, err := os.Create("trace.prof")
  597. if err != nil {
  598. //log.Fatal(err)
  599. }
  600. pprof.StartCPUProfile(f)
  601. defer pprof.StopCPUProfile()
  602. */
  603. chunkW := chunkSize
  604. chunkH := chunkSize
  605. for y := 0; y < fb.h; y += chunkH {
  606. for x := 0; x < fb.w; x += chunkW {
  607. context.channelJob <- WorkChunk{x, y, chunkW, chunkH}
  608. }
  609. }
  610. for w := 0; w < numWorkers; w++ {
  611. context.channelQuit <- true
  612. }
  613. for w := 0; w < numWorkers; w++ {
  614. <-context.channelJoin
  615. }
  616. duration := time.Since(tstart)
  617. fmt.Printf("Chunk: %v, Took %s\n", chunkSize, duration)
  618. WriteTGA("test.tga", fb.buf, uint16(fb.w), uint16(fb.h))
  619. }