Exemple #1
0
    def init(self):
        """ Initializes the vertex buffers and makes them available as shader
        inputs. """

        self.vertexBuffers = []

        for i in xrange(2):
            vertexBuffer = Texture("VertexPositionBuffer-" + str(i))
            vertexBuffer.setup2dTexture(self.split,
                                        self.maxVertexCount / self.split,
                                        Texture.TFloat, Texture.FRgba32)
            vertexBuffer.setClearColor(Vec4(0))
            vertexBuffer.clearImage()

            MemoryMonitor.addTexture("DynamicObjectVtxBuffer" + str(i),
                                     vertexBuffer)
            Globals.render.setShaderInput("dynamicObjectVtxBuffer" + str(i),
                                          vertexBuffer)

            BufferViewerGUI.registerTexture("Vtx Positions " + str(i),
                                            vertexBuffer)
            vertexBuffer.setWrapU(Texture.WMClamp)
            vertexBuffer.setWrapV(Texture.WMClamp)
            vertexBuffer.setMinfilter(Texture.FTNearest)
            vertexBuffer.setMagfilter(Texture.FTNearest)
            self.vertexBuffers.append(vertexBuffer)

        Globals.render.setShaderInput("dynamicVtxSplit", self.split)
    def init(self):
        """ Initializes the vertex buffers and makes them available as shader
        inputs. """

        self.vertexBuffers = []

        for i in xrange(2):
            vertexBuffer = Texture("VertexPositionBuffer-" + str(i))
            vertexBuffer.setup2dTexture(self.split, self.maxVertexCount / self.split, Texture.TFloat, Texture.FRgba32)
            vertexBuffer.setClearColor(Vec4(0))
            vertexBuffer.clearImage()

            MemoryMonitor.addTexture("DynamicObjectVtxBuffer"+str(i), vertexBuffer)
            Globals.render.setShaderInput("dynamicObjectVtxBuffer"+str(i), vertexBuffer)

            BufferViewerGUI.registerTexture("Vtx Positions " + str(i), vertexBuffer)
            vertexBuffer.setWrapU(Texture.WMClamp)
            vertexBuffer.setWrapV(Texture.WMClamp)
            vertexBuffer.setMinfilter(Texture.FTNearest)
            vertexBuffer.setMagfilter(Texture.FTNearest)
            self.vertexBuffers.append(vertexBuffer)

        Globals.render.setShaderInput("dynamicVtxSplit", self.split)
