Esempio n. 1
0
    def _initLightCulling(self):
        """ Creates the pass which gets a list of lights and computes which
        light affects which tile """

        # Fetch patch size
        self.patchSize = LVecBase2i(self.pipeline.settings.computePatchSizeX,
                                    self.pipeline.settings.computePatchSizeY)

        # size has to be a multiple of the compute unit size
        # but still has to cover the whole screen
        sizeX = int(math.ceil(float(Globals.resolution.x) / self.patchSize.x))
        sizeY = int(math.ceil(float(Globals.resolution.y) / self.patchSize.y))

        self.lightCullingPass = LightCullingPass(self.pipeline)
        self.lightCullingPass.setSize(sizeX, sizeY)
        self.lightCullingPass.setPatchSize(self.patchSize.x, self.patchSize.y)

        self.pipeline.getRenderPassManager().registerPass(
            self.lightCullingPass)
        self.pipeline.getRenderPassManager().registerStaticVariable(
            "lightingTileCount", LVecBase2i(sizeX, sizeY))

        self.debug("Batch size =", sizeX, "x", sizeY, "Actual Buffer size=",
                   int(sizeX * self.patchSize.x), "x",
                   int(sizeY * self.patchSize.y))

        self.numTiles = LVecBase2i(sizeX, sizeY)

        # Create the buffer which stores the rendered lights
        self._makeRenderedLightsBuffer()
Esempio n. 2
0
    def create(self):
        curr_tex = None

        for i in range(3):

            target_h = self.create_target("BlurH-" + str(i))
            target_h.add_color_attachment(bits=16)
            target_h.prepare_buffer()
            target_h.set_shader_input("direction", LVecBase2i(1, 0))
            if curr_tex is not None:
                target_h.set_shader_input("ShadedScene",
                                          curr_tex,
                                          override=True)

            curr_tex = target_h.color_tex

            target_v = self.create_target("BlurV-" + str(i))
            target_v.add_color_attachment(bits=16)
            target_v.prepare_buffer()
            target_v.set_shader_input("ShadedScene", curr_tex, override=True)
            target_v.set_shader_input("direction", LVecBase2i(0, 1))

            curr_tex = target_v.color_tex

        self.final_tex = curr_tex
Esempio n. 3
0
    def create(self):
        self.camera = Camera("PSSMDistShadowsESM")
        self.cam_lens = OrthographicLens()
        self.cam_lens.set_film_size(12000, 12000)
        self.cam_lens.set_near_far(10.0, self.sun_distance * 2)
        self.camera.set_lens(self.cam_lens)
        self.cam_node = Globals.base.render.attach_new_node(self.camera)

        self.target = self.create_target("ShadowMap")
        self.target.size = self.resolution
        self.target.add_depth_attachment(bits=32)
        self.target.prepare_render(self.cam_node)

        self.target_convert = self.create_target("ConvertToESM")
        self.target_convert.size = self.resolution
        self.target_convert.add_color_attachment(bits=(32, 0, 0, 0))
        self.target_convert.prepare_buffer()
        self.target_convert.set_shader_input("SourceTex", self.target.depth_tex)

        self.target_blur_v = self.create_target("BlurVert")
        self.target_blur_v.size = self.resolution
        self.target_blur_v.add_color_attachment(bits=(32, 0, 0, 0))
        self.target_blur_v.prepare_buffer()
        self.target_blur_v.set_shader_input("SourceTex", self.target_convert.color_tex)
        self.target_blur_v.set_shader_input("direction", LVecBase2i(1, 0))

        self.target_blur_h = self.create_target("BlurHoriz")
        self.target_blur_h.size = self.resolution
        self.target_blur_h.add_color_attachment(bits=(32, 0, 0, 0))
        self.target_blur_h.prepare_buffer()
        self.target_blur_h.set_shader_input("SourceTex", self.target_blur_v.color_tex)
        self.target_blur_h.set_shader_input("direction", LVecBase2i(0, 1))

        # Register shadow camera
        self._pipeline.tag_mgr.register_camera("shadow", self.camera)
Esempio n. 4
0
    def _handle_window_event(self, event):
        """ Checks for window events. This mainly handles incoming resizes,
        and calls the required handlers """
        self._showbase.windowEvent(event)
        window_dims = LVecBase2i(self._showbase.win.get_x_size(),
                                 self._showbase.win.get_y_size())
        if window_dims != self._last_window_dims and window_dims != Globals.native_resolution:
            self._last_window_dims = LVecBase2i(window_dims)

            # Ensure the dimensions are a multiple of 4, and if not, correct it
            if window_dims.x % 4 != 0 or window_dims.y % 4 != 0:
                self.debug("Correcting non-multiple of 4 window size:",
                           window_dims)
                window_dims.x = window_dims.x - window_dims.x % 4
                window_dims.y = window_dims.y - window_dims.y % 4
                props = WindowProperties.size(window_dims.x, window_dims.y)
                self._showbase.win.request_properties(props)

            self.debug("Resizing to", window_dims.x, "x", window_dims.y)
            Globals.native_resolution = window_dims
            self._compute_render_resolution()
            self.light_mgr.compute_tile_size()
            self.stage_mgr.handle_window_resize()
            self.debugger.handle_window_resize()
            self.plugin_mgr.trigger_hook("window_resized")
    def _update(self, task=None):

        self.temporalProjXOffs += 1
        self.temporalProjXOffs = self.temporalProjXOffs % self.temporalProjFactor

        self.cullBounds = self._computeCameraBounds()

        self.lightManager.setCullBounds(self.cullBounds)
        self.lightManager.update()

        self.lightingComputeContainer.setShaderInput("cameraPosition",
                                                     base.cam.getPos(render))
        self.lightingComputeContainer.setShaderInput(
            "temporalProjXOffs", LVecBase2i(self.temporalProjXOffs))
        self.combiner.setShaderInput("lastMVP", self.lastMVP)
        render.setShaderInput("lastMVP", self.lastMVP)
        self.combiner.setShaderInput("temporalProjXOffs",
                                     LVecBase2i(self.temporalProjXOffs))
        self._computeMVP()
        self.combiner.setShaderInput("currentMVP", self.lastMVP)

        self.combiner.setShaderInput("cameraPosition", base.cam.getPos(render))

        if task is not None:
            return task.cont
