render3d.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. package renderer
  2. import (
  3. . "github.com/fogleman/fauxgl"
  4. "github.com/nfnt/resize"
  5. "errors"
  6. "image"
  7. "log"
  8. "os"
  9. "path/filepath"
  10. "strings"
  11. )
  12. const (
  13. scale = 1 // optional supersampling
  14. width = 1000 // output width in pixels
  15. height = 1000 // output height in pixels
  16. fovy = 10 // vertical field of view in degrees
  17. near = 1 // near clipping plane
  18. far = 40 // far clipping plane
  19. )
  20. var (
  21. eye = V(-6, -6, 5) // camera position
  22. center = V(0, -0.07, 0) // view center position
  23. up = V(0, 0, 1) // up vector
  24. light = V(-1, -2, 5).Normalize() // light direction
  25. color = ("#42f5b3") // object color
  26. background = HexColor("#e0e0e0") //Background color
  27. )
  28. type RenderOption struct {
  29. Color string
  30. BackgroundColor string
  31. Width int
  32. Height int
  33. }
  34. type Renderer struct {
  35. Option RenderOption
  36. }
  37. func New3DRenderer(option RenderOption) *Renderer {
  38. return &Renderer{
  39. Option: option,
  40. }
  41. }
  42. func (r *Renderer) RenderModel(filename string) (image.Image, error) {
  43. // load a mesh
  44. var mesh *Mesh
  45. if strings.ToLower(filepath.Ext(filename)) == ".stl" {
  46. m, err := LoadSTL(filename)
  47. if err != nil {
  48. return nil, err
  49. }
  50. mesh = m
  51. } else if strings.ToLower(filepath.Ext(filename)) == ".obj" {
  52. m, err := LoadOBJ(filename)
  53. if err != nil {
  54. return nil, err
  55. }
  56. mesh = m
  57. } else {
  58. log.Println("Not supported format, given: " + filepath.Ext(filename))
  59. return nil, errors.New("Not supported model format")
  60. }
  61. // fit mesh in a bi-unit cube centered at the origin
  62. mesh.UnitCube()
  63. //log.Println(mesh.BoundingBox(), filename)
  64. // smooth the normals
  65. mesh.SmoothNormalsThreshold(Radians(30))
  66. // create a rendering context
  67. context := NewContext(r.Option.Width*scale, r.Option.Height*scale)
  68. context.ClearColorBufferWith(HexColor(r.Option.BackgroundColor))
  69. // create transformation matrix and light direction
  70. aspect := float64(width) / float64(height)
  71. matrix := LookAt(eye, center, up).Perspective(fovy, aspect, near, far)
  72. // use builtin phong shader
  73. shader := NewPhongShader(matrix, light, eye)
  74. shader.ObjectColor = HexColor(r.Option.Color)
  75. context.Shader = shader
  76. // render
  77. context.DrawMesh(mesh)
  78. // downsample image for antialiasing
  79. image := context.Image()
  80. image = resize.Resize(width, height, image, resize.Bilinear)
  81. return image, nil
  82. }
  83. func fileExists(filename string) bool {
  84. info, err := os.Stat(filename)
  85. if os.IsNotExist(err) {
  86. return false
  87. }
  88. return !info.IsDir()
  89. }