Exemple #3
0
class HDR(DirectObject):

    NumBuckets = 16
    Size = 128
    
    def __init__(self):
        self.ptaBucketRange = None
        self.sceneQuad = None
        self.sceneBuf = None
        self.sceneTex = None
        self.histogramCompute = None
        self.histogramTex = None
        self.exposureCompute = None
        self.exposureTex = None
        self.debugTex = None
        self.debugCompute = None
        self.debugImg = None

    def isSupported(self):
        gsg = base.win.getGsg()
        return (gsg.getSupportsComputeShaders() and
                not metadata.MULTITHREADED_PIPELINE)

    def disable(self):
        base.filters.delExposure()

        taskMgr.remove("hdrUpdate")

        if self.debugCompute:
            self.debugCompute.removeNode()
        self.debugCompute = None
        if self.debugImg:
            self.debugImg.destroy()
        self.debugImg = None
        self.debugTex = None

        if self.exposureCompute:
            self.exposureCompute.removeNode()
        self.exposureCompute = None
        self.exposureTex = None

        if self.histogramCompute:
            self.histogramCompute.removeNode()
        self.histogramCompute = None
        self.histogramTex = None

        self.__cleanupSceneQuad()
        self.sceneTex = None

        self.ptaBucketRange = None
		
        
    def __cleanupSceneQuad(self):
        if self.sceneQuad:
            self.sceneQuad.removeNode()
        self.sceneQuad = None
        if self.sceneBuf:
            self.sceneBuf.clearRenderTextures()
            base.filters.manager.engine.removeWindow(self.sceneBuf)
        self.sceneBuf = None
		
    def __setupSceneQuad(self):
        self.sceneQuad, self.sceneBuf = base.filters.manager.renderQuadInto(colortex = self.sceneTex, size = (self.Size, self.Size), addToList = False)
        self.sceneQuad.setShader(Shader.load(Shader.SLGLSL, "phase_14/models/shaders/hdr_scene.vert.glsl",
                                             "phase_14/models/shaders/hdr_scene.frag.glsl"))
        self.sceneQuad.setShaderInput("scene_tex", base.filters.textures["color"])

    def enable(self):
        if not self.isSupported():
            # HDR/auto exposure is implemented using compute
            # shaders, which are only supported by more modern
            # graphics cards and requires at least OpenGL 4.3.
            return

        self.sceneTex = Texture('hdrSceneTex')
        self.sceneTex.setup2dTexture(self.Size, self.Size, Texture.TFloat, Texture.FRgba32)
        self.sceneTex.setWrapU(SamplerState.WMClamp)
        self.sceneTex.setWrapV(SamplerState.WMClamp)
        self.sceneTex.clearImage()
        
        self.__setupSceneQuad()

        # Build luminance histogram bucket ranges.
        self.ptaBucketRange = PTALVecBase2f.emptyArray(self.NumBuckets)
        for i in xrange(self.NumBuckets):
            # Use even distribution
            bmin = float(i) / float(self.NumBuckets)
            bmax = float(i + 1) / float(self.NumBuckets)

            # Use a distribution with slightly more bins in the low range.
            if bmin > 0.0:
                bmin = math.pow(bmin, 1.5)
            if bmax > 0.0:
                bmax = math.pow(bmax, 1.5)

            self.ptaBucketRange.setElement(i, Vec2(bmin, bmax))

        self.histogramTex = Texture('histogram')
        self.histogramTex.setup1dTexture(self.NumBuckets, Texture.TInt, Texture.FR32i)
        self.histogramTex.setClearColor(Vec4(0))
        self.histogramTex.clearImage()
        self.histogramCompute = base.computeRoot.attachNewNode(ComputeNode('histogramCompute'))
        self.histogramCompute.node().addDispatch(self.Size / 8, self.Size / 8, self.Size / 16)
        self.histogramCompute.setShader(Shader.loadCompute(Shader.SLGLSL, "phase_14/models/shaders/build_histogram.compute.glsl"), 1)
        self.histogramCompute.setShaderInput("scene_texture", self.sceneTex)
        self.histogramCompute.setShaderInput("histogram_texture", self.histogramTex)
        self.histogramCompute.setShaderInput("bucketrange", self.ptaBucketRange)
        self.histogramCompute.setBin("fixed", 0)

        self.exposureTex = Texture('exposure')
        self.exposureTex.setup1dTexture(1, Texture.TFloat, Texture.FR16)
        self.exposureTex.setClearColor(Vec4(0.0))
        self.exposureTex.clearImage()
        self.exposureCompute = base.computeRoot.attachNewNode(ComputeNode('exposureCompute'))
        self.exposureCompute.node().addDispatch(1, 1, 1)
        self.exposureCompute.setShader(Shader.loadCompute(Shader.SLGLSL, "phase_14/models/shaders/calc_luminance.compute.glsl"), 1)
        self.exposureCompute.setShaderInput("histogram_texture", self.histogramTex)
        self.exposureCompute.setShaderInput("avg_lum_texture", self.exposureTex)
        self.exposureCompute.setShaderInput("bucketrange", self.ptaBucketRange)
        self.exposureCompute.setShaderInput("exposure_minmax", Vec2(1.0, 2.0))
        self.exposureCompute.setShaderInput("adaption_rate_brightdark", Vec2(0.6, 0.6))
        self.exposureCompute.setShaderInput("exposure_scale", base.config.GetFloat("hdr-tonemapscale", 1.75))
        self.exposureCompute.setShaderInput("config_minAvgLum", base.config.GetFloat("hdr-min-avglum", 3.0))
        self.exposureCompute.setShaderInput("config_perctBrightPixels", base.config.GetFloat("hdr-percent-bright-pixels", 2.0))
        self.exposureCompute.setShaderInput("config_perctTarget", base.config.GetFloat("hdr-percent-target", 60.0))
        self.exposureCompute.setBin("fixed", 1)
        
        base.filters.setExposure(self.exposureTex)

        taskMgr.add(self.__update, "hdrUpdate", sort = -10000000)

        if base.config.GetBool("hdr-debug-histogram", False):
            self.debugTex = Texture('histogramDebugTex')
            self.debugTex.setup2dTexture(self.NumBuckets, 1, Texture.TFloat, Texture.FRgba32)
            self.debugTex.setMagfilter(SamplerState.FTNearest)
            self.debugTex.setClearColor(Vec4(0.3, 0.3, 0.5, 1.0))
            self.debugTex.clearImage()
            self.debugCompute = base.computeRoot.attachNewNode(ComputeNode('debugHistogramCompute'))
            self.debugCompute.node().addDispatch(1, 1, 1)
            self.debugCompute.setShader(Shader.loadCompute(Shader.SLGLSL, "phase_14/models/shaders/debug_histogram.compute.glsl"), 1)
            self.debugCompute.setShaderInput("histogram_texture", self.histogramTex)
            self.debugCompute.setShaderInput("debug_texture", self.debugTex)
            self.debugCompute.setBin("fixed", 2)
            self.debugImg = OnscreenImage(image = self.debugTex, scale = 0.3, pos = (-0.6, -0.7, -0.7))

    def __update(self, task):          
        # We need to calculate a brand new histogram each frame,
        # so let's clear the one from last frame.
        self.histogramTex.clearImage()
			
        return task.cont
