def __init__(self): # Preliminary capabilities check. if (base.win.getGsg().getSupportsBasicShaders()==0): self.t=addTitle("Firefly Demo: Video driver reports that shaders are not supported.") return if (base.win.getGsg().getSupportsDepthTexture()==0): self.t=addTitle("Firefly Demo: Video driver reports that depth textures are not supported.") return # This algorithm uses two offscreen buffers, one of which has # an auxiliary bitplane, and the offscreen buffers share a single # depth buffer. This is a heck of a complicated buffer setup. self.modelbuffer = self.makeFBO("model buffer",1) self.lightbuffer = self.makeFBO("light buffer",0) # Creation of a high-powered buffer can fail, if the graphics card # doesn't support the necessary OpenGL extensions. if (self.modelbuffer == None) or (self.lightbuffer == None): self.t=addTitle("Toon Shader: Video driver does not support multiple render targets") return # Create four render textures: depth, normal, albedo, and final. # attach them to the various bitplanes of the offscreen buffers. self.texDepth = Texture() self.texDepth.setFormat(Texture.FDepthStencil) self.texAlbedo = Texture() self.texNormal = Texture() self.texFinal = Texture() self.modelbuffer.addRenderTexture(self.texDepth, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPDepthStencil) self.modelbuffer.addRenderTexture(self.texAlbedo, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPColor) self.modelbuffer.addRenderTexture(self.texNormal, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPAuxRgba0) self.lightbuffer.addRenderTexture(self.texFinal, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPColor) # Set the near and far clipping planes. base.cam.node().getLens().setNear(50.0) base.cam.node().getLens().setFar(500.0) lens = base.cam.node().getLens() # This algorithm uses three cameras: one to render the models into the # model buffer, one to render the lights into the light buffer, and # one to render "plain" stuff (non-deferred shaded) stuff into the light # buffer. Each camera has a bitmask to identify it. self.modelMask = 1 self.lightMask = 2 self.plainMask = 4 self.modelcam=base.makeCamera(self.modelbuffer, lens=lens, scene=render, mask=self.modelMask) self.lightcam=base.makeCamera(self.lightbuffer, lens=lens, scene=render, mask=self.lightMask) self.plaincam=base.makeCamera(self.lightbuffer, lens=lens, scene=render, mask=self.plainMask) # Panda's main camera is not used. base.cam.node().setActive(0) # Take explicit control over the order in which the three # buffers are rendered. self.modelbuffer.setSort(1) self.lightbuffer.setSort(2) base.win.setSort(3) # Within the light buffer, control the order of the two cams. self.lightcam.node().getDisplayRegion(0).setSort(1) self.plaincam.node().getDisplayRegion(0).setSort(2) # By default, panda usually clears the screen before every # camera and before every window. Tell it not to do that. # Then, tell it specifically when to clear and what to clear. self.modelcam.node().getDisplayRegion(0).disableClears() self.lightcam.node().getDisplayRegion(0).disableClears() self.plaincam.node().getDisplayRegion(0).disableClears() base.cam.node().getDisplayRegion(0).disableClears() base.cam2d.node().getDisplayRegion(0).disableClears() self.modelbuffer.disableClears() base.win.disableClears() self.modelbuffer.setClearColorActive(1) self.modelbuffer.setClearDepthActive(1) self.lightbuffer.setClearColorActive(1) self.lightbuffer.setClearColor(Vec4(0,0,0,1)) # Miscellaneous stuff. base.disableMouse() base.camera.setPos(-9.112,-211.077,46.951) base.camera.setHpr(0, -7.5, 2.4) base.setBackgroundColor(Vec4(0,0,0,0)) random.seed() # Calculate the projection parameters for the final shader. # The math here is too complex to explain in an inline comment, # I've put in a full explanation into the HTML intro. proj = base.cam.node().getLens().getProjectionMat() proj_x = 0.5 * proj.getCell(3,2) / proj.getCell(0,0) proj_y = 0.5 * proj.getCell(3,2) proj_z = 0.5 * proj.getCell(3,2) / proj.getCell(2,1) proj_w = -0.5 - 0.5*proj.getCell(1,2) # Configure the render state of the model camera. tempnode = NodePath(PandaNode("temp node")) tempnode.setAttrib(AlphaTestAttrib.make(RenderAttrib.MGreaterEqual, 0.5)) tempnode.setShader(Shader.load(os.path.join(PANDA_SHADER_PATH, \ "samples/fireflies/fireflies_model.sha"))) tempnode.setAttrib(DepthTestAttrib.make(RenderAttrib.MLessEqual)) self.modelcam.node().setInitialState(tempnode.getState()) # Configure the render state of the light camera. tempnode = NodePath(PandaNode("temp node")) tempnode.setShader(Shader.load(os.path.join(PANDA_SHADER_PATH, \ "samples/fireflies/fireflies_lighting.sha"))) tempnode.setShaderInput("texnormal",self.texNormal) tempnode.setShaderInput("texalbedo",self.texAlbedo) tempnode.setShaderInput("texdepth",self.texDepth) tempnode.setShaderInput("proj",Vec4(proj_x,proj_y,proj_z,proj_w)) tempnode.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OOne, ColorBlendAttrib.OOne)) tempnode.setAttrib(CullFaceAttrib.make(CullFaceAttrib.MCullCounterClockwise)) # The next line causes problems on Linux. #tempnode.setAttrib(DepthTestAttrib.make(RenderAttrib.MGreaterEqual)) tempnode.setAttrib(DepthWriteAttrib.make(DepthWriteAttrib.MOff)) self.lightcam.node().setInitialState(tempnode.getState()) # Configure the render state of the plain camera. rs = RenderState.makeEmpty() self.plaincam.node().setInitialState(rs) # Clear any render attribs on the root node. This is necessary # because by default, panda assigns some attribs to the root # node. These default attribs will override the # carefully-configured render attribs that we just attached # to the cameras. The simplest solution is to just clear # them all out. render.setState(RenderState.makeEmpty()) # My artist created a model in which some of the polygons # don't have textures. This confuses the shader I wrote. # This little hack guarantees that everything has a texture. white = loader.loadTexture("models/samples/fireflies/white.jpg") render.setTexture(white,0) # Create two subroots, to help speed cull traversal. self.lightroot = NodePath(PandaNode("lightroot")) self.lightroot.reparentTo(render) self.modelroot = NodePath(PandaNode("modelroot")) self.modelroot.reparentTo(render) self.lightroot.hide(BitMask32(self.modelMask)) self.modelroot.hide(BitMask32(self.lightMask)) self.modelroot.hide(BitMask32(self.plainMask)) # Load the model of a forest. Make it visible to the model camera. self.forest=NodePath(PandaNode("Forest Root")) self.forest.reparentTo(render) loader.loadModel( \ "models/samples/fireflies/background").reparentTo(self.forest) loader.loadModel( \ "models/samples/fireflies/foliage01").reparentTo(self.forest) loader.loadModel( \ "models/samples/fireflies/foliage02").reparentTo(self.forest) loader.loadModel( \ "models/samples/fireflies/foliage03").reparentTo(self.forest) loader.loadModel( \ "models/samples/fireflies/foliage04").reparentTo(self.forest) loader.loadModel( \ "models/samples/fireflies/foliage05").reparentTo(self.forest) loader.loadModel( \ "models/samples/fireflies/foliage06").reparentTo(self.forest) loader.loadModel( \ "models/samples/fireflies/foliage07").reparentTo(self.forest) loader.loadModel( \ "models/samples/fireflies/foliage08").reparentTo(self.forest) loader.loadModel( \ "models/samples/fireflies/foliage09").reparentTo(self.forest) self.forest.hide(BitMask32(self.lightMask | self.plainMask)) # Cause the final results to be rendered into the main window on a card. cm = CardMaker("card") cm.setFrameFullscreenQuad() self.card = render2d.attachNewNode(cm.generate()) self.card.setTexture(self.texFinal) # Post the instructions. self.title = addTitle("Panda3D: Tutorial - Fireflies using Deferred Shading") self.inst1 = addInstructions(0.95,"ESC: Quit") self.inst2 = addInstructions(0.90,"Up/Down: More / Fewer Fireflies (Count: unknown)") self.inst3 = addInstructions(0.85,"Right/Left: Bigger / Smaller Fireflies (Radius: unknown)") self.inst4 = addInstructions(0.80,"V: View the render-to-texture results") # Panda contains a built-in viewer that lets you view the results of # your render-to-texture operations. This code configures the viewer. base.bufferViewer.setPosition("llcorner") base.bufferViewer.setCardSize(0,0.40) base.bufferViewer.setLayout("vline") self.toggleCards() self.toggleCards() # Firefly parameters self.fireflies = [] self.sequences = [] self.scaleseqs = [] self.glowspheres = [] self.fireflysize = 1.0 self.spheremodel = loader.loadModel("models/misc/sphere.flt") self.setFireflySize(25.0) while (len(self.fireflies)<5): self.addFirefly() self.updateReadout() # these allow you to change parameters in realtime self.accept("escape", sys.exit, [0]) self.accept("arrow_up", self.incFireflyCount, [1.1111111]) self.accept("arrow_down", self.decFireflyCount, [0.9000000]) self.accept("arrow_right", self.setFireflySize, [1.1111111]) self.accept("arrow_left", self.setFireflySize, [0.9000000]) self.accept("v", self.toggleCards) self.accept("V", self.toggleCards) self.nextadd = 0 taskMgr.add(self.spawnTask, "spawner")
def drawBody(nodePath, vdata, pos, vecList, radius=1, keepDrawing=True,numVertices=8): circleGeom=Geom(vdata) vertWriter=GeomVertexWriter(vdata, "vertex") colorWriter=GeomVertexWriter(vdata, "color") normalWriter=GeomVertexWriter(vdata, "normal") drawReWriter=GeomVertexRewriter(vdata, "drawFlag") texReWriter=GeomVertexRewriter(vdata, "texcoord") startRow=vdata.getNumRows() vertWriter.setRow(startRow) colorWriter.setRow(startRow) normalWriter.setRow(startRow) sCoord=0 if (startRow!=0): texReWriter.setRow(startRow-numVertices) sCoord=texReWriter.getData2f().getX()+1 drawReWriter.setRow(startRow-numVertices) if(drawReWriter.getData1f()==False): sCoord-=1 drawReWriter.setRow(startRow) texReWriter.setRow(startRow) angleSlice=2*math.pi/numVertices currAngle=0 #axisAdj=Mat4.rotateMat(45, axis)*Mat4.scaleMat(radius)*Mat4.translateMat(pos) perp1=vecList[1] perp2=vecList[2] #vertex information is written here for i in range(numVertices): adjCircle=pos+(perp1*math.cos(currAngle)+perp2*math.sin(currAngle))*radius normal=perp1*math.cos(currAngle)+perp2*math.sin(currAngle) normalWriter.addData3f(normal) vertWriter.addData3f(adjCircle) texReWriter.addData2f(sCoord,(i+0.001)/(numVertices-1)) colorWriter.addData4f(0.5,0.5,0.5,1) drawReWriter.addData1f(keepDrawing) currAngle+=angleSlice drawReader=GeomVertexReader(vdata, "drawFlag") drawReader.setRow(startRow-numVertices) #we cant draw quads directly so we use Tristrips if (startRow!=0) & (drawReader.getData1f()!=False): lines=GeomTristrips(Geom.UHStatic) half=int(numVertices*0.5) for i in range(numVertices): lines.addVertex(i+startRow) if i< half: lines.addVertex(i+startRow-half) else: lines.addVertex(i+startRow-half-numVertices) lines.addVertex(startRow) lines.addVertex(startRow-half) lines.closePrimitive() lines.decompose() circleGeom.addPrimitive(lines) circleGeomNode=GeomNode("Debug") circleGeomNode.addGeom(circleGeom) #I accidentally made the front-face face inwards. Make reverse makes the tree render properly and #should cause any surprises to any poor programmer that tries to use this code circleGeomNode.setAttrib(CullFaceAttrib.makeReverse(),1) global numPrimitives numPrimitives+=numVertices*2 nodePath.attachNewNode(circleGeomNode)
def __init__(self): # Preliminary capabilities check. if (base.win.getGsg().getSupportsBasicShaders() == 0): self.t = addTitle( "Firefly Demo: Video driver reports that shaders are not supported." ) return if (base.win.getGsg().getSupportsDepthTexture() == 0): self.t = addTitle( "Firefly Demo: Video driver reports that depth textures are not supported." ) return # This algorithm uses two offscreen buffers, one of which has # an auxiliary bitplane, and the offscreen buffers share a single # depth buffer. This is a heck of a complicated buffer setup. self.modelbuffer = self.makeFBO("model buffer", 1) self.lightbuffer = self.makeFBO("light buffer", 0) # Creation of a high-powered buffer can fail, if the graphics card # doesn't support the necessary OpenGL extensions. if (self.modelbuffer == None) or (self.lightbuffer == None): self.t = addTitle( "Toon Shader: Video driver does not support multiple render targets" ) return # Create four render textures: depth, normal, albedo, and final. # attach them to the various bitplanes of the offscreen buffers. self.texDepth = Texture() self.texDepth.setFormat(Texture.FDepthStencil) self.texAlbedo = Texture() self.texNormal = Texture() self.texFinal = Texture() self.modelbuffer.addRenderTexture(self.texDepth, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPDepthStencil) self.modelbuffer.addRenderTexture(self.texAlbedo, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPColor) self.modelbuffer.addRenderTexture(self.texNormal, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPAuxRgba0) self.lightbuffer.addRenderTexture(self.texFinal, GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPColor) # Set the near and far clipping planes. base.cam.node().getLens().setNear(50.0) base.cam.node().getLens().setFar(500.0) lens = base.cam.node().getLens() # This algorithm uses three cameras: one to render the models into the # model buffer, one to render the lights into the light buffer, and # one to render "plain" stuff (non-deferred shaded) stuff into the light # buffer. Each camera has a bitmask to identify it. self.modelMask = 1 self.lightMask = 2 self.plainMask = 4 self.modelcam = base.makeCamera(self.modelbuffer, lens=lens, scene=render, mask=self.modelMask) self.lightcam = base.makeCamera(self.lightbuffer, lens=lens, scene=render, mask=self.lightMask) self.plaincam = base.makeCamera(self.lightbuffer, lens=lens, scene=render, mask=self.plainMask) # Panda's main camera is not used. base.cam.node().setActive(0) # Take explicit control over the order in which the three # buffers are rendered. self.modelbuffer.setSort(1) self.lightbuffer.setSort(2) base.win.setSort(3) # Within the light buffer, control the order of the two cams. self.lightcam.node().getDisplayRegion(0).setSort(1) self.plaincam.node().getDisplayRegion(0).setSort(2) # By default, panda usually clears the screen before every # camera and before every window. Tell it not to do that. # Then, tell it specifically when to clear and what to clear. self.modelcam.node().getDisplayRegion(0).disableClears() self.lightcam.node().getDisplayRegion(0).disableClears() self.plaincam.node().getDisplayRegion(0).disableClears() base.cam.node().getDisplayRegion(0).disableClears() base.cam2d.node().getDisplayRegion(0).disableClears() self.modelbuffer.disableClears() base.win.disableClears() self.modelbuffer.setClearColorActive(1) self.modelbuffer.setClearDepthActive(1) self.lightbuffer.setClearColorActive(1) self.lightbuffer.setClearColor(Vec4(0, 0, 0, 1)) # Miscellaneous stuff. base.disableMouse() base.camera.setPos(-9.112, -211.077, 46.951) base.camera.setHpr(0, -7.5, 2.4) base.setBackgroundColor(Vec4(0, 0, 0, 0)) random.seed() # Calculate the projection parameters for the final shader. # The math here is too complex to explain in an inline comment, # I've put in a full explanation into the HTML intro. proj = base.cam.node().getLens().getProjectionMat() proj_x = 0.5 * proj.getCell(3, 2) / proj.getCell(0, 0) proj_y = 0.5 * proj.getCell(3, 2) proj_z = 0.5 * proj.getCell(3, 2) / proj.getCell(2, 1) proj_w = -0.5 - 0.5 * proj.getCell(1, 2) # Configure the render state of the model camera. tempnode = NodePath(PandaNode("temp node")) tempnode.setAttrib( AlphaTestAttrib.make(RenderAttrib.MGreaterEqual, 0.5)) tempnode.setShader(Shader.load(os.path.join(PANDA_SHADER_PATH, \ "samples/fireflies/fireflies_model.sha"))) tempnode.setAttrib(DepthTestAttrib.make(RenderAttrib.MLessEqual)) self.modelcam.node().setInitialState(tempnode.getState()) # Configure the render state of the light camera. tempnode = NodePath(PandaNode("temp node")) tempnode.setShader(Shader.load(os.path.join(PANDA_SHADER_PATH, \ "samples/fireflies/fireflies_lighting.sha"))) tempnode.setShaderInput("texnormal", self.texNormal) tempnode.setShaderInput("texalbedo", self.texAlbedo) tempnode.setShaderInput("texdepth", self.texDepth) tempnode.setShaderInput("proj", Vec4(proj_x, proj_y, proj_z, proj_w)) tempnode.setAttrib( ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OOne, ColorBlendAttrib.OOne)) tempnode.setAttrib( CullFaceAttrib.make(CullFaceAttrib.MCullCounterClockwise)) # The next line causes problems on Linux. #tempnode.setAttrib(DepthTestAttrib.make(RenderAttrib.MGreaterEqual)) tempnode.setAttrib(DepthWriteAttrib.make(DepthWriteAttrib.MOff)) self.lightcam.node().setInitialState(tempnode.getState()) # Configure the render state of the plain camera. rs = RenderState.makeEmpty() self.plaincam.node().setInitialState(rs) # Clear any render attribs on the root node. This is necessary # because by default, panda assigns some attribs to the root # node. These default attribs will override the # carefully-configured render attribs that we just attached # to the cameras. The simplest solution is to just clear # them all out. render.setState(RenderState.makeEmpty()) # My artist created a model in which some of the polygons # don't have textures. This confuses the shader I wrote. # This little hack guarantees that everything has a texture. white = loader.loadTexture("models/samples/fireflies/white.jpg") render.setTexture(white, 0) # Create two subroots, to help speed cull traversal. self.lightroot = NodePath(PandaNode("lightroot")) self.lightroot.reparentTo(render) self.modelroot = NodePath(PandaNode("modelroot")) self.modelroot.reparentTo(render) self.lightroot.hide(BitMask32(self.modelMask)) self.modelroot.hide(BitMask32(self.lightMask)) self.modelroot.hide(BitMask32(self.plainMask)) # Load the model of a forest. Make it visible to the model camera. self.forest = NodePath(PandaNode("Forest Root")) self.forest.reparentTo(render) loader.loadModel( \ "models/samples/fireflies/background").reparentTo(self.forest) loader.loadModel( \ "models/samples/fireflies/foliage01").reparentTo(self.forest) loader.loadModel( \ "models/samples/fireflies/foliage02").reparentTo(self.forest) loader.loadModel( \ "models/samples/fireflies/foliage03").reparentTo(self.forest) loader.loadModel( \ "models/samples/fireflies/foliage04").reparentTo(self.forest) loader.loadModel( \ "models/samples/fireflies/foliage05").reparentTo(self.forest) loader.loadModel( \ "models/samples/fireflies/foliage06").reparentTo(self.forest) loader.loadModel( \ "models/samples/fireflies/foliage07").reparentTo(self.forest) loader.loadModel( \ "models/samples/fireflies/foliage08").reparentTo(self.forest) loader.loadModel( \ "models/samples/fireflies/foliage09").reparentTo(self.forest) self.forest.hide(BitMask32(self.lightMask | self.plainMask)) # Cause the final results to be rendered into the main window on a card. cm = CardMaker("card") cm.setFrameFullscreenQuad() self.card = render2d.attachNewNode(cm.generate()) self.card.setTexture(self.texFinal) # Post the instructions. self.title = addTitle( "Panda3D: Tutorial - Fireflies using Deferred Shading") self.inst1 = addInstructions(0.95, "ESC: Quit") self.inst2 = addInstructions( 0.90, "Up/Down: More / Fewer Fireflies (Count: unknown)") self.inst3 = addInstructions( 0.85, "Right/Left: Bigger / Smaller Fireflies (Radius: unknown)") self.inst4 = addInstructions(0.80, "V: View the render-to-texture results") # Panda contains a built-in viewer that lets you view the results of # your render-to-texture operations. This code configures the viewer. base.bufferViewer.setPosition("llcorner") base.bufferViewer.setCardSize(0, 0.40) base.bufferViewer.setLayout("vline") self.toggleCards() self.toggleCards() # Firefly parameters self.fireflies = [] self.sequences = [] self.scaleseqs = [] self.glowspheres = [] self.fireflysize = 1.0 self.spheremodel = loader.loadModel("models/misc/sphere.flt") self.setFireflySize(25.0) while (len(self.fireflies) < 5): self.addFirefly() self.updateReadout() # these allow you to change parameters in realtime self.accept("escape", sys.exit, [0]) self.accept("arrow_up", self.incFireflyCount, [1.1111111]) self.accept("arrow_down", self.decFireflyCount, [0.9000000]) self.accept("arrow_right", self.setFireflySize, [1.1111111]) self.accept("arrow_left", self.setFireflySize, [0.9000000]) self.accept("v", self.toggleCards) self.accept("V", self.toggleCards) self.nextadd = 0 taskMgr.add(self.spawnTask, "spawner")