Ejemplo n.º 1
0
void AmbientOcclusion::computeCSZ
   (RenderDevice*                   rd, 
    const Array<shared_ptr<Framebuffer> >& cszFramebuffers,
    const shared_ptr<Texture>&      csZBuffer,
    const AmbientOcclusionSettings& settings,
    const shared_ptr<Texture>&      depthBuffer, 
    const Vector3&                  clipInfo,
    const shared_ptr<Texture>&      peeledDepthBuffer) {

    BEGIN_PROFILER_EVENT("computeCSZ");
        
    // Generate level 0
    cszFramebuffers[0]->set(Framebuffer::DEPTH, depthBuffer);
    rd->push2D(cszFramebuffers[0]); {
        rd->clear(true, false, false);
        rd->setDepthWrite(false);
        rd->setDepthTest(RenderDevice::DEPTH_GREATER);
        Args args;
        args.setUniform(SYMBOL_clipInfo,                 clipInfo);
        args.setUniform(SYMBOL_DEPTH_AND_STENCIL_buffer, depthBuffer, Sampler::buffer());
        alwaysAssertM(!settings.useDepthPeelBuffer || notNull(peeledDepthBuffer),
                    "Tried to run AO with peeled depth buffer, but buffer was null");
        args.setMacro(SYMBOL_USE_PEELED_DEPTH_BUFFER, settings.useDepthPeelBuffer);
        if (settings.useDepthPeelBuffer) {
            args.setUniform(SYMBOL_peeledDepthBuffer, peeledDepthBuffer, Sampler::buffer());
        }
        args.setRect(rd->viewport());

        LAUNCH_SHADER("AmbientOcclusion_reconstructCSZ.*", args);
    } rd->pop2D();


    // Generate the other levels (we don't have a depth texture to cull against for these)
    for (int i = 1; i <= MAX_MIP_LEVEL; ++i) {
        Args args;
        args.setUniform("CSZ_buffer", csZBuffer, cszSamplerSettings());
        args.setMacro(SYMBOL_USE_PEELED_DEPTH_BUFFER, settings.useDepthPeelBuffer);

        rd->push2D(cszFramebuffers[i]); {
            rd->clear();
            args.setUniform(SYMBOL_previousMIPNumber, i - 1);
            args.setRect(rd->viewport());
            LAUNCH_SHADER("AmbientOcclusion_minify.*", args);
        } rd->pop2D();
    }

    END_PROFILER_EVENT();
}
Ejemplo n.º 2
0
shared_ptr<Texture> TemporalFilter::apply
   (RenderDevice* rd, 
    const Vector3&                  clipConstant,
    const Vector4&                  projConstant,
    const CFrame&                   currentCameraFrame,
    const CFrame&                   prevCameraFrame, 
    const shared_ptr<Texture>&      unfilteredValue, 
    const shared_ptr<Texture>&      depth, 
    const shared_ptr<Texture>&      ssVelocity, 
    const Vector2&                  guardBandSize,
    int                             numFilterComponents, 
    const Settings&                 settings) {

    if (settings.hysteresis == 0.0f) {
        return unfilteredValue;
    }

    alwaysAssertM((settings.hysteresis >= 0.0f) && (settings.hysteresis <= 1.0f), "TemporalFilter::Settings::hysteresis must be in [0.0, 1.0]");
    alwaysAssertM(notNull(unfilteredValue) && notNull(depth) && notNull(ssVelocity), "Sent null buffer to TemporalFilter::apply");
    alwaysAssertM((numFilterComponents >= 1) && (numFilterComponents <= 4), "numFilterComponents must be between 1 and 4");

    if (isNull(m_previousDepthBuffer) || 
        isNull(m_previousTexture) ||
        (m_previousDepthBuffer->vector2Bounds() != depth->vector2Bounds()) ||
        (m_previousTexture->vector2Bounds() != unfilteredValue->vector2Bounds())) {

        unfilteredValue->copyInto(m_previousTexture);
        depth->copyInto(m_previousDepthBuffer);
        m_resultFramebuffer = Framebuffer::create(Texture::createEmpty("TemporalFilter::m_resultFramebuffer", m_previousTexture->width(), m_previousTexture->height(), m_previousTexture->format()));
        Texture::copy(m_previousTexture, m_resultFramebuffer->texture(0));
        return m_resultFramebuffer->texture(0);
    }

    rd->push2D(m_resultFramebuffer); {
        Args args;
        args.setMacro("FILTER_COMPONENT_COUNT", numFilterComponents);
        ssVelocity->setShaderArgs(args, "ssVelocity_", Sampler::buffer());
        unfilteredValue->setShaderArgs(args, "unfilteredValue_", Sampler::buffer());
        depth->setShaderArgs(args, "depth_", Sampler::buffer());
        m_previousDepthBuffer->setShaderArgs(args, "previousDepth_", Sampler::video());
        m_previousTexture->setShaderArgs(args, "previousValue_", Sampler::video());

        args.setUniform("guardBandSize", guardBandSize);

        args.setUniform("cameraToWorld", currentCameraFrame);    
        args.setUniform("cameraToWorldPrevious", prevCameraFrame);
        
        args.setUniform("clipInfo", clipConstant);
        args.setUniform("projInfo", projConstant);

        settings.setArgs(args);
        args.setRect(rd->viewport());

        LAUNCH_SHADER("TemporalFilter_apply.*", args);
        m_resultFramebuffer->texture(0)->copyInto(m_previousTexture);

        depth->copyInto(m_previousDepthBuffer);
    } rd->pop2D();
     return m_resultFramebuffer->texture(0);
}
Ejemplo n.º 3
0
void App::handlePlayPulses() {
    for (int i = m_currentPlayPulses.size() - 1; i >= 0; --i) {
        int currentSampleIndex = (g_sampleWindowIndex * g_currentAudioBuffer.size());
        shared_ptr<SonicSculpturePiece> piece = m_currentPlayPulses[i].piece;
        int endIndex = m_currentPlayPulses[i].initialSample + (piece->size() * g_currentAudioBuffer.size());

        RenderDevice* rd = RenderDevice::current;
        static shared_ptr<Framebuffer> playPulseFB = Framebuffer::create("Play Pulse FB");
        shared_ptr<UniversalMaterial> material = piece->material();
        if (currentSampleIndex >= endIndex) {
            
            playPulseFB->set(Framebuffer::COLOR0, material->emissive().texture());
            rd->push2D(playPulseFB); {
                rd->setColorClearValue(Color3::black());
                rd->clear();
            } rd->pop2D();
            material->emissive().texture()->generateMipMaps();
            m_currentPlayPulses.remove(i);
            continue;
        }
        float alpha = float(currentSampleIndex - m_currentPlayPulses[i].initialSample) / (endIndex - m_currentPlayPulses[i].initialSample);
        
       
        playPulseFB->set(Framebuffer::COLOR0, material->emissive().texture());
        rd->push2D(playPulseFB); {
            Args args;
            args.setUniform("pulsePos", alpha * playPulseFB->width());
            args.setRect(rd->viewport());
            LAUNCH_SHADER("playPulse.pix", args);
        } rd->pop2D();
        material->emissive().texture()->generateMipMaps();
    }
}
Ejemplo n.º 4
0
Archivo: App.cpp Proyecto: lieff/g3d
// Called before the application loop begins.  Load data here and
// not in the constructor so that common exceptions will be
// automatically caught.
void App::onInit() {
    RenderDevice* rd = renderDevice;

    Vector2 destSize(1024, 1024);
    const Rect2D& dest = Rect2D::xywh(Vector2(0, 0), destSize);

    Args args;
//    args.appendToPreamble("#define KERNEL_RADIUS 9\nfloat gaussCoef[KERNEL_RADIUS] = float[KERNEL_RADIUS](0.00194372, 0.00535662, 0.01289581, 0.02712094, 0.04982645, 0.07996757, 0.11211578, 0.13731514, 0.14691596);");

    rd->push2D(dest); {
        args.setRect(dest);
        LAUNCH_SHADER("apply.*", args);
    } rd->pop2D();

    // Or Equivalently:
    //GaussianBlur::apply(renderDevice, Texture::createEmpty("test",1024,1024));
}
Ejemplo n.º 5
0
void App::onGraphics3D(RenderDevice* rd, Array<shared_ptr<Surface> >& surface3D) {

    m_gbuffer->setSpecification(m_gbufferSpecification);
    m_gbuffer->resize(m_framebuffer->width(), m_framebuffer->height());
    m_gbuffer->prepare(rd, activeCamera(), 0, -(float)previousSimTimeStep(), m_settings.depthGuardBandThickness, m_settings.colorGuardBandThickness);

    m_renderer->render(rd, m_framebuffer, m_depthPeelFramebuffer, scene()->lightingEnvironment(), m_gbuffer, surface3D);

    rd->pushState(m_framebuffer); {

        rd->setProjectionAndCameraMatrix(m_debugCamera->projection(), m_debugCamera->frame());
        Array< shared_ptr<Surface> > mySurfaces;
        // Pose our model based on the manipulator axes
        model->pose(mySurfaces, manipulator->frame());
            
        // Set up shared arguments
        Args args;
        configureShaderArgs(args);
            
        // Send model geometry to the graphics card
        CFrame cframe;
        for (int i = 0; i < mySurfaces.size(); ++i) {

            // Downcast to UniversalSurface to access its fields
            shared_ptr<UniversalSurface> surface = dynamic_pointer_cast<UniversalSurface>(mySurfaces[i]);
            if (notNull(surface)) {
                surface->getCoordinateFrame(cframe);
                rd->setObjectToWorldMatrix(cframe);
                surface->gpuGeom()->setShaderArgs(args);

                // (If you want to manually set the material properties and vertex attributes
                // for shader args, they can be accessed from the fields of the gpuGeom.)
                LAUNCH_SHADER("phong.*", args);
            }
        }
    } rd->popState();

    swapBuffers();
    rd->clear();
    m_film->exposeAndRender(rd, m_debugCamera->filmSettings(), m_framebuffer->texture(0), 1);
}
Ejemplo n.º 6
0
void DepthOfField::composite
(RenderDevice*              rd,
 shared_ptr<Texture>        packedBuffer,
 shared_ptr<Texture>        blurBuffer,
 Vector2int16               outputGuardBandThickness) {

    rd->push2D();
    {
        rd->clear(true, false, false);
        rd->setGuardBandClip2D(outputGuardBandThickness);
        Args args;

        // Shift blur buffer texture coordinates by the guard band amount
        args.setUniform("blurBuffer",   blurBuffer);
        args.setUniform("packedBuffer", packedBuffer);
        args.setUniform("shift",        Vector2(outputGuardBandThickness));
        args.setRect(rd->viewport());

        LAUNCH_SHADER("DepthOfField_composite.*", args);
    }
    rd->pop2D();
}
Ejemplo n.º 7
0
void AmbientOcclusion::blurOneDirection
    (RenderDevice*                      rd,
    const AmbientOcclusionSettings&     settings,
    const shared_ptr<Texture>&          depthBuffer,
    const Vector4&                      projConstant,
    const shared_ptr<Texture>&          normalBuffer,
    const Vector2int16&                 axis,
    const shared_ptr<Framebuffer>&      framebuffer,
    const shared_ptr<Texture>&          source) {

    framebuffer->set(Framebuffer::DEPTH, depthBuffer);
    rd->push2D(framebuffer); {
        // For quick early-out testing vs. skybox 
        rd->setDepthTest(RenderDevice::DEPTH_GREATER);
        rd->setColorClearValue(Color3::white());
        rd->clear(true, false, false);
        Args args;
        args.setUniform(SYMBOL_source,          source, Sampler::buffer());
        args.setUniform(SYMBOL_axis,            axis);

        args.setUniform(SYMBOL_projInfo, projConstant);

        args.setMacro(SYMBOL_EDGE_SHARPNESS,    settings.edgeSharpness);
        args.setMacro(SYMBOL_SCALE,             settings.blurStepSize);
        args.setMacro(SYMBOL_R,                 settings.blurRadius);
        args.setMacro(SYMBOL_MDB_WEIGHTS,       settings.monotonicallyDecreasingBilateralWeights ? 1 : 0);
        
        if (settings.useNormalsInBlur && settings.useNormalBuffer) {
            normalBuffer->setShaderArgs(args, "normal_", Sampler::buffer());
        } 

        rd->setClip2D(Rect2D::xyxy((float)m_guardBandSize, (float)m_guardBandSize, rd->viewport().width() - m_guardBandSize, rd->viewport().height() - m_guardBandSize));
        args.setRect(rd->viewport());

        LAUNCH_SHADER("AmbientOcclusion_blur.*", args);
    } rd->pop2D();
}
Ejemplo n.º 8
0
void App::onGraphics3D(RenderDevice* rd, Array<shared_ptr<Surface> >& allSurfaces) {
    // This implementation is equivalent to the default GApp's. It is repeated here to make it
    // easy to modify rendering. If you don't require custom rendering, just delete this
    // method from your application and rely on the base class.

    if (! scene()) {
        return;
    }
	
    m_gbuffer->setSpecification(m_gbufferSpecification);
    m_gbuffer->resize(m_framebuffer->width(), m_framebuffer->height());
    m_gbuffer->prepare(rd, activeCamera(), 0, -(float)previousSimTimeStep(), m_settings.hdrFramebuffer.depthGuardBandThickness, m_settings.hdrFramebuffer.colorGuardBandThickness);

	
	m_renderer->render(rd, m_framebuffer, m_depthPeelFramebuffer, scene()->lightingEnvironment(), m_gbuffer, allSurfaces);
	

    // Debug visualizations and post-process effects
    rd->pushState(m_framebuffer); {

		if (m_enableSVO) {
			rd->clear();
			//rd->setProjectionAndCameraMatrix(activeCamera()->projection(), activeCamera()->frame());

			rd->push2D();
			const Vector2int32 guardBand(m_settings.hdrFramebuffer.depthGuardBandThickness - m_settings.hdrFramebuffer.colorGuardBandThickness);
			const Vector2int32 colorRegionExtent = Vector2int32(m_framebuffer->vector2Bounds()) - guardBand * 2;

			Args args;
			rd->setGuardBandClip2D(Vector2int16(guardBand));
			args.setRect(rd->viewport());

			Matrix4 proj;
			activeCamera()->getProjectUnitMatrix(m_framebuffer->rect2DBounds(), proj);
			float focalLength = proj[0][0];

			m_svo->setCurSvoId(0);
			args.setUniform("guardBand", guardBand);

			args.setUniform("focalLength", focalLength);
			args.setUniform("renderRes", Vector2(colorRegionExtent));
			args.setUniform("renderResI", colorRegionExtent);
			args.setUniform("screenRatio", float(colorRegionExtent.y) / float(colorRegionExtent.x));

			m_svo->connectToShader(args, Access::READ, m_svo->maxDepth(), m_svo->maxDepth());

			rd->setColorWrite(true);
			rd->setDepthWrite(false);
			
			const Matrix4& cameraToVoxelMatrix = Matrix4(m_svo->svoToWorldMatrix()).inverse() * activeCamera()->frame();

			args.setUniform("cameraToVoxelMatrix", cameraToVoxelMatrix);
			args.setUniform("voxelToWorldMatrix", m_svo->svoToWorldMatrix());
			args.setUniform("worldToVoxelMatrix", m_svo->worldToSVOMatrix());
			args.setUniform("wsCameraPos", activeCamera()->frame().translation);
			scene()->lightingEnvironment().setShaderArgs(args);
			args.setUniform("raycastingConeFactor", m_voxelConeAperture);
			

			rd->setDepthTest(RenderDevice::DEPTH_ALWAYS_PASS); // TODO: write gl_FragDepth and use a regular depth test here
			m_gbuffer->texture(GBuffer::Field::DEPTH_AND_STENCIL)->setShaderArgs(args, "depth_", Sampler::buffer());
			//rd->setBlendFunc(RenderDevice::BLEND_ONE, RenderDevice::BLEND_ONE_MINUS_SRC_ALPHA);
			
			LAUNCH_SHADER("raycast.pix", args);
			rd->pop2D();
		}

		// Call to make the App show the output of debugDraw(...)
		rd->setProjectionAndCameraMatrix(activeCamera()->projection(), activeCamera()->frame());
		drawDebugShapes();
		const shared_ptr<Entity>& selectedEntity = (notNull(developerWindow) && notNull(developerWindow->sceneEditorWindow)) ? developerWindow->sceneEditorWindow->selectedEntity() : shared_ptr<Entity>();
        scene()->visualize(rd, selectedEntity, allSurfaces, sceneVisualizationSettings(), activeCamera());		

		rd->setPolygonOffset(-0.2f);
		if (m_debugSVONodes) {
			m_svo->visualizeNodes(rd, m_debugSVONodeLevel);
		}
		if (m_debugSVOFragments) {
			m_svo->visualizeFragments(rd);
		}
		rd->setPolygonOffset(0.0f);

        // Post-process special effects
        m_depthOfField->apply(rd, m_framebuffer->texture(0), m_framebuffer->texture(Framebuffer::DEPTH), activeCamera(), m_settings.hdrFramebuffer.depthGuardBandThickness - m_settings.hdrFramebuffer.colorGuardBandThickness);
        
        m_motionBlur->apply(rd, m_framebuffer->texture(0), m_gbuffer->texture(GBuffer::Field::SS_EXPRESSIVE_MOTION), 
                            m_framebuffer->texture(Framebuffer::DEPTH), activeCamera(), 
                            m_settings.hdrFramebuffer.depthGuardBandThickness - m_settings.hdrFramebuffer.colorGuardBandThickness);



    } rd->popState();

    if ((submitToDisplayMode() == SubmitToDisplayMode::MAXIMIZE_THROUGHPUT) && (!renderDevice->swapBuffersAutomatically())) {
        // We're about to render to the actual back buffer, so swap the buffers now.
        // This call also allows the screenshot and video recording to capture the
        // previous frame just before it is displayed.
        swapBuffers();
    }

	// Clear the entire screen (needed even though we'll render over it, since
    // AFR uses clear() to detect that the buffer is not re-used.)
    rd->clear();

    // Perform gamma correction, bloom, and SSAA, and write to the native window frame buffer
    m_film->exposeAndRender(rd, activeCamera()->filmSettings(), m_framebuffer->texture(0));
}
Ejemplo n.º 9
0
void App::onGraphics3D(RenderDevice* rd, Array<shared_ptr<Surface> >& surface3D) {
    rd->setColorClearValue(Color3::white() * 0.3f);
    rd->clear();

    // Draw the base geometry as gray with black wireframe
    rd->pushState();    
    rd->setPolygonOffset(0.2f);
    rd->setColor(Color3::white() * 0.10f);
    for (int i = 0; i < m_sceneGeometry.size(); ++i) {
        const shared_ptr<Surface>& surface = m_sceneGeometry[i];
        CFrame cframe;
        surface->getCoordinateFrame(cframe);
        rd->setObjectToWorldMatrix(cframe);
        surface->sendGeometry(rd);
    }
    rd->popState();

    rd->pushState();
    rd->setColor(Color3::black());
    rd->setRenderMode(RenderDevice::RENDER_WIREFRAME);
    for (int i = 0; i < m_sceneGeometry.size(); ++i) {
        const shared_ptr<Surface>& surface = m_sceneGeometry[i];
        CFrame cframe;
        surface->getCoordinateFrame(cframe);
        rd->setObjectToWorldMatrix(cframe);
        surface->sendGeometry(rd);
    }
    rd->popState();

    // Draw the extruded geometry as colored wireframe with "glass" interior
    rd->pushState();
    rd->setBlendFunc(RenderDevice::BLEND_ONE, RenderDevice::BLEND_ONE);
    rd->setDepthWrite(false);
    Args args;
    args.setUniform("intensity", 0.1f); 
    CFrame cframe;
    for (int i = 0; i < m_sceneGeometry.size(); ++i) {
        const shared_ptr<UniversalSurface>& surface = dynamic_pointer_cast<UniversalSurface>(m_sceneGeometry[i]);
        if (surface) {
            surface->getCoordinateFrame(cframe);
            args.setUniform("MVP", 
                rd->invertYMatrix() * rd->projectionMatrix() * (rd->cameraToWorldMatrix().inverse() * 
                cframe));
            surface->gpuGeom()->setShaderArgs(args);
            LAUNCH_SHADER("extrude.*", args);
        }
    }
    rd->popState();

    rd->pushState();
    rd->setRenderMode(RenderDevice::RENDER_WIREFRAME);
    rd->setCullFace(CullFace::NONE);

    args.setUniform("intensity", 1.0f); 
    for (int i = 0; i < m_sceneGeometry.size(); ++i) {
        const shared_ptr<UniversalSurface>& surface = dynamic_pointer_cast<UniversalSurface>(m_sceneGeometry[i]);
        if (notNull(surface)) {

            surface->getCoordinateFrame(cframe);
            args.setUniform("MVP", rd->invertYMatrix() * rd->projectionMatrix() * (rd->cameraToWorldMatrix().inverse() * cframe));
            surface->gpuGeom()->setShaderArgs(args);

            LAUNCH_SHADER("extrude.*", args);
        }
    }
    rd->popState();

    drawDebugShapes();
}
Ejemplo n.º 10
0
void AmbientOcclusion::computeRawAO
   (RenderDevice*                   rd,
    const AmbientOcclusionSettings& settings,
    const shared_ptr<Texture>&      depthBuffer,
    const Vector3&                  clipConstant,
    const Vector4&                  projConstant,
    const float                     projScale,
    const shared_ptr<Texture>&      csZBuffer,
    const shared_ptr<Texture>&      peeledCSZBuffer,
    const shared_ptr<Texture>&      normalBuffer) {

    debugAssert(projScale > 0);
    m_rawAOFramebuffer->set(Framebuffer::DEPTH, depthBuffer);
    rd->push2D(m_rawAOFramebuffer); {

        // For quick early-out testing vs. skybox 
        rd->setDepthTest(RenderDevice::DEPTH_GREATER);

        // Values that are never touched due to the depth test will be white
        rd->setColorClearValue(Color3::white());
        rd->clear(true, false, false);
        Args args;

        args.setMacro(SYMBOL_NUM_SAMPLES,    settings.numSamples);
        args.setMacro(SYMBOL_NUM_SPIRAL_TURNS, settings.numSpiralTurns());
        args.setUniform(SYMBOL_radius,       settings.radius);
        args.setUniform(SYMBOL_bias,         settings.bias);
        args.setUniform(SYMBOL_clipInfo,     clipConstant);
        args.setUniform(SYMBOL_projInfo,     projConstant);
        args.setUniform(SYMBOL_projScale,    projScale);
        args.setUniform(SYMBOL_CS_Z_buffer,  csZBuffer, cszSamplerSettings());
        args.setUniform(SYMBOL_intensityDivR6, (float)(settings.intensity / powf(settings.radius, 6.0f)));
        args.setUniform(SYMBOL_intensity, settings.intensity);
        args.setUniform(SYMBOL_radius2, square(settings.radius));
        args.setUniform(SYMBOL_invRadius2, 1.0f / square(settings.radius));
        args.setMacro("TEMPORALLY_VARY_SAMPLES", settings.temporallyVarySamples);
        
        const bool useDepthPeel = settings.useDepthPeelBuffer;
        args.setMacro(SYMBOL_USE_DEPTH_PEEL, useDepthPeel ? 1 : 0);
        if ( useDepthPeel ) {
            bool cszPackedTogether = csZBuffer == peeledCSZBuffer;
            args.setMacro(SYMBOL_CS_Z_PACKED_TOGETHER, cszPackedTogether ? 1 : 0);
            if ( !cszPackedTogether ) {
                args.setUniform(SYMBOL_peeled_CS_Z_buffer, peeledCSZBuffer, cszSamplerSettings());
                const Vector2& peeledExtent     = peeledCSZBuffer->rect2DBounds().extent();
                const Vector2& unpeeledExtent   = csZBuffer->rect2DBounds().extent();
                bool differingDepthExtents = peeledExtent != unpeeledExtent;
                args.setMacro(SYMBOL_DIFFERENT_DEPTH_RESOLUTIONS, differingDepthExtents ? 1 : 0);
                if (differingDepthExtents) {
                    //TODO: only calculate one dimension in the first place
                    args.setUniform(SYMBOL_peeledToUnpeeledScale, (peeledExtent / unpeeledExtent).x);
                }
            } else {
                args.setMacro(SYMBOL_DIFFERENT_DEPTH_RESOLUTIONS, 0);
            }
        } else {
            args.setMacro(SYMBOL_CS_Z_PACKED_TOGETHER, 0);
            args.setMacro(SYMBOL_DIFFERENT_DEPTH_RESOLUTIONS, 0);
        }
        
        if (settings.useNormalBuffer && notNull(normalBuffer)) {
            debugAssertM(normalBuffer->encoding().frame == FrameName::CAMERA, "AmbientOcclusion expects camera-space normals");
            normalBuffer->setShaderArgs(args, "normal_", Sampler::buffer());
        }

        rd->setClip2D(Rect2D::xyxy((float)m_guardBandSize, (float)m_guardBandSize, rd->viewport().width() - m_guardBandSize, rd->viewport().height() - m_guardBandSize));
        args.setRect(rd->viewport());
        LAUNCH_SHADER("AmbientOcclusion_AO.*", args);
    } rd->pop2D();
}