Bläddra i källkod

implemented frustum culling

danaugrs 7 år sedan
förälder
incheckning
aece9f2fc4
10 ändrade filer med 173 tillägg och 75 borttagningar
  1. 3 0
      geometry/box.go
  2. 29 12
      geometry/geometry.go
  3. 6 1
      geometry/sphere.go
  4. 61 27
      gls/vbo.go
  5. 20 2
      graphic/graphic.go
  6. 4 4
      graphic/mesh.go
  7. 7 5
      graphic/points.go
  8. 1 0
      graphic/skybox.go
  9. 11 21
      math32/frustum.go
  10. 31 3
      renderer/renderer.go

+ 3 - 0
geometry/box.go

@@ -139,5 +139,8 @@ func NewSegmentedBox(width, height, length float32, widthSegments, heightSegment
 	box.AddVBO(gls.NewVBO().AddAttrib("VertexNormal", 3).SetBuffer(normals))
 	box.AddVBO(gls.NewVBO().AddAttrib("VertexTexcoord", 2).SetBuffer(uvs))
 
+	box.boundingBox = math32.Box3{math32.Vector3{-wHalf, -hHalf, -lHalf}, math32.Vector3{wHalf, hHalf, lHalf}}
+	box.boundingBoxValid = true
+
 	return box
 }

+ 29 - 12
geometry/geometry.go

