Merge pull request #6837 from zzmp/feat/render-shadow

Add RenderShadowTask
This commit is contained in:
samcake 2016-01-18 15:45:51 -08:00
commit 29945bba92
36 changed files with 889 additions and 285 deletions

View file

@ -188,6 +188,7 @@ Item {
visible: root.expanded;
text: "\tItems Rendered Opaque: " + root.opaqueRendered +
" / Translucent: " + root.translucentRendered +
" / Shadow: " + root.shadowRendered +
" / Other: " + root.otherRendered;
}
Text {
@ -198,6 +199,14 @@ Item {
" / Out of view: " + root.opaqueOutOfView +
" / Too small: " + root.opaqueTooSmall;
}
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
visible: root.expanded;
text: "\tShadow considered: " + root.shadowConsidered +
" / Out of view: " + root.shadowOutOfView +
" / Too small: " + root.shadowTooSmall;
}
Text {
color: root.fontColor;
font.pixelSize: root.fontSize

View file

@ -348,6 +348,10 @@ void Stats::setRenderDetails(const RenderDetails& details) {
STAT_UPDATE(opaqueOutOfView, details._opaque._outOfView);
STAT_UPDATE(opaqueTooSmall, details._opaque._tooSmall);
STAT_UPDATE(opaqueRendered, (int)details._opaque._rendered);
STAT_UPDATE(shadowConsidered, (int)details._shadow._considered);
STAT_UPDATE(shadowOutOfView, details._shadow._outOfView);
STAT_UPDATE(shadowTooSmall, details._shadow._tooSmall);
STAT_UPDATE(shadowRendered, (int)details._shadow._rendered);
STAT_UPDATE(translucentConsidered, (int)details._translucent._considered);
STAT_UPDATE(translucentOutOfView, details._translucent._outOfView);
STAT_UPDATE(translucentTooSmall, details._translucent._tooSmall);

View file

@ -64,6 +64,10 @@ class Stats : public QQuickItem {
STATS_PROPERTY(int, opaqueOutOfView, 0)
STATS_PROPERTY(int, opaqueTooSmall, 0)
STATS_PROPERTY(int, opaqueRendered, 0)
STATS_PROPERTY(int, shadowConsidered, 0)
STATS_PROPERTY(int, shadowOutOfView, 0)
STATS_PROPERTY(int, shadowTooSmall, 0)
STATS_PROPERTY(int, shadowRendered, 0)
STATS_PROPERTY(int, translucentConsidered, 0)
STATS_PROPERTY(int, translucentOutOfView, 0)
STATS_PROPERTY(int, translucentTooSmall, 0)
@ -151,6 +155,10 @@ signals:
void opaqueOutOfViewChanged();
void opaqueTooSmallChanged();
void opaqueRenderedChanged();
void shadowConsideredChanged();
void shadowOutOfViewChanged();
void shadowTooSmallChanged();
void shadowRenderedChanged();
void translucentConsideredChanged();
void translucentOutOfViewChanged();
void translucentTooSmallChanged();

View file

@ -49,7 +49,9 @@ Framebuffer* Framebuffer::create( const Format& colorBufferFormat, const Format&
Framebuffer* Framebuffer::createShadowmap(uint16 width) {
auto framebuffer = Framebuffer::create();
auto depthTexture = TexturePointer(Texture::create2D(Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH), width, width));
auto depthFormat = Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); // Depth32 texel format
auto depthTexture = TexturePointer(Texture::create2D(depthFormat, width, width));
Sampler::Desc samplerDesc;
samplerDesc._borderColor = glm::vec4(1.0f);
@ -59,8 +61,11 @@ Framebuffer* Framebuffer::createShadowmap(uint16 width) {
samplerDesc._comparisonFunc = LESS_EQUAL;
depthTexture->setSampler(Sampler(samplerDesc));
framebuffer->setDepthStencilBuffer(depthTexture, depthFormat);
framebuffer->setDepthStencilBuffer(depthTexture, Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH));
// Use a render buffer to allow use of the DebugDeferredBuffer Job
gpu::TexturePointer colorbuffer{gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, width)};
framebuffer->setRenderBuffer(0, colorbuffer);
return framebuffer;
}

View file

@ -51,7 +51,7 @@ void GLBackend::do_setViewportTransform(Batch& batch, size_t paramOffset) {
void GLBackend::do_setDepthRangeTransform(Batch& batch, size_t paramOffset) {
Vec2 depthRange(batch._params[paramOffset + 0]._float, batch._params[paramOffset + 1]._float);
Vec2 depthRange(batch._params[paramOffset + 1]._float, batch._params[paramOffset + 0]._float);
if ((depthRange.x != _transform._depthRange.x) || (depthRange.y != _transform._depthRange.y)) {
_transform._depthRange = depthRange;

View file

@ -736,6 +736,23 @@ void ViewFrustum::getFurthestPointFromCamera(const AACube& box, glm::vec3& furth
}
}
const ViewFrustum::Corners ViewFrustum::getCorners(const float& depth) {
glm::vec3 normal = glm::normalize(_direction);
auto getCorner = [&](enum::BoxVertex nearCorner, enum::BoxVertex farCorner) {
auto dir = glm::normalize(_cornersWorld[nearCorner] - _cornersWorld[farCorner]);
auto factor = depth / glm::dot(dir, normal);
return _position + factor * dir;
};
return Corners{
getCorner(TOP_LEFT_NEAR, TOP_LEFT_FAR),
getCorner(TOP_RIGHT_NEAR, TOP_RIGHT_FAR),
getCorner(BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR),
getCorner(BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR)
};
}
float ViewFrustum::distanceToCamera(const glm::vec3& point) const {
glm::vec3 temp = getPosition() - point;
float distanceToPoint = sqrtf(glm::dot(temp, temp));

View file

@ -61,11 +61,23 @@ public:
float getFarClip() const { return _farClip; }
float getFocalLength() const { return _focalLength; }
class Corners {
public:
Corners(glm::vec3&& topLeft, glm::vec3&& topRight, glm::vec3&& bottomLeft, glm::vec3&& bottomRight)
: topLeft{ topLeft }, topRight{ topRight }, bottomLeft{ bottomLeft }, bottomRight{ bottomRight } {}
glm::vec3 topLeft;
glm::vec3 topRight;
glm::vec3 bottomLeft;
glm::vec3 bottomRight;
// Get the corners depth units from frustum position, along frustum orientation
};
const Corners getCorners(const float& depth);
// getters for corners
const glm::vec3& getFarTopLeft() const { return _cornersWorld[TOP_LEFT_FAR]; }
const glm::vec3& getFarTopRight() const { return _cornersWorld[TOP_RIGHT_FAR]; }
const glm::vec3& getFarBottomLeft() const { return _cornersWorld[BOTTOM_LEFT_FAR]; }
const glm::vec3& getFarBottomRight() const { return _cornersWorld[BOTTOM_RIGHT_FAR]; }
const glm::vec3& getNearTopLeft() const { return _cornersWorld[TOP_LEFT_NEAR]; }
const glm::vec3& getNearTopRight() const { return _cornersWorld[TOP_RIGHT_NEAR]; }
const glm::vec3& getNearBottomLeft() const { return _cornersWorld[BOTTOM_LEFT_NEAR]; }
@ -156,6 +168,7 @@ private:
// Used to project points
glm::mat4 _ourModelViewProjectionMatrix;
};
using ViewFrustumPointer = std::shared_ptr<ViewFrustum>;
float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale);

View file

@ -20,6 +20,7 @@
#include "GeometryCache.h"
#include "FramebufferCache.h"
#include "DeferredLightingEffect.h"
#include "debug_deferred_buffer_vert.h"
#include "debug_deferred_buffer_frag.h"
@ -31,45 +32,53 @@ enum Slots {
Normal,
Specular,
Depth,
Lighting
Lighting,
Shadow
};
static const std::string DEEFAULT_DIFFUSE_SHADER {
static const std::string DEFAULT_DIFFUSE_SHADER {
"vec4 getFragmentColor() {"
" return vec4(pow(texture(diffuseMap, uv).xyz, vec3(1.0 / 2.2)), 1.0);"
" }"
};
static const std::string DEEFAULT_ALPHA_SHADER {
static const std::string DEFAULT_ALPHA_SHADER {
"vec4 getFragmentColor() {"
" return vec4(vec3(texture(diffuseMap, uv).a), 1.0);"
" }"
};
static const std::string DEEFAULT_SPECULAR_SHADER {
static const std::string DEFAULT_SPECULAR_SHADER {
"vec4 getFragmentColor() {"
" return vec4(texture(specularMap, uv).xyz, 1.0);"
" }"
};
static const std::string DEEFAULT_ROUGHNESS_SHADER {
static const std::string DEFAULT_ROUGHNESS_SHADER {
"vec4 getFragmentColor() {"
" return vec4(vec3(texture(specularMap, uv).a), 1.0);"
" }"
};
static const std::string DEEFAULT_NORMAL_SHADER {
static const std::string DEFAULT_NORMAL_SHADER {
"vec4 getFragmentColor() {"
" return vec4(normalize(texture(normalMap, uv).xyz), 1.0);"
" }"
};
static const std::string DEEFAULT_DEPTH_SHADER {
static const std::string DEFAULT_DEPTH_SHADER {
"vec4 getFragmentColor() {"
" return vec4(vec3(texture(depthMap, uv).x), 1.0);"
" }"
};
static const std::string DEEFAULT_LIGHTING_SHADER {
static const std::string DEFAULT_LIGHTING_SHADER {
"vec4 getFragmentColor() {"
" return vec4(pow(texture(lightingMap, uv).xyz, vec3(1.0 / 2.2)), 1.0);"
" }"
};
static const std::string DEEFAULT_CUSTOM_SHADER {
static const std::string DEFAULT_SHADOW_SHADER {
"uniform sampler2D shadowMapColor;"
// The actual shadowMap is a sampler2DShadow, so we cannot normally sample it
"vec4 getFragmentColor() {"
" return vec4(texture(shadowMapColor, uv).xyz, 1.0);"
" }"
};
static const std::string DEFAULT_CUSTOM_SHADER {
"vec4 getFragmentColor() {"
" return vec4(1.0, 0.0, 0.0, 1.0);"
" }"
@ -98,21 +107,23 @@ DebugDeferredBuffer::DebugDeferredBuffer() {
std::string DebugDeferredBuffer::getShaderSourceCode(Modes mode, std::string customFile) {
switch (mode) {
case DiffuseMode:
return DEEFAULT_DIFFUSE_SHADER;
return DEFAULT_DIFFUSE_SHADER;
case AlphaMode:
return DEEFAULT_ALPHA_SHADER;
return DEFAULT_ALPHA_SHADER;
case SpecularMode:
return DEEFAULT_SPECULAR_SHADER;
return DEFAULT_SPECULAR_SHADER;
case RoughnessMode:
return DEEFAULT_ROUGHNESS_SHADER;
return DEFAULT_ROUGHNESS_SHADER;
case NormalMode:
return DEEFAULT_NORMAL_SHADER;
return DEFAULT_NORMAL_SHADER;
case DepthMode:
return DEEFAULT_DEPTH_SHADER;
return DEFAULT_DEPTH_SHADER;
case LightingMode:
return DEEFAULT_LIGHTING_SHADER;
return DEFAULT_LIGHTING_SHADER;
case ShadowMode:
return DEFAULT_SHADOW_SHADER;
case CustomMode:
return getFileContent(customFile, DEEFAULT_CUSTOM_SHADER);
return getFileContent(customFile, DEFAULT_CUSTOM_SHADER);
}
Q_UNREACHABLE();
return std::string();
@ -158,6 +169,7 @@ const gpu::PipelinePointer& DebugDeferredBuffer::getPipeline(Modes mode, std::st
slotBindings.insert(gpu::Shader::Binding("specularMap", Specular));
slotBindings.insert(gpu::Shader::Binding("depthMap", Depth));
slotBindings.insert(gpu::Shader::Binding("lightingMap", Lighting));
slotBindings.insert(gpu::Shader::Binding("shadowMapColor", Shadow));
gpu::Shader::makeProgram(*program, slotBindings);
auto pipeline = gpu::Pipeline::create(program, std::make_shared<gpu::State>());
@ -182,10 +194,18 @@ void DebugDeferredBuffer::run(const SceneContextPointer& sceneContext, const Ren
assert(renderContext->getArgs());
assert(renderContext->getArgs()->_viewFrustum);
RenderArgs* args = renderContext->getArgs();
// Guard against unspecified modes
auto mode = renderContext->_deferredDebugMode;
if (mode > (int)CustomMode) {
renderContext->_deferredDebugMode = -1;
return;
}
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
const auto geometryBuffer = DependencyManager::get<GeometryCache>();
const auto framebufferCache = DependencyManager::get<FramebufferCache>();
const auto& lightStage = DependencyManager::get<DeferredLightingEffect>()->getLightStage();
glm::mat4 projMat;
Transform viewMat;
@ -205,6 +225,7 @@ void DebugDeferredBuffer::run(const SceneContextPointer& sceneContext, const Ren
batch.setResourceTexture(Specular, framebufferCache->getDeferredSpecularTexture());
batch.setResourceTexture(Depth, framebufferCache->getPrimaryDepthTexture());
batch.setResourceTexture(Lighting, framebufferCache->getLightingTexture());
batch.setResourceTexture(Shadow, lightStage.lights[0]->shadow.framebuffer->getRenderBuffer(0));
const glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f);
const glm::vec2 bottomLeft(renderContext->_deferredDebugSize.x, renderContext->_deferredDebugSize.y);

View file

@ -33,6 +33,7 @@ private:
NormalMode,
DepthMode,
LightingMode,
ShadowMode,
CustomMode // Needs to stay last
};

View file

@ -31,6 +31,10 @@
#include "directional_ambient_light_frag.h"
#include "directional_skybox_light_frag.h"
#include "directional_light_shadow_frag.h"
#include "directional_ambient_light_shadow_frag.h"
#include "directional_skybox_light_shadow_frag.h"
#include "point_light_frag.h"
#include "spot_light_frag.h"
@ -42,25 +46,24 @@ struct LightLocations {
int texcoordMat;
int coneParam;
int deferredTransformBuffer;
int shadowTransformBuffer;
};
static void loadLightProgram(const char* vertSource, const char* fragSource, bool lightVolume, gpu::PipelinePointer& program, LightLocationsPtr& locations);
void DeferredLightingEffect::init() {
_directionalLightLocations = std::make_shared<LightLocations>();
_directionalAmbientSphereLightLocations = std::make_shared<LightLocations>();
_directionalSkyboxLightLocations = std::make_shared<LightLocations>();
_pointLightLocations = std::make_shared<LightLocations>();
_spotLightLocations = std::make_shared<LightLocations>();
// TODO: To use shadowmaps, replace directional_*_light_frag with directional_*_light_shadow_frag shaders.
loadLightProgram(deferred_light_vert, directional_light_frag, false, _directionalLight, _directionalLightLocations);
loadLightProgram(deferred_light_vert, directional_ambient_light_frag, false, _directionalAmbientSphereLight, _directionalAmbientSphereLightLocations);
loadLightProgram(deferred_light_vert, directional_skybox_light_frag, false, _directionalSkyboxLight, _directionalSkyboxLightLocations);
loadLightProgram(deferred_light_limited_vert, point_light_frag, true, _pointLight, _pointLightLocations);
loadLightProgram(deferred_light_spot_vert, spot_light_frag, true, _spotLight, _spotLightLocations);
@ -69,6 +72,8 @@ void DeferredLightingEffect::init() {
_allocatedLights.push_back(std::make_shared<model::Light>());
model::LightPointer lp = _allocatedLights[0];
// Add the global light to the light stage (for later shadow rendering)
_lightStage.addLight(lp);
lp->setDirection(-glm::vec3(1.0f, 1.0f, 1.0f));
lp->setColor(glm::vec3(1.0f));
@ -172,6 +177,12 @@ void DeferredLightingEffect::render(RenderArgs* args) {
batch.setResourceTexture(2, framebufferCache->getDeferredSpecularTexture());
batch.setResourceTexture(3, framebufferCache->getPrimaryDepthTexture());
assert(_lightStage.lights.size() > 0);
const auto& globalShadow = _lightStage.lights[0]->shadow;
// Bind the shadow buffer
batch.setResourceTexture(4, 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();
@ -291,6 +302,10 @@ void DeferredLightingEffect::render(RenderArgs* args) {
program = _directionalAmbientSphereLight;
locations = _directionalAmbientSphereLightLocations;
}
if (locations->shadowTransformBuffer >= 0) {
batch.setUniformBuffer(locations->shadowTransformBuffer, globalShadow.getBuffer());
}
batch.setPipeline(program);
}
@ -478,17 +493,18 @@ static void loadLightProgram(const char* vertSource, const char* fragSource, boo
slotBindings.insert(gpu::Shader::Binding(std::string("normalMap"), 1));
slotBindings.insert(gpu::Shader::Binding(std::string("specularMap"), 2));
slotBindings.insert(gpu::Shader::Binding(std::string("depthMap"), 3));
slotBindings.insert(gpu::Shader::Binding(std::string("shadowMap"), 4));
slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), 5));
static const int LIGHT_GPU_SLOT = 3;
slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), LIGHT_GPU_SLOT));
static const int ATMOSPHERE_GPU_SLOT = 4;
slotBindings.insert(gpu::Shader::Binding(std::string("atmosphereBufferUnit"), ATMOSPHERE_GPU_SLOT));
static const int DEFERRED_TRANSFORM_BUFFER_SLOT = 2;
slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), LIGHT_GPU_SLOT));
slotBindings.insert(gpu::Shader::Binding(std::string("atmosphereBufferUnit"), ATMOSPHERE_GPU_SLOT));
slotBindings.insert(gpu::Shader::Binding(std::string("deferredTransformBuffer"), DEFERRED_TRANSFORM_BUFFER_SLOT));
gpu::Shader::makeProgram(*program, slotBindings);
locations->radius = program->getUniforms().findLocation("radius");
locations->ambientSphere = program->getUniforms().findLocation("ambientSphere.L00");
@ -498,6 +514,7 @@ static void loadLightProgram(const char* vertSource, const char* fragSource, boo
locations->lightBufferUnit = program->getBuffers().findLocation("lightBuffer");
locations->atmosphereBufferUnit = program->getBuffers().findLocation("atmosphereBufferUnit");
locations->deferredTransformBuffer = program->getBuffers().findLocation("deferredTransformBuffer");
locations->shadowTransformBuffer = program->getBuffers().findLocation("shadowTransformBuffer");
auto state = std::make_shared<gpu::State>();
state->setColorWriteMask(true, true, true, false);

View file

@ -21,6 +21,8 @@
#include "model/Stage.h"
#include "model/Geometry.h"
#include "LightStage.h"
class RenderArgs;
struct LightLocations;
using LightLocationsPtr = std::shared_ptr<LightLocations>;
@ -48,10 +50,13 @@ public:
void setAmbientLightMode(int preset);
void setGlobalLight(const glm::vec3& direction, const glm::vec3& diffuse, float intensity, float ambientIntensity);
void setGlobalAtmosphere(const model::AtmospherePointer& atmosphere) { _atmosphere = atmosphere; }
void setGlobalSkybox(const model::SkyboxPointer& skybox);
const LightStage& getLightStage() { return _lightStage; }
private:
LightStage _lightStage;
DeferredLightingEffect() = default;
model::MeshPointer _spotLightMesh;
@ -71,26 +76,7 @@ private:
gpu::PipelinePointer _spotLight;
LightLocationsPtr _spotLightLocations;
class PointLight {
public:
glm::vec4 position;
float radius;
glm::vec4 ambient;
glm::vec4 diffuse;
glm::vec4 specular;
float constantAttenuation;
float linearAttenuation;
float quadraticAttenuation;
};
class SpotLight : public PointLight {
public:
glm::vec3 direction;
float exponent;
float cutoff;
};
typedef std::vector< model::LightPointer > Lights;
using Lights = std::vector<model::LightPointer>;
Lights _allocatedLights;
std::vector<int> _globalLights;

View file

@ -183,14 +183,6 @@ void FramebufferCache::releaseFramebuffer(const gpu::FramebufferPointer& framebu
}
}
gpu::FramebufferPointer FramebufferCache::getShadowFramebuffer() {
if (!_shadowFramebuffer) {
const int SHADOW_MAP_SIZE = 2048;
_shadowFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::createShadowmap(SHADOW_MAP_SIZE));
}
return _shadowFramebuffer;
}
gpu::FramebufferPointer FramebufferCache::getSelfieFramebuffer() {
if (!_selfieFramebuffer) {
createPrimaryFramebuffer();

View file

@ -23,6 +23,9 @@ class FramebufferCache : public Dependency {
SINGLETON_DEPENDENCY
public:
// Shadow map size is static
static const int SHADOW_MAP_SIZE = 2048;
/// Sets the desired texture resolution for the framebuffer objects.
void setFrameBufferSize(QSize frameBufferSize);
const QSize& getFrameBufferSize() const { return _frameBufferSize; }
@ -45,9 +48,6 @@ public:
gpu::TexturePointer getLightingTexture();
gpu::FramebufferPointer getLightingFramebuffer();
/// Returns the framebuffer object used to render shadow maps;
gpu::FramebufferPointer getShadowFramebuffer();
/// Returns the framebuffer object used to render selfie maps;
gpu::FramebufferPointer getSelfieFramebuffer();

View file

@ -0,0 +1,91 @@
//
// LightStage.cpp
// render-utils/src
//
// Created by Zach Pomerantz on 1/14/2015.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "ViewFrustum.h"
#include "LightStage.h"
LightStage::Shadow::Shadow(model::LightPointer light) : _light{ light}, _frustum{ std::make_shared<ViewFrustum>() } {
framebuffer = gpu::FramebufferPointer(gpu::Framebuffer::createShadowmap(MAP_SIZE));
map = framebuffer->getDepthStencilBuffer();
Schema schema;
_schemaBuffer = std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema);
}
void LightStage::Shadow::setKeylightFrustum(ViewFrustum* viewFrustum, float nearDepth, float farDepth) {
assert(nearDepth < farDepth);
// Orient the keylight frustum
const auto& direction = glm::normalize(_light->getDirection());
glm::quat orientation;
if (direction == IDENTITY_UP) {
orientation = glm::quat(glm::mat3(IDENTITY_RIGHT, IDENTITY_UP, IDENTITY_FRONT));
} else {
auto side = glm::normalize(glm::cross(direction, IDENTITY_UP));
auto up = glm::normalize(glm::cross(side, direction));
orientation = glm::quat_cast(glm::mat3(side, up, -direction));
}
_frustum->setOrientation(orientation);
// Position the keylight frustum
_frustum->setPosition(viewFrustum->getPosition() - (nearDepth + farDepth)*direction);
const Transform view{ _frustum->getView()};
const Transform viewInverse{ view.getInverseMatrix() };
viewFrustum->calculate();
auto nearCorners = viewFrustum->getCorners(nearDepth);
auto farCorners = viewFrustum->getCorners(farDepth);
vec3 min{ viewInverse.transform(nearCorners.bottomLeft) };
vec3 max{ min };
// Expand keylight frustum to fit view frustum
auto fitFrustum = [&min, &max, &viewInverse](const vec3& viewCorner) {
const auto corner = viewInverse.transform(viewCorner);
min.x = glm::min(min.x, corner.x);
min.y = glm::min(min.y, corner.y);
min.z = glm::min(min.z, corner.z);
max.x = glm::max(max.x, corner.x);
max.y = glm::max(max.y, corner.y);
max.z = glm::max(max.z, corner.z);
};
fitFrustum(nearCorners.bottomRight);
fitFrustum(nearCorners.topLeft);
fitFrustum(nearCorners.topRight);
fitFrustum(farCorners.bottomLeft);
fitFrustum(farCorners.bottomRight);
fitFrustum(farCorners.topLeft);
fitFrustum(farCorners.topRight);
glm::mat4 ortho = glm::ortho<float>(min.x, max.x, min.y, max.y, -max.z, -min.z);
_frustum->setProjection(ortho);
// Update the buffer
_schemaBuffer.edit<Schema>().projection = ortho;
_schemaBuffer.edit<Schema>().viewInverse = viewInverse.getMatrix();
}
const glm::mat4& LightStage::Shadow::getView() const {
return _frustum->getView();
}
const glm::mat4& LightStage::Shadow::getProjection() const {
return _frustum->getProjection();
}
const LightStage::LightPointer LightStage::addLight(model::LightPointer light) {
Shadow stageShadow{light};
LightPointer stageLight = std::make_shared<Light>(std::move(stageShadow));
lights.push_back(stageLight);
return stageLight;
}

View file

@ -0,0 +1,74 @@
//
// LightStage.h
// render-utils/src
//
// Created by Zach Pomerantz on 1/14/2015.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_render_utils_LightStage_h
#define hifi_render_utils_LightStage_h
#include "gpu/Framebuffer.h"
#include "model/Light.h"
class ViewFrustum;
// Light stage to set up light-related rendering tasks
class LightStage {
public:
class Shadow {
public:
using UniformBufferView = gpu::BufferView;
static const int MAP_SIZE = 1024;
Shadow(model::LightPointer light);
void setKeylightFrustum(ViewFrustum* viewFrustum, float nearDepth, float farDepth);
const std::shared_ptr<ViewFrustum> getFrustum() const { return _frustum; }
const glm::mat4& getView() const;
const glm::mat4& getProjection() const;
const UniformBufferView& getBuffer() const { return _schemaBuffer; }
gpu::FramebufferPointer framebuffer;
gpu::TexturePointer map;
protected:
model::LightPointer _light;
std::shared_ptr<ViewFrustum> _frustum;
class Schema {
public:
glm::mat4 projection;
glm::mat4 viewInverse;
glm::float32 bias = 0.005f;
glm::float32 scale = 1 / MAP_SIZE;
};
UniformBufferView _schemaBuffer = nullptr;
};
using ShadowPointer = std::shared_ptr<Shadow>;
class Light {
public:
Light(Shadow&& shadow) : shadow{ shadow } {}
model::LightPointer light;
Shadow shadow;
};
using LightPointer = std::shared_ptr<Light>;
using Lights = std::vector<LightPointer>;
const LightPointer addLight(model::LightPointer light);
// TODO: removeLight
Lights lights;
};
#endif

View file

@ -77,30 +77,28 @@ RenderDeferredTask::RenderDeferredTask() : Task() {
ShapePlumberPointer shapePlumber = std::make_shared<ShapePlumber>();
initDeferredPipelines(*shapePlumber);
// CPU only, create the list of renderedOpaques items
addJob<FetchItems>("FetchOpaque", FetchItems([](const RenderContextPointer& context, int count) {
// CPU: Fetch the renderOpaques
auto fetchedOpaques = addJob<FetchItems>("FetchOpaque", FetchItems([](const RenderContextPointer& context, int count) {
context->getItemsConfig().opaque.numFeed = count;
}));
addJob<CullItems<RenderDetails::OPAQUE_ITEM>>("CullOpaque", _jobs.back().getOutput());
addJob<DepthSortItems>("DepthSortOpaque", _jobs.back().getOutput());
auto& renderedOpaques = _jobs.back().getOutput();
auto culledOpaques = addJob<CullItems<RenderDetails::OPAQUE_ITEM>>("CullOpaque", fetchedOpaques);
auto opaques = addJob<DepthSortItems>("DepthSortOpaque", culledOpaques);
// CPU only, create the list of renderedTransparents items
addJob<FetchItems>("FetchTransparent", FetchItems(
auto fetchedTransparents = addJob<FetchItems>("FetchTransparent", FetchItems(
ItemFilter::Builder::transparentShape().withoutLayered(),
[](const RenderContextPointer& context, int count) {
context->getItemsConfig().transparent.numFeed = count;
}
));
addJob<CullItems<RenderDetails::TRANSLUCENT_ITEM>>("CullTransparent", _jobs.back().getOutput());
addJob<DepthSortItems>("DepthSortTransparent", _jobs.back().getOutput(), DepthSortItems(false));
auto& renderedTransparents = _jobs.back().getOutput();
));
auto culledTransparents = addJob<CullItems<RenderDetails::TRANSLUCENT_ITEM>>("CullTransparent", fetchedTransparents);
auto transparents = addJob<DepthSortItems>("DepthSortTransparent", culledTransparents, DepthSortItems(false));
// GPU Jobs: Start preparing the deferred and lighting buffer
addJob<PrepareDeferred>("PrepareDeferred");
// Render opaque objects in DeferredBuffer
addJob<DrawOpaqueDeferred>("DrawOpaqueDeferred", renderedOpaques, shapePlumber);
addJob<DrawOpaqueDeferred>("DrawOpaqueDeferred", opaques, shapePlumber);
// Once opaque is all rendered create stencil background
addJob<DrawStencilDeferred>("DrawOpaqueStencil");
@ -116,16 +114,16 @@ RenderDeferredTask::RenderDeferredTask() : Task() {
// AO job, to be revisited
addJob<AmbientOcclusion>("AmbientOcclusion");
_jobs.back().setEnabled(false);
_occlusionJobIndex = (int)_jobs.size() - 1;
enableJob(_occlusionJobIndex, false);
// AA job to be revisited
addJob<Antialiasing>("Antialiasing");
_jobs.back().setEnabled(false);
_antialiasingJobIndex = (int)_jobs.size() - 1;
enableJob(_antialiasingJobIndex, false);
// Render transparent objects forward in LigthingBuffer
addJob<DrawTransparentDeferred>("DrawTransparentDeferred", renderedTransparents, shapePlumber);
addJob<DrawTransparentDeferred>("DrawTransparentDeferred", transparents, shapePlumber);
// Lighting Buffer ready for tone mapping
addJob<ToneMappingDeferred>("ToneMapping");
@ -133,24 +131,24 @@ RenderDeferredTask::RenderDeferredTask() : Task() {
// Debugging Deferred buffer job
addJob<DebugDeferredBuffer>("DebugDeferredBuffer");
_jobs.back().setEnabled(false);
_drawDebugDeferredBufferIndex = (int)_jobs.size() - 1;
enableJob(_drawDebugDeferredBufferIndex, false);
// Status icon rendering job
{
// Grab a texture map representing the different status icons and assign that to the drawStatsuJob
auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg";
auto statusIconMap = DependencyManager::get<TextureCache>()->getImageTexture(iconMapPath);
addJob<DrawStatus>("DrawStatus", renderedOpaques, DrawStatus(statusIconMap));
_jobs.back().setEnabled(false);
addJob<DrawStatus>("DrawStatus", opaques, DrawStatus(statusIconMap));
_drawStatusJobIndex = (int)_jobs.size() - 1;
enableJob(_drawStatusJobIndex, false);
}
addJob<DrawOverlay3D>("DrawOverlay3D", shapePlumber);
addJob<HitEffect>("HitEffect");
_jobs.back().setEnabled(false);
_drawHitEffectJobIndex = (int)_jobs.size() -1;
enableJob(_drawHitEffectJobIndex, false);
addJob<Blit>("Blit");
}
@ -168,26 +166,15 @@ void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const Rend
return;
}
// Make sure we turn the deferred buffer debug on/off
setDrawDebugDeferredBuffer(renderContext->_deferredDebugMode);
// Make sure we turn the displayItemStatus on/off
setDrawItemStatus(renderContext->getDrawStatus());
// Make sure we display hit effect on screen, as desired from a script
setDrawHitEffect(renderContext->getDrawHitEffect());
// TODO: turn on/off AO through menu item
setOcclusionStatus(renderContext->getOcclusionStatus());
setAntialiasingStatus(renderContext->getFxaaStatus());
setToneMappingExposure(renderContext->getTone().exposure);
setToneMappingToneCurve(renderContext->getTone().toneCurve);
renderContext->getArgs()->_context->syncCache();
for (auto job : _jobs) {
job.run(sceneContext, renderContext);
}
@ -404,7 +391,7 @@ void DrawBackgroundDeferred::run(const SceneContextPointer& sceneContext, const
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat);
renderLights(sceneContext, renderContext, inItems);
renderItems(sceneContext, renderContext, inItems);
});
args->_batch = nullptr;
}

View file

@ -113,41 +113,32 @@ public:
class RenderDeferredTask : public render::Task {
public:
RenderDeferredTask();
int _drawDebugDeferredBufferIndex = -1;
int _drawStatusJobIndex = -1;
int _drawHitEffectJobIndex = -1;
int _drawDebugDeferredBufferIndex;
int _drawStatusJobIndex;
int _drawHitEffectJobIndex;
int _occlusionJobIndex;
int _antialiasingJobIndex;
int _toneMappingJobIndex;
void setDrawDebugDeferredBuffer(int draw) {
if (_drawDebugDeferredBufferIndex >= 0) {
_jobs[_drawDebugDeferredBufferIndex].setEnabled(draw >= 0);
}
}
bool doDrawDebugDeferredBuffer() const { if (_drawDebugDeferredBufferIndex >= 0) { return _jobs[_drawDebugDeferredBufferIndex].isEnabled(); } else { return false; } }
void setDrawDebugDeferredBuffer(int draw) { enableJob(_drawDebugDeferredBufferIndex, draw >= 0); }
bool doDrawDebugDeferredBuffer() const { return getEnableJob(_drawDebugDeferredBufferIndex); }
void setDrawItemStatus(int draw) {
if (_drawStatusJobIndex >= 0) {
_jobs[_drawStatusJobIndex].setEnabled(draw > 0);
}
}
bool doDrawItemStatus() const { if (_drawStatusJobIndex >= 0) { return _jobs[_drawStatusJobIndex].isEnabled(); } else { return false; } }
void setDrawItemStatus(int draw) { enableJob(_drawStatusJobIndex, draw > 0); }
bool doDrawItemStatus() const { return getEnableJob(_drawStatusJobIndex); }
void setDrawHitEffect(bool draw) { if (_drawHitEffectJobIndex >= 0) { _jobs[_drawHitEffectJobIndex].setEnabled(draw); } }
bool doDrawHitEffect() const { if (_drawHitEffectJobIndex >=0) { return _jobs[_drawHitEffectJobIndex].isEnabled(); } else { return false; } }
void setDrawHitEffect(bool draw) { enableJob(_drawHitEffectJobIndex, draw); }
bool doDrawHitEffect() const { return getEnableJob(_drawHitEffectJobIndex); }
int _occlusionJobIndex = -1;
void setOcclusionStatus(bool draw) { if (_occlusionJobIndex >= 0) { _jobs[_occlusionJobIndex].setEnabled(draw); } }
bool doOcclusionStatus() const { if (_occlusionJobIndex >= 0) { return _jobs[_occlusionJobIndex].isEnabled(); } else { return false; } }
void setOcclusionStatus(bool draw) { enableJob(_occlusionJobIndex, draw); }
bool doOcclusionStatus() const { return getEnableJob(_occlusionJobIndex); }
int _antialiasingJobIndex = -1;
void setAntialiasingStatus(bool draw) { if (_antialiasingJobIndex >= 0) { _jobs[_antialiasingJobIndex].setEnabled(draw); } }
bool doAntialiasingStatus() const { if (_antialiasingJobIndex >= 0) { return _jobs[_antialiasingJobIndex].isEnabled(); } else { return false; } }
void setAntialiasingStatus(bool draw) { enableJob(_antialiasingJobIndex, draw); }
bool doAntialiasingStatus() const { return getEnableJob(_antialiasingJobIndex); }
int _toneMappingJobIndex = -1;
void setToneMappingExposure(float exposure);
float getToneMappingExposure() const;
@ -156,11 +147,6 @@ public:
int getToneMappingToneCurve() const;
virtual void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
gpu::Queries _timerQueries;
int _currentTimerQueryIndex = 0;
};
#endif // hifi_RenderDeferredTask_h

View file

@ -0,0 +1,152 @@
//
// RenderShadowTask.cpp
// render-utils/src/
//
// Created by Zach Pomerantz on 1/7/2016.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <gpu/Context.h>
#include <ViewFrustum.h>
#include "DeferredLightingEffect.h"
#include "FramebufferCache.h"
#include "RenderShadowTask.h"
#include "model_shadow_vert.h"
#include "skin_model_shadow_vert.h"
#include "model_shadow_frag.h"
#include "skin_model_shadow_frag.h"
using namespace render;
void RenderShadowMap::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext,
const render::ShapesIDsBounds& inShapes) {
assert(renderContext->getArgs());
assert(renderContext->getArgs()->_viewFrustum);
const auto& lightStage = DependencyManager::get<DeferredLightingEffect>()->getLightStage();
const auto globalLight = lightStage.lights[0];
const auto& shadow = globalLight->shadow;
const auto& fbo = shadow.framebuffer;
RenderArgs* args = renderContext->getArgs();
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
args->_batch = &batch;
glm::ivec4 viewport{0, 0, fbo->getWidth(), fbo->getHeight()};
batch.setViewportTransform(viewport);
batch.setStateScissorRect(viewport);
batch.setFramebuffer(fbo);
batch.clearFramebuffer(
gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH,
vec4(vec3(1.0, 1.0, 1.0), 1.0), 1.0, 0, true);
batch.setProjectionTransform(shadow.getProjection());
batch.setViewTransform(shadow.getView());
auto shadowPipeline = _shapePlumber->pickPipeline(args, ShapeKey());
auto shadowSkinnedPipeline = _shapePlumber->pickPipeline(args, ShapeKey::Builder().withSkinned());
args->_pipeline = shadowPipeline;
batch.setPipeline(shadowPipeline->pipeline);
std::vector<ShapeKey> skinnedShapeKeys{};
for (auto items : inShapes) {
if (items.first.isSkinned()) {
skinnedShapeKeys.push_back(items.first);
} else {
renderItems(sceneContext, renderContext, items.second);
}
}
args->_pipeline = shadowSkinnedPipeline;
batch.setPipeline(shadowSkinnedPipeline->pipeline);
for (const auto& key : skinnedShapeKeys) {
renderItems(sceneContext, renderContext, inShapes.at(key));
}
args->_pipeline = nullptr;
args->_batch = nullptr;
});
}
RenderShadowTask::RenderShadowTask() : Task() {
// Prepare the ShapePipeline
ShapePlumberPointer shapePlumber = std::make_shared<ShapePlumber>();
{
auto state = std::make_shared<gpu::State>();
state->setCullMode(gpu::State::CULL_BACK);
state->setDepthTest(true, true, gpu::LESS_EQUAL);
auto modelVertex = gpu::Shader::createVertex(std::string(model_shadow_vert));
auto modelPixel = gpu::Shader::createPixel(std::string(model_shadow_frag));
gpu::ShaderPointer modelProgram = gpu::Shader::createProgram(modelVertex, modelPixel);
shapePlumber->addPipeline(
ShapeKey::Filter::Builder().withoutSkinned(),
modelProgram, state);
auto skinVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_vert));
auto skinPixel = gpu::Shader::createPixel(std::string(skin_model_shadow_frag));
gpu::ShaderPointer skinProgram = gpu::Shader::createProgram(skinVertex, skinPixel);
shapePlumber->addPipeline(
ShapeKey::Filter::Builder().withSkinned(),
skinProgram, state);
}
// CPU: Fetch shadow-casting opaques
auto fetchedItems = addJob<FetchItems>("FetchShadowMap");
// CPU: Cull against KeyLight frustum (nearby viewing camera)
auto culledItems = addJob<CullItems<RenderDetails::SHADOW_ITEM>>("CullShadowMap", fetchedItems);
// CPU: Sort by pipeline
auto sortedShapes = addJob<PipelineSortShapes>("PipelineSortShadowSort", culledItems);
// CPU: Sort front to back
auto shadowShapes = addJob<DepthSortShapes>("DepthSortShadowMap", sortedShapes);
// GPU: Render to shadow map
addJob<RenderShadowMap>("RenderShadowMap", shadowShapes, shapePlumber);
}
void RenderShadowTask::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
assert(sceneContext);
RenderArgs* args = renderContext->getArgs();
// sanity checks
if (!sceneContext->_scene || !args) {
return;
}
const auto& lightStage = DependencyManager::get<DeferredLightingEffect>()->getLightStage();
const auto globalLight = lightStage.lights[0];
// If the global light is not set, bail
if (!globalLight) {
return;
}
ViewFrustum* viewFrustum = args->_viewFrustum;
auto nearClip = viewFrustum->getNearClip();
const int SHADOW_NEAR_DEPTH = -2;
const int SHADOW_FAR_DEPTH = 20;
globalLight->shadow.setKeylightFrustum(viewFrustum, nearClip + SHADOW_NEAR_DEPTH, nearClip + SHADOW_FAR_DEPTH);
// Set the keylight frustum
args->_viewFrustum = globalLight->shadow.getFrustum().get();
for (auto job : _jobs) {
job.run(sceneContext, renderContext);
}
// Reset the view frustum
args->_viewFrustum = viewFrustum;
};

View file

@ -0,0 +1,41 @@
//
// RenderShadowTask.h
// render-utils/src/
//
// Created by Zach Pomerantz on 1/7/2016.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_RenderShadowTask_h
#define hifi_RenderShadowTask_h
#include <gpu/Framebuffer.h>
#include <gpu/Pipeline.h>
#include <render/Scene.h>
#include <render/DrawTask.h>
class ViewFrustum;
class RenderShadowMap {
public:
RenderShadowMap(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {}
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext,
const render::ShapesIDsBounds& inShapes);
using JobModel = render::Task::Job::ModelI<RenderShadowMap, render::ShapesIDsBounds>;
protected:
render::ShapePlumberPointer _shapePlumber;
};
class RenderShadowTask : public render::Task {
public:
RenderShadowTask();
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
};
#endif // hifi_RenderShadowTask_h

View file

@ -14,34 +14,51 @@
// the shadow texture
uniform sampler2DShadow shadowMap;
struct EyePlanes {
vec4 _S[1];
vec4 _T[1];
vec4 _R[1];
vec4 _Q[1];
struct ShadowTransform {
mat4 projection;
mat4 viewInverse;
float bias;
float scale;
};
uniform eyePlanes {
EyePlanes _eyePlanes;
uniform shadowTransformBuffer {
ShadowTransform _shadowTransform;
};
EyePlanes getEyePlanes() {
return _eyePlanes;
mat4 getShadowViewInverse() {
return _shadowTransform.viewInverse;
}
// Fetching it
float fetchShadow(vec3 texcoord) {
return texture(shadowMap, texcoord);
mat4 getShadowProjection() {
return _shadowTransform.projection;
}
// the distances to the cascade sections
uniform vec3 shadowDistances;
float getShadowScale() {
return _shadowTransform.scale;
}
// the inverse of the size of the shadow map
uniform float shadowScale;
float getShadowBias() {
return _shadowTransform.bias;
}
uniform mat4 shadowMatrices[4];
// Compute the texture coordinates from world coordinates
vec4 evalShadowTexcoord(vec4 position) {
mat4 biasMatrix = mat4(
0.5, 0.0, 0.0, 0.0,
0.0, 0.5, 0.0, 0.0,
0.0, 0.0, 0.5, 0.0,
0.5, 0.5, 0.5, 1.0);
float bias = -getShadowBias();
vec4 shadowCoord = biasMatrix * getShadowProjection() * getShadowViewInverse() * position;
return vec4(shadowCoord.xy, shadowCoord.z + bias, 1.0);
}
// Sample the shadowMap with PCF (built-in)
float fetchShadow(vec3 shadowTexcoord) {
return texture(shadowMap, shadowTexcoord);
}
vec2 samples[8] = vec2[8](
vec2(-2.0, -2.0),
@ -54,80 +71,33 @@ vec2 samples[8] = vec2[8](
vec2(0.0, -1.0)
);
vec4 evalShadowTexcoord(vec4 position) {
// compute the corresponding texture coordinates
EyePlanes eyePlanes = getEyePlanes();
vec3 shadowTexcoord = vec3(dot(eyePlanes._S[0], position), dot(eyePlanes._T[0], position), dot(eyePlanes._R[0], position));
return vec4(shadowTexcoord, 0.0);
}
float evalShadowAttenuationSampling(vec4 shadowTexcoord) {
float shadowScale = getShadowScale();
vec4 evalCascadedShadowTexcoord(vec4 position) {
EyePlanes eyePlanes = getEyePlanes();
// compute the index of the cascade to use and the corresponding texture coordinates
int shadowIndex = int(dot(step(vec3(position.z), shadowDistances), vec3(1.0, 1.0, 1.0)));
vec3 shadowTexcoord = vec3(
dot(eyePlanes._S[shadowIndex], position),
dot(eyePlanes._T[shadowIndex], position),
dot(eyePlanes._R[shadowIndex], position));
return vec4(shadowTexcoord, shadowIndex);
}
float evalShadowAttenuationPCF(vec4 shadowTexcoord) {
float radiusScale = (shadowTexcoord.w + 1.0);
float shadowAttenuation = (0.25 * (
fetchShadow(shadowTexcoord.xyz + radiusScale * shadowScale * vec3(samples[0], 0.0)) +
fetchShadow(shadowTexcoord.xyz + radiusScale * shadowScale * vec3(samples[1], 0.0)) +
fetchShadow(shadowTexcoord.xyz + radiusScale * shadowScale * vec3(samples[2], 0.0)) +
fetchShadow(shadowTexcoord.xyz + radiusScale * shadowScale * vec3(samples[3], 0.0))
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(samples[0], 0.0)) +
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(samples[1], 0.0)) +
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(samples[2], 0.0)) +
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(samples[3], 0.0))
));
// Check for early bailing
if ((shadowAttenuation > 0) && (shadowAttenuation < 1.0)) {
radiusScale *= 0.5;
shadowAttenuation = 0.5 * shadowAttenuation + (0.125 * (
fetchShadow(shadowTexcoord.xyz + radiusScale * shadowScale * vec3(samples[4], 0.0)) +
fetchShadow(shadowTexcoord.xyz + radiusScale * shadowScale * vec3(samples[5], 0.0)) +
fetchShadow(shadowTexcoord.xyz + radiusScale * shadowScale * vec3(samples[6], 0.0)) +
fetchShadow(shadowTexcoord.xyz + radiusScale * shadowScale * vec3(samples[7], 0.0))
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(samples[4], 0.0)) +
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(samples[5], 0.0)) +
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(samples[6], 0.0)) +
fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(samples[7], 0.0))
));
}
return shadowAttenuation;
}
float evalShadowAttenuationBasic(vec4 shadowTexcoord) {
float radiusScale = 0.5;
float shadowAttenuation = (0.25 * (
fetchShadow(shadowTexcoord.xyz + radiusScale * shadowScale * vec3(samples[0], 0.0)) +
fetchShadow(shadowTexcoord.xyz + radiusScale * shadowScale * vec3(samples[1], 0.0)) +
fetchShadow(shadowTexcoord.xyz + radiusScale * shadowScale * vec3(samples[2], 0.0)) +
fetchShadow(shadowTexcoord.xyz + radiusScale * shadowScale * vec3(samples[3], 0.0))
));
return shadowAttenuation;
float evalShadowAttenuation(vec4 position) {
vec4 shadowTexcoord = evalShadowTexcoord(position);
return evalShadowAttenuationSampling(shadowTexcoord);
}
float evalShadowAttenuation(vec4 shadowTexcoord) {
return evalShadowAttenuationBasic(shadowTexcoord);
}
<!
vec3 debugShadowMap(float shadowAttenuation, vec4 shadowTexcoord) {
vec3 colorArray[4];
colorArray[0].xyz = vec3(1.0, 1.0, 1.0);
colorArray[1].xyz = vec3(1.0, 0.0, 0.0);
colorArray[2].xyz = vec3(0.0, 1.0, 0.0);
colorArray[3].xyz = vec3(0.0, 0.0, 1.0);
vec2 offsetArray[4];
offsetArray[0] = vec2(0.0, 0.0);
offsetArray[1] = vec2(0.5, 0.0);
offsetArray[2] = vec2(0.0, 0.5);
offsetArray[3] = vec2(0.5, 0.5);
return shadowAttenuation * colorArray[int(shadowTexcoord.w)];
// return shadowAttenuation * vec3(2.0*(shadowTexcoord.xy - offsetArray[int(shadowTexcoord.w)]), 0);
}
!>
<@endif@>

View file

@ -6,15 +6,13 @@
// fragment shader
//
// Created by Andrzej Kapolka on 9/3/14.
// Copyright 2014 High Fidelity, Inc.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// Everything about deferred buffer
<@include DeferredBuffer.slh@>
<@include DeferredGlobalLight.slh@>
<$declareEvalLightmappedColor()$>
@ -27,10 +25,12 @@ void main(void) {
DeferredTransform deferredTransform = getDeferredTransform();
DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0);
float shadowAttenuation = 1.0;
if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) {
vec3 color = evalLightmappedColor(
deferredTransform.viewInverse,
1.0,
shadowAttenuation,
frag.normal,
frag.diffuse,
frag.specularVal.xyz);
@ -38,7 +38,7 @@ void main(void) {
} else {
vec3 color = evalAmbientSphereGlobalColor(
deferredTransform.viewInverse,
1.0,
shadowAttenuation,
frag.position.xyz,
frag.normal,
frag.diffuse,

View file

@ -0,0 +1,51 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// directional_light_shadow.frag
// fragment shader
//
// Created by Zach Pomerantz on 1/18/2016.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include Shadow.slh@>
<@include DeferredBuffer.slh@>
<@include DeferredGlobalLight.slh@>
<$declareEvalLightmappedColor()$>
<$declareEvalAmbientSphereGlobalColor()$>
in vec2 _texCoord0;
out vec4 _fragColor;
void main(void) {
DeferredTransform deferredTransform = getDeferredTransform();
DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0);
vec4 worldPos = deferredTransform.viewInverse * vec4(frag.position.xyz, 1.0);
float shadowAttenuation = evalShadowAttenuation(worldPos);
if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) {
vec3 color = evalLightmappedColor(
deferredTransform.viewInverse,
shadowAttenuation,
frag.normal,
frag.diffuse,
frag.specularVal.xyz);
_fragColor = vec4(color, 1.0);
} else {
vec3 color = evalAmbientSphereGlobalColor(
deferredTransform.viewInverse,
shadowAttenuation,
frag.position.xyz,
frag.normal,
frag.diffuse,
frag.specular,
frag.gloss);
_fragColor = vec4(color, frag.normalVal.a);
}
}

View file

@ -6,15 +6,13 @@
// fragment shader
//
// Created by Andrzej Kapolka on 9/3/14.
// Copyright 2014 High Fidelity, Inc.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// Everything about deferred buffer
<@include DeferredBuffer.slh@>
<@include DeferredGlobalLight.slh@>
<$declareEvalLightmappedColor()$>
@ -27,11 +25,13 @@ void main(void) {
DeferredTransform deferredTransform = getDeferredTransform();
DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0);
float shadowAttenuation = 1.0;
// Light mapped or not ?
if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) {
vec3 color = evalLightmappedColor(
deferredTransform.viewInverse,
1.0,
shadowAttenuation,
frag.normal,
frag.diffuse,
frag.specularVal.xyz);
@ -39,7 +39,7 @@ void main(void) {
} else {
vec3 color = evalAmbientGlobalColor(
deferredTransform.viewInverse,
1.0,
shadowAttenuation,
frag.position.xyz,
frag.normal,
frag.diffuse,

View file

@ -0,0 +1,52 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// directional_light_shadow.frag
// fragment shader
//
// Created by Zach Pomerantz on 1/18/2016.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include Shadow.slh@>
<@include DeferredBuffer.slh@>
<@include DeferredGlobalLight.slh@>
<$declareEvalLightmappedColor()$>
<$declareEvalAmbientGlobalColor()$>
in vec2 _texCoord0;
out vec4 _fragColor;
void main(void) {
DeferredTransform deferredTransform = getDeferredTransform();
DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0);
vec4 worldPos = deferredTransform.viewInverse * vec4(frag.position.xyz, 1.0);
float shadowAttenuation = evalShadowAttenuation(worldPos);
// Light mapped or not ?
if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) {
vec3 color = evalLightmappedColor(
deferredTransform.viewInverse,
shadowAttenuation,
frag.normal,
frag.diffuse,
frag.specularVal.xyz);
_fragColor = vec4(color, 1.0);
} else {
vec3 color = evalAmbientGlobalColor(
deferredTransform.viewInverse,
shadowAttenuation,
frag.position.xyz,
frag.normal,
frag.diffuse,
frag.specular,
frag.gloss);
_fragColor = vec4(color, frag.normalVal.a);
}
}

View file

@ -3,18 +3,16 @@
// Generated on <$_SCRIBE_DATE$>
//
// directional_light.frag
<!// fragment shader
// fragment shader
//
// Created by Sam Gateau on 5/8/2015.
// Copyright 2014 High Fidelity, Inc.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//!>
//
// Everything about deferred buffer
<@include DeferredBuffer.slh@>
<@include DeferredGlobalLight.slh@>
<$declareEvalLightmappedColor()$>
@ -27,11 +25,13 @@ void main(void) {
DeferredTransform deferredTransform = getDeferredTransform();
DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0);
float shadowAttenuation = 1.0;
// Light mapped or not ?
if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) {
vec3 color = evalLightmappedColor(
deferredTransform.viewInverse,
1.0,
shadowAttenuation,
frag.normal,
frag.diffuse,
frag.specularVal.xyz);
@ -39,7 +39,7 @@ void main(void) {
} else {
vec3 color = evalSkyboxGlobalColor(
deferredTransform.viewInverse,
1.0,
shadowAttenuation,
frag.position.xyz,
frag.normal,
frag.diffuse,

View file

@ -0,0 +1,53 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// directional_light_shadow.frag
// fragment shader
//
// Created by Zach Pomerantz on 1/18/2016.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//!>
<@include Shadow.slh@>
<@include DeferredBuffer.slh@>
<@include DeferredGlobalLight.slh@>
<$declareEvalLightmappedColor()$>
<$declareEvalSkyboxGlobalColor()$>
in vec2 _texCoord0;
out vec4 _fragColor;
void main(void) {
DeferredTransform deferredTransform = getDeferredTransform();
DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0);
vec4 worldPos = deferredTransform.viewInverse * vec4(frag.position.xyz, 1.0);
float shadowAttenuation = evalShadowAttenuation(worldPos);
// Light mapped or not ?
if ((frag.normalVal.a >= 0.45) && (frag.normalVal.a <= 0.55)) {
vec3 color = evalLightmappedColor(
deferredTransform.viewInverse,
shadowAttenuation,
frag.normal,
frag.diffuse,
frag.specularVal.xyz);
_fragColor = vec4(color, 1.0);
} else {
vec3 color = evalSkyboxGlobalColor(
deferredTransform.viewInverse,
shadowAttenuation,
frag.position.xyz,
frag.normal,
frag.diffuse,
frag.specular,
frag.gloss);
_fragColor = vec4(color, frag.normalVal.a);
}
}

View file

@ -12,9 +12,9 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
out vec4 _fragColor;
layout(location = 0) out vec4 _fragColor;
void main(void) {
// fixed color for now (we may eventually want to use texture alpha)
_fragColor = vec4(1.0, 1.0, 1.0, 0.0);
// stencil in solid color for debugging
_fragColor = vec4(0.0, 0.0, 1.0, 1.0);
}

View file

@ -0,0 +1,20 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// model_shadow.frag
// fragment shader
//
// Created by Andrzej Kapolka on 3/24/14.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
layout(location = 0) out vec4 _fragColor;
void main(void) {
// stencil in solid color for debugging
_fragColor = vec4(1.0, 0.0, 0.0, 1.0);
}

View file

@ -18,7 +18,6 @@
#include <ViewFrustum.h>
#include <gpu/Context.h>
using namespace render;
void render::cullItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) {
@ -62,27 +61,6 @@ void render::cullItems(const SceneContextPointer& sceneContext, const RenderCont
renderDetails->_rendered += outItems.size();
}
void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemIDsBounds& outItems) {
auto& scene = sceneContext->_scene;
outItems.clear();
const auto& bucket = scene->getMasterBucket();
const auto& items = bucket.find(_filter);
if (items != bucket.end()) {
outItems.reserve(items->second.size());
for (auto& id : items->second) {
auto& item = scene->getItem(id);
outItems.emplace_back(ItemIDAndBounds(id, item.getBound()));
}
}
if (_probeNumItems) {
_probeNumItems(renderContext, (int)outItems.size());
}
}
struct ItemBound {
float _centerDepth = 0.0f;
float _nearDepth = 0.0f;
@ -146,14 +124,7 @@ void render::depthSortItems(const SceneContextPointer& sceneContext, const Rende
}
}
void DepthSortItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) {
outItems.clear();
outItems.reserve(inItems.size());
depthSortItems(sceneContext, renderContext, _frontToBack, inItems, outItems);
}
void render::renderLights(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) {
void render::renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) {
auto& scene = sceneContext->_scene;
RenderArgs* args = renderContext->getArgs();
@ -190,6 +161,30 @@ void render::renderShapes(const SceneContextPointer& sceneContext, const RenderC
}
}
void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemIDsBounds& outItems) {
auto& scene = sceneContext->_scene;
outItems.clear();
const auto& bucket = scene->getMasterBucket();
const auto& items = bucket.find(_filter);
if (items != bucket.end()) {
outItems.reserve(items->second.size());
for (auto& id : items->second) {
auto& item = scene->getItem(id);
outItems.emplace_back(ItemIDAndBounds(id, item.getBound()));
}
}
if (_probeNumItems) {
_probeNumItems(renderContext, (int)outItems.size());
}
}
void DepthSortItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) {
depthSortItems(sceneContext, renderContext, _frontToBack, inItems, outItems);
}
void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
assert(renderContext->getArgs());
assert(renderContext->getArgs()->_viewFrustum);
@ -214,7 +209,39 @@ void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContext
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
args->_batch = &batch;
renderLights(sceneContext, renderContext, culledItems);
renderItems(sceneContext, renderContext, culledItems);
args->_batch = nullptr;
});
}
void PipelineSortShapes::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ShapesIDsBounds& outShapes) {
auto& scene = sceneContext->_scene;
outShapes.clear();
for (const auto& item : inItems) {
auto key = scene->getItem(item.id).getShapeKey();
auto outItems = outShapes.find(key);
if (outItems == outShapes.end()) {
outItems = outShapes.insert(std::make_pair(key, ItemIDsBounds{})).first;
outItems->second.reserve(inItems.size());
}
outItems->second.push_back(item);
}
for (auto& items : outShapes) {
items.second.shrink_to_fit();
}
}
void DepthSortShapes::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapesIDsBounds& inShapes, ShapesIDsBounds& outShapes) {
for (auto& pipeline : inShapes) {
auto& inItems = pipeline.second;
auto outItems = outShapes.find(pipeline.first);
if (outItems == outShapes.end()) {
outItems = outShapes.insert(std::make_pair(pipeline.first, ItemIDsBounds{})).first;
}
depthSortItems(sceneContext, renderContext, _frontToBack, inItems, outItems->second);
}
}