Esempio n. 6
0
    def __init__(self, N, sourceTex, normalizationFactor):
        """ Creates a new fft instance. The source texture has to specified
        from the begining, as the shaderAttributes are pregenerated for
        performance reasons """
        DebugObject.__init__(self, "GPU-FFT")

        self.size = N
        self.log2Size = int(math.log(N, 2))
        self.normalizationFactor = normalizationFactor

        # Create a ping and a pong texture, because we can't write to the
        # same texture while reading to it (that would lead to unexpected
        # behaviour, we could solve that by using an appropriate thread size,
        # but it works fine so far)
        self.pingTexture = Texture("FFTPing")
        self.pingTexture.setup2dTexture(
            self.size, self.size, Texture.TFloat, Texture.FRgba32)
        self.pongTexture = Texture("FFTPong")
        self.pongTexture.setup2dTexture(
            self.size, self.size, Texture.TFloat, Texture.FRgba32)
        self.sourceTex = sourceTex

        for tex in [self.pingTexture, self.pongTexture, sourceTex]:
            tex.setMinfilter(Texture.FTNearest)
            tex.setMagfilter(Texture.FTNearest)
            tex.setWrapU(Texture.WMClamp)
            tex.setWrapV(Texture.WMClamp)

        # Pregenerate weights & indices for the shaders
        self._computeWeighting()

        # Pre generate the shaders, we have 2 passes: Horizontal and Vertical
        # which both execute log2(N) times with varying radii
        self.horizontalFFTShader = BetterShader.loadCompute(
            "Shader/Water/HorizontalFFT.compute")
        self.horizontalFFT = NodePath("HorizontalFFT")
        self.horizontalFFT.setShader(self.horizontalFFTShader)
        self.horizontalFFT.setShaderInput(
            "precomputedWeights", self.weightsLookupTex)
        self.horizontalFFT.setShaderInput("N", LVecBase2i(self.size))

        self.verticalFFTShader = BetterShader.loadCompute(
            "Shader/Water/VerticalFFT.compute")
        self.verticalFFT = NodePath("VerticalFFT")
        self.verticalFFT.setShader(self.verticalFFTShader)
        self.verticalFFT.setShaderInput(
            "precomputedWeights", self.weightsLookupTex)
        self.verticalFFT.setShaderInput("N", LVecBase2i(self.size))

        # Create a texture where the result is stored
        self.resultTexture = Texture("Result")
        self.resultTexture.setup2dTexture(
            self.size, self.size, Texture.TFloat, Texture.FRgba16)
        self.resultTexture.setMinfilter(Texture.FTLinear)
        self.resultTexture.setMagfilter(Texture.FTLinear)

        # Prepare the shader attributes, so we don't have to regenerate them
        # every frame -> That is VERY slow (3ms per fft instance)
        self._prepareAttributes()
Esempio n. 7
0
    def __init__(self, size, source_tex, normalization_factor):
        """ Creates a new fft instance. The source texture has to specified
        from the begining, as the shaderAttributes are pregenerated for
        performance reasons """

        self.size = size
        self.log2_size = int(math.log(size, 2))
        self.normalization_factor = normalization_factor

        # Create a ping and a pong texture, because we can't write to the
        # same texture while reading to it (that would lead to unexpected
        # behaviour, we could solve that by using an appropriate thread size,
        # but it works fine so far)
        self.ping_texture = Texture("FFTPing")
        self.ping_texture.setup_2d_texture(self.size, self.size,
                                           Texture.TFloat, Texture.FRgba32)
        self.pong_texture = Texture("FFTPong")
        self.pong_texture.setup_2d_texture(self.size, self.size,
                                           Texture.TFloat, Texture.FRgba32)
        self.source_tex = source_tex

        for tex in [self.ping_texture, self.pong_texture, source_tex]:
            tex.set_minfilter(Texture.FTNearest)
            tex.set_magfilter(Texture.FTNearest)
            tex.set_wrap_u(Texture.WMClamp)
            tex.set_wrap_v(Texture.WMClamp)

        # Pregenerate weights & indices for the shaders
        self._compute_weighting()

        # Pre generate the shaders, we have 2 passes: Horizontal and Vertical
        # which both execute log2(N) times with varying radii
        self.horizontal_fft_shader = Shader.load_compute(
            Shader.SLGLSL, "/$$rp/rpcore/water/shader/horizontal_fft.compute")
        self.horizontal_fft = NodePath("HorizontalFFT")
        self.horizontal_fft.set_shader(self.horizontal_fft_shader)
        self.horizontal_fft.set_shader_input("precomputedWeights",
                                             self.weights_lookup_tex)
        self.horizontal_fft.set_shader_input("N", LVecBase2i(self.size))

        self.vertical_fft_shader = Shader.load_compute(
            Shader.SLGLSL, "/$$rp/rpcore/water/shader/vertical_fft.compute")
        self.vertical_fft = NodePath("VerticalFFT")
        self.vertical_fft.set_shader(self.vertical_fft_shader)
        self.vertical_fft.set_shader_input("precomputedWeights",
                                           self.weights_lookup_tex)
        self.vertical_fft.set_shader_input("N", LVecBase2i(self.size))

        # Create a texture where the result is stored
        self.result_texture = Texture("Result")
        self.result_texture.setup2dTexture(self.size, self.size,
                                           Texture.TFloat, Texture.FRgba16)
        self.result_texture.set_minfilter(Texture.FTLinear)
        self.result_texture.set_magfilter(Texture.FTLinear)

        # Prepare the shader attributes, so we don't have to regenerate them
        # every frame -> That is VERY slow (3ms per fft instance)
        self._prepare_attributes()