@@ -139,10 +139,19 @@ func (g *Geometry) Indices() math32.ArrayU32 {
 // AddVBO adds a Vertex Buffer Object for this geometry
 func (g *Geometry) AddVBO(vbo *gls.VBO) {
 
+	// Check that the provided VBO doesn't have conflicting attributes with existing VBOs
+	for _, existingVbo := range g.vbos {
+		for _, attrib := range vbo.Attributes() {
+			if existingVbo.Attrib(attrib.Name) != nil {
+				panic("Geometry.AddVBO: geometry already has a VBO with attribute " + attrib.Name)
+			}
+		}
+	}
+
 	g.vbos = append(g.vbos, vbo)
 }
 
-// VBO returns a pointer to this geometry VBO for the specified attribute.
+// VBO returns a pointer to this geometry's VBO which contain the specified attribute.
 // Returns nil if the VBO is not found.
 func (g *Geometry) VBO(attrib string) *gls.VBO {
 
@@ -166,7 +175,7 @@ func (g *Geometry) Items() int {
 	if vbo.AttribCount() == 0 {
 		return 0
 	}
-	return vbo.Buffer().Bytes() / vbo.Stride()
+	return vbo.Buffer().Bytes() / vbo.StrideSize()
 }
 
 // BoundingBox computes the bounding box of the geometry if necessary
@@ -179,17 +188,19 @@ func (g *Geometry) BoundingBox() math32.Box3 {
 	}
 
 	// Get buffer with position vertices
-	vbPos := g.VBO("VertexPosition")
-	if vbPos == nil {
+	vboPos := g.VBO("VertexPosition")
+	if vboPos == nil {
 		return g.boundingBox
 	}
-	positions := vbPos.Buffer()
+	stride := vboPos.Stride()
+	offset := vboPos.AttribOffset("VertexPosition")
+	positions := vboPos.Buffer()
 
 	// Calculates bounding box
 	var vertex math32.Vector3
 	g.boundingBox.Min.Set(0, 0, 0)
 	g.boundingBox.Max.Set(0, 0, 0)
-	for i := 0; i < positions.Size(); i += 3 {
+	for i := offset; i < positions.Size(); i += stride {
 		positions.GetVector3(i, &vertex)
 		g.boundingBox.ExpandByPoint(&vertex)
 	}
@@ -207,11 +218,13 @@ func (g *Geometry) BoundingSphere() math32.Sphere {
 	}
 
 	// Get buffer with position vertices
-	vbPos := g.VBO("VertexPosition")
-	if vbPos == nil {
+	vboPos := g.VBO("VertexPosition")
+	if vboPos == nil {
 		return g.boundingSphere
 	}
-	positions := vbPos.Buffer()
+	stride := vboPos.Stride()
+	offset := vboPos.AttribOffset("VertexPosition")
+	positions := vboPos.Buffer()
 
 	// Get/calculates the bounding box
 	box := g.BoundingBox()
@@ -222,7 +235,7 @@ func (g *Geometry) BoundingSphere() math32.Sphere {
 
 	// Find the radius of the bounding sphere
 	maxRadiusSq := float32(0.0)
-	for i := 0; i < positions.Size(); i += 3 {
+	for i := offset; i < positions.Size(); i += stride {
 		var vertex math32.Vector3
 		positions.GetVector3(i, &vertex)
 		maxRadiusSq = math32.Max(maxRadiusSq, center.DistanceToSquared(&vertex))
@@ -247,9 +260,11 @@ func (g *Geometry) ApplyMatrix(m *math32.Matrix4) {
 	if vboPos == nil {
 		return
 	}
+	stride := vboPos.Stride()
+	offset := vboPos.AttribOffset("VertexPosition")
 	positions := vboPos.Buffer()
 	// Apply matrix to all position vertices
-	for i := 0; i < positions.Size(); i += 3 {
+	for i := offset; i < positions.Size(); i += stride {
 		var vertex math32.Vector3
 		positions.GetVector3(i, &vertex)
 		vertex.ApplyMatrix4(m)
@@ -262,11 +277,13 @@ func (g *Geometry) ApplyMatrix(m *math32.Matrix4) {
 	if vboNormals == nil {
 		return
 	}
+	stride = vboNormals.Stride()
+	offset = vboNormals.AttribOffset("VertexNormal")
 	normals := vboNormals.Buffer()
 	// Apply normal matrix to all normal vectors
 	var normalMatrix math32.Matrix3
 	normalMatrix.GetNormalMatrix(m)
-	for i := 0; i < normals.Size(); i += 3 {
+	for i := offset; i < normals.Size(); i += stride {
 		var vertex math32.Vector3
 		normals.GetVector3(i, &vertex)
 		vertex.ApplyMatrix3(&normalMatrix).Normalize()

+ 6 - 1
geometry/sphere.go

@@ -87,6 +87,11 @@ func NewSphere(radius float64, widthSegments, heightSegments int, phiStart, phiL
 	s.AddVBO(gls.NewVBO().AddAttrib("VertexNormal", 3).SetBuffer(normals))
 	s.AddVBO(gls.NewVBO().AddAttrib("VertexTexcoord", 2).SetBuffer(uvs))
 
-	//s.BoundingSphere = math32.NewSphere(math32.NewVector3(0, 0, 0), float32(radius))
+	r := float32(s.Radius)
+	s.boundingSphere = math32.Sphere{math32.Vector3{0, 0, 0}, r}
+	s.boundingSphereValid = true
+	s.boundingBox = math32.Box3{math32.Vector3{-r, -r, -r}, math32.Vector3{r, r, r}}
+	s.boundingBoxValid = true
+
 	return s
 }

+ 61 - 27
gls/vbo.go

@@ -9,23 +9,23 @@ import (
 	"unsafe"
 )
 
-// VBO abstracts an OpenGL Vertex Buffer Object
+// VBO abstracts an OpenGL Vertex Buffer Object.
 type VBO struct {
-	gs      *GLS            // reference to GLS state
+	gs      *GLS            // Reference to OpenGL state
 	handle  uint32          // OpenGL handle for this VBO
-	usage   uint32          // Expected usage patter of the buffer
+	usage   uint32          // Expected usage pattern of the buffer
 	update  bool            // Update flag
 	buffer  math32.ArrayF32 // Data buffer
 	attribs []VBOattrib     // List of attributes
 }
 
-// VBOattrib describes one attribute of an OpenGL Vertex Buffer Object
+// VBOattrib describes one attribute of an OpenGL Vertex Buffer Object.
 type VBOattrib struct {
 	Name     string // Name of of the attribute
-	ItemSize int32  // Number of elements for each item
+	ItemSize int32  // Number of elements
 }
 
-// NewVBO creates and returns a pointer to a new OpenGL Vertex Buffer Object
+// NewVBO creates and returns a pointer to a new OpenGL Vertex Buffer Object.
 func NewVBO() *VBO {
 
 	vbo := new(VBO)
@@ -33,7 +33,7 @@ func NewVBO() *VBO {
 	return vbo
 }
 
-// init initializes this VBO
+// init initializes the VBO.
 func (vbo *VBO) init() {
 
 	vbo.gs = nil
@@ -43,7 +43,7 @@ func (vbo *VBO) init() {
 	vbo.attribs = make([]VBOattrib, 0)
 }
 
-// AddAttrib adds a new attribute to this VBO
+// AddAttrib adds a new attribute to the VBO.
 func (vbo *VBO) AddAttrib(name string, itemSize int32) *VBO {
 
 	vbo.attribs = append(vbo.attribs, VBOattrib{
@@ -53,8 +53,8 @@ func (vbo *VBO) AddAttrib(name string, itemSize int32) *VBO {
 	return vbo
 }
 
-// Attrib finds and returns pointer the attribute with the specified name
-// or nil if not found
+// Attrib finds and returns a pointer to the VBO attribute with the specified name.
+// Returns nil if not found.
 func (vbo *VBO) Attrib(name string) *VBOattrib {
 
 	for _, attr := range vbo.attribs {
@@ -65,20 +65,26 @@ func (vbo *VBO) Attrib(name string) *VBOattrib {
 	return nil
 }
 
-// AttribAt returns pointer to the VBO attribute at the specified index
+// AttribAt returns a pointer to the VBO attribute at the specified index.
 func (vbo *VBO) AttribAt(idx int) *VBOattrib {
 
 	return &vbo.attribs[idx]
 }
 
-// AttribCount returns the current number of attributes for this VBO
+// AttribCount returns the current number of attributes for this VBO.
 func (vbo *VBO) AttribCount() int {
 
 	return len(vbo.attribs)
 }
 
-// Dispose disposes of this VBO OpenGL resources
-// As currently the VBO is used only by the Geometry object
+// Attributes returns the attributes for this VBO.
+func (vbo *VBO) Attributes() []VBOattrib {
+
+	return vbo.attribs
+}
+
+// Dispose disposes of the OpenGL resources used by the VBO.
+// As currently the VBO is used only by Geometry objects
 // it is not referenced counted.
 func (vbo *VBO) Dispose() {
 
@@ -88,7 +94,7 @@ func (vbo *VBO) Dispose() {
 	vbo.gs = nil
 }
 
-// SetBuffer sets the VBO buffer
+// SetBuffer sets the VBO buffer.
 func (vbo *VBO) SetBuffer(buffer math32.ArrayF32) *VBO {
 
 	vbo.buffer = buffer
@@ -103,33 +109,58 @@ func (vbo *VBO) SetUsage(usage uint32) {
 	vbo.usage = usage
 }
 
-// Buffer returns pointer to the VBO buffer
+// Buffer returns a pointer to the VBO buffer.
 func (vbo *VBO) Buffer() *math32.ArrayF32 {
 
 	return &vbo.buffer
 }
 
-// Update sets the update flag to force the VBO update
+// Update sets the update flag to force the VBO update.
 func (vbo *VBO) Update() {
 
 	vbo.update = true
 }
 
-// Stride returns the stride of this VBO which is the number of bytes
-// used by one group attributes side by side in the buffer
-// Ex: x y z r g b x y z r g b ...x y z r g b
-// The stride will be: sizeof(float) * 6 = 24
+// AttribOffset returns the total number of elements from
+// all attributes preceding the specified attribute.
+func (vbo *VBO) AttribOffset(name string) int {
+
+	elementCount := 0
+	for _, attr := range vbo.attribs {
+		if attr.Name == name {
+			return elementCount
+		}
+		elementCount += int(attr.ItemSize)
+	}
+	return elementCount
+}
+
+// Stride returns the stride of the VBO, which is the number of elements in
+// one complete set of group attributes. E.g. for an interleaved VBO with two attributes:
+// "VertexPosition" (3 elements) and "VertexTexcoord" (2 elements), the stride would be 5:
+// [X, Y, Z, U, V], X, Y, Z, U, V, X, Y, Z, U, V... X, Y, Z, U, V.
 func (vbo *VBO) Stride() int {
 
 	stride := 0
-	elsize := int(unsafe.Sizeof(float32(0)))
 	for _, attrib := range vbo.attribs {
-		stride += elsize * int(attrib.ItemSize)
+		stride += int(attrib.ItemSize)
 	}
 	return stride
 }
 
-// Transfer is called internally and transfer the data in the VBO buffer to OpenGL if necessary
+// StrideSize returns the number of bytes used by one complete set of group attributes.
+// E.g. for an interleaved VBO with two attributes: "VertexPosition" (3 elements)
+// and "VertexTexcoord" (2 elements), the stride would be 5:
+// [X, Y, Z, U, V], X, Y, Z, U, V, X, Y, Z, U, V... X, Y, Z, U, V
+// and the stride size would be: sizeof(float)*stride = 4*5 = 20
+func (vbo *VBO) StrideSize() int {
+
+	stride := vbo.Stride()
+	elsize := int(unsafe.Sizeof(float32(0)))
+	return stride * elsize
+}
+
+// Transfer (called internally) transfers the data from the VBO buffer to OpenGL if necessary.
 func (vbo *VBO) Transfer(gs *GLS) {
 
 	// If the VBO buffer is empty, ignore
@@ -141,8 +172,8 @@ func (vbo *VBO) Transfer(gs *GLS) {
 	if vbo.gs == nil {
 		vbo.handle = gs.GenBuffer()
 		gs.BindBuffer(ARRAY_BUFFER, vbo.handle)
-		// Calculates stride
-		stride := vbo.Stride()
+		// Calculates stride size
+		strideSize := vbo.StrideSize()
 		// For each attribute
 		var items uint32
 		var offset uint32
@@ -155,15 +186,18 @@ func (vbo *VBO) Transfer(gs *GLS) {
 			}
 			// Enables attribute and sets its stride and offset in the buffer
 			gs.EnableVertexAttribArray(uint32(loc))
-			gs.VertexAttribPointer(uint32(loc), attrib.ItemSize, FLOAT, false, int32(stride), offset)
+			gs.VertexAttribPointer(uint32(loc), attrib.ItemSize, FLOAT, false, int32(strideSize), offset)
 			items += uint32(attrib.ItemSize)
 			offset = uint32(elsize) * items
 		}
 		vbo.gs = gs // this indicates that the vbo was initialized
 	}
+
+	// If nothing has changed, no need to transfer data to OpenGL
 	if !vbo.update {
 		return
 	}
+
 	// Transfer the VBO data to OpenGL
 	gs.BindBuffer(ARRAY_BUFFER, vbo.handle)
 	gs.BufferData(ARRAY_BUFFER, vbo.buffer.Bytes(), &vbo.buffer[0], vbo.usage)

+ 20 - 2
graphic/graphic.go

@@ -21,6 +21,7 @@ type Graphic struct {
 	materials  []GraphicMaterial  // Materials
 	mode       uint32             // OpenGL primitive
 	renderable bool               // Renderable flag
+	cullable   bool               // Cullable flag
 }
 
 // GraphicMaterial specifies the material to be used for
@@ -40,6 +41,8 @@ type IGraphic interface {
 	GetGeometry() *geometry.Geometry
 	Renderable() bool
 	SetRenderable(bool)
+	Cullable() bool
+	SetCullable(bool)
 	RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo)
 }
 
@@ -61,6 +64,7 @@ func (gr *Graphic) Init(igeom geometry.IGeometry, mode uint32) *Graphic {
 	gr.mode = mode
 	gr.materials = make([]GraphicMaterial, 0)
 	gr.renderable = true
+	gr.cullable = true
 	return gr
 }
 
@@ -88,19 +92,33 @@ func (gr *Graphic) Dispose() {
 }
 
 // SetRenderable satisfies the IGraphic interface and
-// sets the renderable state of this Graphic (default = true)
+// sets the renderable state of this Graphic (default = true).
 func (gr *Graphic) SetRenderable(state bool) {
 
 	gr.renderable = state
 }
 
 // Renderable satisfies the IGraphic interface and
-// returns the renderable state of this graphic
+// returns the renderable state of this graphic.
 func (gr *Graphic) Renderable() bool {
 
 	return gr.renderable
 }
 
+// SetCullable satisfies the IGraphic interface and
+// sets the cullable state of this Graphic (default = true).
+func (gr *Graphic) SetCullable(state bool) {
+
+	gr.cullable = state
+}
+
+// Cullable satisfies the IGraphic interface and
+// returns the cullable state of this graphic.
+func (gr *Graphic) Cullable() bool {
+
+	return gr.cullable
+}
+
 // AddMaterial adds a material for the specified subset of vertices.
 // If the material applies to all vertices, start and count must be 0.
 func (gr *Graphic) AddMaterial(igr IGraphic, imat material.IMaterial, start, count int) {

+ 4 - 4
graphic/mesh.go

@@ -155,9 +155,7 @@ func (m *Mesh) Raycast(rc *core.Raycaster, intersects *[]core.Intersect) {
 	positions := vboPos.Buffer()
 	indices := geom.Indices()
 
-	var vA math32.Vector3
-	var vB math32.Vector3
-	var vC math32.Vector3
+	var vA, vB, vC math32.Vector3
 
 	// Geometry has indexed vertices
 	if indices.Size() > 0 {
@@ -181,7 +179,9 @@ func (m *Mesh) Raycast(rc *core.Raycaster, intersects *[]core.Intersect) {
 		}
 		// Geometry has NO indexed vertices
 	} else {
-		for i := 0; i < positions.Size(); i += 9 {
+		stride := vboPos.Stride()
+		offset := vboPos.AttribOffset("VertexPosition")
+		for i := offset; i < positions.Size(); i += stride {
 			// Get face indices
 			a := i / 3
 			b := a + 1

+ 7 - 5
graphic/points.go

@@ -96,11 +96,11 @@ func (p *Points) Raycast(rc *core.Raycaster, intersects *[]core.Intersect) {
 	}
 
 	// Get buffer with position vertices
-	vbPos := geom.VBO("VertexPosition")
-	if vbPos == nil {
+	vboPos := geom.VBO("VertexPosition")
+	if vboPos == nil {
 		panic("points.Raycast(): VertexPosition VBO not found")
 	}
-	positions := vbPos.Buffer()
+	positions := vboPos.Buffer()
 
 	var point math32.Vector3
 	indices := geom.Indices()
@@ -112,8 +112,10 @@ func (p *Points) Raycast(rc *core.Raycaster, intersects *[]core.Intersect) {
 			testPoint(&point, i)
 		}
 	} else {
-		for i := 0; i < positions.Size()/3; i++ {
-			positions.GetVector3(i*3, &point)
+		stride := vboPos.Stride()
+		offset := vboPos.AttribOffset("VertexPosition")
+		for i := offset; i < positions.Size(); i += stride {
+			positions.GetVector3(i, &point)
 			testPoint(&point, i)
 		}
 	}

+ 1 - 0
graphic/skybox.go

@@ -35,6 +35,7 @@ func NewSkybox(data SkyboxData) (*Skybox, error) {
 
 	geom := geometry.NewCube(1)
 	skybox.Graphic.Init(geom, gls.TRIANGLES)
+	skybox.Graphic.SetCullable(false)
 
 	for i := 0; i < 6; i++ {
 		tex, err := texture.NewTexture2DFromImage(data.DirAndPrefix + data.Suffixes[i] + "." + data.Extension)

+ 11 - 21
math32/frustum.go

@@ -9,29 +9,19 @@ type Frustum struct {
 	planes []Plane
 }
 
+func NewFrustumFromMatrix(m *Matrix4) *Frustum {
+	f := new(Frustum)
+	f.planes = make([]Plane, 6)
+	f.SetFromMatrix(m)
+	return f
+}
+
 // NewFrustum returns a pointer to a new Frustum object
 func NewFrustum(p0, p1, p2, p3, p4, p5 *Plane) *Frustum {
 
 	f := new(Frustum)
 	f.planes = make([]Plane, 6)
-	if p0 != nil {
-		f.planes[0] = *p0
-	}
-	if p1 != nil {
-		f.planes[1] = *p1
-	}
-	if p2 != nil {
-		f.planes[2] = *p2
-	}
-	if p3 != nil {
-		f.planes[3] = *p3
-	}
-	if p4 != nil {
-		f.planes[4] = *p4
-	}
-	if p5 != nil {
-		f.planes[5] = *p5
-	}
+	f.Set(p0, p1, p2, p3, p4, p5)
 	return f
 }
 
@@ -143,9 +133,9 @@ func (f *Frustum) IntersectsBox(box *Box3) bool {
 			p2.X = box.Min.X
 		}
 		if plane.normal.Y > 0 {
-			p2.Y = box.Min.Y
+			p1.Y = box.Min.Y
 		} else {
-			p2.Y = box.Max.Y
+			p1.Y = box.Max.Y
 		}
 		if plane.normal.Y > 0 {
 			p2.Y = box.Max.Y
@@ -158,7 +148,7 @@ func (f *Frustum) IntersectsBox(box *Box3) bool {
 			p1.Z = box.Max.Z
 		}
 		if plane.normal.Z > 0 {
-			p1.Z = box.Max.Z
+			p2.Z = box.Max.Z
 		} else {
 			p2.Z = box.Min.Z
 		}

+ 31 - 3
renderer/renderer.go

@@ -11,6 +11,7 @@ import (
 	"github.com/g3n/engine/graphic"
 	"github.com/g3n/engine/gui"
 	"github.com/g3n/engine/light"
+	"github.com/g3n/engine/math32"
 )
 
 // Renderer renders a 3D scene and/or a 2D GUI on the current window.
@@ -27,7 +28,8 @@ type Renderer struct {
 	pointLights  []*light.Point             // Array of point
 	spotLights   []*light.Spot              // Array of spot lights for the scene
 	others       []core.INode               // Other nodes (audio, players, etc)
-	grmats       []*graphic.GraphicMaterial // Array of all graphic materials for scene
+	grmats       []*graphic.GraphicMaterial // Array of rendered 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
 	redrawGui    bool                       // Flag indicating the gui must be redrawn completely
@@ -59,6 +61,7 @@ func NewRenderer(gs *gls.GLS) *Renderer {
 	r.spotLights = make([]*light.Spot, 0)
 	r.others = make([]core.INode, 0)
 	r.grmats = make([]*graphic.GraphicMaterial, 0)
+	r.cgrmats = make([]*graphic.GraphicMaterial, 0)
 	r.panList = make([]gui.IPanel, 0)
 	r.frameBuffers = 2
 	return r
@@ -170,6 +173,12 @@ func (r *Renderer) renderScene(iscene core.INode, icam camera.ICamera) error {
 	r.spotLights = r.spotLights[0:0]
 	r.others = r.others[0:0]
 	r.grmats = r.grmats[0:0]
+	r.cgrmats = r.cgrmats[0:0]
+
+	// Prepare for frustum culling
+	var proj math32.Matrix4
+	proj.MultiplyMatrices(&r.rinfo.ProjMatrix, &r.rinfo.ViewMatrix)
+	frustum := math32.NewFrustumFromMatrix(&proj)
 
 	// Internal function to classify a node and its children
 	var classifyNode func(inode core.INode)
@@ -185,9 +194,26 @@ func (r *Renderer) renderScene(iscene core.INode, icam camera.ICamera) error {
 		igr, ok := inode.(graphic.IGraphic)
 		if ok {
 			if igr.Renderable() {
-				// Appends to list each graphic material for this graphic
+
 				gr := igr.GetGraphic()
 				materials := gr.Materials()
+
+				// Frustum culling
+				if igr.Cullable() {
+					mw := gr.MatrixWorld()
+					geom := igr.GetGeometry()
+					bb := geom.BoundingBox()
+					bb.ApplyMatrix4(&mw)
+					if !frustum.IntersectsBox(&bb) {
+						for i := 0; i < len(materials); i++ {
+							// Record any culled materials
+							r.cgrmats = append(r.cgrmats, &materials[i])
+						}
+						return
+					}
+				}
+
+				// Appends to list each graphic material for this graphic
 				for i := 0; i < len(materials); i++ {
 					r.grmats = append(r.grmats, &materials[i])
 				}
@@ -224,6 +250,8 @@ func (r *Renderer) renderScene(iscene core.INode, icam camera.ICamera) error {
 	// Classify all scene nodes
 	classifyNode(scene)
 
+	//log.Debug("Rendered/Culled: %v/%v", len(r.grmats), len(r.cgrmats))
+
 	// Sets lights count in shader specs
 	r.specs.AmbientLightsMax = len(r.ambLights)
 	r.specs.DirLightsMax = len(r.dirLights)
@@ -304,7 +332,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 {
+	if (len(r.grmats) == 0) && (len(r.cgrmats) == 0) {
 		r.panelGui.SetRenderable(true)
 		if r.panel3D != nil {
 			r.panel3D.SetRenderable(true)