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()
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
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)
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
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()
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()
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"]
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)
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
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"]
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
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()
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()
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)
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)
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)
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)
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)
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)
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
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()
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"]
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)
def __init__(self, **kwargs): """ Creates a new formatted label. """ LUIObject.__init__(self) LUIInitialState.init(self, kwargs) self._cursor = LVecBase2i(0) self._last_size = 14
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()
def _getSize(self): """ Returns the window size. """ return LVecBase2i(self.showbase.win.getXSize(), self.showbase.win.getYSize())
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)