Esempio n. 8
0
 def _init_globals(self):
     """ Inits all global bindings. This includes references to the global
     ShowBase instance, as well as the render resolution, the GUI font,
     and various global logging and output methods. """
     Globals.load(self._showbase)
     native_w, native_h = self._showbase.win.get_x_size(), self._showbase.win.get_y_size()
     Globals.native_resolution = LVecBase2i(native_w, native_h)
     self._last_window_dims = LVecBase2i(Globals.native_resolution)
     self._compute_render_resolution()
     RenderTarget.RT_OUTPUT_FUNC = lambda *args: RPObject.global_warn(
         "RenderTarget", *args[1:])
     RenderTarget.USE_R11G11B10 = self.settings["pipeline.use_r11_g11_b10"]
Esempio n. 9
0
 def compute_tile_size(self):
     """ Computes how many tiles there are on screen """
     self.tile_size = LVecBase2i(
         self.pipeline.settings["lighting.culling_grid_size_x"],
         self.pipeline.settings["lighting.culling_grid_size_y"])
     num_tiles_x = int(
         math.ceil(Globals.resolution.x / float(self.tile_size.x)))
     num_tiles_y = int(
         math.ceil(Globals.resolution.y / float(self.tile_size.y)))
     self.debug("Tile size =", self.tile_size.x, "x", self.tile_size.y,
                ", Num tiles =", num_tiles_x, "x", num_tiles_y)
     self.num_tiles = LVecBase2i(num_tiles_x, num_tiles_y)
Esempio n. 10
0
    def reserveTiles(self, width, height, tileIndex):
        """ Reserves enough tiles to store a tile with the ID tileIndex
        and the dimensions width*height in the atlas and returns the
        top-left coordinates of the reserved space """

        # Convert to tile space
        tileW, tileH = width / self.tileSize, height / self.tileSize

        # self.warn("Finding position for tile of size", tileW, "x", tileH)

        maxIterations = self.tileCount - tileW + 1, self.tileCount - tileH + 1
        tileFound = False
        tilePos = LVecBase2i(-1)

        # Iterate all tiles
        for j in range(0, maxIterations[0]):
            if not tileFound:
                for i in range(0, maxIterations[1]):
                    if not tileFound:

                        # First, assume the space is free
                        tileFree = True

                        # Now, check if anything is blocking the space, and if so,
                        # mark the tile as reserved
                        for x in range(tileW):
                            for y in range(tileH):
                                if not tileFree:
                                    break
                                if self.tiles[j + y][i + x] is not None:
                                    tileFree = False
                                    break

                        # When the tile is free, we have found our tile
                        if tileFree:
                            tileFound = True
                            tilePos = LVecBase2i(i, j)
                            break

        if tileFound:
            # self.debug("Tile #" + str(tileIndex) + " found:",
            # tilePos.x * self.tileSize, "/", tilePos.y * self.tileSize)
            self._reserveTile(tilePos.x, tilePos.y, tileW, tileH, tileIndex)

            return Vec2(
                float(tilePos.x) / float(self.tileCount),
                float(tilePos.y) / float(self.tileCount))

        else:
            self.error("No free tile found! Have to update whole atlas maybe?")
            return None
Esempio n. 11
0
    def create(self):
        # Create a target for the specular GI
        # self.target_spec = self.create_target("SpecularGI")
        # self.target_spec.add_color_attachment(bits=16, alpha=True)
        # self.target_spec.prepare_buffer()

        # Create a target for the diffuse GI
        self.target_diff = self.create_target("DiffuseGI")
        self.target_diff.size = -2
        self.target_diff.add_color_attachment(bits=16)
        self.target_diff.prepare_buffer()

        # Create the target which blurs the diffuse result
        self.target_blur_v = self.create_target("BlurV")
        self.target_blur_v.size = -2
        self.target_blur_v.add_color_attachment(bits=16)
        self.target_blur_v.has_color_alpha = True
        self.target_blur_v.prepare_buffer()
        self.target_blur_v.set_shader_input("SourceTex",
                                            self.target_diff.color_tex)

        self.target_blur_h = self.create_target("BlurH")
        self.target_blur_h.size = -2
        self.target_blur_h.add_color_attachment(bits=16)
        self.target_blur_h.has_color_alpha = True
        self.target_blur_h.prepare_buffer()
        self.target_blur_h.set_shader_input("SourceTex",
                                            self.target_blur_v.color_tex)

        # Set blur parameters
        self.target_blur_v.set_shader_input("blur_direction", LVecBase2i(0, 1))
        self.target_blur_h.set_shader_input("blur_direction", LVecBase2i(1, 0))

        # Create the target which bilateral upsamples the diffuse target
        self.target_upscale_diff = self.create_target("UpscaleDiffuse")
        self.target_upscale_diff.add_color_attachment(bits=16)
        self.target_upscale_diff.prepare_buffer()
        self.target_upscale_diff.set_shader_input("SourceTex",
                                                  self.target_blur_h.color_tex)
        self.target_upscale_diff.set_shader_input("upscaleWeights",
                                                  Vec2(0.0001, 0.001))

        self.target_resolve = self.create_target("ResolveVXGI")
        self.target_resolve.add_color_attachment(bits=16)
        self.target_resolve.prepare_buffer()
        self.target_resolve.set_shader_input(
            "CurrentTex", self.target_upscale_diff.color_tex)

        # Make the ambient stage use the GI result
        AmbientStage.required_pipes += ["VXGIDiffuse"]
