示例#1
0
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 DeferredScenePass(RenderPass):

    """ This is the main scene pass which generates the G-Buffer used for 
    deferred rendering """

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

    def getID(self):
        return "DeferredScenePass"

    def getRequiredInputs(self):
        return {
            "readyState": "SceneReadyState"
        }

    def create(self):

        earlyZ = self.pipeline.settings.enableEarlyZ

        if earlyZ:
            self.prepassCam = Camera(Globals.base.camNode)
            self.prepassCam.setTagStateKey("EarlyZShader")
            self.prepassCam.setName("EarlyZCamera")
            self.prepassCamNode = Globals.base.camera.attachNewNode(self.prepassCam)
            Globals.render.setTag("EarlyZShader", "Default")
        else:
            self.prepassCam = None

        # modify default camera initial state
        initial = Globals.base.camNode.getInitialState()
        initialNode = NodePath("IntiialState")
        initialNode.setState(initial)
        
        if earlyZ:
            initialNode.setAttrib(DepthWriteAttrib.make(DepthWriteAttrib.MOff))
            initialNode.setAttrib(DepthTestAttrib.make(DepthTestAttrib.MEqual))
            pass
        else:
            initialNode.setAttrib(DepthTestAttrib.make(DepthTestAttrib.MLessEqual))

        # initialNode.setAttrib(ColorWriteAttrib.make(ColorWriteAttrib.COff), 10000)
        # initialNode.setAttrib(CullFaceAttrib.make(CullFaceAttrib.MCullClockwise), 10000)
        Globals.base.camNode.setInitialState(initialNode.getState())

        self.target = RenderTarget("DeferredScenePass")
        self.target.addColorAndDepth()
        self.target.addAuxTextures(3)
        self.target.setAuxBits(16)
        self.target.setDepthBits(32)
        self.target.setCreateOverlayQuad(False)
        
        if earlyZ:
            self.target.prepareSceneRender(earlyZ=True, earlyZCam=self.prepassCamNode)
        else:
            self.target.prepareSceneRender()

        self.target.setClearColor(True, color=Vec4(1, 1, 0, 1))

        if earlyZ:
            self.target.setClearDepth(False)
        else:
            self.target.setClearDepth(True)


    def registerEarlyZTagState(self, name, state):
        """ Registers a new tag state """
        if not self.prepassCam:
            return

        # state.setAttrib(CullFaceAttrib.make(CullFaceAttrib.MCullClockwise), 10000)
        state.setAttrib(ColorWriteAttrib.make(ColorWriteAttrib.COff), 10000)
        state.setAttrib(AlphaTestAttrib.make(AlphaTestAttrib.MNone, 1.0), 10000)
        state.setAttrib(DepthWriteAttrib.make(DepthWriteAttrib.MOn), 10000)
        state.setAttrib(DepthTestAttrib.make(DepthTestAttrib.MLess), 10000)
        state.setAttrib(TransparencyAttrib.make(TransparencyAttrib.MNone), 10000)

        self.prepassCam.setTagState(name, state.getState()) 

    def setShaders(self):
        self.registerEarlyZTagState("Default", NodePath("DefaultEarlyZPass"))
        return []

    def getOutputs(self):
        return {
            "DeferredScenePass.wsNormal": lambda: self.target.getColorTexture(),
            "DeferredScenePass.velocity": lambda: self.target.getAuxTexture(0),

            "DeferredScenePass.depth": lambda: self.target.getDepthTexture(),
            "DeferredScenePass.data0": lambda: self.target.getColorTexture(),
            "DeferredScenePass.data1": lambda: self.target.getAuxTexture(0),
            "DeferredScenePass.data2": lambda: self.target.getAuxTexture(1),
            "DeferredScenePass.data3": lambda: self.target.getAuxTexture(2),
        }

    def setShaderInput(self, name, value):
        pass