Exemple #4
0
class MotionTrails(ShowBase):
    def __init__(self):
        # Initialize the ShowBase class from which we inherit, which will
        # create a window and set up everything we need for rendering into it.
        ShowBase.__init__(self)

        self.disableMouse()
        self.camera.setPos(0, -26, 4)
        self.setBackgroundColor(0, 0, 0)

        # Create a texture into which we can copy the main window.
        # We set it to RTMTriggeredCopyTexture mode, which tells it that we
        # want it to copy the window contents into a texture every time we
        # call self.win.triggerCopy().
        self.tex = Texture()
        self.tex.setMinfilter(Texture.FTLinear)
        self.win.addRenderTexture(self.tex,
            GraphicsOutput.RTMTriggeredCopyTexture)

        # Set the initial color to clear the texture to, before rendering it.
        # This is necessary because we don't clear the texture while rendering,
        # and otherwise the user might see garbled random data from GPU memory.
        self.tex.setClearColor((0, 0, 0, 1))
        self.tex.clearImage()

        # Create another 2D camera. Tell it to render before the main camera.
        self.backcam = self.makeCamera2d(self.win, sort=-10)
        self.background = NodePath("background")
        self.backcam.reparentTo(self.background)
        self.background.setDepthTest(0)
        self.background.setDepthWrite(0)
        self.backcam.node().getDisplayRegion(0).setClearDepthActive(0)

        # Obtain two texture cards. One renders before the dragon, the other
        # after.
        self.bcard = self.win.getTextureCard()
        self.bcard.reparentTo(self.background)
        self.bcard.setTransparency(1)
        self.fcard = self.win.getTextureCard()
        self.fcard.reparentTo(self.render2d)
        self.fcard.setTransparency(1)

        # Initialize one of the nice effects.
        self.chooseEffectGhost()

        # Add the task that initiates the screenshots.
        taskMgr.add(self.takeSnapShot, "takeSnapShot")

        # Create some black squares on top of which we will
        # place the instructions.
        blackmaker = CardMaker("blackmaker")
        blackmaker.setColor(0, 0, 0, 1)
        blackmaker.setFrame(-1.00, -0.50, 0.65, 1.00)
        instcard = NodePath(blackmaker.generate())
        instcard.reparentTo(self.render2d)
        blackmaker.setFrame(-0.5, 0.5, -1.00, -0.85)
        titlecard = NodePath(blackmaker.generate())
        titlecard.reparentTo(self.render2d)

        # Panda does its best to hide the differences between DirectX and
        # OpenGL.  But there are a few differences that it cannot hide.
        # One such difference is that when OpenGL copies from a
        # visible window to a texture, it gets it right-side-up.  When
        # DirectX does it, it gets it upside-down.  There is nothing panda
        # can do to compensate except to expose a flag and let the
        # application programmer deal with it.  You should only do this
        # in the rare event that you're copying from a visible window
        # to a texture.
        if self.win.getGsg().getCopyTextureInverted():
            print("Copy texture is inverted.")
            self.bcard.setScale(1, 1, -1)
            self.fcard.setScale(1, 1, -1)

        # Put up the instructions
        title = OnscreenText(text="Panda3D: Tutorial - Motion Trails",
                             fg=(1, 1, 1, 1), parent=base.a2dBottomCenter,
                             pos=(0, 0.1), scale=.08)

        instr0 = addInstructions(0.06, "Press ESC to exit")
        instr1 = addInstructions(0.12, "Press 1: Ghost effect")
        instr2 = addInstructions(0.18, "Press 2: PaintBrush effect")
        instr3 = addInstructions(0.24, "Press 3: Double Vision effect")
        instr4 = addInstructions(0.30, "Press 4: Wings of Blue effect")
        instr5 = addInstructions(0.36, "Press 5: Whirlpool effect")

        # Enable the key events
        self.accept("escape", sys.exit, [0])
        self.accept("1", self.chooseEffectGhost)
        self.accept("2", self.chooseEffectPaintBrush)
        self.accept("3", self.chooseEffectDoubleVision)
        self.accept("4", self.chooseEffectWingsOfBlue)
        self.accept("5", self.chooseEffectWhirlpool)

    def takeSnapShot(self, task):
        if task.time > self.nextclick:
            self.nextclick += 1.0 / self.clickrate
            if self.nextclick < task.time:
                self.nextclick = task.time
            self.win.triggerCopy()
        return Task.cont

    def chooseEffectGhost(self):
        self.setBackgroundColor(0, 0, 0, 1)
        self.bcard.hide()
        self.fcard.show()
        self.fcard.setColor(1.0, 1.0, 1.0, 0.99)
        self.fcard.setScale(1.00)
        self.fcard.setPos(0, 0, 0)
        self.fcard.setR(0)
        self.clickrate = 30
        self.nextclick = 0

    def chooseEffectPaintBrush(self):
        self.setBackgroundColor(0, 0, 0, 1)
        self.bcard.show()
        self.fcard.hide()
        self.bcard.setColor(1, 1, 1, 1)
        self.bcard.setScale(1.0)
        self.bcard.setPos(0, 0, 0)
        self.bcard.setR(0)
        self.clickrate = 10000
        self.nextclick = 0

    def chooseEffectDoubleVision(self):
        self.setBackgroundColor(0, 0, 0, 1)
        self.bcard.show()
        self.bcard.setColor(1, 1, 1, 1)
        self.bcard.setScale(1.0)
        self.bcard.setPos(-0.05, 0, 0)
        self.bcard.setR(0)
        self.fcard.show()
        self.fcard.setColor(1, 1, 1, 0.60)
        self.fcard.setScale(1.0)
        self.fcard.setPos(0.05, 0, 0)
        self.fcard.setR(0)
        self.clickrate = 10000
        self.nextclick = 0

    def chooseEffectWingsOfBlue(self):
        self.setBackgroundColor(0, 0, 0, 1)
        self.fcard.hide()
        self.bcard.show()
        self.bcard.setColor(1.0, 0.90, 1.0, 254.0 / 255.0)
        self.bcard.setScale(1.1, 1, 0.95)
        self.bcard.setPos(0, 0, 0.05)
        self.bcard.setR(0)
        self.clickrate = 30
        self.nextclick = 0

    def chooseEffectWhirlpool(self):
        self.setBackgroundColor(0, 0, 0, 1)
        self.bcard.show()
        self.fcard.hide()
        self.bcard.setColor(1, 1, 1, 1)
        self.bcard.setScale(0.999)
        self.bcard.setPos(0, 0, 0)
        self.bcard.setR(1)
        self.clickrate = 10000
        self.nextclick = 0