Esempio n. 12
0
    def reserveTiles(self, width, height, tileIndex):
        """ Reserves enough tiles to store a tile with the ID tileIndex
        and the dimensions width*height in the atlas and returns the
        top-left coordinates of the reserved space """

        # Convert to tile space
        tileW, tileH = width / self.tileSize, height / self.tileSize

        # Iterate over all tiles
        maxIterations = self.tileCount - tileW + 1, self.tileCount - tileH + 1
        tileFound = False
        tilePos = LVecBase2i(-1)
        for j in range(0, maxIterations[0]):
            if not tileFound:
                for i in range(0, maxIterations[1]):
                    if not tileFound:

                        # First, assume the space is free
                        tileFree = True

                        # Now, check if anything is blocking the space, and if so,
                        # mark the tile as reserved
                        for x in range(tileW):
                            for y in range(tileH):
                                if not tileFree:
                                    break
                                if self.tiles[j + y][i + x] is not None:
                                    tileFree = False
                                    break

                        # When the tile is free, we have found our tile
                        if tileFree:
                            tileFound = True
                            tilePos = LVecBase2i(i, j)
                            break

        # When there is a tile found, compute its relative coordinates and reserve it
        if tileFound:
            self._reserveTile(tilePos.x, tilePos.y, tileW, tileH, tileIndex)

            return Vec2(
                float(tilePos.x) / float(self.tileCount),
                float(tilePos.y) / float(self.tileCount))

        # Otherwise throw an error
        else:
            self.error("No free tile found! Have to update whole atlas maybe?")
            return None
Esempio n. 13
0
    def _createLightingPipeline(self):
        """ Creates the lighting pipeline, including shadow handling """

        if not self.haveLightingPass:
            self.debug("Skipping lighting pipeline")
            return

        self.debug("Creating lighting pipeline ..")

        # size has to be a multiple of the compute unit size
        # but still has to cover the whole screen
        sizeX = int(math.ceil(float(self.size.x) / self.patchSize.x))
        sizeY = int(math.ceil(float(self.size.y) / self.patchSize.y))

        self.precomputeSize = LVecBase2i(sizeX, sizeY)

        self.debug("Batch size =", sizeX, "x", sizeY, "Actual Buffer size=",
                   int(sizeX * self.patchSize.x), "x",
                   int(sizeY * self.patchSize.y))

        self._makeLightPerTileStorage()

        # Create a buffer which computes which light affects which tile
        self._makeLightBoundsComputationBuffer(sizeX, sizeY)

        # Create a buffer which applies the lighting
        self._makeLightingComputeBuffer()

        # Register for light manager
        self.lightManager.setLightingComputator(self.lightingComputeContainer)
        self.lightManager.setLightingCuller(self.lightBoundsComputeBuff)

        self._loadFallbackCubemap()
        self._loadLookupCubemap()
Esempio n. 14
0
    def _prepareAttributes(self):
        """ Prepares all shaderAttributes, so that we have a list of
        ShaderAttributes we can simply walk through in the update method,
        that is MUCH faster than using setShaderInput, as each call to
        setShaderInput forces the generation of a new ShaderAttrib """
        self.attributes = []
        textures = [self.pingTexture, self.pongTexture]

        currentIndex = 0
        firstPass = True

        # Horizontal
        for step in xrange(self.log2Size):
            source = textures[currentIndex]
            dest = textures[1 - currentIndex]

            if firstPass:
                source = self.sourceTex
                firstPass = False

            index = self.log2Size - step - 1
            self.horizontalFFT.setShaderInput("source", source)
            self.horizontalFFT.setShaderInput("dest", dest)
            self.horizontalFFT.setShaderInput(
                "butterflyIndex", LVecBase2i(index))
            self._queueShader(self.horizontalFFT)
            currentIndex = 1 - currentIndex

        # Vertical
        for step in xrange(self.log2Size):
            source = textures[currentIndex]
            dest = textures[1 - currentIndex]
            isLastPass = step == self.log2Size - 1
            if isLastPass:
                dest = self.resultTexture
            index = self.log2Size - step - 1
            self.verticalFFT.setShaderInput("source", source)
            self.verticalFFT.setShaderInput("dest", dest)
            self.verticalFFT.setShaderInput(
                "isLastPass", isLastPass)
            self.verticalFFT.setShaderInput(
                "normalizationFactor", self.normalizationFactor)
            self.verticalFFT.setShaderInput(
                "butterflyIndex", LVecBase2i(index))
            self._queueShader(self.verticalFFT)

            currentIndex = 1 - currentIndex
 def __init__(self):
     self._slot = -1
     self._needs_update = True
     self._resolution = 512
     self._mvp = 0.0
     self._region = LVecBase2i(-1)
     self._region_uv = LVecBase2f(0.0)
     self._bounds = BoundingSphere()
