Procházet zdrojové kódy

improved z-sorting

danaugrs před 7 roky
rodič
revize
2076df8c8c
3 změnil soubory, kde provedl 158 přidání a 111 odebrání
  1. 17 10
      graphic/graphic.go
  2. 14 0
      material/material.go
  3. 127 101
      renderer/renderer.go

+ 17 - 10
graphic/graphic.go

@@ -22,11 +22,12 @@ type Graphic struct {
 	mode       uint32             // OpenGL primitive
 	renderable bool               // Renderable flag
 	cullable   bool               // Cullable flag
+	// TODO store cached mv, mvp matrices ?
 }
 
 // GraphicMaterial specifies the material to be used for
 // a subset of vertices from the Graphic geometry
-// A Graphic object has at least one GraphicMaterial
+// A Graphic object has at least one GraphicMaterial.
 type GraphicMaterial struct {
 	imat     material.IMaterial // Associated material
 	start    int                // Index of first element in the geometry
@@ -34,7 +35,7 @@ type GraphicMaterial struct {
 	igraphic IGraphic           // Graphic which contains this GraphicMaterial
 }
 
-// IGraphic is the interface for all Graphic objects
+// IGraphic is the interface for all Graphic objects.
 type IGraphic interface {
 	core.INode
 	GetGraphic() *Graphic
@@ -69,20 +70,20 @@ func (gr *Graphic) Init(igeom geometry.IGeometry, mode uint32) *Graphic {
 }
 
 // GetGraphic satisfies the IGraphic interface and
-// returns pointer to the base Graphic
+// returns pointer to the base Graphic.
 func (gr *Graphic) GetGraphic() *Graphic {
 
 	return gr
 }
 
 // GetGeometry satisfies the IGraphic interface and returns
-// a pointer to the geometry associated with this graphic
+// a pointer to the geometry associated with this graphic.
 func (gr *Graphic) GetGeometry() *geometry.Geometry {
 
 	return gr.igeom.GetGeometry()
 }
 
-// Dispose overrides the embedded Node Dispose method
+// Dispose overrides the embedded Node Dispose method.
 func (gr *Graphic) Dispose() {
 
 	gr.igeom.Dispose()
@@ -132,7 +133,7 @@ func (gr *Graphic) AddMaterial(igr IGraphic, imat material.IMaterial, start, cou
 	gr.materials = append(gr.materials, gmat)
 }
 
-// AddGroupMaterial adds a material for the specified geometry group
+// AddGroupMaterial adds a material for the specified geometry group.
 func (gr *Graphic) AddGroupMaterial(igr IGraphic, imat material.IMaterial, gindex int) {
 
 	geom := gr.igeom.GetGeometry()
@@ -143,13 +144,13 @@ func (gr *Graphic) AddGroupMaterial(igr IGraphic, imat material.IMaterial, ginde
 	gr.AddMaterial(igr, imat, group.Start, group.Count)
 }
 
-// Materials returns slice with this graphic materials
+// Materials returns slice with this graphic materials.
 func (gr *Graphic) Materials() []GraphicMaterial {
 
 	return gr.materials
 }
 
-// GetMaterial returns the  material associated with the specified vertex position
+// GetMaterial returns the material associated with the specified vertex position.
 func (gr *Graphic) GetMaterial(vpos int) material.IMaterial {
 
 	for _, gmat := range gr.materials {
@@ -164,13 +165,19 @@ func (gr *Graphic) GetMaterial(vpos int) material.IMaterial {
 	return nil
 }
 
-// GetMaterial returns the material associated with the GraphicMaterial
+// GetMaterial returns the material associated with the GraphicMaterial.
 func (grmat *GraphicMaterial) GetMaterial() material.IMaterial {
 
 	return grmat.imat
 }
 
-// Render is called by the renderer to render this graphic material
+// GetGraphic returns the graphic associated with the GraphicMaterial.
+func (grmat *GraphicMaterial) GetGraphic() IGraphic {
+
+	return grmat.igraphic
+}
+
+// Render is called by the renderer to render this graphic material.
 func (grmat *GraphicMaterial) Render(gs *gls.GLS, rinfo *core.RenderInfo) {
 
 	// Setup the associated material (set states and transfer material uniforms and textures)

+ 14 - 0
material/material.go

@@ -62,6 +62,7 @@ type Material struct {
 	shaderUnique     bool                 // shader has only one instance (does not depend on lights or textures)
 	uselights        UseLights            // consider lights for shader selection
 	sidevis          Side                 // sides visible
+	transparent      bool                 // whether at all transparent
 	wireframe        bool                 // show as wirefrme
 	depthMask        bool                 // Enable writing into the depth buffer
 	depthTest        bool                 // Enable depth buffer test
@@ -91,6 +92,7 @@ func (mat *Material) Init() *Material {
 	mat.refcount = 1
 	mat.uselights = UseLightAll
 	mat.sidevis = SideFront
+	mat.transparent = false
 	mat.wireframe = false
 	mat.depthMask = true
 	mat.depthFunc = gls.LEQUAL
@@ -187,6 +189,18 @@ func (mat *Material) Side() Side {
 	return mat.sidevis
 }
 
+// SetTransparent sets whether this material is transparent
+func (mat *Material) SetTransparent(state bool) {
+
+	mat.transparent = state
+}
+
+// Transparent returns whether this material is transparent
+func (mat *Material) Transparent() bool {
+
+	return mat.transparent
+}
+
 func (mat *Material) SetWireframe(state bool) {
 
 	mat.wireframe = state

+ 127 - 101
renderer/renderer.go

@@ -18,28 +18,28 @@ import (
 // Renderer renders a 3D scene and/or a 2D GUI on the current window.
 type Renderer struct {
 	gs           *gls.GLS
-	shaman       Shaman                    // Internal shader manager
-	stats        Stats                     // Renderer statistics
-	prevStats    Stats                     // Renderer statistics for previous frame
-	scene        core.INode                // Node containing 3D scene to render
-	panelGui     gui.IPanel                // Panel containing GUI to render
-	panel3D     gui.IPanel                 // Panel which contains the 3D scene
-	ambLights   []*light.Ambient           // Array of ambient lights for last scene
-	dirLights   []*light.Directional       // Array of directional lights for last scene
-	pointLights []*light.Point             // Array of point
-	spotLights  []*light.Spot              // Array of spot lights for the scene
-	others      []core.INode               // Other nodes (audio, players, etc)
-	rgraphics   []graphic.IGraphic         // Array of rendered graphics for scene
-	cgraphics   []graphic.IGraphic         // Array of culled graphics for scene
-	grmats      []*graphic.GraphicMaterial // Array of rendered graphic materials for scene
-	rinfo       core.RenderInfo            // Preallocated Render info
-	specs       ShaderSpecs                // Preallocated Shader specs
-	sortObjects bool                       // Flag indicating whether objects should be sorted before rendering
-	redrawGui   bool                       // Flag indicating the gui must be redrawn completely
-	rendered    bool                       // Flag indicating if anything was rendered
-	panList      []gui.IPanel              // list of panels to render
-	frameBuffers int                       // Number of frame buffers
-	frameCount   int                       // Current number of frame buffers to write
+	shaman       Shaman                      // Internal shader manager
+	stats        Stats                       // Renderer statistics
+	prevStats    Stats                       // Renderer statistics for previous frame
+	scene        core.INode                  // Node containing 3D scene to render
+	panelGui     gui.IPanel                  // Panel containing GUI to render
+	panel3D      gui.IPanel                  // Panel which contains the 3D scene
+	ambLights    []*light.Ambient            // Array of ambient lights for last scene
+	dirLights    []*light.Directional        // Array of directional lights for last scene
+	pointLights  []*light.Point              // Array of point
+	spotLights   []*light.Spot               // Array of spot lights for the scene
+	others       []core.INode                // Other nodes (audio, players, etc)
+	grmatsOpaque []*graphic.GraphicMaterial  // Array of rendered opaque graphic materials for scene
+	grmatsTransp []*graphic.GraphicMaterial  // Array of rendered transparent graphic materials for scene
+	cgrmats      []*graphic.GraphicMaterial  // Array of culled graphic materials for scene
+	rinfo        core.RenderInfo             // Preallocated Render info
+	specs        ShaderSpecs                 // Preallocated Shader specs
+	sortObjects  bool                        // Flag indicating whether objects should be sorted before rendering
+	redrawGui    bool                        // Flag indicating the gui must be redrawn completely
+	rendered     bool                        // Flag indicating if anything was rendered
+	panList      []gui.IPanel                // list of panels to render
+	frameBuffers int                         // Number of frame buffers
+	frameCount   int                         // Current number of frame buffers to write
 }
 
 // Stats describes how many object types were rendered.
@@ -63,9 +63,9 @@ func NewRenderer(gs *gls.GLS) *Renderer {
 	r.pointLights = make([]*light.Point, 0)
 	r.spotLights = make([]*light.Spot, 0)
 	r.others = make([]core.INode, 0)
-	r.rgraphics = make([]graphic.IGraphic, 0)
-	r.cgraphics = make([]graphic.IGraphic, 0)
-	r.grmats = make([]*graphic.GraphicMaterial, 0)
+	r.grmatsOpaque = make([]*graphic.GraphicMaterial, 0)
+	r.grmatsTransp = make([]*graphic.GraphicMaterial, 0)
+	r.cgrmats = make([]*graphic.GraphicMaterial, 0)
 	r.panList = make([]gui.IPanel, 0)
 	r.frameBuffers = 2
 	r.sortObjects = true
@@ -189,9 +189,9 @@ func (r *Renderer) renderScene(iscene core.INode, icam camera.ICamera) error {
 	r.pointLights = r.pointLights[0:0]
 	r.spotLights = r.spotLights[0:0]
 	r.others = r.others[0:0]
-	r.rgraphics = r.rgraphics[0:0]
-	r.cgraphics = r.cgraphics[0:0]
-	r.grmats = r.grmats[0:0]
+	r.grmatsOpaque = r.grmatsOpaque[0:0]
+	r.grmatsTransp = r.grmatsTransp[0:0]
+	r.cgrmats = r.cgrmats[0:0]
 
 	// Prepare for frustum culling
 	var proj math32.Matrix4
@@ -213,23 +213,41 @@ func (r *Renderer) renderScene(iscene core.INode, icam camera.ICamera) error {
 		if ok {
 			if igr.Renderable() {
 
+				gr := igr.GetGraphic()
+				materials := gr.Materials()
+
 				// Frustum culling
 				if igr.Cullable() {
-					gr := igr.GetGraphic()
 					mw := gr.MatrixWorld()
 					geom := igr.GetGeometry()
 					bb := geom.BoundingBox()
 					bb.ApplyMatrix4(&mw)
 					if frustum.IntersectsBox(&bb) {
-						// Append graphic to list of graphics to be rendered
-						r.rgraphics = append(r.rgraphics, igr)
+						// Append all graphic materials of this graphic to list of graphic materials to be rendered
+						for i := 0; i < len(materials); i++ {
+							mat := materials[i].GetMaterial().GetMaterial()
+							if mat.Transparent() {
+								r.grmatsTransp = append(r.grmatsTransp, &materials[i])
+							} else {
+								r.grmatsOpaque = append(r.grmatsOpaque, &materials[i])
+							}
+						}
 					} else {
-						// Append graphic to list of culled graphics
-						r.cgraphics = append(r.cgraphics, igr)
+						// Append all graphic materials of this graphic to list of culled graphic materials
+						for i := 0; i < len(materials); i++ {
+							r.cgrmats = append(r.cgrmats, &materials[i])
+						}
 					}
 				} else {
-					// Append graphic to list of graphics to be rendered
-					r.rgraphics = append(r.rgraphics, igr)
+					// Append all graphic materials of this graphic to list of graphic materials to be rendered
+					for i := 0; i < len(materials); i++ {
+						mat := materials[i].GetMaterial().GetMaterial()
+						if mat.Transparent() {
+							r.grmatsTransp = append(r.grmatsTransp, &materials[i])
+						} else {
+							r.grmatsOpaque = append(r.grmatsOpaque, &materials[i])
+						}
+					}
 				}
 			}
 			// Node is not a Graphic
@@ -272,39 +290,37 @@ func (r *Renderer) renderScene(iscene core.INode, icam camera.ICamera) error {
 	r.specs.PointLightsMax = len(r.pointLights)
 	r.specs.SpotLightsMax = len(r.spotLights)
 
-	// Z-sort graphics
+	// Z-sort graphic materials
 	if r.sortObjects {
-		sort.Slice(r.rgraphics, func(i, j int) bool {
-
-			gr1 := r.rgraphics[i].GetGraphic()
-			gr2 := r.rgraphics[j].GetGraphic()
-
-			mw1 := gr1.MatrixWorld()
-			mw2 := gr2.MatrixWorld()
-
-			// TODO OPTIMIZATION - this calculation is already done in IGraphic.RenderSetup, should cache results in Graphic.
-			var mvm1, mvm2 math32.Matrix4
-			mvm1.MultiplyMatrices(&r.rinfo.ViewMatrix, &mw1)
-			mvm2.MultiplyMatrices(&r.rinfo.ViewMatrix, &mw2)
-
-			g1pos := gr1.Position()
-			g2pos := gr2.Position()
-
-			g1pos.ApplyMatrix4(&mvm1)
-			g2pos.ApplyMatrix4(&mvm2)
-
-			return g1pos.Z < g2pos.Z
-		})
-	}
-
-	// Compile list of all graphic materials to be rendered
-	for i := 0; i < len(r.rgraphics); i++ {
-		gr := r.rgraphics[i].GetGraphic()
-		materials := gr.Materials()
-		// Appends to list each graphic material for this graphic
-		for j := 0; j < len(materials); j++ {
-			r.grmats = append(r.grmats, &materials[j])
+		// Internal function to render a list of graphic materials
+		var zSortGraphicMaterials func(grmats []*graphic.GraphicMaterial, backToFront bool)
+		zSortGraphicMaterials = func(grmats []*graphic.GraphicMaterial, backToFront bool) {
+			sort.Slice(grmats, func(i, j int) bool {
+				gr1 := grmats[i].GetGraphic().GetGraphic()
+				gr2 := grmats[j].GetGraphic().GetGraphic()
+				mw1 := gr1.MatrixWorld()
+				mw2 := gr2.MatrixWorld()
+
+				// TODO OPTIMIZATION - this calculation is already generally performed in IGraphic.RenderSetup, should probably cache results in Graphic.
+				var mvm1, mvm2 math32.Matrix4
+				mvm1.MultiplyMatrices(&r.rinfo.ViewMatrix, &mw1)
+				mvm2.MultiplyMatrices(&r.rinfo.ViewMatrix, &mw2)
+
+				g1pos := gr1.Position()
+				g2pos := gr2.Position()
+				g1pos.ApplyMatrix4(&mvm1)
+				g2pos.ApplyMatrix4(&mvm2)
+
+				if backToFront {
+					return g1pos.Z < g2pos.Z
+				} else {
+					return g1pos.Z > g2pos.Z
+				}
+			})
 		}
+
+		zSortGraphicMaterials(r.grmatsOpaque, false) // Sort opaque graphics front to back
+		zSortGraphicMaterials(r.grmatsTransp, true) // Sort transparent graphics back to front
 	}
 
 	// Render other nodes (audio players, etc)
@@ -319,7 +335,7 @@ func (r *Renderer) renderScene(iscene core.INode, icam camera.ICamera) error {
 
 	// If there is graphic material to render or there was in the previous frame
 	// it is necessary to clear the screen.
-	if len(r.grmats) > 0 || r.prevStats.Graphics > 0 {
+	if len(r.grmatsOpaque) > 0 || len(r.grmatsTransp) > 0 || r.prevStats.Graphics > 0 {
 		// If the 3D scene to draw is to be confined to user specified panel
 		// sets scissor to avoid erasing gui elements outside of this panel
 		if r.panel3D != nil {
@@ -337,43 +353,53 @@ func (r *Renderer) renderScene(iscene core.INode, icam camera.ICamera) error {
 		r.rendered = true
 	}
 
-	// For each *GraphicMaterial
-	for _, grmat := range r.grmats {
-		mat := grmat.GetMaterial().GetMaterial()
+	err := error(nil)
+
+	// Internal function to render a list of graphic materials
+	var renderGraphicMaterials func(grmats []*graphic.GraphicMaterial)
+	renderGraphicMaterials = func(grmats []*graphic.GraphicMaterial) {
+		// For each *GraphicMaterial
+		for _, grmat := range grmats {
+			mat := grmat.GetMaterial().GetMaterial()
+
+			// Sets the shader specs for this material and sets shader program
+			r.specs.Name = mat.Shader()
+			r.specs.ShaderUnique = mat.ShaderUnique()
+			r.specs.UseLights = mat.UseLights()
+			r.specs.MatTexturesMax = mat.TextureCount()
+			_, err = r.shaman.SetProgram(&r.specs)
+			if err != nil {
+				return
+			}
 
-		// Sets the shader specs for this material and sets shader program
-		r.specs.Name = mat.Shader()
-		r.specs.ShaderUnique = mat.ShaderUnique()
-		r.specs.UseLights = mat.UseLights()
-		r.specs.MatTexturesMax = mat.TextureCount()
-		_, err := r.shaman.SetProgram(&r.specs)
-		if err != nil {
-			return err
-		}
+			// Setup lights (transfer lights uniforms)
+			for idx, l := range r.ambLights {
+				l.RenderSetup(r.gs, &r.rinfo, idx)
+				r.stats.Lights++
+			}
+			for idx, l := range r.dirLights {
+				l.RenderSetup(r.gs, &r.rinfo, idx)
+				r.stats.Lights++
+			}
+			for idx, l := range r.pointLights {
+				l.RenderSetup(r.gs, &r.rinfo, idx)
+				r.stats.Lights++
+			}
+			for idx, l := range r.spotLights {
+				l.RenderSetup(r.gs, &r.rinfo, idx)
+				r.stats.Lights++
+			}
 
-		// Setup lights (transfer lights uniforms)
-		for idx, l := range r.ambLights {
-			l.RenderSetup(r.gs, &r.rinfo, idx)
-			r.stats.Lights++
+			// Render this graphic material
+			grmat.Render(r.gs, &r.rinfo)
+			r.stats.Graphics++
 		}
-		for idx, l := range r.dirLights {
-			l.RenderSetup(r.gs, &r.rinfo, idx)
-			r.stats.Lights++
-		}
-		for idx, l := range r.pointLights {
-			l.RenderSetup(r.gs, &r.rinfo, idx)
-			r.stats.Lights++
-		}
-		for idx, l := range r.spotLights {
-			l.RenderSetup(r.gs, &r.rinfo, idx)
-			r.stats.Lights++
-		}
-
-		// Render this graphic material
-		grmat.Render(r.gs, &r.rinfo)
-		r.stats.Graphics++
 	}
-	return nil
+
+	renderGraphicMaterials(r.grmatsOpaque) // Render opaque objects front to back
+	renderGraphicMaterials(r.grmatsTransp) // Render transparent objects back to front
+
+	return err
 }
 
 // renderGui renders the Gui
@@ -381,7 +407,7 @@ func (r *Renderer) renderGui() error {
 
 	// If no 3D scene was rendered sets Gui panels as renderable for background
 	// User must define the colors
-	if (len(r.grmats) == 0) && (len(r.cgraphics) == 0) {
+	if (len(r.grmatsOpaque) == 0) && (len(r.grmatsTransp) == 0) && (len(r.cgrmats) == 0) {
 		r.panelGui.SetRenderable(true)
 		if r.panel3D != nil {
 			r.panel3D.SetRenderable(true)