class DeferredScenePass(RenderPass):
    """ This is the main scene pass which generates the G-Buffer used for 
    deferred rendering """
    def __init__(self, pipeline):
        RenderPass.__init__(self)
        self.pipeline = pipeline

    def getID(self):
        return "DeferredScenePass"

    def getRequiredInputs(self):
        return {"readyState": "SceneReadyState"}

    def create(self):

        earlyZ = self.pipeline.settings.enableEarlyZ

        if earlyZ:
            self.prepassCam = Camera(Globals.base.camNode)
            self.prepassCam.setTagStateKey("EarlyZShader")
            self.prepassCam.setName("EarlyZCamera")
            self.prepassCamNode = Globals.base.camera.attachNewNode(
                self.prepassCam)
            Globals.render.setTag("EarlyZShader", "Default")
        else:
            self.prepassCam = None

        # modify default camera initial state
        initial = Globals.base.camNode.getInitialState()
        initialNode = NodePath("IntiialState")
        initialNode.setState(initial)

        if earlyZ:
            initialNode.setAttrib(DepthWriteAttrib.make(DepthWriteAttrib.MOff))
            initialNode.setAttrib(DepthTestAttrib.make(DepthTestAttrib.MEqual))
            pass
        else:
            initialNode.setAttrib(
                DepthTestAttrib.make(DepthTestAttrib.MLessEqual))

        # initialNode.setAttrib(ColorWriteAttrib.make(ColorWriteAttrib.COff), 10000)
        # initialNode.setAttrib(CullFaceAttrib.make(CullFaceAttrib.MCullClockwise), 10000)
        Globals.base.camNode.setInitialState(initialNode.getState())

        self.target = RenderTarget("DeferredScenePass")
        self.target.addColorAndDepth()
        self.target.addAuxTextures(3)
        self.target.setAuxBits(16)
        self.target.setDepthBits(32)
        self.target.setCreateOverlayQuad(False)

        if earlyZ:
            self.target.prepareSceneRender(earlyZ=True,
                                           earlyZCam=self.prepassCamNode)
        else:
            self.target.prepareSceneRender()

        self.target.setClearColor(True, color=Vec4(1, 1, 0, 1))

        if earlyZ:
            self.target.setClearDepth(False)
        else:
            self.target.setClearDepth(True)

    def registerEarlyZTagState(self, name, state):
        """ Registers a new tag state """
        if not self.prepassCam:
            return

        # state.setAttrib(CullFaceAttrib.make(CullFaceAttrib.MCullClockwise), 10000)
        state.setAttrib(ColorWriteAttrib.make(ColorWriteAttrib.COff), 10000)
        state.setAttrib(AlphaTestAttrib.make(AlphaTestAttrib.MNone, 1.0),
                        10000)
        state.setAttrib(DepthWriteAttrib.make(DepthWriteAttrib.MOn), 10000)
        state.setAttrib(DepthTestAttrib.make(DepthTestAttrib.MLess), 10000)
        state.setAttrib(TransparencyAttrib.make(TransparencyAttrib.MNone),
                        10000)

        self.prepassCam.setTagState(name, state.getState())

    def setShaders(self):
        self.registerEarlyZTagState("Default", NodePath("DefaultEarlyZPass"))
        return []

    def getOutputs(self):
        return {
            "DeferredScenePass.wsNormal":
            lambda: self.target.getColorTexture(),
            "DeferredScenePass.velocity": lambda: self.target.getAuxTexture(0),
            "DeferredScenePass.depth": lambda: self.target.getDepthTexture(),
            "DeferredScenePass.data0": lambda: self.target.getColorTexture(),
            "DeferredScenePass.data1": lambda: self.target.getAuxTexture(0),
            "DeferredScenePass.data2": lambda: self.target.getAuxTexture(1),
            "DeferredScenePass.data3": lambda: self.target.getAuxTexture(2),
        }

    def setShaderInput(self, name, value):
        pass
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, pipeline):
        RenderPass.__init__(self)
        self.pipeline = pipeline

    def getID(self):
        return "VoxelizePass"

    def getRequiredInputs(self):
        return {
            # Lighting
            "renderedLightsBuffer": "Variables.renderedLightsBuffer",
            "lights": "Variables.allLights",
            "shadowAtlasPCF": "ShadowScenePass.atlasPCF",
            "shadowAtlas": "ShadowScenePass.atlas",
            "shadowSources": "Variables.allShadowSources",
            "directionToFace": "Variables.directionToFaceLookup",
            "cameraPosition": "Variables.cameraPosition",
            "mainCam": "Variables.mainCam",
            "mainRender": "Variables.mainRender",
        }

    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 setGridResolutionMultiplier(self, factor):
        """ Sets the density of the voxel grid. """
        self.gridResolutionMultiplier = factor

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

    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("VoxelizeCamera")
        self.voxelizeCamera.setCameraMask(BitMask32.bit(4))
        self.voxelizeCameraNode = Globals.render.attachNewNode(self.voxelizeCamera)
        self.voxelizeLens = OrthographicLens()
        self.voxelizeLens.setFilmSize(self.voxelGridSize * 2, self.voxelGridSize * 2)
        self.voxelizeLens.setNearFar(0.0, self.voxelGridSize * 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 * self.gridResolutionMultiplier)

        if self.pipeline.settings.useDebugAttachments:
            self.target.addColorTexture()
        else:
            self.target.setColorWrite(False)

        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"]
        self.setActive(True)

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

    def setShaders(self):
        """ Creates the tag state and loades the voxelizer shader """
        self.registerTagState("Default", NodePath("DefaultVoxelizeState"))
        return []

    def registerTagState(self, name, state):
        """ Registers a new tag state """
        state.setAttrib(CullFaceAttrib.make(CullFaceAttrib.MCullNone))
        state.setDepthWrite(False)
        state.setDepthTest(False)
        state.setAttrib(DepthTestAttrib.make(DepthTestAttrib.MNone))
        state.setShaderInput("voxelizeCam", self.voxelizeCameraNode)
        self.voxelizeCamera.setTagState(name, state.getState())

    def setShaderInput(self, *args):
        Globals.base.render.setShaderInput(*args)

    def getOutputs(self):
        return {}