Esempio n. 16
0
 def size(self, *args):
     """ Sets the render target size. This can be either a single integer,
     in which case it applies to both dimensions. Negative integers cause
     the render target to be proportional to the screen size, i.e. a value
     of -4 produces a quarter resolution target, a value of -2 a half
     resolution target, and a value of -1 a full resolution target
     (the default). """
     self._size_constraint = LVecBase2i(*args)
Esempio n. 17
0
    def __init__(self, **kwargs):
        """ Creates a new block of text. """
        LUIObject.__init__(self)
        LUIInitialState.init(self, kwargs)
        self._cursor = LVecBase2i(0)
        self._last_size = 14

        self.labels = []
    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)
Esempio n. 19
0
    def create(self):
        self.target = self.create_target("Sample")
        self.target.size = -2
        self.target.add_color_attachment(bits=(8, 0, 0, 0))
        self.target.prepare_buffer()

        self.target_upscale = self.create_target("Upscale")
        self.target_upscale.add_color_attachment(bits=(8, 0, 0, 0))
        self.target_upscale.prepare_buffer()

        self.target_upscale.set_shader_input("SourceTex", self.target.color_tex)
        self.target_upscale.set_shader_input("upscaleWeights", Vec2(0.001, 0.001))
        self.target_upscale.set_shader_input("useZAsWeight", False)

        self.tarrget_detail_ao = self.create_target("DetailAO")
        self.tarrget_detail_ao.add_color_attachment(bits=(8, 0, 0, 0))
        self.tarrget_detail_ao.prepare_buffer()
        self.tarrget_detail_ao.set_shader_input("AOResult", self.target_upscale.color_tex)

        self.blur_targets = []

        current_tex = self.tarrget_detail_ao.color_tex

        for i in range(1):
            target_blur_v = self.create_target("BlurV-" + str(i))
            target_blur_v.add_color_attachment(bits=(8, 0, 0, 0))
            target_blur_v.prepare_buffer()

            target_blur_h = self.create_target("BlurH-" + str(i))
            target_blur_h.add_color_attachment(bits=(8, 0, 0, 0))
            target_blur_h.prepare_buffer()

            target_blur_v.set_shader_input("SourceTex", current_tex)
            target_blur_h.set_shader_input("SourceTex", target_blur_v.color_tex)

            target_blur_v.set_shader_input("blur_direction", LVecBase2i(0, 1))
            target_blur_h.set_shader_input("blur_direction", LVecBase2i(1, 0))

            current_tex = target_blur_h.color_tex
            self.blur_targets += [target_blur_v, target_blur_h]

        self.target_resolve = self.create_target("ResolveAO")
        self.target_resolve.add_color_attachment(bits=(8, 0, 0, 0))
        self.target_resolve.prepare_buffer()
        self.target_resolve.set_shader_input("CurrentTex", current_tex)
Esempio n. 20
0
 def consider_resize(self):
     """ Checks if the target has to get resized, and if this is the case,
     performs the resize. This should be called when the window resolution
     changed. """
     current_size = LVecBase2i(self._size)
     self._compute_size_from_constraint()
     if current_size != self._size:
         if self._internal_buffer:
             self._internal_buffer.set_size(self._size.x, self._size.y)
Esempio n. 21
0
 def _compute_size_from_constraint(self):
     """ Computes the actual size in pixels from the targets size constraint """
     w, h = Globals.resolution.x, Globals.resolution.y
     self._size = LVecBase2i(self._size_constraint)
     if self._size_constraint.x < 0:
         self._size.x = (w - self._size_constraint.x -
                         1) // (-self._size_constraint.x)
     if self._size_constraint.y < 0:
         self._size.y = (h - self._size_constraint.y -
                         1) // (-self._size_constraint.y)
Esempio n. 22
0
 def _compute_render_resolution(self):
     """ Computes the internally used render resolution. This might differ
     from the window dimensions in case a resolution scale is set. """
     scale_factor = self.settings["pipeline.resolution_scale"]
     w = int(float(Globals.native_resolution.x) * scale_factor)
     h = int(float(Globals.native_resolution.y) * scale_factor)
     # Make sure the resolution is a multiple of 4
     w, h = w - w % 4, h - h % 4
     self.debug("Render resolution is", w, "x", h)
     Globals.resolution = LVecBase2i(w, h)
Esempio n. 23
0
    def _create_blur_pass(self, i, tex):
        target_blur_v = self.create_target("BlurV-" + str(i))
        target_blur_v.add_color_attachment(bits=(8, 0, 0, 0))
        target_blur_v.prepare_buffer()

        target_blur_h = self.create_target("BlurH-" + str(i))
        target_blur_h.add_color_attachment(bits=(8, 0, 0, 0))
        target_blur_h.prepare_buffer()

        target_blur_v.set_shader_inputs(SourceTex=tex,
                                        blur_direction=LVecBase2i(0, 1),
                                        pixel_stretch=self.pixel_stretch)

        target_blur_h.set_shader_inputs(SourceTex=target_blur_v.color_tex,
                                        blur_direction=LVecBase2i(1, 0),
                                        pixel_stretch=self.pixel_stretch)

        self.blur_targets += [target_blur_v, target_blur_h]

        return target_blur_h.color_tex
