void DeferredLightingEffect::render(const render::RenderContextPointer& renderContext) { auto args = renderContext->args; gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { // Allocate the parameters buffer used by all the deferred shaders if (!_deferredTransformBuffer[0]._buffer) { DeferredTransform parameters; _deferredTransformBuffer[0] = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(DeferredTransform), (const gpu::Byte*) ¶meters)); _deferredTransformBuffer[1] = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(DeferredTransform), (const gpu::Byte*) ¶meters)); } // Framebuffer copy operations cannot function as multipass stereo operations. batch.enableStereo(false); // perform deferred lighting, rendering to free fbo auto framebufferCache = DependencyManager::get<FramebufferCache>(); auto textureCache = DependencyManager::get<TextureCache>(); QSize framebufferSize = framebufferCache->getFrameBufferSize(); // binding the first framebuffer auto lightingFBO = framebufferCache->getLightingFramebuffer(); batch.setFramebuffer(lightingFBO); batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); // Bind the G-Buffer surfaces batch.setResourceTexture(DEFERRED_BUFFER_COLOR_UNIT, framebufferCache->getDeferredColorTexture()); batch.setResourceTexture(DEFERRED_BUFFER_NORMAL_UNIT, framebufferCache->getDeferredNormalTexture()); batch.setResourceTexture(DEFERRED_BUFFER_EMISSIVE_UNIT, framebufferCache->getDeferredSpecularTexture()); batch.setResourceTexture(DEFERRED_BUFFER_DEPTH_UNIT, framebufferCache->getPrimaryDepthTexture()); // FIXME: Different render modes should have different tasks if (args->_renderMode == RenderArgs::DEFAULT_RENDER_MODE && _ambientOcclusionEnabled) { batch.setResourceTexture(DEFERRED_BUFFER_OBSCURANCE_UNIT, framebufferCache->getOcclusionTexture()); } else { // need to assign the white texture if ao is off batch.setResourceTexture(DEFERRED_BUFFER_OBSCURANCE_UNIT, textureCache->getWhiteTexture()); } assert(_lightStage.lights.size() > 0); const auto& globalShadow = _lightStage.lights[0]->shadow; // Bind the shadow buffer batch.setResourceTexture(SHADOW_MAP_UNIT, globalShadow.map); // THe main viewport is assumed to be the mono viewport (or the 2 stereo faces side by side within that viewport) auto monoViewport = args->_viewport; float sMin = args->_viewport.x / (float)framebufferSize.width(); float sWidth = args->_viewport.z / (float)framebufferSize.width(); float tMin = args->_viewport.y / (float)framebufferSize.height(); float tHeight = args->_viewport.w / (float)framebufferSize.height(); // The view frustum is the mono frustum base auto viewFrustum = args->_viewFrustum; // Eval the mono projection mat4 monoProjMat; viewFrustum->evalProjectionMatrix(monoProjMat); // The mono view transform Transform monoViewTransform; viewFrustum->evalViewTransform(monoViewTransform); // THe mono view matrix coming from the mono view transform glm::mat4 monoViewMat; monoViewTransform.getMatrix(monoViewMat); // Running in stero ? bool isStereo = args->_context->isStereo(); int numPasses = 1; mat4 projMats[2]; Transform viewTransforms[2]; ivec4 viewports[2]; vec4 clipQuad[2]; vec2 screenBottomLeftCorners[2]; vec2 screenTopRightCorners[2]; vec4 fetchTexcoordRects[2]; DeferredTransform deferredTransforms[2]; auto geometryCache = DependencyManager::get<GeometryCache>(); if (isStereo) { numPasses = 2; mat4 eyeViews[2]; args->_context->getStereoProjections(projMats); args->_context->getStereoViews(eyeViews); float halfWidth = 0.5f * sWidth; for (int i = 0; i < numPasses; i++) { // In stereo, the 2 sides are layout side by side in the mono viewport and their width is half int sideWidth = monoViewport.z >> 1; viewports[i] = ivec4(monoViewport.x + (i * sideWidth), monoViewport.y, sideWidth, monoViewport.w); deferredTransforms[i].projection = projMats[i]; auto sideViewMat = monoViewMat * glm::inverse(eyeViews[i]); viewTransforms[i].evalFromRawMatrix(sideViewMat); deferredTransforms[i].viewInverse = sideViewMat; deferredTransforms[i].stereoSide = (i == 0 ? -1.0f : 1.0f); clipQuad[i] = glm::vec4(sMin + i * halfWidth, tMin, halfWidth, tHeight); screenBottomLeftCorners[i] = glm::vec2(-1.0f + i * 1.0f, -1.0f); screenTopRightCorners[i] = glm::vec2(i * 1.0f, 1.0f); fetchTexcoordRects[i] = glm::vec4(sMin + i * halfWidth, tMin, halfWidth, tHeight); } } else { viewports[0] = monoViewport; projMats[0] = monoProjMat; deferredTransforms[0].projection = monoProjMat; deferredTransforms[0].viewInverse = monoViewMat; viewTransforms[0] = monoViewTransform; deferredTransforms[0].stereoSide = 0.0f; clipQuad[0] = glm::vec4(sMin, tMin, sWidth, tHeight); screenBottomLeftCorners[0] = glm::vec2(-1.0f, -1.0f); screenTopRightCorners[0] = glm::vec2(1.0f, 1.0f); fetchTexcoordRects[0] = glm::vec4(sMin, tMin, sWidth, tHeight); } auto eyePoint = viewFrustum->getPosition(); float nearRadius = glm::distance(eyePoint, viewFrustum->getNearTopLeft()); for (int side = 0; side < numPasses; side++) { // Render in this side's viewport batch.setViewportTransform(viewports[side]); batch.setStateScissorRect(viewports[side]); // Sync and Bind the correct DeferredTransform ubo _deferredTransformBuffer[side]._buffer->setSubData(0, sizeof(DeferredTransform), (const gpu::Byte*) &deferredTransforms[side]); batch.setUniformBuffer(_directionalLightLocations->deferredTransformBuffer, _deferredTransformBuffer[side]); glm::vec2 topLeft(-1.0f, -1.0f); glm::vec2 bottomRight(1.0f, 1.0f); glm::vec2 texCoordTopLeft(clipQuad[side].x, clipQuad[side].y); glm::vec2 texCoordBottomRight(clipQuad[side].x + clipQuad[side].z, clipQuad[side].y + clipQuad[side].w); // First Global directional light and ambient pass { auto& program = _shadowMapEnabled ? _directionalLightShadow : _directionalLight; LightLocationsPtr locations = _shadowMapEnabled ? _directionalLightShadowLocations : _directionalLightLocations; const auto& keyLight = _allocatedLights[_globalLights.front()]; // Setup the global directional pass pipeline { if (_shadowMapEnabled) { if (keyLight->getAmbientMap()) { program = _directionalSkyboxLightShadow; locations = _directionalSkyboxLightShadowLocations; } else { program = _directionalAmbientSphereLightShadow; locations = _directionalAmbientSphereLightShadowLocations; } } else { if (keyLight->getAmbientMap()) { program = _directionalSkyboxLight; locations = _directionalSkyboxLightLocations; } else { program = _directionalAmbientSphereLight; locations = _directionalAmbientSphereLightLocations; } } if (locations->shadowTransformBuffer >= 0) { batch.setUniformBuffer(locations->shadowTransformBuffer, globalShadow.getBuffer()); } batch.setPipeline(program); } { // Setup the global lighting setupKeyLightBatch(batch, locations->lightBufferUnit, SKYBOX_MAP_UNIT); } { batch.setModelTransform(Transform()); batch.setProjectionTransform(glm::mat4()); batch.setViewTransform(Transform()); glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f); geometryCache->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, color); } if (keyLight->getAmbientMap()) { batch.setResourceTexture(SKYBOX_MAP_UNIT, nullptr); } } auto texcoordMat = glm::mat4(); /* texcoordMat[0] = glm::vec4(sWidth / 2.0f, 0.0f, 0.0f, sMin + sWidth / 2.0f); texcoordMat[1] = glm::vec4(0.0f, tHeight / 2.0f, 0.0f, tMin + tHeight / 2.0f); */ texcoordMat[0] = glm::vec4(fetchTexcoordRects[side].z / 2.0f, 0.0f, 0.0f, fetchTexcoordRects[side].x + fetchTexcoordRects[side].z / 2.0f); texcoordMat[1] = glm::vec4(0.0f, fetchTexcoordRects[side].w / 2.0f, 0.0f, fetchTexcoordRects[side].y + fetchTexcoordRects[side].w / 2.0f); texcoordMat[2] = glm::vec4(0.0f, 0.0f, 1.0f, 0.0f); texcoordMat[3] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); // enlarge the scales slightly to account for tesselation const float SCALE_EXPANSION = 0.05f; batch.setProjectionTransform(projMats[side]); batch.setViewTransform(viewTransforms[side]); // Splat Point lights if (!_pointLights.empty()) { batch.setPipeline(_pointLight); batch._glUniformMatrix4fv(_pointLightLocations->texcoordMat, 1, false, reinterpret_cast< const float* >(&texcoordMat)); for (auto lightID : _pointLights) { auto& light = _allocatedLights[lightID]; // IN DEBUG: light->setShowContour(true); batch.setUniformBuffer(_pointLightLocations->lightBufferUnit, light->getSchemaBuffer()); float expandedRadius = light->getMaximumRadius() * (1.0f + SCALE_EXPANSION); // TODO: We shouldn;t have to do that test and use a different volume geometry for when inside the vlight volume, // we should be able to draw thre same geometry use DepthClamp but for unknown reason it's s not working... if (glm::distance(eyePoint, glm::vec3(light->getPosition())) < expandedRadius + nearRadius) { Transform model; model.setTranslation(glm::vec3(0.0f, 0.0f, -1.0f)); batch.setModelTransform(model); batch.setViewTransform(Transform()); batch.setProjectionTransform(glm::mat4()); glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f); DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, color); batch.setProjectionTransform(projMats[side]); batch.setViewTransform(viewTransforms[side]); } else { Transform model; model.setTranslation(glm::vec3(light->getPosition().x, light->getPosition().y, light->getPosition().z)); batch.setModelTransform(model.postScale(expandedRadius)); batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f); geometryCache->renderSphere(batch); } } } // Splat spot lights if (!_spotLights.empty()) { batch.setPipeline(_spotLight); batch._glUniformMatrix4fv(_spotLightLocations->texcoordMat, 1, false, reinterpret_cast< const float* >(&texcoordMat)); for (auto lightID : _spotLights) { auto light = _allocatedLights[lightID]; // IN DEBUG: light->setShowContour(true); batch.setUniformBuffer(_spotLightLocations->lightBufferUnit, light->getSchemaBuffer()); auto eyeLightPos = eyePoint - light->getPosition(); auto eyeHalfPlaneDistance = glm::dot(eyeLightPos, light->getDirection()); const float TANGENT_LENGTH_SCALE = 0.666f; glm::vec4 coneParam(light->getSpotAngleCosSin(), TANGENT_LENGTH_SCALE * tanf(0.5f * light->getSpotAngle()), 1.0f); float expandedRadius = light->getMaximumRadius() * (1.0f + SCALE_EXPANSION); // TODO: We shouldn;t have to do that test and use a different volume geometry for when inside the vlight volume, // we should be able to draw thre same geometry use DepthClamp but for unknown reason it's s not working... if ((eyeHalfPlaneDistance > -nearRadius) && (glm::distance(eyePoint, glm::vec3(light->getPosition())) < expandedRadius + nearRadius)) { coneParam.w = 0.0f; batch._glUniform4fv(_spotLightLocations->coneParam, 1, reinterpret_cast< const float* >(&coneParam)); Transform model; model.setTranslation(glm::vec3(0.0f, 0.0f, -1.0f)); batch.setModelTransform(model); batch.setViewTransform(Transform()); batch.setProjectionTransform(glm::mat4()); glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f); DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, color); batch.setProjectionTransform( projMats[side]); batch.setViewTransform(viewTransforms[side]); } else { coneParam.w = 1.0f; batch._glUniform4fv(_spotLightLocations->coneParam, 1, reinterpret_cast< const float* >(&coneParam)); Transform model; model.setTranslation(light->getPosition()); model.postRotate(light->getOrientation()); model.postScale(glm::vec3(expandedRadius, expandedRadius, expandedRadius)); batch.setModelTransform(model); auto mesh = getSpotLightMesh(); batch.setIndexBuffer(mesh->getIndexBuffer()); batch.setInputBuffer(0, mesh->getVertexBuffer()); batch.setInputFormat(mesh->getVertexFormat()); auto& part = mesh->getPartBuffer().get<model::Mesh::Part>(); batch.drawIndexed(model::Mesh::topologyToPrimitive(part._topology), part._numIndices, part._startIndex); } } } } // Probably not necessary in the long run because the gpu layer would unbound this texture if used as render target batch.setResourceTexture(DEFERRED_BUFFER_COLOR_UNIT, nullptr); batch.setResourceTexture(DEFERRED_BUFFER_NORMAL_UNIT, nullptr); batch.setResourceTexture(DEFERRED_BUFFER_EMISSIVE_UNIT, nullptr); batch.setResourceTexture(DEFERRED_BUFFER_DEPTH_UNIT, nullptr); batch.setResourceTexture(DEFERRED_BUFFER_OBSCURANCE_UNIT, nullptr); batch.setResourceTexture(SHADOW_MAP_UNIT, nullptr); batch.setResourceTexture(SKYBOX_MAP_UNIT, nullptr); batch.setUniformBuffer(_directionalLightLocations->deferredTransformBuffer, nullptr); });
void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) { assert(renderContext->getArgs()); assert(renderContext->getArgs()->_viewFrustum); RenderArgs* args = renderContext->getArgs(); gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { auto framebufferCache = DependencyManager::get<FramebufferCache>(); QSize framebufferSize = framebufferCache->getFrameBufferSize(); float fbWidth = framebufferSize.width(); float fbHeight = framebufferSize.height(); float sMin = args->_viewport.x / fbWidth; float sWidth = args->_viewport.z / fbWidth; float tMin = args->_viewport.y / fbHeight; float tHeight = args->_viewport.w / fbHeight; glm::mat4 projMat; Transform viewMat; args->_viewFrustum->evalProjectionMatrix(projMat); args->_viewFrustum->evalViewTransform(viewMat); batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); batch.setModelTransform(Transform()); // Occlusion step getOcclusionPipeline(); batch.setResourceTexture(0, framebufferCache->getPrimaryDepthTexture()); batch.setResourceTexture(1, framebufferCache->getDeferredNormalTexture()); _occlusionBuffer->setRenderBuffer(0, _occlusionTexture); batch.setFramebuffer(_occlusionBuffer); // Occlusion uniforms g_scale = 1.0f; g_bias = 1.0f; g_sample_rad = 1.0f; g_intensity = 1.0f; // Bind the first gpu::Pipeline we need - for calculating occlusion buffer batch.setPipeline(getOcclusionPipeline()); batch._glUniform1f(_gScaleLoc, g_scale); batch._glUniform1f(_gBiasLoc, g_bias); batch._glUniform1f(_gSampleRadiusLoc, g_sample_rad); batch._glUniform1f(_gIntensityLoc, g_intensity); // setup uniforms for unpacking a view-space position from the depth buffer // This is code taken from DeferredLightEffect.render() method in DeferredLightingEffect.cpp. // DeferredBuffer.slh shows how the unpacking is done and what variables are needed. // initialize the view-space unpacking uniforms using frustum data float left, right, bottom, top, nearVal, farVal; glm::vec4 nearClipPlane, farClipPlane; args->_viewFrustum->computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); float depthScale = (farVal - nearVal) / farVal; float nearScale = -1.0f / nearVal; float depthTexCoordScaleS = (right - left) * nearScale / sWidth; float depthTexCoordScaleT = (top - bottom) * nearScale / tHeight; float depthTexCoordOffsetS = left * nearScale - sMin * depthTexCoordScaleS; float depthTexCoordOffsetT = bottom * nearScale - tMin * depthTexCoordScaleT; // now set the position-unpacking unforms batch._glUniform1f(_nearLoc, nearVal); batch._glUniform1f(_depthScaleLoc, depthScale); batch._glUniform2f(_depthTexCoordOffsetLoc, depthTexCoordOffsetS, depthTexCoordOffsetT); batch._glUniform2f(_depthTexCoordScaleLoc, depthTexCoordScaleS, depthTexCoordScaleT); batch._glUniform2f(_renderTargetResLoc, fbWidth, fbHeight); batch._glUniform2f(_renderTargetResInvLoc, 1.0f / fbWidth, 1.0f / fbHeight); glm::vec4 color(0.0f, 0.0f, 0.0f, 1.0f); glm::vec2 bottomLeft(-1.0f, -1.0f); glm::vec2 topRight(1.0f, 1.0f); glm::vec2 texCoordTopLeft(0.0f, 0.0f); glm::vec2 texCoordBottomRight(1.0f, 1.0f); DependencyManager::get<GeometryCache>()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color); // Vertical blur step getVBlurPipeline(); batch.setResourceTexture(0, _occlusionTexture); _vBlurBuffer->setRenderBuffer(0, _vBlurTexture); batch.setFramebuffer(_vBlurBuffer); // Bind the second gpu::Pipeline we need - for calculating blur buffer batch.setPipeline(getVBlurPipeline()); DependencyManager::get<GeometryCache>()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color); // Horizontal blur step getHBlurPipeline(); batch.setResourceTexture(0, _vBlurTexture); _hBlurBuffer->setRenderBuffer(0, _hBlurTexture); batch.setFramebuffer(_hBlurBuffer); // Bind the third gpu::Pipeline we need - for calculating blur buffer batch.setPipeline(getHBlurPipeline()); DependencyManager::get<GeometryCache>()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color); // Blend step getBlendPipeline(); batch.setResourceTexture(0, _hBlurTexture); batch.setFramebuffer(framebufferCache->getDeferredFramebuffer()); // Bind the fourth gpu::Pipeline we need - for blending the primary color buffer with blurred occlusion texture batch.setPipeline(getBlendPipeline()); DependencyManager::get<GeometryCache>()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color); }); }