class TransparencyManager(DebugObject):

    """ This class manages rendering of transparency. It creates the buffers to
    store transparency data in, and also provides the default transparency shader.

    Internal OIT is used, with per pixel linked lists. The sorting happens in the
    final transparency pass. """

    def __init__(self, pipeline):
        """ Creates the manager, but does not init the buffers """
        DebugObject.__init__(self, "TransparencyManager")
        self.debug("Initializing ..")

        self.pipeline = pipeline

        # This stores the maximum amount of transparent pixels which can be on the
        # screen at one time. If the amount of pixels exceeds this value, strong
        # artifacts will occur!
        self.maxPixelCount = 1920 * 1080 * 2
        self.initTransparencyPass()

    def initTransparencyPass(self):
        """ Creates the pass which renders the transparent objects into the scene """
        self.transparencyPass = TransparencyPass()
        self.pipeline.getRenderPassManager().registerPass(self.transparencyPass)

        # Create the atomic counter which stores the amount of rendered transparent
        # pixels. For now this a 1x1 texture, as atomic counters are not implemented.
        self.pixelCountBuffer = Texture("MaterialCountBuffer")
        self.pixelCountBuffer.setup2dTexture(1, 1, Texture.TInt, Texture.FR32i)

        # Creates the buffer which stores all transparent pixels. Pixels are inserted
        # into the buffer in the order they are rendered, using the pixelCountBuffer
        # to determine their index 
        self.materialDataBuffer = Texture("MaterialDataBuffer")
        self.materialDataBuffer.setupBufferTexture(self.maxPixelCount, Texture.TFloat, 
            Texture.FRgba32, GeomEnums.UH_static)

        # Creates the list head buffer, which stores the first transparent pixel for
        # each window pixel. The index stored in this buffer is the index into the 
        # materialDataBuffer
        self.listHeadBuffer = Texture("ListHeadBuffer")
        self.listHeadBuffer.setup2dTexture(self.pipeline.getSize().x, self.pipeline.getSize().y, 
            Texture.TInt, Texture.FR32i)

        # Creates the spinlock buffer, which ensures that writing to the listHeadBuffer
        # is sequentially
        self.spinLockBuffer = Texture("SpinLockBuffer")
        self.spinLockBuffer.setup2dTexture(self.pipeline.getSize().x, self.pipeline.getSize().y, 
            Texture.TInt, Texture.FR32i)

        # Set the buffers as input to the main scene. Maybe we can do this more elegant
        target = self.pipeline.showbase.render
        target.setShaderInput("pixelCountBuffer", self.pixelCountBuffer)
        target.setShaderInput("spinLockBuffer", self.spinLockBuffer)
        target.setShaderInput("materialDataBuffer", self.materialDataBuffer)
        target.setShaderInput("listHeadBuffer", self.listHeadBuffer)

        # Provides the buffers as global shader inputs
        self.pipeline.getRenderPassManager().registerStaticVariable("transpPixelCountBuffer", self.pixelCountBuffer)
        self.pipeline.getRenderPassManager().registerStaticVariable("transpSpinLockBuffer", self.spinLockBuffer)
        self.pipeline.getRenderPassManager().registerStaticVariable("transpListHeadBuffer", self.listHeadBuffer)
        self.pipeline.getRenderPassManager().registerStaticVariable("transpMaterialDataBuffer", self.materialDataBuffer)

        # Registers the transparency settings to the shaders
        self.pipeline.getRenderPassManager().registerDefine("USE_TRANSPARENCY", 1)
        self.pipeline.getRenderPassManager().registerDefine("MAX_TRANSPARENCY_LAYERS", 
            self.pipeline.settings.maxTransparencyLayers)

        self.pixelCountBuffer.setClearColor(Vec4(0, 0, 0, 0))
        self.spinLockBuffer.setClearColor(Vec4(0, 0, 0, 0))
        self.listHeadBuffer.setClearColor(Vec4(0, 0, 0, 0))

        MemoryMonitor.addTexture("MaterialCountBuffer", self.pixelCountBuffer)
        MemoryMonitor.addTexture("MaterialDataBuffer", self.materialDataBuffer)
        MemoryMonitor.addTexture("ListHeadBuffer", self.listHeadBuffer)
        MemoryMonitor.addTexture("SpinLockBuffer", self.spinLockBuffer)

    def update(self):
        """ The update method clears the buffers before rendering the next frame """
        self.pixelCountBuffer.clearImage()
        self.spinLockBuffer.clearImage()
        self.listHeadBuffer.clearImage()

    def getDefaultShader(self):
        """ Returns the default shader for transparent objects """
        shader = Shader.load(Shader.SLGLSL, 
                "Shader/DefaultShaders/Transparent/vertex.glsl",
                "Shader/DefaultShaders/Transparent/fragment.glsl")
        return shader