Esempio n. 24
0
    def __init__(self, name="target"):
        RPObject.__init__(self, name)
        self._targets = {}
        self._color_bits = (0, 0, 0, 0)
        self._aux_bits = 8
        self._aux_count = 0
        self._depth_bits = 0
        self._size = LVecBase2i(-1)
        self._size_constraint = LVecBase2i(-1)
        self._source_window = Globals.base.win
        self._source_region = None
        self._active = False
        self._internal_buffer = None
        self.sort = None

        # Public attributes
        self.engine = Globals.base.graphicsEngine
        self.support_transparency = False
        self.create_default_region = True

        # Disable all global clears, since they are not required
        for region in Globals.base.win.get_display_regions():
            region.disable_clears()
Esempio n. 25
0
    def _init_globals(self):
        """ Inits all global bindings """
        Globals.load(self._showbase)
        w, h = self._showbase.win.get_x_size(), self._showbase.win.get_y_size()

        scale_factor = self.settings["pipeline.resolution_scale"]
        w = int(float(w) * scale_factor)
        h = int(float(h) * scale_factor)

        # Make sure the resolution is a multiple of 4
        w = w - w % 4
        h = h - h % 4

        self.debug("Render resolution is", w, "x", h)
        Globals.resolution = LVecBase2i(w, h)

        # Connect the render target output function to the debug object
        RenderTarget.RT_OUTPUT_FUNC = lambda *args: RPObject.global_warn(
            "RenderTarget", *args[1:])

        RenderTarget.USE_R11G11B10 = self.settings["pipeline.use_r11_g11_b10"]
Esempio n. 26
0
    def create(self):
        self.target = self.create_target("Sample")
        self.target.size = -2
        self.target.add_color_attachment(bits=(8, 0, 0, 0))
        self.target.prepare_buffer()

        self.target_upscale = self.create_target("Upscale")
        self.target_upscale.add_color_attachment(bits=(8, 0, 0, 0))
        self.target_upscale.prepare_buffer()

        self.target_upscale.set_shader_inputs(SourceTex=self.target.color_tex,
                                              upscaleWeights=Vec2(
                                                  0.001, 0.001))

        self.tarrget_detail_ao = self.create_target("DetailAO")
        self.tarrget_detail_ao.add_color_attachment(bits=(8, 0, 0, 0))
        self.tarrget_detail_ao.prepare_buffer()
        self.tarrget_detail_ao.set_shader_input("AOResult",
                                                self.target_upscale.color_tex)

        self.debug("Blur quality is", self.quality)

        # Low
        pixel_stretch = 2.0
        blur_passes = 1

        if self.quality == "MEDIUM":
            pixel_stretch = 1.0
            blur_passes = 2
        elif self.quality == "HIGH":
            pixel_stretch = 1.0
            blur_passes = 3
        elif self.quality == "ULTRA":
            pixel_stretch = 1.0
            blur_passes = 5

        self.blur_targets = []

        current_tex = self.tarrget_detail_ao.color_tex

        for i in range(blur_passes):
            target_blur_v = self.create_target("BlurV-" + str(i))
            target_blur_v.add_color_attachment(bits=(8, 0, 0, 0))
            target_blur_v.prepare_buffer()

            target_blur_h = self.create_target("BlurH-" + str(i))
            target_blur_h.add_color_attachment(bits=(8, 0, 0, 0))
            target_blur_h.prepare_buffer()

            target_blur_v.set_shader_inputs(SourceTex=current_tex,
                                            blur_direction=LVecBase2i(0, 1),
                                            pixel_stretch=pixel_stretch)

            target_blur_h.set_shader_inputs(SourceTex=target_blur_v.color_tex,
                                            blur_direction=LVecBase2i(1, 0),
                                            pixel_stretch=pixel_stretch)

            current_tex = target_blur_h.color_tex
            self.blur_targets += [target_blur_v, target_blur_h]

        self.target_resolve = self.create_target("ResolveAO")
        self.target_resolve.add_color_attachment(bits=(8, 0, 0, 0))
        self.target_resolve.prepare_buffer()
        self.target_resolve.set_shader_input("CurrentTex", current_tex)
Esempio n. 27
0
 def __init__(self, **kwargs):
     """ Creates a new formatted label. """
     LUIObject.__init__(self)
     LUIInitialState.init(self, kwargs)
     self._cursor = LVecBase2i(0)
     self._last_size = 14