View file

@ -19,15 +19,15 @@
namespace render {
void cullItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outITems);
void depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemIDsBounds& inItems, ItemIDsBounds& outITems);
void renderLights(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems);
void cullItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems);
void depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemIDsBounds& inItems, ItemIDsBounds& outItems);
void renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems);
void renderShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapePlumberPointer& shapeContext, const ItemIDsBounds& inItems, int maxDrawnItems = -1);
class FetchItems {
public:
typedef std::function<void (const RenderContextPointer& context, int count)> ProbeNumItems;
FetchItems() {}
FetchItems(const ProbeNumItems& probe): _probeNumItems(probe) {}
FetchItems(const ItemFilter& filter, const ProbeNumItems& probe): _filter(filter), _probeNumItems(probe) {}
@ -35,7 +35,6 @@ public:
ProbeNumItems _probeNumItems;
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemIDsBounds& outItems);
using JobModel = Task::Job::ModelO<FetchItems, ItemIDsBounds>;
};
@ -54,22 +53,34 @@ public:
class DepthSortItems {
public:
bool _frontToBack = true;
bool _frontToBack;
DepthSortItems(bool frontToBack = true) : _frontToBack(frontToBack) {}
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outITems);
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems);
using JobModel = Task::Job::ModelIO<DepthSortItems, ItemIDsBounds, ItemIDsBounds>;
};
class DrawLight {
public:
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext);
using JobModel = Task::Job::Model<DrawLight>;
};
class PipelineSortShapes {
public:
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ShapesIDsBounds& outShapes);
using JobModel = Task::Job::ModelIO<PipelineSortShapes, ItemIDsBounds, ShapesIDsBounds>;
};
class DepthSortShapes {
public:
bool _frontToBack;
DepthSortShapes(bool frontToBack = true) : _frontToBack(frontToBack) {}
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapesIDsBounds& inShapes, ShapesIDsBounds& outShapes);
using JobModel = Task::Job::ModelIO<DepthSortShapes, ShapesIDsBounds, ShapesIDsBounds>;
};
}
#endif // hifi_render_DrawTask_h

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <gpu/Context.h>
#include "Engine.h"
using namespace render;
@ -34,7 +36,9 @@ void Engine::addTask(const TaskPointer& task) {
}
void Engine::run() {
// TODO: Tasks will need to be specified such that their data can feed into each other
// Sync GPU state before beginning to render
_renderContext->getArgs()->_context->syncCache();
for (auto task : _tasks) {
task->run(_sceneContext, _renderContext);
}

View file

@ -432,7 +432,11 @@ public:
AABox bounds;
};
typedef std::vector< ItemIDAndBounds > ItemIDsBounds;
// A list of items to be passed between rendering jobs
using ItemIDsBounds = std::vector<ItemIDAndBounds>;
// A map of items by ShapeKey to optimize rendering pipeline assignments
using ShapesIDsBounds = std::unordered_map<ShapeKey, ItemIDsBounds, ShapeKey::Hash, ShapeKey::KeyEqual>;
// A map of ItemIDSets allowing to create bucket lists of items which are filtering correctly