class VoxelizePass(RenderPass):

    """ This pass manages voxelizing the scene from multiple directions to generate
    a 3D voxel grid. It handles the camera setup and provides a simple interface. """

    def __init__(self):
        RenderPass.__init__(self)

    def getID(self):
        return "VoxelizePass"

    def getRequiredInputs(self):
        return {
        }

    def setVoxelGridResolution(self, voxelGridResolution):
        """ Sets the voxel grid resolution, this is the amount of voxels in every
        direction, so the voxel grid will have voxelGridResolution**3 total voxels. """
        self.voxelGridResolution = voxelGridResolution

    def setVoxelGridSize(self, voxelGridSize):
        """ Sets the size of the voxel grid in world space units. This is the
        size going from the mid of the voxel grid, so the effective voxel grid
        will have twice the size specified in voxelGridSize """
        self.voxelGridSize = voxelGridSize

    def setPhotonScaleFactor(self, factor):
        """ Sets the density of the photon grid. A number of 1 means that for
        every bright voxel 1 photon will be spawned. A number of 4 for example
        means that for ever bright voxel 4x4x4 = 64 Photons will be spawned. """
        self.photonScaleFactor = factor

    def setActive(self, active):
        """ Enables and disables this pass """
        self.target.setActive(active)

    def initVoxelStorage(self):
        """ Creates t he 3D Texture to store the voxel generation grid """
        self.voxelGenTex = Texture("VoxelsTemp")
        self.voxelGenTex.setup3dTexture(self.voxelGridResolution.x, self.voxelGridResolution.y, 
                                        self.voxelGridResolution.z, Texture.TInt, Texture.FR32i)

        # Set appropriate filter types
        self.voxelGenTex.setMinfilter(SamplerState.FTNearest)
        self.voxelGenTex.setMagfilter(Texture.FTNearest)
        self.voxelGenTex.setWrapU(Texture.WMClamp)
        self.voxelGenTex.setWrapV(Texture.WMClamp)
        self.voxelGenTex.setWrapW(Texture.WMClamp)
        self.voxelGenTex.setClearColor(Vec4(0))

        # Register texture
        MemoryMonitor.addTexture("Voxel Temp Texture", self.voxelGenTex)

    def getVoxelTex(self):
        """ Returns a handle to the generated voxel texture """
        return self.voxelGenTex

    def clearGrid(self):
        """ Clears the voxel grid """
        self.voxelGenTex.clearImage()

    def create(self):
        # Create voxelize camera
        self.voxelizeCamera = Camera("VoxelizeScene")
        self.voxelizeCamera.setCameraMask(BitMask32.bit(4))
        self.voxelizeCameraNode = Globals.render.attachNewNode(self.voxelizeCamera)
        self.voxelizeLens = OrthographicLens()
        self.voxelizeLens.setFilmSize(self.voxelGridSize.x*2, self.voxelGridSize.y*2)
        self.voxelizeLens.setNearFar(0.0, self.voxelGridSize.x*2)
        self.voxelizeCamera.setLens(self.voxelizeLens)
        self.voxelizeCamera.setTagStateKey("VoxelizePassShader")
        Globals.render.setTag("VoxelizePassShader", "Default")

        # Create voxelize tareet
        self.target = RenderTarget("VoxelizePass")
        self.target.setSize(self.voxelGridResolution.x * self.photonScaleFactor)
        # self.target.setColorWrite(False)
        self.target.addColorTexture()
        self.target.setCreateOverlayQuad(False)
        self.target.setSource(self.voxelizeCameraNode, Globals.base.win)
        self.target.prepareSceneRender()
        self.target.setActive(False)

        self.target.getInternalRegion().setSort(-400)
        self.target.getInternalBuffer().setSort(-399)

    def voxelizeSceneFromDirection(self, gridPos, direction="x"):
        """ Voxelizes the scene from a given direction. This method handles setting 
        the camera position aswell as the near and far plane. If the pass was disabled,
        it also enables this pass.  """
        assert(direction in "x y z".split())
        self.setActive(True)

        if direction == "x":
            self.voxelizeLens.setFilmSize(self.voxelGridSize.y*2, self.voxelGridSize.z*2)
            self.voxelizeLens.setNearFar(0.0, self.voxelGridSize.x*2)
            self.voxelizeCameraNode.setPos(gridPos - Vec3(self.voxelGridSize.x, 0, 0))
            self.voxelizeCameraNode.lookAt(gridPos)
        elif direction == "y":
            self.voxelizeLens.setFilmSize(self.voxelGridSize.x*2, self.voxelGridSize.z*2)
            self.voxelizeLens.setNearFar(0.0, self.voxelGridSize.y*2)
            self.voxelizeCameraNode.setPos(gridPos - Vec3(0, self.voxelGridSize.y, 0))
            self.voxelizeCameraNode.lookAt(gridPos)
        elif direction == "z":
            self.voxelizeLens.setFilmSize(self.voxelGridSize.x*2, self.voxelGridSize.y*2)
            self.voxelizeLens.setNearFar(0.0, self.voxelGridSize.z*2)
            self.voxelizeCameraNode.setPos(gridPos + Vec3(0, 0, self.voxelGridSize.z))
            self.voxelizeCameraNode.lookAt(gridPos)

    def setShaders(self):
        """ Creates the tag state and loades the voxelizer shader """
        voxelizeShader = Shader.load(Shader.SLGLSL, 
            "Shader/GI/Voxelize.vertex",
            "Shader/GI/Voxelize.fragment")

        # Create tag state
        initialState = NodePath("VoxelizerState")
        initialState.setShader(voxelizeShader, 500)
        initialState.setAttrib(CullFaceAttrib.make(CullFaceAttrib.MCullNone))
        initialState.setDepthWrite(False)
        initialState.setDepthTest(False)
        initialState.setAttrib(DepthTestAttrib.make(DepthTestAttrib.MNone))
        initialState.setShaderInput("giVoxelGenerationTex", self.voxelGenTex)

        # Apply tag state
        self.voxelizeCamera.setTagState("Default", initialState.getState())

        return [voxelizeShader]

    def getOutputs(self):
        return {
        }