Esempio n. 28
0
    def create(self):
        """ Creates this pipeline """

        self.debug("Setting up render pipeline")

        if self.settings is None:
            self.error("You have to call loadSettings first!")
            return

        self.debug("Analyzing system ..")
        SystemAnalyzer.analyze()

        self.debug("Checking required Panda3D version ..")
        SystemAnalyzer.checkPandaVersionOutOfDate(01, 12, 2014)

        # Mount everything first
        self.mountManager.mount()

        # Store globals, as cython can't handle them
        self.debug("Setting up globals")
        Globals.load(self.showbase)
        Globals.font = loader.loadFont("Data/Font/SourceSansPro-Semibold.otf")
        Globals.font.setPixelsPerUnit(25)

        # Setting up shader loading
        BetterShader._DumpShaders = self.settings.dumpGeneratedShaders

        # We use PTA's for shader inputs, because that's faster than
        # using setShaderInput
        self.temporalProjXOffs = PTAInt.emptyArray(1)
        self.cameraPosition = PTAVecBase3f.emptyArray(1)
        self.motionBlurFactor = PTAFloat.emptyArray(1)
        self.lastMVP = PTALMatrix4f.emptyArray(1)
        self.currentMVP = PTALMatrix4f.emptyArray(1)
        self.currentShiftIndex = PTAInt.emptyArray(1)

        # Initialize variables
        self.camera = self.showbase.cam
        self.size = self._getSize()
        self.cullBounds = None

        # For the temporal reprojection it is important that the window width
        # is a multiple of 2
        if self.settings.enableTemporalReprojection and self.size.x % 2 == 1:
            self.error(
                "The window has to have a width which is a multiple of 2 "
                "(Current: ", self.showbase.win.getXSize(), ")")
            self.error(
                "I'll correct that for you, but next time pass the correct "
                "window size!")

            wp = WindowProperties()
            wp.setSize(self.showbase.win.getXSize() + 1,
                       self.showbase.win.getYSize())
            self.showbase.win.requestProperties(wp)
            self.showbase.graphicsEngine.openWindows()

            # Get new size
            self.size = self._getSize()

        # Debug variables to disable specific features
        self.haveLightingPass = True

        # haveCombiner can only be true when haveLightingPass is enabled
        self.haveCombiner = True
        self.haveMRT = True

        # Not as good as I want it, so disabled. I'll work on it.
        self.blurEnabled = False

        self.debug("Window size is", self.size.x, "x", self.size.y)

        self.showbase.camLens.setNearFar(0.1, 50000)
        self.showbase.camLens.setFov(90)

        self.showbase.win.setClearColor(Vec4(1.0, 0.0, 1.0, 1.0))

        # Create GI handler
        if self.settings.enableGlobalIllumination:
            self._setupGlobalIllumination()

        # Create occlusion handler
        self._setupOcclusion()

        if self.settings.displayOnscreenDebugger:
            self.guiManager = PipelineGuiManager(self)
            self.guiManager.setup()

        # Generate auto-configuration for shaders
        self._generateShaderConfiguration()

        # Create light manager, which handles lighting + shadows
        if self.haveLightingPass:
            self.lightManager = LightManager(self)

        self.patchSize = LVecBase2i(self.settings.computePatchSizeX,
                                    self.settings.computePatchSizeY)

        # Create separate scene graphs. The deferred graph is render
        self.forwardScene = NodePath("Forward-Rendering")
        self.transparencyScene = NodePath("Transparency-Rendering")
        self.transparencyScene.setBin("transparent", 30)

        # We need no transparency (we store other information in the alpha
        # channel)
        self.showbase.render.setAttrib(
            TransparencyAttrib.make(TransparencyAttrib.MNone), 100)

        # Now create deferred render buffers
        self._makeDeferredTargets()

        # Create the target which constructs the view-space normals and
        # position from world-space position
        if self.occlusion.requiresViewSpacePosNrm():
            self._createNormalPrecomputeBuffer()

        if self.settings.enableGlobalIllumination:
            self._creatGIPrecomputeBuffer()

        # Setup the buffers for lighting
        self._createLightingPipeline()

        # Setup combiner for temporal reprojetion
        if self.haveCombiner and self.settings.enableTemporalReprojection:
            self._createCombiner()

        if self.occlusion.requiresBlurring():
            self._createOcclusionBlurBuffer()

        self._setupAntialiasing()

        if self.blurEnabled:
            self._createDofStorage()
            self._createBlurBuffer()

        # Not sure why it has to be 0.25. But that leads to the best result
        aspect = float(self.size.y) / self.size.x
        self.onePixelShift = Vec2(0.125 / self.size.x, 0.125 / self.size.y /
                                  aspect) * self.settings.jitterAmount

        # Annoying that Vec2 has no multliply-operator for non-floats
        multiplyVec2 = lambda a, b: Vec2(a.x * b.x, a.y * b.y)

        if self.antialias.requiresJittering():
            self.pixelShifts = [
                multiplyVec2(self.onePixelShift, Vec2(-0.25, 0.25)),
                multiplyVec2(self.onePixelShift, Vec2(0.25, -0.25))
            ]
        else:
            self.pixelShifts = [Vec2(0), Vec2(0)]
        self.currentPixelShift = PTAVecBase2f.emptyArray(1)
        self.lastPixelShift = PTAVecBase2f.emptyArray(1)

        self._setupFinalPass()
        self._setShaderInputs()

        # Give the gui a hint when the pipeline is done loading
        if self.settings.displayOnscreenDebugger:
            self.guiManager.onPipelineLoaded()

        # add update task
        self._attachUpdateTask()
Esempio n. 29
0
 def _getSize(self):
     """ Returns the window size. """
     return LVecBase2i(self.showbase.win.getXSize(),
                       self.showbase.win.getYSize())