class LightManager(DebugObject):
    def __init__(self):
        DebugObject.__init__(self, "LightManager")

        self._initArrays()

        # create arrays to store lights & shadow sources
        self.lights = []
        self.shadowSources = []
        self.allLightsArray = ShaderStructArray(Light, 30)

        self.cullBounds = None
        self.shadowScene = render

        ## SHADOW ATLAS ##
        # todo: move to separate class

        # When you change this, change also SHADOW_MAP_ATLAS_SIZE in configuration.include,
        # and reduce the default shadow map resolution of point lights
        self.shadowAtlasSize = 512
        self.maxShadowMaps = 24

        # When you change it , change also SHAODOW_GEOMETRY_MAX_VERTICES and
        # SHADOW_MAX_UPDATES_PER_FRAME in configuration.include!
        self.maxShadowUpdatesPerFrame = 2
        self.tileSize = 128
        self.tileCount = self.shadowAtlasSize / self.tileSize
        self.tiles = []

        self.updateShadowsArray = ShaderStructArray(
            ShadowSource, self.maxShadowUpdatesPerFrame)
        self.allShadowsArray = ShaderStructArray(ShadowSource,
                                                 self.maxShadowMaps)

        # self.shadowAtlasTex = Texture("ShadowAtlas")
        # self.shadowAtlasTex.setup2dTexture(
        #     self.shadowAtlasSize, self.shadowAtlasSize, Texture.TFloat, Texture.FRg16)
        # self.shadowAtlasTex.setMinfilter(Texture.FTLinear)
        # self.shadowAtlasTex.setMagfilter(Texture.FTLinear)

        self.debug("Init shadow atlas with tileSize =", self.tileSize,
                   ", tileCount =", self.tileCount)

        for i in xrange(self.tileCount):
            self.tiles.append([None for j in xrange(self.tileCount)])

        # create shadow compute buffer
        self.shadowComputeCamera = Camera("ShadowComputeCamera")
        self.shadowComputeCameraNode = self.shadowScene.attachNewNode(
            self.shadowComputeCamera)
        self.shadowComputeCamera.getLens().setFov(90, 90)
        self.shadowComputeCamera.getLens().setNearFar(10.0, 100000.0)

        self.shadowComputeCameraNode.setPos(0, 0, 150)
        self.shadowComputeCameraNode.lookAt(0, 0, 0)

        self.shadowComputeTarget = RenderTarget("ShadowCompute")
        self.shadowComputeTarget.setSize(self.shadowAtlasSize,
                                         self.shadowAtlasSize)
        # self.shadowComputeTarget.setLayers(self.maxShadowUpdatesPerFrame)
        self.shadowComputeTarget.addRenderTexture(RenderTargetType.Depth)
        self.shadowComputeTarget.setDepthBits(32)
        self.shadowComputeTarget.setSource(self.shadowComputeCameraNode,
                                           base.win)
        self.shadowComputeTarget.prepareSceneRender()

        self.shadowComputeTarget.getInternalRegion().setSort(3)
        self.shadowComputeTarget.getRegion().setSort(3)

        self.shadowComputeTarget.getInternalRegion().setNumRegions(
            self.maxShadowUpdatesPerFrame + 1)
        self.shadowComputeTarget.getInternalRegion().setDimensions(
            0, (0, 1, 0, 1))
        self.shadowComputeTarget.setClearDepth(False)

        self.depthClearer = []

        for i in xrange(self.maxShadowUpdatesPerFrame):
            buff = self.shadowComputeTarget.getInternalBuffer()
            dr = buff.makeDisplayRegion()
            dr.setSort(2)
            dr.setClearDepthActive(True)
            dr.setClearDepth(1.0)
            dr.setClearColorActive(False)
            dr.setDimensions(0, 0, 0, 0)
            self.depthClearer.append(dr)

        self.queuedShadowUpdates = []

        # Assign copy shader
        self._setCopyShader()
        # self.shadowComputeTarget.setShaderInput("atlas", self.shadowComputeTarget.getColorTexture())
        # self.shadowComputeTarget.setShaderInput(
        #     "renderResult", self.shadowComputeTarget.getDepthTexture())

        # self.shadowComputeTarget.setActive(False)

        # Create shadow caster shader
        self.shadowCasterShader = BetterShader.load(
            "Shader/DefaultShadowCaster.vertex",
            "Shader/DefaultShadowCaster.fragment",
            "Shader/DefaultShadowCaster.geometry")

        self.shadowComputeCamera.setTagStateKey("ShadowPass")
        initialState = NodePath("ShadowCasterState")
        initialState.setShader(self.shadowCasterShader, 30)
        self.shadowComputeCamera.setInitialState(
            RenderState.make(ColorWriteAttrib.make(ColorWriteAttrib.C_off),
                             DepthWriteAttrib.make(DepthWriteAttrib.M_on),
                             100))

        self.shadowComputeCamera.setTagState("True", initialState.getState())
        self.shadowScene.setTag("ShadowPass", "True")

        self._createDebugTexts()

        self.updateShadowsArray.bindTo(self.shadowScene, "updateSources")
        self.updateShadowsArray.bindTo(self.shadowComputeTarget,
                                       "updateSources")

        self.numShadowUpdatesPTA = PTAInt.emptyArray(1)

        # Set initial inputs
        for target in [self.shadowComputeTarget, self.shadowScene]:
            target.setShaderInput("numUpdates", self.numShadowUpdatesPTA)

        self.lightingComputator = None
        self.lightCuller = None

    # Tries to create debug text to show how many lights are currently visible
    # / rendered
    def _createDebugTexts(self):
        try:
            from FastText import FastText
            self.lightsVisibleDebugText = FastText(pos=Vec2(
                base.getAspectRatio() - 0.1, 0.84),
                                                   rightAligned=True,
                                                   color=Vec3(1, 0, 0),
                                                   size=0.036)
            self.lightsUpdatedDebugText = FastText(pos=Vec2(
                base.getAspectRatio() - 0.1, 0.8),
                                                   rightAligned=True,
                                                   color=Vec3(1, 0, 0),
                                                   size=0.036)

        except Exception, msg:
            self.debug("Could not load fast text:", msg)
            self.debug("Overlay is disabled because FastText wasn't loaded")
            self.lightsVisibleDebugText = None
            self.lightsUpdatedDebugText = None