class MotionTrails:
    def __init__(self):
        # Create a texture into which we can copy the main window.
        # We set it to RTMTriggeredCopyTexture mode, which tells it that we
        # want it to copy the window contents into a texture every time we
        # call self.win.triggerCopy().
        self.tex = Texture()
        self.tex.setMinfilter(Texture.FTLinear)
        self.win = GraphicsOutput()
        self.win.addRenderTexture(self.tex,
                                  GraphicsOutput.RTMTriggeredCopyTexture)

        # Set the initial color to clear the texture to, before rendering it.
        # This is necessary because we don't clear the texture while rendering,
        # and otherwise the user might see garbled random data from GPU memory.
        self.tex.setClearColor((0, 0, 0, 1))
        self.tex.clearImage()

        # Create another 2D camera. Tell it to render before the main camera.
        self.backcam = self.makeCamera2d(self.win, sort=-10)
        self.background = NodePath("background")
        self.backcam.reparentTo(self.background)
        self.background.setDepthTest(0)
        self.background.setDepthWrite(0)
        self.backcam.node().getDisplayRegion(0).setClearDepthActive(0)

        # Obtain two texture cards. One renders before the dragon, the other
        # after.
        self.bcard = self.win.getTextureCard()
        self.bcard.reparentTo(self.background)
        self.bcard.setTransparency(1)
        self.fcard = self.win.getTextureCard()
        self.fcard.reparentTo(self.render2d)
        self.fcard.setTransparency(1)

        # Initialize one of the nice effects.
        self.chooseEffectGhost()

        # Add the task that initiates the screenshots.
        taskMgr.add(self.takeSnapShot, "takeSnapShot")

        # Create some black squares on top of which we will
        # place the instructions.
        blackmaker = CardMaker("blackmaker")
        blackmaker.setColor(0, 0, 0, 1)
        blackmaker.setFrame(-1.00, -0.50, 0.65, 1.00)
        instcard = NodePath(blackmaker.generate())
        instcard.reparentTo(self.render2d)
        blackmaker.setFrame(-0.5, 0.5, -1.00, -0.85)
        titlecard = NodePath(blackmaker.generate())
        titlecard.reparentTo(self.render2d)

        # Panda does its best to hide the differences between DirectX and
        # OpenGL.  But there are a few differences that it cannot hide.
        # One such difference is that when OpenGL copies from a
        # visible window to a texture, it gets it right-side-up.  When
        # DirectX does it, it gets it upside-down.  There is nothing panda
        # can do to compensate except to expose a flag and let the
        # application programmer deal with it.  You should only do this
        # in the rare event that you're copying from a visible window
        # to a texture.
        if self.win.getGsg().getCopyTextureInverted():
            print("Copy texture is inverted.")
            self.bcard.setScale(1, 1, -1)
            self.fcard.setScale(1, 1, -1)

        # Put up the instructions
        title = OnscreenText(text="Panda3D: Tutorial - Motion Trails",
                             fg=(1, 1, 1, 1),
                             parent=base.a2dBottomCenter,
                             pos=(0, 0.1),
                             scale=.08)

        self.accept("5", self.chooseEffectWhirlpool)

    def takeSnapShot(self, task):
        if task.time > self.nextclick:
            self.nextclick += 1.0 / self.clickrate
            if self.nextclick < task.time:
                self.nextclick = task.time
            self.win.triggerCopy()
        return Task.cont

    def chooseEffectGhost(self):
        self.setBackgroundColor(0, 0, 0, 1)
        self.bcard.hide()
        self.fcard.show()
        self.fcard.setColor(1.0, 1.0, 1.0, 0.99)
        self.fcard.setScale(1.00)
        self.fcard.setPos(0, 0, 0)
        self.fcard.setR(0)
        self.clickrate = 30
        self.nextclick = 0

    def chooseEffectPaintBrush(self):
        self.setBackgroundColor(0, 0, 0, 1)
        self.bcard.show()
        self.fcard.hide()
        self.bcard.setColor(1, 1, 1, 1)
        self.bcard.setScale(1.0)
        self.bcard.setPos(0, 0, 0)
        self.bcard.setR(0)
        self.clickrate = 10000
        self.nextclick = 0

    def chooseEffectDoubleVision(self):
        self.setBackgroundColor(0, 0, 0, 1)
        self.bcard.show()
        self.bcard.setColor(1, 1, 1, 1)
        self.bcard.setScale(1.0)
        self.bcard.setPos(-0.05, 0, 0)
        self.bcard.setR(0)
        self.fcard.show()
        self.fcard.setColor(1, 1, 1, 0.60)
        self.fcard.setScale(1.0)
        self.fcard.setPos(0.05, 0, 0)
        self.fcard.setR(0)
        self.clickrate = 10000
        self.nextclick = 0

    def chooseEffectWingsOfBlue(self):
        self.setBackgroundColor(0, 0, 0, 1)
        self.fcard.hide()
        self.bcard.show()
        self.bcard.setColor(1.0, 0.90, 1.0, 254.0 / 255.0)
        self.bcard.setScale(1.1, 1, 0.95)
        self.bcard.setPos(0, 0, 0.05)
        self.bcard.setR(0)
        self.clickrate = 30
        self.nextclick = 0

    def chooseEffectWhirlpool(self):
        self.setBackgroundColor(0, 0, 0, 1)
        self.bcard.show()
        self.fcard.hide()
        self.bcard.setColor(1, 1, 1, 1)
        self.bcard.setScale(0.999)
        self.bcard.setPos(0, 0, 0)
        self.bcard.setR(1)
        self.clickrate = 10000
        self.nextclick = 0
class DynamicExposurePass(RenderPass):

    """ This pass handles the dynamic exposure feature, it downscales the
    Scene to get the average brightness and then outputs a new exposure which
    can be used by the lighting pass. """

    def __init__(self, pipeline):
        RenderPass.__init__(self)
        self.pipeline = pipeline

        # Create the storage for the exposure. We cannot simply use the color output
        # as the RenderTargetMatcher would have problems with that (Circular Reference)
        self.lastExposureStorage = Texture("Last Exposure")
        self.lastExposureStorage.setup2dTexture(1, 1, Texture.TFloat, Texture.FR32)

        # Registers the texture so the lighting pass can use it
        self.pipeline.renderPassManager.registerStaticVariable(
            "dynamicExposureTex", self.lastExposureStorage)


    def getID(self):
        return "DynamicExposurePass"

    def getRequiredInputs(self):
        return {
            "colorTex": "LightingPass.resultTex",
            "dt": "Variables.frameDelta"
        }

    def create(self):

        # Fetch the original texture size from the window size
        size = LVecBase2i(Globals.resolution.x, Globals.resolution.y)

        # Create the first downscale pass which reads the scene texture, does a 
        # 2x2 inplace box filter, and then converts the result to luminance. 
        # Using luminance allows faster downscaling, as we can use texelGather then
        self.downscalePass0 = RenderTarget("Downscale Initial")
        self.downscalePass0.addColorTexture()
        self.downscalePass0.setSize(size.x / 2, size.y / 2)
        self.downscalePass0.prepareOffscreenBuffer()

        # Store the current size of the pass
        workSizeX, workSizeY = int(size.x / 2), int(size.y / 2)

        self.downscalePasses = []
        passIdx = 0
        lastTex = self.downscalePass0.getColorTexture()

        # Scale the scene until there are only a few pixels left. Each pass does a 
        # 4x4 inplace box filter, which is cheap because we can sample the luminance
        # only.
        while workSizeX * workSizeY > 128:
            workSizeX /= 4
            workSizeY /= 4
            passIdx += 1
            scalePass = RenderTarget("Downscale Pass " + str(passIdx))
            scalePass.setSize(workSizeX, workSizeY)
            scalePass.addColorTexture()
            scalePass.prepareOffscreenBuffer()
            scalePass.setShaderInput("luminanceTex", lastTex)
            lastTex = scalePass.getColorTexture()
            self.downscalePasses.append(scalePass)

        # Create the final pass which computes the average of all left pixels,
        # compares that with the last exposure and stores the difference.
        self.finalDownsamplePass = RenderTarget("Downscale Final")
        self.finalDownsamplePass.setSize(1, 1)
        # self.finalDownsamplePass.setColorBits(16)
        # self.finalDownsamplePass.addColorTexture()
        self.finalDownsamplePass.setColorWrite(False)
        self.finalDownsamplePass.prepareOffscreenBuffer()
        self.finalDownsamplePass.setShaderInput("luminanceTex", lastTex)
        self.finalDownsamplePass.setShaderInput("targetExposure", 
            self.pipeline.settings.targetExposure)
        self.finalDownsamplePass.setShaderInput("adaptionSpeed", 
            self.pipeline.settings.brightnessAdaptionSpeed)

        # Clear the storage in the beginning
        self.lastExposureStorage.setClearColor(Vec4(0))
        self.lastExposureStorage.clearImage()

        # Set defines and other inputs
        self.finalDownsamplePass.setShaderInput("lastExposureTex", self.lastExposureStorage)
        self.pipeline.renderPassManager.registerDefine("USE_DYNAMIC_EXPOSURE", 1)

    def setShaders(self):
        shaderFirstPass = Shader.load(Shader.SLGLSL, 
            "Shader/DefaultPostProcess.vertex",
            "Shader/DownsampleFirstPass.fragment")
        self.downscalePass0.setShader(shaderFirstPass)

        shaderDownsample = Shader.load(Shader.SLGLSL, 
            "Shader/DefaultPostProcess.vertex",
            "Shader/Downsample.fragment")
        for scalePass in self.downscalePasses:
            scalePass.setShader(shaderDownsample)

        shaderFinal = Shader.load(Shader.SLGLSL, 
            "Shader/DefaultPostProcess.vertex",
            "Shader/DownsampleFinalPass.fragment")
        self.finalDownsamplePass.setShader(shaderFinal)

        return [shaderFirstPass, shaderDownsample, shaderFinal]

    def setShaderInput(self, name, value, *args):
        self.downscalePass0.setShaderInput(name, value, *args)
        self.finalDownsamplePass.setShaderInput(name, value, *args)

    def getOutputs(self):
        return {
        }