Esempio n. 30
0
    def __init__(self):
        DebugObject.__init__(self, "WaterManager")
        self.options = OceanOptions()
        self.options.size = 512
        self.options.windDir.normalize()
        self.options.waveAmplitude *= 1e-7

        self.displacementTex = Texture("Displacement")
        self.displacementTex.setup2dTexture(self.options.size,
                                            self.options.size, Texture.TFloat,
                                            Texture.FRgba16)

        self.normalTex = Texture("Normal")
        self.normalTex.setup2dTexture(self.options.size, self.options.size,
                                      Texture.TFloat, Texture.FRgba16)

        self.combineShader = BetterShader.loadCompute(
            "Shader/Water/Combine.compute")

        self.ptaTime = PTAFloat.emptyArray(1)

        # Create a gaussian random texture, as shaders aren't well suited
        # for that
        setRandomSeed(523)
        self.randomStorage = PNMImage(self.options.size, self.options.size, 4)
        self.randomStorage.setMaxval((2**16) - 1)

        for x in xrange(self.options.size):
            for y in xrange(self.options.size):
                rand1 = self._getGaussianRandom() / 10.0 + 0.5
                rand2 = self._getGaussianRandom() / 10.0 + 0.5
                self.randomStorage.setXel(x, y, LVecBase3d(rand1, rand2, 0))
                self.randomStorage.setAlpha(x, y, 1.0)

        self.randomStorageTex = Texture("RandomStorage")
        self.randomStorageTex.load(self.randomStorage)
        self.randomStorageTex.setFormat(Texture.FRgba16)
        self.randomStorageTex.setMinfilter(Texture.FTNearest)
        self.randomStorageTex.setMagfilter(Texture.FTNearest)

        # Create the texture wwhere the intial height (H0 + Omega0) is stored.
        self.texInitialHeight = Texture("InitialHeight")
        self.texInitialHeight.setup2dTexture(self.options.size,
                                             self.options.size, Texture.TFloat,
                                             Texture.FRgba16)
        self.texInitialHeight.setMinfilter(Texture.FTNearest)
        self.texInitialHeight.setMagfilter(Texture.FTNearest)

        # Create the shader which populates the initial height texture
        self.shaderInitialHeight = BetterShader.loadCompute(
            "Shader/Water/InitialHeight.compute")
        self.nodeInitialHeight = NodePath("initialHeight")
        self.nodeInitialHeight.setShader(self.shaderInitialHeight)
        self.nodeInitialHeight.setShaderInput("dest", self.texInitialHeight)
        self.nodeInitialHeight.setShaderInput("N",
                                              LVecBase2i(self.options.size))
        self.nodeInitialHeight.setShaderInput("patchLength",
                                              self.options.patchLength)
        self.nodeInitialHeight.setShaderInput("windDir", self.options.windDir)
        self.nodeInitialHeight.setShaderInput("windSpeed",
                                              self.options.windSpeed)
        self.nodeInitialHeight.setShaderInput("waveAmplitude",
                                              self.options.waveAmplitude)
        self.nodeInitialHeight.setShaderInput("windDependency",
                                              self.options.windDependency)
        self.nodeInitialHeight.setShaderInput("randomTex",
                                              self.randomStorageTex)

        self.attrInitialHeight = self.nodeInitialHeight.getAttrib(ShaderAttrib)

        self.heightTextures = []
        for i in xrange(3):

            tex = Texture("Height")
            tex.setup2dTexture(self.options.size, self.options.size,
                               Texture.TFloat, Texture.FRgba16)
            tex.setMinfilter(Texture.FTNearest)
            tex.setMagfilter(Texture.FTNearest)
            tex.setWrapU(Texture.WMClamp)
            tex.setWrapV(Texture.WMClamp)
            self.heightTextures.append(tex)

        # Also create the shader which updates the spectrum
        self.shaderUpdate = BetterShader.loadCompute(
            "Shader/Water/Update.compute")
        self.nodeUpdate = NodePath("update")
        self.nodeUpdate.setShader(self.shaderUpdate)
        self.nodeUpdate.setShaderInput("outH0x", self.heightTextures[0])
        self.nodeUpdate.setShaderInput("outH0y", self.heightTextures[1])
        self.nodeUpdate.setShaderInput("outH0z", self.heightTextures[2])
        self.nodeUpdate.setShaderInput("initialHeight", self.texInitialHeight)
        self.nodeUpdate.setShaderInput("N", LVecBase2i(self.options.size))
        self.nodeUpdate.setShaderInput("time", self.ptaTime)
        self.attrUpdate = self.nodeUpdate.getAttrib(ShaderAttrib)

        # Create 3 FFTs
        self.fftX = GPUFFT(self.options.size, self.heightTextures[0],
                           self.options.normalizationFactor)
        self.fftY = GPUFFT(self.options.size, self.heightTextures[1],
                           self.options.normalizationFactor)
        self.fftZ = GPUFFT(self.options.size, self.heightTextures[2],
                           self.options.normalizationFactor)

        self.combineNode = NodePath("Combine")
        self.combineNode.setShader(self.combineShader)
        self.combineNode.setShaderInput("displacementX",
                                        self.fftX.getResultTexture())
        self.combineNode.setShaderInput("displacementY",
                                        self.fftY.getResultTexture())
        self.combineNode.setShaderInput("displacementZ",
                                        self.fftZ.getResultTexture())
        self.combineNode.setShaderInput("normalDest", self.normalTex)
        self.combineNode.setShaderInput("displacementDest",
                                        self.displacementTex)
        self.combineNode.setShaderInput("N", LVecBase2i(self.options.size))
        self.combineNode.setShaderInput("choppyScale",
                                        self.options.choppyScale)
        self.combineNode.setShaderInput("gridLength", self.options.patchLength)
        # Store only the shader attrib as this is way faster
        self.attrCombine = self.combineNode.getAttrib(ShaderAttrib)