renderer.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. // Copyright 2016 The G3N Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // Package renderer implements the scene renderer.
  5. package renderer
  6. import (
  7. "sort"
  8. "github.com/g3n/engine/camera"
  9. "github.com/g3n/engine/core"
  10. "github.com/g3n/engine/gls"
  11. "github.com/g3n/engine/graphic"
  12. "github.com/g3n/engine/gui"
  13. "github.com/g3n/engine/light"
  14. "github.com/g3n/engine/material"
  15. "github.com/g3n/engine/math32"
  16. "github.com/g3n/engine/util/logger"
  17. )
  18. // Package logger
  19. var log = logger.New("RENDERER", logger.Default)
  20. // Renderer renders a scene containing 3D objects and/or 2D GUI elements.
  21. type Renderer struct {
  22. Shaman // Embedded shader manager
  23. gs *gls.GLS // Reference to OpenGL state
  24. rinfo core.RenderInfo // Preallocated Render info
  25. specs ShaderSpecs // Preallocated Shader specs
  26. sortObjects bool // Flag indicating whether objects should be sorted before rendering
  27. stats Stats // Renderer statistics
  28. // Populated each frame
  29. ambLights []*light.Ambient // Ambient lights in the scene
  30. dirLights []*light.Directional // Directional lights in the scene
  31. pointLights []*light.Point // Point lights in the scene
  32. spotLights []*light.Spot // Spot lights in the scene
  33. others []core.INode // Other nodes (audio, players, etc)
  34. graphics []*graphic.Graphic // Graphics to be rendered
  35. grmatsOpaque []*graphic.GraphicMaterial // Opaque graphic materials to be rendered
  36. grmatsTransp []*graphic.GraphicMaterial // Transparent graphic materials to be rendered
  37. zLayers map[int][]gui.IPanel // All IPanels to be rendered organized by Z-layer
  38. zLayerKeys []int // Z-layers being used (initially in no particular order, sorted later)
  39. }
  40. // Stats describes how many objects of each type are being rendered.
  41. // It is cleared at the start of each render.
  42. type Stats struct {
  43. GraphicMats int // Number of graphic materials rendered
  44. Lights int // Number of lights rendered
  45. Panels int // Number of GUI panels rendered
  46. Others int // Number of other objects rendered
  47. }
  48. // NewRenderer creates and returns a pointer to a new Renderer.
  49. func NewRenderer(gs *gls.GLS) *Renderer {
  50. r := new(Renderer)
  51. r.gs = gs
  52. r.Shaman.Init(gs)
  53. r.sortObjects = true
  54. r.ambLights = make([]*light.Ambient, 0)
  55. r.dirLights = make([]*light.Directional, 0)
  56. r.pointLights = make([]*light.Point, 0)
  57. r.spotLights = make([]*light.Spot, 0)
  58. r.others = make([]core.INode, 0)
  59. r.graphics = make([]*graphic.Graphic, 0)
  60. r.grmatsOpaque = make([]*graphic.GraphicMaterial, 0)
  61. r.grmatsTransp = make([]*graphic.GraphicMaterial, 0)
  62. r.zLayers = make(map[int][]gui.IPanel)
  63. r.zLayers[0] = make([]gui.IPanel, 0)
  64. r.zLayerKeys = append(r.zLayerKeys, 0)
  65. return r
  66. }
  67. // Stats returns a copy of the statistics for the last frame.
  68. // Should be called after the frame was rendered.
  69. func (r *Renderer) Stats() Stats {
  70. return r.stats
  71. }
  72. // SetObjectSorting sets whether objects will be sorted before rendering.
  73. func (r *Renderer) SetObjectSorting(sort bool) {
  74. r.sortObjects = sort
  75. }
  76. // ObjectSorting returns whether objects will be sorted before rendering.
  77. func (r *Renderer) ObjectSorting() bool {
  78. return r.sortObjects
  79. }
  80. // Render renders the specified scene using the specified camera. Returns an an error.
  81. func (r *Renderer) Render(scene core.INode, cam camera.ICamera) error {
  82. // Updates world matrices of all scene nodes
  83. scene.UpdateMatrixWorld()
  84. // Build RenderInfo
  85. cam.ViewMatrix(&r.rinfo.ViewMatrix)
  86. cam.ProjMatrix(&r.rinfo.ProjMatrix)
  87. // Clear stats and scene arrays
  88. r.stats = Stats{}
  89. r.ambLights = r.ambLights[0:0]
  90. r.dirLights = r.dirLights[0:0]
  91. r.pointLights = r.pointLights[0:0]
  92. r.spotLights = r.spotLights[0:0]
  93. r.others = r.others[0:0]
  94. r.graphics = r.graphics[0:0]
  95. r.grmatsOpaque = r.grmatsOpaque[0:0]
  96. r.grmatsTransp = r.grmatsTransp[0:0]
  97. r.zLayers = make(map[int][]gui.IPanel)
  98. r.zLayers[0] = make([]gui.IPanel, 0)
  99. r.zLayerKeys = r.zLayerKeys[0:1]
  100. r.zLayerKeys[0] = 0
  101. // Prepare for frustum culling
  102. var proj math32.Matrix4
  103. proj.MultiplyMatrices(&r.rinfo.ProjMatrix, &r.rinfo.ViewMatrix)
  104. frustum := math32.NewFrustumFromMatrix(&proj)
  105. // Classify scene and all scene nodes, culling renderable IGraphics which are fully outside of the camera frustum
  106. r.classifyAndCull(scene, frustum, 0)
  107. // Set light counts in shader specs
  108. r.specs.AmbientLightsMax = len(r.ambLights)
  109. r.specs.DirLightsMax = len(r.dirLights)
  110. r.specs.PointLightsMax = len(r.pointLights)
  111. r.specs.SpotLightsMax = len(r.spotLights)
  112. // Pre-calculate MV and MVP matrices and compile initial lists of opaque and transparent graphic materials
  113. for _, gr := range r.graphics {
  114. // Calculate MV and MVP matrices for all non-GUI graphics to be rendered
  115. gr.CalculateMatrices(r.gs, &r.rinfo)
  116. // Append all graphic materials of this graphic to lists of graphic materials to be rendered
  117. materials := gr.Materials()
  118. for i := range materials {
  119. r.stats.GraphicMats++
  120. if materials[i].IMaterial().GetMaterial().Transparent() {
  121. r.grmatsTransp = append(r.grmatsTransp, &materials[i])
  122. } else {
  123. r.grmatsOpaque = append(r.grmatsOpaque, &materials[i])
  124. }
  125. }
  126. }
  127. // TODO: If both GraphicMaterials belong to same Graphic we might want to keep their relative order...
  128. // Z-sort graphic materials back to front
  129. if r.sortObjects {
  130. zSort(r.grmatsOpaque)
  131. zSort(r.grmatsTransp)
  132. }
  133. // Sort zLayers back to front
  134. sort.Ints(r.zLayerKeys)
  135. // Iterate over all panels from back to front, setting Z and adding graphic materials to grmatsTransp/grmatsOpaque
  136. const deltaZ = 0.00001
  137. panZ := float32(-1 + float32(r.stats.Panels)*deltaZ)
  138. for _, k := range r.zLayerKeys {
  139. for _, ipan := range r.zLayers[k] {
  140. // Set panel Z
  141. ipan.SetPositionZ(panZ)
  142. panZ -= deltaZ
  143. // Append the panel's graphic material to lists of graphic materials to be rendered
  144. mat := ipan.GetGraphic().Materials()[0]
  145. if mat.IMaterial().GetMaterial().Transparent() {
  146. r.grmatsTransp = append(r.grmatsTransp, &mat)
  147. } else {
  148. r.grmatsOpaque = append(r.grmatsOpaque, &mat)
  149. }
  150. }
  151. }
  152. // Render opaque objects front to back
  153. for i := len(r.grmatsOpaque) - 1; i >= 0; i-- {
  154. err := r.renderGraphicMaterial(r.grmatsOpaque[i])
  155. if err != nil {
  156. return err
  157. }
  158. }
  159. // Render transparent objects back to front
  160. for _, grmat := range r.grmatsTransp {
  161. err := r.renderGraphicMaterial(grmat)
  162. if err != nil {
  163. return err
  164. }
  165. }
  166. // Render other nodes (audio players, etc)
  167. for _, inode := range r.others {
  168. inode.Render(r.gs)
  169. }
  170. // Enable depth mask so that clearing the depth buffer works
  171. r.gs.DepthMask(true)
  172. // TODO enable color mask, stencil mask?
  173. // TODO clear the buffers for the user, and set the appropriate masks to true before clearing
  174. return nil
  175. }
  176. // classifyAndCull classifies the provided INode and all of its descendents.
  177. // It ignores (culls) renderable IGraphics which are fully outside of the specified frustum.
  178. func (r *Renderer) classifyAndCull(inode core.INode, frustum *math32.Frustum, zLayer int) {
  179. // Ignore invisible nodes and their descendants
  180. if !inode.Visible() {
  181. return
  182. }
  183. // If node is an IPanel append it to appropriate list
  184. if ipan, ok := inode.(gui.IPanel); ok {
  185. zLayer += ipan.ZLayerDelta()
  186. if ipan.Renderable() {
  187. // TODO cull panels
  188. _, ok := r.zLayers[zLayer]
  189. if !ok {
  190. r.zLayerKeys = append(r.zLayerKeys, zLayer)
  191. r.zLayers[zLayer] = make([]gui.IPanel, 0)
  192. }
  193. r.zLayers[zLayer] = append(r.zLayers[zLayer], ipan)
  194. r.stats.Panels++
  195. }
  196. // Check if node is an IGraphic
  197. } else if igr, ok := inode.(graphic.IGraphic); ok {
  198. if igr.Renderable() {
  199. gr := igr.GetGraphic()
  200. // Frustum culling
  201. if igr.Cullable() {
  202. mw := gr.MatrixWorld()
  203. bb := igr.GetGeometry().BoundingBox()
  204. bb.ApplyMatrix4(&mw)
  205. if frustum.IntersectsBox(&bb) {
  206. // Append graphic to list of graphics to be rendered
  207. r.graphics = append(r.graphics, gr)
  208. }
  209. } else {
  210. // Append graphic to list of graphics to be rendered
  211. r.graphics = append(r.graphics, gr)
  212. }
  213. }
  214. // Node is not a Graphic
  215. } else {
  216. // Check if node is a Light
  217. if il, ok := inode.(light.ILight); ok {
  218. switch l := il.(type) {
  219. case *light.Ambient:
  220. r.ambLights = append(r.ambLights, l)
  221. case *light.Directional:
  222. r.dirLights = append(r.dirLights, l)
  223. case *light.Point:
  224. r.pointLights = append(r.pointLights, l)
  225. case *light.Spot:
  226. r.spotLights = append(r.spotLights, l)
  227. default:
  228. panic("Invalid light type")
  229. }
  230. // Other nodes
  231. } else {
  232. r.others = append(r.others, inode)
  233. r.stats.Others++
  234. }
  235. }
  236. // Classify children
  237. for _, ichild := range inode.Children() {
  238. r.classifyAndCull(ichild, frustum, zLayer)
  239. }
  240. }
  241. // zSort sorts a list of graphic materials based on the user-specified render order
  242. // then based on their Z position relative to the camera, back to front.
  243. func zSort(grmats []*graphic.GraphicMaterial) {
  244. sort.Slice(grmats, func(i, j int) bool {
  245. gr1 := grmats[i].IGraphic().GetGraphic()
  246. gr2 := grmats[j].IGraphic().GetGraphic()
  247. // Check for user-supplied render order
  248. rO1 := gr1.RenderOrder()
  249. rO2 := gr2.RenderOrder()
  250. if rO1 != rO2 {
  251. return rO1 < rO2
  252. }
  253. mvm1 := gr1.ModelViewMatrix()
  254. mvm2 := gr2.ModelViewMatrix()
  255. g1pos := gr1.Position()
  256. g2pos := gr2.Position()
  257. g1pos.ApplyMatrix4(mvm1)
  258. g2pos.ApplyMatrix4(mvm2)
  259. return g1pos.Z < g2pos.Z
  260. })
  261. }
  262. // renderGraphicMaterial renders the specified graphic material.
  263. func (r *Renderer) renderGraphicMaterial(grmat *graphic.GraphicMaterial) error {
  264. mat := grmat.IMaterial().GetMaterial()
  265. geom := grmat.IGraphic().GetGeometry()
  266. gr := grmat.IGraphic().GetGraphic()
  267. // Add defines from material, geometry and graphic
  268. r.specs.Defines = *gls.NewShaderDefines()
  269. r.specs.Defines.Add(&mat.ShaderDefines)
  270. r.specs.Defines.Add(&geom.ShaderDefines)
  271. r.specs.Defines.Add(&gr.ShaderDefines)
  272. // Set the shader specs for this material and set shader program
  273. r.specs.Name = mat.Shader()
  274. r.specs.ShaderUnique = mat.ShaderUnique()
  275. r.specs.UseLights = mat.UseLights()
  276. r.specs.MatTexturesMax = mat.TextureCount()
  277. // Set active program and apply shader specs
  278. _, err := r.Shaman.SetProgram(&r.specs)
  279. if err != nil {
  280. return err
  281. }
  282. // Set up lights (transfer lights' uniforms)
  283. if r.specs.UseLights != material.UseLightNone {
  284. if r.specs.UseLights&material.UseLightAmbient != 0 {
  285. for idx, l := range r.ambLights {
  286. l.RenderSetup(r.gs, &r.rinfo, idx)
  287. r.stats.Lights++
  288. }
  289. }
  290. if r.specs.UseLights&material.UseLightDirectional != 0 {
  291. for idx, l := range r.dirLights {
  292. l.RenderSetup(r.gs, &r.rinfo, idx)
  293. r.stats.Lights++
  294. }
  295. }
  296. if r.specs.UseLights&material.UseLightPoint != 0 {
  297. for idx, l := range r.pointLights {
  298. l.RenderSetup(r.gs, &r.rinfo, idx)
  299. r.stats.Lights++
  300. }
  301. }
  302. if r.specs.UseLights&material.UseLightSpot != 0 {
  303. for idx, l := range r.spotLights {
  304. l.RenderSetup(r.gs, &r.rinfo, idx)
  305. r.stats.Lights++
  306. }
  307. }
  308. }
  309. // Render this graphic material
  310. grmat.Render(r.gs, &r.rinfo)
  311. return nil
  312. }
  313. type Postprocessor struct {
  314. Width int32
  315. Height int32
  316. Fbo uint32
  317. Tex uint32
  318. Vao uint32
  319. Prg *gls.Program
  320. screen []float32
  321. Renderer *Renderer
  322. }
  323. func (r *Renderer) CreatePostprocessor(width, height int32, vertexShaderSource, fragmentShaderSource string) *Postprocessor {
  324. pp := &Postprocessor{
  325. Width: width,
  326. Height: height,
  327. Renderer: r,
  328. screen: []float32{
  329. // xyz color texture coords
  330. -1, 1, 0, 1, 1, 1, 0, 1,
  331. -1, -1, 0, 1, 1, 1, 0, 0,
  332. 1, -1, 0, 1, 1, 1, 1, 0,
  333. 1, 1, 0, 1, 1, 1, 1, 1,
  334. -1, 1, 0, 1, 1, 1, 0, 1,
  335. 1, -1, 0, 1, 1, 1, 1, 0,
  336. },
  337. }
  338. pp.Fbo = r.gs.GenFramebuffer()
  339. r.gs.BindFramebuffer(pp.Fbo)
  340. // set up a texture to render into
  341. pp.Tex = r.gs.GenTexture()
  342. r.gs.BindTexture(gls.TEXTURE_2D, pp.Tex)
  343. r.gs.TexImage2D(gls.TEXTURE_2D, 0, gls.RGB, width, height, gls.RGB, gls.UNSIGNED_BYTE, nil)
  344. r.gs.TexParameteri(gls.TEXTURE_2D, gls.TEXTURE_WRAP_S, gls.CLAMP_TO_EDGE)
  345. r.gs.TexParameteri(gls.TEXTURE_2D, gls.TEXTURE_WRAP_T, gls.CLAMP_TO_EDGE)
  346. r.gs.TexParameteri(gls.TEXTURE_2D, gls.TEXTURE_MIN_FILTER, gls.NEAREST)
  347. r.gs.TexParameteri(gls.TEXTURE_2D, gls.TEXTURE_MAG_FILTER, gls.NEAREST)
  348. r.gs.BindTexture(gls.TEXTURE_2D, 0)
  349. r.gs.FramebufferTexture2D(gls.COLOR_ATTACHMENT0, gls.TEXTURE_2D, pp.Tex)
  350. // attach depth and stencil buffers
  351. rbo := r.gs.GenRenderbuffer()
  352. r.gs.BindRenderbuffer(rbo)
  353. r.gs.RenderbufferStorage(gls.DEPTH24_STENCIL8, int(width), int(height))
  354. r.gs.BindRenderbuffer(0)
  355. r.gs.FramebufferRenderbuffer(gls.DEPTH_STENCIL_ATTACHMENT, rbo)
  356. // check the framebuffer status
  357. if r.gs.CheckFramebufferStatus() != gls.FRAMEBUFFER_COMPLETE {
  358. log.Fatal("Can't create frame buffer")
  359. }
  360. r.gs.BindFramebuffer(0)
  361. // create the "screen" quad
  362. vbo := r.gs.GenBuffer()
  363. r.gs.BindBuffer(gls.ARRAY_BUFFER, vbo)
  364. r.gs.BufferData(gls.ARRAY_BUFFER, 4*len(pp.screen), pp.screen, gls.STATIC_DRAW)
  365. pp.Vao = r.gs.GenVertexArray()
  366. r.gs.BindVertexArray(pp.Vao)
  367. r.gs.BindBuffer(gls.ARRAY_BUFFER, vbo)
  368. var offset uint32
  369. // position attribute
  370. r.gs.VertexAttribPointer(0, 3, gls.FLOAT, false, 8*4, offset)
  371. r.gs.EnableVertexAttribArray(0)
  372. offset += 3 * 4
  373. // color attribute
  374. r.gs.VertexAttribPointer(1, 3, gls.FLOAT, false, 8*4, offset)
  375. r.gs.EnableVertexAttribArray(1)
  376. offset += 3 * 4
  377. // texture coord attribute
  378. r.gs.VertexAttribPointer(2, 2, gls.FLOAT, false, 8*4, offset)
  379. r.gs.EnableVertexAttribArray(2)
  380. offset += 2 * 4
  381. // the screen shaders
  382. pp.Prg = r.gs.NewProgram()
  383. pp.Prg.AddShader(gls.VERTEX_SHADER, vertexShaderSource)
  384. pp.Prg.AddShader(gls.FRAGMENT_SHADER, fragmentShaderSource)
  385. err := pp.Prg.Build()
  386. if err != nil {
  387. log.Fatal("can't create shader: %e", err)
  388. }
  389. return pp
  390. }
  391. func (pp *Postprocessor) Render(fbwidth, fbheight int, render func()) {
  392. // render into the low-res texture
  393. gs := pp.Renderer.gs
  394. gs.Viewport(0, 0, pp.Width, pp.Height)
  395. gs.BindFramebuffer(pp.Fbo)
  396. gs.Enable(gls.DEPTH_TEST)
  397. render()
  398. // show texture on screen
  399. gs.Viewport(0, 0, int32(fbwidth), int32(fbheight))
  400. gs.BindFramebuffer(0)
  401. gs.ClearColor(1, 1, 1, 1)
  402. gs.Clear(gls.COLOR_BUFFER_BIT)
  403. gs.UseProgram(pp.Prg)
  404. gs.Disable(gls.DEPTH_TEST)
  405. gs.BindTexture(gls.TEXTURE_2D, pp.Tex)
  406. gs.BindVertexArray(pp.Vao)
  407. gs.DrawArrays(gls.TRIANGLES, 0, int32(len(pp.screen)/8))
  408. }