class DynamicExposurePass(RenderPass):
    """ This pass handles the dynamic exposure feature, it downscales the
    Scene to get the average brightness and then outputs a new exposure which
    can be used by the lighting pass. """
    def __init__(self, pipeline):
        RenderPass.__init__(self)
        self.pipeline = pipeline

        # Create the storage for the exposure. We cannot simply use the color output
        # as the RenderTargetMatcher would have problems with that (Circular Reference)
        self.lastExposureStorage = Texture("Last Exposure")
        self.lastExposureStorage.setup2dTexture(1, 1, Texture.TFloat,
                                                Texture.FR32)

        # Registers the texture so the lighting pass can use it
        self.pipeline.renderPassManager.registerStaticVariable(
            "dynamicExposureTex", self.lastExposureStorage)

    def getID(self):
        return "DynamicExposurePass"

    def getRequiredInputs(self):
        return {
            "colorTex": "LightingPass.resultTex",
            "dt": "Variables.frameDelta"
        }

    def create(self):

        # Fetch the original texture size from the window size
        size = LVecBase2i(Globals.resolution.x, Globals.resolution.y)

        # Create the first downscale pass which reads the scene texture, does a
        # 2x2 inplace box filter, and then converts the result to luminance.
        # Using luminance allows faster downscaling, as we can use texelGather then
        self.downscalePass0 = RenderTarget("Downscale Initial")
        self.downscalePass0.addColorTexture()
        self.downscalePass0.setSize(size.x / 2, size.y / 2)
        self.downscalePass0.prepareOffscreenBuffer()

        # Store the current size of the pass
        workSizeX, workSizeY = int(size.x / 2), int(size.y / 2)

        self.downscalePasses = []
        passIdx = 0
        lastTex = self.downscalePass0.getColorTexture()

        # Scale the scene until there are only a few pixels left. Each pass does a
        # 4x4 inplace box filter, which is cheap because we can sample the luminance
        # only.
        while workSizeX * workSizeY > 128:
            workSizeX /= 4
            workSizeY /= 4
            passIdx += 1
            scalePass = RenderTarget("Downscale Pass " + str(passIdx))
            scalePass.setSize(workSizeX, workSizeY)
            scalePass.addColorTexture()
            scalePass.prepareOffscreenBuffer()
            scalePass.setShaderInput("luminanceTex", lastTex)
            lastTex = scalePass.getColorTexture()
            self.downscalePasses.append(scalePass)

        # Create the final pass which computes the average of all left pixels,
        # compares that with the last exposure and stores the difference.
        self.finalDownsamplePass = RenderTarget("Downscale Final")
        self.finalDownsamplePass.setSize(1, 1)
        # self.finalDownsamplePass.setColorBits(16)
        # self.finalDownsamplePass.addColorTexture()
        self.finalDownsamplePass.setColorWrite(False)
        self.finalDownsamplePass.prepareOffscreenBuffer()
        self.finalDownsamplePass.setShaderInput("luminanceTex", lastTex)
        self.finalDownsamplePass.setShaderInput(
            "targetExposure", self.pipeline.settings.targetExposure)
        self.finalDownsamplePass.setShaderInput(
            "adaptionSpeed", self.pipeline.settings.brightnessAdaptionSpeed)

        # Clear the storage in the beginning
        self.lastExposureStorage.setClearColor(Vec4(0))
        self.lastExposureStorage.clearImage()

        # Set defines and other inputs
        self.finalDownsamplePass.setShaderInput("lastExposureTex",
                                                self.lastExposureStorage)
        self.pipeline.renderPassManager.registerDefine("USE_DYNAMIC_EXPOSURE",
                                                       1)

    def setShaders(self):
        shaderFirstPass = Shader.load(Shader.SLGLSL,
                                      "Shader/DefaultPostProcess.vertex",
                                      "Shader/DownsampleFirstPass.fragment")
        self.downscalePass0.setShader(shaderFirstPass)

        shaderDownsample = Shader.load(Shader.SLGLSL,
                                       "Shader/DefaultPostProcess.vertex",
                                       "Shader/Downsample.fragment")
        for scalePass in self.downscalePasses:
            scalePass.setShader(shaderDownsample)

        shaderFinal = Shader.load(Shader.SLGLSL,
                                  "Shader/DefaultPostProcess.vertex",
                                  "Shader/DownsampleFinalPass.fragment")
        self.finalDownsamplePass.setShader(shaderFinal)

        return [shaderFirstPass, shaderDownsample, shaderFinal]

    def setShaderInput(self, name, value, *args):
        self.downscalePass0.setShaderInput(name, value, *args)
        self.finalDownsamplePass.setShaderInput(name, value, *args)

    def getOutputs(self):
        return {}