示例#6
0
class GlobalIllumination(DebugObject):
    """ This class handles the global illumination processing. It is still
    experimental, and thus not commented. """

    updateEnabled = False

    def __init__(self, pipeline):
        DebugObject.__init__(self, "GlobalIllumnination")
        self.pipeline = pipeline

        self.targetCamera = Globals.base.cam
        self.targetSpace = Globals.base.render

        self.voxelBaseResolution = 512 * 4
        self.voxelGridSizeWS = Vec3(50, 50, 20)
        self.voxelGridResolution = LVecBase3i(512, 512, 128)
        self.targetLight = None
        self.helperLight = None
        self.ptaGridPos = PTALVecBase3f.emptyArray(1)
        self.gridPos = Vec3(0)

    @classmethod
    def setUpdateEnabled(self, enabled):
        self.updateEnabled = enabled

    def setTargetLight(self, light):
        """ Sets the sun light which is the main source of GI """

        if light._getLightType() != LightType.Directional:
            self.error("setTargetLight expects a directional light!")
            return

        self.targetLight = light
        self._createHelperLight()

    def _prepareVoxelScene(self):
        """ Creates the internal buffer to voxelize the scene on the fly """
        self.voxelizeScene = Globals.render
        self.voxelizeCamera = Camera("VoxelizeScene")
        self.voxelizeCameraNode = self.voxelizeScene.attachNewNode(
            self.voxelizeCamera)
        self.voxelizeLens = OrthographicLens()
        self.voxelizeLens.setFilmSize(self.voxelGridSizeWS.x * 2,
                                      self.voxelGridSizeWS.y * 2)
        self.voxelizeLens.setNearFar(0.0, self.voxelGridSizeWS.x * 2)
        self.voxelizeCamera.setLens(self.voxelizeLens)
        self.voxelizeCamera.setTagStateKey("VoxelizePassShader")

        self.targetSpace.setTag("VoxelizePassShader", "Default")

        self.voxelizeCameraNode.setPos(0, 0, 0)
        self.voxelizeCameraNode.lookAt(0, 0, 0)

        self.voxelizeTarget = RenderTarget("DynamicVoxelization")
        self.voxelizeTarget.setSize(self.voxelBaseResolution)
        # self.voxelizeTarget.addDepthTexture()
        # self.voxelizeTarget.addColorTexture()
        # self.voxelizeTarget.setColorBits(16)
        self.voxelizeTarget.setSource(self.voxelizeCameraNode,
                                      Globals.base.win)
        self.voxelizeTarget.prepareSceneRender()

        self.voxelizeTarget.getQuad().node().removeAllChildren()
        self.voxelizeTarget.getInternalRegion().setSort(-400)
        self.voxelizeTarget.getInternalBuffer().setSort(-399)

        # for tex in [self.voxelizeTarget.getColorTexture()]:
        #     tex.setWrapU(Texture.WMClamp)
        #     tex.setWrapV(Texture.WMClamp)
        #     tex.setMinfilter(Texture.FTNearest)
        #     tex.setMagfilter(Texture.FTNearest)

        voxelSize = Vec3(
            self.voxelGridSizeWS.x * 2.0 / self.voxelGridResolution.x,
            self.voxelGridSizeWS.y * 2.0 / self.voxelGridResolution.y,
            self.voxelGridSizeWS.z * 2.0 / self.voxelGridResolution.z)

        self.targetSpace.setShaderInput("dv_gridSize",
                                        self.voxelGridSizeWS * 2)
        self.targetSpace.setShaderInput("dv_voxelSize", voxelSize)
        self.targetSpace.setShaderInput("dv_gridResolution",
                                        self.voxelGridResolution)

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

        initialState = NodePath("VoxelizerState")
        initialState.setShader(self.voxelizeShader, 50)
        initialState.setAttrib(CullFaceAttrib.make(CullFaceAttrib.MCullNone))
        initialState.setDepthWrite(False)
        initialState.setDepthTest(False)
        initialState.setAttrib(DepthTestAttrib.make(DepthTestAttrib.MNone))

        initialState.setShaderInput("dv_dest_tex", self.voxelGenTex)

        self.voxelizeCamera.setTagState("Default", initialState.getState())

    def _createHelperLight(self):
        """ Creates the helper light. We can't use the main directional light
        because it uses PSSM, so we need an extra shadow map """
        self.helperLight = GIHelperLight()
        self.helperLight.setPos(Vec3(50, 50, 100))
        self.helperLight.setShadowMapResolution(512)
        self.helperLight.setFilmSize(
            math.sqrt((self.voxelGridSizeWS.x**2) * 2) * 2)
        self.helperLight.setCastsShadows(True)
        self.pipeline.addLight(self.helperLight)

        self.targetSpace.setShaderInput(
            "dv_uv_size",
            float(self.helperLight.shadowResolution) /
            self.pipeline.settings.shadowAtlasSize)
        self.targetSpace.setShaderInput(
            "dv_atlas",
            self.pipeline.getLightManager().getAtlasTex())

        self._updateGridPos()

    def setup(self):
        """ Setups everything for the GI to work """

        # if self.pipeline.settings.useHardwarePCF:
        #     self.fatal(
        #         "Global Illumination does not work in combination with PCF!")
        #     return

        self._prepareVoxelScene()

        # Create 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)
        self.voxelGenTex.setMinfilter(Texture.FTLinearMipmapLinear)
        self.voxelGenTex.setMagfilter(Texture.FTLinear)

        # Create 3D Texture which is a copy of the voxel generation grid but
        # stable, as the generation grid is updated part by part
        self.voxelStableTex = Texture("VoxelsStable")
        self.voxelStableTex.setup3dTexture(self.voxelGridResolution.x,
                                           self.voxelGridResolution.y,
                                           self.voxelGridResolution.z,
                                           Texture.TFloat, Texture.FRgba8)
        self.voxelStableTex.setMinfilter(Texture.FTLinearMipmapLinear)
        self.voxelStableTex.setMagfilter(Texture.FTLinear)

        for prepare in [self.voxelGenTex, self.voxelStableTex]:
            prepare.setMagfilter(Texture.FTLinear)
            prepare.setMinfilter(Texture.FTLinearMipmapLinear)
            prepare.setWrapU(Texture.WMBorderColor)
            prepare.setWrapV(Texture.WMBorderColor)
            prepare.setWrapW(Texture.WMBorderColor)
            prepare.setBorderColor(Vec4(0, 0, 0, 0))

        self.voxelGenTex.setMinfilter(Texture.FTNearest)
        self.voxelGenTex.setMagfilter(Texture.FTNearest)
        self.voxelGenTex.setWrapU(Texture.WMClamp)
        self.voxelGenTex.setWrapV(Texture.WMClamp)
        self.voxelGenTex.setWrapW(Texture.WMClamp)

        # self.voxelStableTex.generateRamMipmapImages()

        self._createVoxelizeState()

        self.clearTextureNode = NodePath("ClearTexture")
        self.copyTextureNode = NodePath("CopyTexture")
        self.generateMipmapsNode = NodePath("GenerateMipmaps")
        self.convertGridNode = NodePath("ConvertGrid")

        self.reloadShader()

    def _generateMipmaps(self, tex):
        """ Generates all mipmaps for a 3D texture, using a gaussian function """

        pstats_GenerateMipmaps.start()
        currentMipmap = 0
        computeSize = LVecBase3i(self.voxelGridResolution)
        self.generateMipmapsNode.setShaderInput("source", tex)
        self.generateMipmapsNode.setShaderInput("pixelSize",
                                                1.0 / computeSize.x)

        while computeSize.z > 1:
            computeSize /= 2
            self.generateMipmapsNode.setShaderInput("sourceMipmap",
                                                    LVecBase3i(currentMipmap))
            self.generateMipmapsNode.setShaderInput("currentMipmapSize",
                                                    LVecBase3i(computeSize))
            self.generateMipmapsNode.setShaderInput("dest", tex, False, True,
                                                    -1, currentMipmap + 1)
            self._executeShader(self.generateMipmapsNode,
                                (computeSize.x + 7) / 8,
                                (computeSize.y + 7) / 8,
                                (computeSize.z + 7) / 8)
            currentMipmap += 1

        pstats_GenerateMipmaps.stop()

    def _createCleanShader(self):
        shader = BetterShader.loadCompute("Shader/GI/ClearTexture.compute")
        self.clearTextureNode.setShader(shader)

    def _createConvertShader(self):
        shader = BetterShader.loadCompute("Shader/GI/ConvertGrid.compute")
        self.convertGridNode.setShader(shader)

    def _createGenerateMipmapsShader(self):
        shader = BetterShader.loadCompute("Shader/GI/GenerateMipmaps.compute")
        self.generateMipmapsNode.setShader(shader)

    def reloadShader(self):
        self._createCleanShader()
        self._createGenerateMipmapsShader()
        self._createConvertShader()
        self._createVoxelizeState()
        self.frameIndex = 0

    def _clear3DTexture(self, tex, clearVal=None):
        """ Clears a 3D Texture to <clearVal> """
        if clearVal is None:
            clearVal = Vec4(0)

        self.clearTextureNode.setShaderInput("target", tex, False, True, -1, 0)
        self.clearTextureNode.setShaderInput("clearValue", clearVal)

        self._executeShader(self.clearTextureNode, (tex.getXSize() + 7) / 8,
                            (tex.getYSize() + 7) / 8, (tex.getZSize() + 7) / 8)

    def _updateGridPos(self):

        snap = 32.0
        stepSizeX = float(self.voxelGridSizeWS.x * 2.0) / float(
            self.voxelGridResolution.x) * snap
        stepSizeY = float(self.voxelGridSizeWS.y * 2.0) / float(
            self.voxelGridResolution.y) * snap
        stepSizeZ = float(self.voxelGridSizeWS.z * 2.0) / float(
            self.voxelGridResolution.z) * snap

        self.gridPos = self.targetCamera.getPos(self.targetSpace)
        self.gridPos.x -= self.gridPos.x % stepSizeX
        self.gridPos.y -= self.gridPos.y % stepSizeY
        self.gridPos.z -= self.gridPos.z % stepSizeZ

    def process(self):
        if self.targetLight is None:
            self.fatal("The GI cannot work without a target light! Set one "
                       "with setTargetLight() first!")

        if not self.updateEnabled:
            self.voxelizeTarget.setActive(False)
            return

        direction = self.targetLight.getDirection()

        # time.sleep(0.4)

        if self.frameIndex == 0:
            # Find out cam pos

            self.targetSpace.setShaderInput(
                "dv_uv_start", self.helperLight.shadowSources[0].getAtlasPos())

            self.voxelizeTarget.setActive(True)
            # self.voxelizeTarget.setActive(False)

            self.voxelizeLens.setFilmSize(self.voxelGridSizeWS.y * 2,
                                          self.voxelGridSizeWS.z * 2)
            self.voxelizeLens.setNearFar(0.0, self.voxelGridSizeWS.x * 2)

            self.targetSpace.setShaderInput(
                "dv_mvp", Mat4(self.helperLight.shadowSources[0].mvp))
            self.targetSpace.setShaderInput(
                "dv_gridStart", self.gridPos - self.voxelGridSizeWS)
            self.targetSpace.setShaderInput(
                "dv_gridEnd", self.gridPos + self.voxelGridSizeWS)
            self.targetSpace.setShaderInput("dv_lightdir", direction)

            # Clear textures
            self._clear3DTexture(self.voxelGenTex, Vec4(0, 0, 0, 0))

            # Voxelize from x axis
            self.voxelizeCameraNode.setPos(self.gridPos -
                                           Vec3(self.voxelGridSizeWS.x, 0, 0))
            self.voxelizeCameraNode.lookAt(self.gridPos)
            self.targetSpace.setShaderInput("dv_direction", LVecBase3i(0))

        elif self.frameIndex == 1:
            # Voxelize from y axis

            # self.voxelizeTarget.setActive(False)

            self.voxelizeLens.setFilmSize(self.voxelGridSizeWS.x * 2,
                                          self.voxelGridSizeWS.z * 2)
            self.voxelizeLens.setNearFar(0.0, self.voxelGridSizeWS.y * 2)

            self.voxelizeCameraNode.setPos(self.gridPos -
                                           Vec3(0, self.voxelGridSizeWS.y, 0))
            self.voxelizeCameraNode.lookAt(self.gridPos)
            self.targetSpace.setShaderInput("dv_direction", LVecBase3i(1))

        elif self.frameIndex == 2:

            # self.voxelizeTarget.setActive(False)
            # Voxelize from z axis
            self.voxelizeLens.setFilmSize(self.voxelGridSizeWS.x * 2,
                                          self.voxelGridSizeWS.y * 2)
            self.voxelizeLens.setNearFar(0.0, self.voxelGridSizeWS.z * 2)

            self.voxelizeCameraNode.setPos(self.gridPos +
                                           Vec3(0, 0, self.voxelGridSizeWS.z))
            self.voxelizeCameraNode.lookAt(self.gridPos)
            self.targetSpace.setShaderInput("dv_direction", LVecBase3i(2))

        elif self.frameIndex == 3:

            self.voxelizeTarget.setActive(False)

            # Copy the cache to the actual texture
            self.convertGridNode.setShaderInput("src", self.voxelGenTex)
            self.convertGridNode.setShaderInput("dest", self.voxelStableTex)
            self._executeShader(self.convertGridNode,
                                (self.voxelGridResolution.x + 7) / 8,
                                (self.voxelGridResolution.y + 7) / 8,
                                (self.voxelGridResolution.z + 7) / 8)

            # Generate the mipmaps
            self._generateMipmaps(self.voxelStableTex)

            self.helperLight.setPos(self.gridPos)
            self.helperLight.setDirection(direction)

            # We are done now, update the inputs
            self.ptaGridPos[0] = Vec3(self.gridPos)
            self._updateGridPos()

        self.frameIndex += 1
        self.frameIndex = self.frameIndex % 5

    def bindTo(self, node, prefix):
        """ Binds all required shader inputs to a target to compute / display
        the global illumination """

        normFactor = Vec3(
            1.0,
            float(self.voxelGridResolution.y) /
            float(self.voxelGridResolution.x) * self.voxelGridSizeWS.y /
            self.voxelGridSizeWS.x,
            float(self.voxelGridResolution.z) /
            float(self.voxelGridResolution.x) * self.voxelGridSizeWS.z /
            self.voxelGridSizeWS.x)
        node.setShaderInput(prefix + ".gridPos", self.ptaGridPos)
        node.setShaderInput(prefix + ".gridHalfSize", self.voxelGridSizeWS)
        node.setShaderInput(prefix + ".gridResolution",
                            self.voxelGridResolution)
        node.setShaderInput(prefix + ".voxels", self.voxelStableTex)
        node.setShaderInput(prefix + ".voxelNormFactor", normFactor)
        node.setShaderInput(prefix + ".geometry", self.voxelStableTex)

    def _executeShader(self, node, threadsX, threadsY, threadsZ=1):
        """ Executes a compute shader, fetching the shader attribute from a NodePath """
        sattr = node.getAttrib(ShaderAttrib)
        Globals.base.graphicsEngine.dispatchCompute(
            (threadsX, threadsY, threadsZ), sattr, Globals.base.win.get_gsg())