View file

@ -91,7 +91,9 @@ const ShapePipelinePointer ShapePlumber::pickPipeline(RenderArgs* args, const Ke
auto& batch = args->_batch;
// Run the pipeline's BatchSetter on the passed in batch
shapePipeline->batchSetter(*shapePipeline, *batch);
if (shapePipeline->batchSetter) {
shapePipeline->batchSetter(*shapePipeline, *batch);
}
// Setup the one pipeline (to rule them all)
batch->setPipeline(shapePipeline->pipeline);

View file

@ -83,32 +83,32 @@ public:
Filter build() const { return Filter(_flags, _mask); }
Builder& withOpaque() { _flags.reset(TRANSLUCENT); _mask.set(TRANSLUCENT); return (*this); }
Builder& withTranslucent() { _flags.set(TRANSLUCENT); _mask.set(TRANSLUCENT); return (*this); }
Builder& withOpaque() { _flags.reset(TRANSLUCENT); _mask.set(TRANSLUCENT); return (*this); }
Builder& withLightmap() { _flags.reset(LIGHTMAP); _mask.set(LIGHTMAP); return (*this); }
Builder& withoutLightmap() { _flags.set(LIGHTMAP); _mask.set(LIGHTMAP); return (*this); }
Builder& withLightmap() { _flags.set(LIGHTMAP); _mask.set(LIGHTMAP); return (*this); }
Builder& withoutLightmap() { _flags.reset(LIGHTMAP); _mask.set(LIGHTMAP); return (*this); }
Builder& withTangents() { _flags.reset(TANGENTS); _mask.set(TANGENTS); return (*this); }
Builder& withoutTangents() { _flags.set(TANGENTS); _mask.set(TANGENTS); return (*this); }
Builder& withTangents() { _flags.set(TANGENTS); _mask.set(TANGENTS); return (*this); }
Builder& withoutTangents() { _flags.reset(TANGENTS); _mask.set(TANGENTS); return (*this); }
Builder& withSpecular() { _flags.reset(SPECULAR); _mask.set(SPECULAR); return (*this); }
Builder& withoutSpecular() { _flags.set(SPECULAR); _mask.set(SPECULAR); return (*this); }
Builder& withSpecular() { _flags.set(SPECULAR); _mask.set(SPECULAR); return (*this); }
Builder& withoutSpecular() { _flags.reset(SPECULAR); _mask.set(SPECULAR); return (*this); }
Builder& withEmissive() { _flags.reset(EMISSIVE); _mask.set(EMISSIVE); return (*this); }
Builder& withoutEmissive() { _flags.set(EMISSIVE); _mask.set(EMISSIVE); return (*this); }
Builder& withEmissive() { _flags.set(EMISSIVE); _mask.set(EMISSIVE); return (*this); }
Builder& withoutEmissive() { _flags.reset(EMISSIVE); _mask.set(EMISSIVE); return (*this); }
Builder& withSkinned() { _flags.reset(SKINNED); _mask.set(SKINNED); return (*this); }
Builder& withoutSkinned() { _flags.set(SKINNED); _mask.set(SKINNED); return (*this); }
Builder& withSkinned() { _flags.set(SKINNED); _mask.set(SKINNED); return (*this); }
Builder& withoutSkinned() { _flags.reset(SKINNED); _mask.set(SKINNED); return (*this); }
Builder& withStereo() { _flags.reset(STEREO); _mask.set(STEREO); return (*this); }
Builder& withoutStereo() { _flags.set(STEREO); _mask.set(STEREO); return (*this); }
Builder& withStereo() { _flags.set(STEREO); _mask.set(STEREO); return (*this); }
Builder& withoutStereo() { _flags.reset(STEREO); _mask.set(STEREO); return (*this); }
Builder& withDepthOnly() { _flags.reset(DEPTH_ONLY); _mask.set(DEPTH_ONLY); return (*this); }
Builder& withoutDepthOnly() { _flags.set(DEPTH_ONLY); _mask.set(DEPTH_ONLY); return (*this); }
Builder& withDepthOnly() { _flags.set(DEPTH_ONLY); _mask.set(DEPTH_ONLY); return (*this); }
Builder& withoutDepthOnly() { _flags.reset(DEPTH_ONLY); _mask.set(DEPTH_ONLY); return (*this); }
Builder& withWireframe() { _flags.reset(WIREFRAME); _mask.set(WIREFRAME); return (*this); }
Builder& withoutWireframe() { _flags.set(WIREFRAME); _mask.set(WIREFRAME); return (*this); }
Builder& withWireframe() { _flags.set(WIREFRAME); _mask.set(WIREFRAME); return (*this); }
Builder& withoutWireframe() { _flags.reset(WIREFRAME); _mask.set(WIREFRAME); return (*this); }
protected:
friend class Filter;

View file

@ -207,12 +207,13 @@ public:
virtual ~Task() = default;
// Queue a new job to the task; returns the job's index
template <class T, class... A> size_t addJob(std::string name, A&&... args) {
template <class T, class... A> const Varying addJob(std::string name, A&&... args) {
_jobs.emplace_back(name, std::make_shared<typename T::JobModel>(std::forward<A>(args)...));
return _jobs.size() - 1;
return _jobs.back().getOutput();
}
const Job& getJob(size_t i) const { return _jobs.at(i); }
void enableJob(size_t jobIndex, bool enable = true) { _jobs.at(jobIndex).setEnabled(enable); }
bool getEnableJob(size_t jobIndex) const { return _jobs.at(jobIndex).isEnabled(); }
virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {}

View file

@ -36,6 +36,7 @@ class RenderDetails {
public:
enum Type {
OPAQUE_ITEM,
SHADOW_ITEM,
TRANSLUCENT_ITEM,
OTHER_ITEM
};
@ -51,6 +52,7 @@ public:
int _trianglesRendered = 0;
Item _opaque;
Item _shadow;
Item _translucent;
Item _other;
@ -61,6 +63,9 @@ public:
case OPAQUE_ITEM:
_item = &_opaque;
break;
case SHADOW_ITEM:
_item = &_shadow;
break;
case TRANSLUCENT_ITEM:
_item = &_translucent;
break;