Exemple #10
0
class TransparencyManager(DebugObject):
    """ This class manages rendering of transparency. It creates the buffers to
    store transparency data in, and also provides the default transparency shader.

    Internal OIT is used, with per pixel linked lists. The sorting happens in the
    final transparency pass. """
    def __init__(self, pipeline):
        """ Creates the manager, but does not init the buffers """
        DebugObject.__init__(self, "TransparencyManager")
        self.debug("Initializing ..")

        self.pipeline = pipeline

        # This stores the maximum amount of transparent pixels which can be on the
        # screen at one time. If the amount of pixels exceeds this value, strong
        # artifacts will occur!
        self.maxPixelCount = 1920 * 1080 / 2
        self.initTransparencyPass()

    def initTransparencyPass(self):
        """ Creates the pass which renders the transparent objects into the scene """
        self.transparencyPass = TransparencyPass()
        self.pipeline.getRenderPassManager().registerPass(
            self.transparencyPass)

        self.transparencyShadePass = TransparencyShadePass(self.pipeline)
        self.transparencyShadePass.setBatchSize(
            self.pipeline.settings.transparencyBatchSize)
        self.pipeline.getRenderPassManager().registerPass(
            self.transparencyShadePass)

        # Create the atomic counter which stores the amount of rendered transparent
        # pixels. For now this a 1x1 texture, as atomic counters are not implemented.
        self.pixelCountBuffer = Texture("MaterialCountBuffer")
        self.pixelCountBuffer.setup2dTexture(1, 1, Texture.TInt, Texture.FR32i)

        # Creates the buffer which stores all transparent pixels. Pixels are inserted
        # into the buffer in the order they are rendered, using the pixelCountBuffer
        # to determine their index
        self.materialDataBuffer = Texture("MaterialDataBuffer")
        self.materialDataBuffer.setupBufferTexture(self.maxPixelCount,
                                                   Texture.TFloat,
                                                   Texture.FRgba32,
                                                   GeomEnums.UH_static)

        # Creates the list head buffer, which stores the first transparent pixel for
        # each window pixel. The index stored in this buffer is the index into the
        # materialDataBuffer
        self.listHeadBuffer = Texture("ListHeadBuffer")
        self.listHeadBuffer.setup2dTexture(Gloabls.resolution.x,
                                           Globals.resolution.y, Texture.TInt,
                                           Texture.FR32i)

        # Creates the spinlock buffer, which ensures that writing to the listHeadBuffer
        # is sequentially
        self.spinLockBuffer = Texture("SpinLockBuffer")
        self.spinLockBuffer.setup2dTexture(Gloabls.resolution.x,
                                           Globals.resolution.y, Texture.TInt,
                                           Texture.FR32i)

        # Set the buffers as input to the main scene. Maybe we can do this more elegant
        target = self.pipeline.showbase.render
        target.setShaderInput("pixelCountBuffer", self.pixelCountBuffer)
        target.setShaderInput("spinLockBuffer", self.spinLockBuffer)
        target.setShaderInput("materialDataBuffer", self.materialDataBuffer)
        target.setShaderInput("listHeadBuffer", self.listHeadBuffer)

        # Provides the buffers as global shader inputs
        self.pipeline.getRenderPassManager().registerStaticVariable(
            "transpPixelCountBuffer", self.pixelCountBuffer)
        self.pipeline.getRenderPassManager().registerStaticVariable(
            "transpSpinLockBuffer", self.spinLockBuffer)
        self.pipeline.getRenderPassManager().registerStaticVariable(
            "transpListHeadBuffer", self.listHeadBuffer)
        self.pipeline.getRenderPassManager().registerStaticVariable(
            "transpMaterialDataBuffer", self.materialDataBuffer)

        # Registers the transparency settings to the shaders
        self.pipeline.getRenderPassManager().registerDefine(
            "USE_TRANSPARENCY", 1)
        self.pipeline.getRenderPassManager().registerDefine(
            "MAX_TRANSPARENCY_LAYERS",
            self.pipeline.settings.maxTransparencyLayers)

        self.pipeline.getRenderPassManager().registerDefine(
            "TRANSPARENCY_RANGE", self.pipeline.settings.maxTransparencyRange)

        self.pipeline.getRenderPassManager().registerDefine(
            "TRANSPARENCY_BATCH_SIZE",
            self.pipeline.settings.transparencyBatchSize)

        self.pixelCountBuffer.setClearColor(Vec4(0, 0, 0, 0))
        self.spinLockBuffer.setClearColor(Vec4(0, 0, 0, 0))
        self.listHeadBuffer.setClearColor(Vec4(0, 0, 0, 0))

        MemoryMonitor.addTexture("MaterialCountBuffer", self.pixelCountBuffer)
        MemoryMonitor.addTexture("MaterialDataBuffer", self.materialDataBuffer)
        MemoryMonitor.addTexture("ListHeadBuffer", self.listHeadBuffer)
        MemoryMonitor.addTexture("SpinLockBuffer", self.spinLockBuffer)

    def update(self):
        """ The update method clears the buffers before rendering the next frame """
        self.pixelCountBuffer.clearImage()
        self.spinLockBuffer.clearImage()
        self.listHeadBuffer.clearImage()