示例#7
0
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, pipeline):
        RenderPass.__init__(self)
        self.pipeline = pipeline

    def getID(self):
        return "VoxelizePass"

    def getRequiredInputs(self):
        return {

            # Lighting
            "renderedLightsBuffer": "Variables.renderedLightsBuffer",
            "lights": "Variables.allLights",
            "shadowAtlasPCF": "ShadowScenePass.atlasPCF",
            "shadowAtlas": "ShadowScenePass.atlas",
            "shadowSources": "Variables.allShadowSources",
            "directionToFace": "Variables.directionToFaceLookup",
            "cameraPosition": "Variables.cameraPosition",
            "mainCam": "Variables.mainCam",
            "mainRender": "Variables.mainRender",
        }

    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 setGridResolutionMultiplier(self, factor):
        """ Sets the density of the voxel grid. """
        self.gridResolutionMultiplier = factor

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

    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("VoxelizeCamera")
        self.voxelizeCamera.setCameraMask(BitMask32.bit(4))
        self.voxelizeCameraNode = Globals.render.attachNewNode(
            self.voxelizeCamera)
        self.voxelizeLens = OrthographicLens()
        self.voxelizeLens.setFilmSize(self.voxelGridSize * 2,
                                      self.voxelGridSize * 2)
        self.voxelizeLens.setNearFar(0.0, self.voxelGridSize * 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 *
                            self.gridResolutionMultiplier)

        if self.pipeline.settings.useDebugAttachments:
            self.target.addColorTexture()
        else:
            self.target.setColorWrite(False)

        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"])
        self.setActive(True)

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

    def setShaders(self):
        """ Creates the tag state and loades the voxelizer shader """
        self.registerTagState("Default", NodePath("DefaultVoxelizeState"))
        return []

    def registerTagState(self, name, state):
        """ Registers a new tag state """
        state.setAttrib(CullFaceAttrib.make(CullFaceAttrib.MCullNone))
        state.setDepthWrite(False)
        state.setDepthTest(False)
        state.setAttrib(DepthTestAttrib.make(DepthTestAttrib.MNone))
        state.setShaderInput("voxelizeCam", self.voxelizeCameraNode)
        self.voxelizeCamera.setTagState(name, state.getState())

    def setShaderInput(self, *args):
        Globals.base.render.setShaderInput(*args)

    def getOutputs(self):
        return {}