Merge pull request #8560 from samcake/hdr

Introducing Clustered Lighting and improve Lighting performances
This commit is contained in:
samcake 2016-11-23 17:34:40 -08:00 committed by GitHub
commit 54543935be
80 changed files with 4249 additions and 692 deletions

View file

@ -11,9 +11,7 @@
#include <glm/gtx/quaternion.hpp>
#include <gpu/Batch.h>
#include <DeferredLightingEffect.h>
#include <GLMHelpers.h>
#include <PerfStat.h>
@ -25,38 +23,71 @@ EntityItemPointer RenderableLightEntityItem::factory(const EntityItemID& entityI
return entity;
}
void RenderableLightEntityItem::render(RenderArgs* args) {
PerformanceTimer perfTimer("RenderableLightEntityItem::render");
assert(getType() == EntityTypes::Light);
checkFading();
RenderableLightEntityItem::RenderableLightEntityItem(const EntityItemID& entityItemID) : LightEntityItem(entityItemID)
{
}
glm::vec3 position = getPosition();
glm::vec3 dimensions = getDimensions();
glm::quat rotation = getRotation();
float largestDiameter = glm::compMax(dimensions);
bool RenderableLightEntityItem::addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
_myItem = scene->allocateID();
glm::vec3 color = toGlm(getXColor());
auto renderItem = std::make_shared<LightPayload>();
updateRenderItemFromEntity((*renderItem));
float intensity = getIntensity() * (_isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f);
float falloffRadius = getFalloffRadius();
float exponent = getExponent();
float cutoff = glm::radians(getCutoff());
auto renderPayload = std::make_shared<LightPayload::Payload>(renderItem);
if (_isSpotlight) {
DependencyManager::get<DeferredLightingEffect>()->addSpotLight(position, largestDiameter / 2.0f,
color, intensity, falloffRadius, rotation, exponent, cutoff);
} else {
DependencyManager::get<DeferredLightingEffect>()->addPointLight(position, largestDiameter / 2.0f,
color, intensity, falloffRadius);
render::Item::Status::Getters statusGetters;
makeEntityItemStatusGetters(self, statusGetters);
renderPayload->addStatusGetters(statusGetters);
pendingChanges.resetItem(_myItem, renderPayload);
return true;
}
void RenderableLightEntityItem::somethingChangedNotification() {
if (_lightPropertiesChanged) {
notifyChanged();
}
#ifdef WANT_DEBUG
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
batch.setModelTransform(getTransformToCenter());
DependencyManager::get<GeometryCache>()->renderWireSphere(batch, 0.5f, 15, 15, glm::vec4(color, 1.0f));
#endif
};
LightEntityItem::somethingChangedNotification();
}
void RenderableLightEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
pendingChanges.removeItem(_myItem);
render::Item::clearID(_myItem);
}
void RenderableLightEntityItem::locationChanged(bool tellPhysics) {
EntityItem::locationChanged(tellPhysics);
notifyChanged();
}
void RenderableLightEntityItem::dimensionsChanged() {
EntityItem::dimensionsChanged();
notifyChanged();
}
void RenderableLightEntityItem::checkFading() {
bool transparent = isTransparent();
if (transparent != _prevIsTransparent) {
notifyChanged();
_isFading = false;
_prevIsTransparent = transparent;
}
}
void RenderableLightEntityItem::notifyChanged() {
if (!render::Item::isValidID(_myItem)) {
return;
}
render::PendingChanges pendingChanges;
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
updateLightFromEntity(pendingChanges);
scene->enqueuePendingChanges(pendingChanges);
}
bool RenderableLightEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElementPointer& element, float& distance,
@ -70,3 +101,59 @@ bool RenderableLightEntityItem::findDetailedRayIntersection(const glm::vec3& ori
// fix this mechanism.
return _lightsArePickable;
}
void RenderableLightEntityItem::updateLightFromEntity(render::PendingChanges& pendingChanges) {
if (!render::Item::isValidID(_myItem)) {
return;
}
pendingChanges.updateItem<LightPayload>(_myItem, [&](LightPayload& data) {
updateRenderItemFromEntity(data);
});
}
void RenderableLightEntityItem::updateRenderItemFromEntity(LightPayload& lightPayload) {
auto entity = this;
lightPayload.setVisible(entity->getVisible());
auto light = lightPayload.editLight();
light->setPosition(entity->getPosition());
light->setOrientation(entity->getRotation());
bool success;
lightPayload.editBound() = entity->getAABox(success);
if (!success) {
lightPayload.editBound() = render::Item::Bound();
}
glm::vec3 dimensions = entity->getDimensions();
float largestDiameter = glm::compMax(dimensions);
light->setMaximumRadius(largestDiameter / 2.0f);
light->setColor(toGlm(entity->getXColor()));
float intensity = entity->getIntensity();//* entity->getFadingRatio();
light->setIntensity(intensity);
light->setFalloffRadius(entity->getFalloffRadius());
float exponent = entity->getExponent();
float cutoff = glm::radians(entity->getCutoff());
if (!entity->getIsSpotlight()) {
light->setType(model::Light::POINT);
} else {
light->setType(model::Light::SPOT);
light->setSpotAngle(cutoff);
light->setSpotExponent(exponent);
}
}

View file

@ -13,21 +13,44 @@
#define hifi_RenderableLightEntityItem_h
#include <LightEntityItem.h>
#include <LightPayload.h>
#include "RenderableEntityItem.h"
class RenderableLightEntityItem : public LightEntityItem {
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
RenderableLightEntityItem(const EntityItemID& entityItemID) : LightEntityItem(entityItemID) { }
RenderableLightEntityItem(const EntityItemID& entityItemID);
virtual void render(RenderArgs* args) override;
virtual bool supportsDetailedRayIntersection() const override { return true; }
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElementPointer& element, float& distance,
BoxFace& face, glm::vec3& surfaceNormal,
void** intersectedObject, bool precisionPicking) const override;
SIMPLE_RENDERABLE();
void updateLightFromEntity(render::PendingChanges& pendingChanges);
virtual bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) override;
virtual void somethingChangedNotification() override;
virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) override;
virtual void locationChanged(bool tellPhysics = true) override;
virtual void dimensionsChanged() override;
void checkFading();
void notifyChanged();
private:
bool _prevIsTransparent { isTransparent() };
render::ItemID _myItem { render::Item::INVALID_ITEM_ID };
// Dirty flag turn true when either setSubClassProperties or readEntitySubclassDataFromBuffer is changing a value
void updateRenderItemFromEntity(LightPayload& lightPayload);
};

View file

@ -1322,6 +1322,10 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
somethingChanged = true;
}
// Now check the sub classes
somethingChanged |= setSubClassProperties(properties);
// Finally notify if change detected
if (somethingChanged) {
uint64_t now = usecTimestampNow();
#ifdef WANT_DEBUG

View file

@ -90,7 +90,15 @@ public:
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const;
/// returns true if something changed
// This function calls setSubClass properties and detects if any property changes value.
// If something changed then the "somethingChangedNotification" calls happens
virtual bool setProperties(const EntityItemProperties& properties);
// Set properties for sub class so they can add their own properties
// it does nothing in the root class
// This function is called by setProperties which then can detects if any property changes value in the SubClass (see aboe comment on setProperties)
virtual bool setSubClassProperties(const EntityItemProperties& properties) { return false; }
// Update properties with empty parent id and globalized/absolute values (applying offset), and apply (non-empty) log template to args id, name-or-type, parent id.
void globalizeProperties(EntityItemProperties& properties, const QString& messageTemplate = QString(), const glm::vec3& offset = glm::vec3(0.0f)) const;
@ -447,6 +455,9 @@ public:
virtual void setProxyWindow(QWindow* proxyWindow) {}
virtual QObject* getEventHandler() { return nullptr; }
bool isFading() const { return _isFading; }
float getFadingRatio() const { return (isFading() ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f); }
virtual void emitScriptEvent(const QVariant& message) {}
QUuid getLastEditedBy() const { return _lastEditedBy; }

View file

@ -70,6 +70,7 @@ EntityItemProperties LightEntityItem::getProperties(EntityPropertyFlags desiredP
void LightEntityItem::setFalloffRadius(float value) {
_falloffRadius = glm::max(value, 0.0f);
_lightPropertiesChanged = true;
}
void LightEntityItem::setIsSpotlight(bool value) {
@ -85,6 +86,7 @@ void LightEntityItem::setIsSpotlight(bool value) {
float maxDimension = glm::compMax(dimensions);
setDimensions(glm::vec3(maxDimension, maxDimension, maxDimension));
}
_lightPropertiesChanged = true;
}
}
@ -98,10 +100,26 @@ void LightEntityItem::setCutoff(float value) {
const float width = length * glm::sin(glm::radians(_cutoff));
setDimensions(glm::vec3(width, width, length));
}
_lightPropertiesChanged = true;
}
bool LightEntityItem::setProperties(const EntityItemProperties& properties) {
bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
if (somethingChanged) {
bool wantDebug = false;
if (wantDebug) {
uint64_t now = usecTimestampNow();
int elapsed = now - getLastEdited();
qCDebug(entities) << "LightEntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
"now=" << now << " getLastEdited()=" << getLastEdited();
}
setLastEdited(properties.getLastEdited());
}
return somethingChanged;
}
bool LightEntityItem::setSubClassProperties(const EntityItemProperties& properties) {
bool somethingChanged = EntityItem::setSubClassProperties(properties); // set the properties in our base class
SET_ENTITY_PROPERTY_FROM_PROPERTIES(isSpotlight, setIsSpotlight);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
@ -110,19 +128,10 @@ bool LightEntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(cutoff, setCutoff);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(falloffRadius, setFalloffRadius);
if (somethingChanged) {
bool wantDebug = false;
if (wantDebug) {
uint64_t now = usecTimestampNow();
int elapsed = now - getLastEdited();
qCDebug(entities) << "LightEntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
"now=" << now << " getLastEdited()=" << getLastEdited();
}
setLastEdited(properties.getLastEdited());
}
return somethingChanged;
}
int LightEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args,
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
@ -193,3 +202,8 @@ void LightEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
APPEND_ENTITY_PROPERTY(PROP_CUTOFF, getCutoff());
APPEND_ENTITY_PROPERTY(PROP_FALLOFF_RADIUS, getFalloffRadius());
}
void LightEntityItem::somethingChangedNotification() {
EntityItem::somethingChangedNotification();
_lightPropertiesChanged = false;
}

View file

@ -31,9 +31,16 @@ public:
/// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately
virtual void setDimensions(const glm::vec3& value) override;
virtual bool setProperties(const EntityItemProperties& properties) override;
virtual bool setSubClassProperties(const EntityItemProperties& properties) override;
// methods for getting/setting all properties of an entity
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
virtual bool setProperties(const EntityItemProperties& properties) override;
/// Override this in your derived class if you'd like to be informed when something about the state of the entity
/// has changed. This will be called with properties change or when new data is loaded from a stream
/// Overriding this function to capture the information that a light properties has changed
virtual void somethingChangedNotification() override;
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
@ -60,6 +67,7 @@ public:
_color[RED_INDEX] = value.red;
_color[GREEN_INDEX] = value.green;
_color[BLUE_INDEX] = value.blue;
_lightPropertiesChanged = true;
}
bool getIsSpotlight() const { return _isSpotlight; }
@ -69,13 +77,19 @@ public:
void setIgnoredAttenuation(float value) { }
float getIntensity() const { return _intensity; }
void setIntensity(float value) { _intensity = value; }
void setIntensity(float value) {
_intensity = value;
_lightPropertiesChanged = true;
}
float getFalloffRadius() const { return _falloffRadius; }
void setFalloffRadius(float value);
float getExponent() const { return _exponent; }
void setExponent(float value) { _exponent = value; }
void setExponent(float value) {
_exponent = value;
_lightPropertiesChanged = true;
}
float getCutoff() const { return _cutoff; }
void setCutoff(float value);
@ -85,6 +99,7 @@ public:
protected:
// properties of a light
rgbColor _color;
bool _isSpotlight { DEFAULT_IS_SPOTLIGHT };
@ -93,6 +108,11 @@ protected:
float _exponent { DEFAULT_EXPONENT };
float _cutoff { DEFAULT_CUTOFF };
// Dirty flag turn true when either light properties is changing values.
// This gets back to false in the somethingChangedNotification() call
// Which is called after a setProperties() or a readEntitySubClassFromBUfferCall on the entity.
bool _lightPropertiesChanged { false };
static bool _lightsArePickable;
};

View file

@ -27,8 +27,11 @@ void GLBackend::do_beginQuery(const Batch& batch, size_t paramOffset) {
auto query = batch._queries.get(batch._params[paramOffset]._uint);
GLQuery* glquery = syncGPUObject(*query);
if (glquery) {
PROFILE_RANGE_BEGIN(glquery->_profileRangeId, query->getName().c_str(), 0xFFFF7F00);
++_queryStage._rangeQueryDepth;
glGetInteger64v(GL_TIMESTAMP, (GLint64*)&glquery->_batchElapsedTime);
if (timeElapsed) {
if (_queryStage._rangeQueryDepth <= MAX_RANGE_QUERY_DEPTH) {
glBeginQuery(GL_TIME_ELAPSED, glquery->_endqo);
@ -52,11 +55,14 @@ void GLBackend::do_endQuery(const Batch& batch, size_t paramOffset) {
} else {
glQueryCounter(glquery->_endqo, GL_TIMESTAMP);
}
--_queryStage._rangeQueryDepth;
GLint64 now;
glGetInteger64v(GL_TIMESTAMP, &now);
glquery->_batchElapsedTime = now - glquery->_batchElapsedTime;
PROFILE_RANGE_END(glquery->_profileRangeId);
(void)CHECK_GL_ERROR();
}
}

View file

@ -213,13 +213,13 @@ void GLBackend::do_setStateStencil(State::StencilActivation activation, State::S
GL_DECR };
if (testFront != testBack) {
glStencilOpSeparate(GL_FRONT, STENCIL_OPS[testFront.getFailOp()], STENCIL_OPS[testFront.getPassOp()], STENCIL_OPS[testFront.getDepthFailOp()]);
glStencilOpSeparate(GL_FRONT, STENCIL_OPS[testFront.getFailOp()], STENCIL_OPS[testFront.getDepthFailOp()], STENCIL_OPS[testFront.getPassOp()]);
glStencilFuncSeparate(GL_FRONT, COMPARISON_TO_GL[testFront.getFunction()], testFront.getReference(), testFront.getReadMask());
glStencilOpSeparate(GL_BACK, STENCIL_OPS[testBack.getFailOp()], STENCIL_OPS[testBack.getPassOp()], STENCIL_OPS[testBack.getDepthFailOp()]);
glStencilOpSeparate(GL_BACK, STENCIL_OPS[testBack.getFailOp()], STENCIL_OPS[testBack.getDepthFailOp()], STENCIL_OPS[testBack.getPassOp()]);
glStencilFuncSeparate(GL_BACK, COMPARISON_TO_GL[testBack.getFunction()], testBack.getReference(), testBack.getReadMask());
} else {
glStencilOp(STENCIL_OPS[testFront.getFailOp()], STENCIL_OPS[testFront.getPassOp()], STENCIL_OPS[testFront.getDepthFailOp()]);
glStencilOp(STENCIL_OPS[testFront.getFailOp()], STENCIL_OPS[testFront.getDepthFailOp()], STENCIL_OPS[testFront.getPassOp()]);
glStencilFunc(COMPARISON_TO_GL[testFront.getFunction()], testFront.getReference(), testFront.getReadMask());
}

View file

@ -49,6 +49,7 @@ public:
const GLuint _beginqo = { 0 };
GLuint64 _result { (GLuint64)-1 };
GLuint64 _batchElapsedTime { (GLuint64) 0 };
uint64_t _profileRangeId { 0 };
uint32_t _rangeQueryDepth { 0 };
protected:

View file

@ -583,7 +583,6 @@ void GL45Texture::stripToMip(uint16_t newMinMip) {
syncSampler();
updateSize();
// Re-insert into the texture-by-mips map if appropriate
mipLevels = usedMipLevels();
if (mipLevels > 1 && (!_sparseInfo.sparse || _minMip < _sparseInfo.maxSparseLevel)) {

View file

@ -32,11 +32,11 @@ namespace gpu {
enum ReservedSlot {
#ifdef GPU_SSBO_DRAW_CALL_INFO
TRANSFORM_OBJECT_SLOT = 6,
TRANSFORM_OBJECT_SLOT = 14,
#else
TRANSFORM_OBJECT_SLOT = 31,
#endif
TRANSFORM_CAMERA_SLOT = 7,
TRANSFORM_CAMERA_SLOT = 15,
};
// The named batch data provides a mechanism for accumulating data into buffers over the course

View file

@ -62,7 +62,7 @@ public:
// The size in bytes of data stored in the buffer
Size getSize() const override;
template <typename T>
Size getTypedSize() const { return getSize() / sizeof(T); };
Size getNumTypedElements() const { return getSize() / sizeof(T); };
const Byte* getData() const { return getSysmem().readData(); }
@ -179,7 +179,7 @@ protected:
public:
using Size = Resource::Size;
using Index = int;
using Index = int32_t;
BufferPointer _buffer;
Size _offset { 0 };
@ -382,6 +382,26 @@ public:
}
};
template <class T> class StructBuffer : public gpu::BufferView {
public:
template <class U> static BufferPointer makeBuffer() {
U t;
return std::make_shared<gpu::Buffer>(sizeof(U), (const gpu::Byte*) &t, sizeof(U));
}
~StructBuffer<T>() {};
StructBuffer<T>() : gpu::BufferView(makeBuffer<T>()) {}
T& edit() {
return BufferView::edit<T>(0);
}
const T& get() const {
return BufferView::get<T>(0);
}
const T* operator ->() const { return &get(); }
};
};
#endif

View file

@ -46,6 +46,25 @@ vec3 colorWheel(float normalizedHue) {
return vec3(1.f, 0.f, 0.f);
}
}
vec3 colorRamp(float normalizedHue) {
float v = normalizedHue * 5.f;
if (v < 0.f) {
return vec3(1.f, 0.f, 0.f);
} else if (v < 1.f) {
return vec3(1.f, v, 0.f);
} else if (v < 2.f) {
return vec3(1.f - (v - 1.f), 1.f, 0.f);
} else if (v < 3.f) {
return vec3(0.f, 1.f, (v - 2.f));
} else if (v < 4.f) {
return vec3(0.f, 1.f - (v - 3.f), 1.f);
} else if (v < 5.f) {
return vec3((v - 4.f), 0.f, 1.f);
} else {
return vec3(1.f, 0.f, 1.f);
}
}
<@endfunc@>
<@endif@>

View file

@ -53,7 +53,7 @@ void Context::beginFrame(const glm::mat4& renderPose) {
_currentFrame->pose = renderPose;
if (!_frameRangeTimer) {
_frameRangeTimer = std::make_shared<RangeTimer>();
_frameRangeTimer = std::make_shared<RangeTimer>("gpu::Context::Frame");
}
}

View file

@ -18,6 +18,7 @@ const Element Element::VEC2F_UV{ VEC2, FLOAT, UV };
const Element Element::VEC2F_XY{ VEC2, FLOAT, XY };
const Element Element::VEC3F_XYZ{ VEC3, FLOAT, XYZ };
const Element Element::VEC4F_XYZW{ VEC4, FLOAT, XYZW };
const Element Element::INDEX_UINT16{ SCALAR, UINT16, INDEX };
const Element Element::INDEX_UINT16 { SCALAR, UINT16, INDEX };
const Element Element::INDEX_INT32 { SCALAR, INT32, INDEX };
const Element Element::PART_DRAWCALL{ VEC4, UINT32, PART };

View file

@ -236,6 +236,7 @@ public:
static const Element VEC3F_XYZ;
static const Element VEC4F_XYZW;
static const Element INDEX_UINT16;
static const Element INDEX_INT32;
static const Element PART_DRAWCALL;
protected:

View file

@ -15,8 +15,9 @@
using namespace gpu;
Query::Query(const Handler& returnHandler) :
_returnHandler(returnHandler)
Query::Query(const Handler& returnHandler, const std::string& name) :
_returnHandler(returnHandler),
_name(name)
{
}
@ -34,19 +35,22 @@ double Query::getBatchElapsedTime() const {
void Query::triggerReturnHandler(uint64_t queryResult, uint64_t batchElapsedTime) {
_queryResult = queryResult;
_usecBatchElapsedTime = batchElapsedTime;
if (_returnHandler) {
_returnHandler(*this);
}
}
RangeTimer::RangeTimer() {
RangeTimer::RangeTimer(const std::string& name) :
_name(name) {
for (int i = 0; i < QUERY_QUEUE_SIZE; i++) {
_timerQueries.push_back(std::make_shared<gpu::Query>([&, i] (const Query& query) {
_tailIndex ++;
_movingAverageGPU.addSample(query.getGPUElapsedTime());
_movingAverageBatch.addSample(query.getBatchElapsedTime());
}));
}, _name));
}
}

View file

@ -15,6 +15,7 @@
#include <memory>
#include <functional>
#include <vector>
#include <string>
#include <SimpleMovingAverage.h>
#include "Format.h"
@ -27,18 +28,21 @@ namespace gpu {
public:
using Handler = std::function<void(const Query&)>;
Query(const Handler& returnHandler);
Query(const Handler& returnHandler, const std::string& name = "gpu::query");
~Query();
double getGPUElapsedTime() const;
double getBatchElapsedTime() const;
const std::string& getName() const { return _name; }
// Only for gpu::Context
const GPUObjectPointer gpuObject {};
void triggerReturnHandler(uint64_t queryResult, uint64_t batchElapsedTime);
protected:
Handler _returnHandler;
const std::string _name;
uint64_t _queryResult { 0 };
uint64_t _usecBatchElapsedTime { 0 };
};
@ -52,7 +56,7 @@ namespace gpu {
// The result is always a late average of the time spent for that same task a few cycles ago.
class RangeTimer {
public:
RangeTimer();
RangeTimer(const std::string& name);
void begin(gpu::Batch& batch);
void end(gpu::Batch& batch);
@ -63,12 +67,14 @@ namespace gpu {
static const int QUERY_QUEUE_SIZE { 4 };
const std::string _name;
gpu::Queries _timerQueries;
int _headIndex = -1;
int _tailIndex = -1;
MovingAverage<double, QUERY_QUEUE_SIZE * 2> _movingAverageGPU;
MovingAverage<double, QUERY_QUEUE_SIZE * 2> _movingAverageBatch;
int rangeIndex(int index) const { return (index % QUERY_QUEUE_SIZE); }
};

View file

@ -35,14 +35,24 @@ layout(std140) uniform transformCameraBuffer {
#ifdef GPU_VERTEX_SHADER
#ifdef GPU_TRANSFORM_IS_STEREO
#ifdef GPU_TRANSFORM_STEREO_CAMERA
#ifdef GPU_TRANSFORM_STEREO_CAMERA_ATTRIBUTED
layout(location=14) in int _inStereoSide;
#endif
flat out int _stereoSide;
// In stereo drawcall mode Instances are drawn twice (left then right) hence the true InstanceID is the gl_InstanceID / 2
int gpu_InstanceID = gl_InstanceID >> 1;
#else
int gpu_InstanceID = gl_InstanceID;
#endif
#else
int gpu_InstanceID = gl_InstanceID;
#endif
@ -54,6 +64,7 @@ flat in int _stereoSide;
#endif
#endif
TransformCamera getTransformCamera() {
#ifdef GPU_TRANSFORM_IS_STEREO
#ifdef GPU_TRANSFORM_STEREO_CAMERA
@ -206,6 +217,16 @@ TransformObject getTransformObject() {
}
<@endfunc@>
<@func transformWorldToClipPos(cameraTransform, worldPos, clipPos)@>
{ // transformWorldToClipPos
vec4 eyeWAPos = <$worldPos$> - vec4(<$cameraTransform$>._viewInverse[3].xyz, 0.0);
<$clipPos$> = <$cameraTransform$>._projectionViewUntranslated * eyeWAPos;
<$transformStereoClipsSpace($cameraTransform$, $clipPos$)$>
}
<@endfunc@>
<@func transformModelToWorldPos(objectTransform, modelPos, worldPos)@>
{ // transformModelToWorldPos
<$worldPos$> = (<$objectTransform$>._model * <$modelPos$>);

View file

@ -13,22 +13,19 @@
using namespace model;
Light::Light() {
// only if created from nothing shall we create the Buffer to store the properties
Schema schema;
_schemaBuffer = std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema);
updateLightRadius();
}
Light::Light(const Light& light) :
_flags(light._flags),
_schemaBuffer(light._schemaBuffer),
_transform(light._transform)
{
}
Light& Light::operator= (const Light& light) {
_flags = (light._flags);
_schemaBuffer = (light._schemaBuffer);
_lightSchemaBuffer = (light._lightSchemaBuffer);
_ambientSchemaBuffer = (light._ambientSchemaBuffer);
_transform = (light._transform);
return (*this);
@ -37,9 +34,22 @@ Light& Light::operator= (const Light& light) {
Light::~Light() {
}
void Light::setType(Type type) {
if (_type != type) {
_type = type;
if (type != SPOT) {
_lightSchemaBuffer.edit().volume.spotCos = -1.f;
} else {
_lightSchemaBuffer.edit().volume.spotCos = _spotCos;
}
updateLightRadius();
}
}
void Light::setPosition(const Vec3& position) {
_transform.setTranslation(position);
editSchema()._position = Vec4(position, 1.f);
_lightSchemaBuffer.edit().volume.position = position;
}
void Light::setOrientation(const glm::quat& orientation) {
@ -48,39 +58,35 @@ void Light::setOrientation(const glm::quat& orientation) {
}
void Light::setDirection(const Vec3& direction) {
editSchema()._direction = glm::normalize(direction);
_lightSchemaBuffer.edit().volume.direction = (direction);
}
const Vec3& Light::getDirection() const {
return getSchema()._direction;
return _lightSchemaBuffer->volume.direction;
}
void Light::setColor(const Color& color) {
editSchema()._color = color;
_lightSchemaBuffer.edit().irradiance.color = color;
updateLightRadius();
}
void Light::setIntensity(float intensity) {
editSchema()._intensity = intensity;
_lightSchemaBuffer.edit().irradiance.intensity = intensity;
updateLightRadius();
}
void Light::setAmbientIntensity(float intensity) {
editSchema()._ambientIntensity = intensity;
}
void Light::setFalloffRadius(float radius) {
if (radius <= 0.0f) {
radius = 0.1f;
}
editSchema()._attenuation.x = radius;
_lightSchemaBuffer.edit().irradiance.falloffRadius = radius;
updateLightRadius();
}
void Light::setMaximumRadius(float radius) {
if (radius <= 0.f) {
radius = 1.0f;
}
editSchema()._attenuation.y = radius;
_lightSchemaBuffer.edit().volume.radius = radius;
updateLightRadius();
}
@ -98,7 +104,7 @@ void Light::updateLightRadius() {
float cutoffRadius = getFalloffRadius() * ((glm::sqrt(intensity / MIN_CUTOFF_INTENSITY) - 1) - 1);
// If it is less than max radius, store it to buffer to avoid extra shading
editSchema()._attenuation.z = std::min(getMaximumRadius(), cutoffRadius);
_lightSchemaBuffer.edit().irradiance.cutoffRadius = std::min(getMaximumRadius(), cutoffRadius);
}
#include <math.h>
@ -111,34 +117,32 @@ void Light::setSpotAngle(float angle) {
if (dangle > glm::half_pi<double>()) {
dangle = glm::half_pi<double>();
}
auto cosAngle = cos(dangle);
auto sinAngle = sin(dangle);
editSchema()._spot.x = (float) std::abs(cosAngle);
editSchema()._spot.y = (float) std::abs(sinAngle);
editSchema()._spot.z = (float) angle;
_spotCos = (float)std::abs(cosAngle);
if (isSpot()) {
_lightSchemaBuffer.edit().volume.spotCos = _spotCos;
}
}
void Light::setSpotExponent(float exponent) {
if (exponent <= 0.f) {
exponent = 0.0f;
}
editSchema()._spot.w = exponent;
_lightSchemaBuffer.edit().irradiance.falloffSpot = exponent;
}
void Light::setShowContour(float show) {
if (show <= 0.f) {
show = 0.0f;
}
editSchema()._control.z = show;
void Light::setAmbientIntensity(float intensity) {
_ambientSchemaBuffer.edit().intensity = intensity;
}
void Light::setAmbientSphere(const gpu::SphericalHarmonics& sphere) {
editSchema()._ambientSphere = sphere;
_ambientSchemaBuffer.edit().ambientSphere = sphere;
}
void Light::setAmbientSpherePreset(gpu::SphericalHarmonics::Preset preset) {
editSchema()._ambientSphere.assignPreset(preset);
_ambientSchemaBuffer.edit().ambientSphere.assignPreset(preset);
}
void Light::setAmbientMap(gpu::TexturePointer ambientMap) {
@ -151,5 +155,6 @@ void Light::setAmbientMap(gpu::TexturePointer ambientMap) {
}
void Light::setAmbientMapNumMips(uint16_t numMips) {
editSchema()._ambientMapNumMips = (float)numMips;
_ambientSchemaBuffer.edit().mapNumMips = (float)numMips;
}

View file

@ -26,8 +26,47 @@ typedef glm::vec3 Vec3;
typedef glm::vec4 Vec4;
typedef glm::quat Quat;
class Light {
public:
struct LightVolume {
vec3 position { 0.f };
float radius { 1.0f };
vec3 direction { 0.f, 0.f, -1.f };
float spotCos { -1.f };
bool isPoint() const { return bool(spotCos < 0.f); }
bool isSpot() const { return bool(spotCos >= 0.f); }
vec3 getPosition() const { return position; }
float getRadius() const { return radius; }
float getRadiusSquare() const { return radius * radius; }
vec3 getDirection() const { return direction; }
float getSpotAngleCos() const { return spotCos; }
vec2 getSpotAngleCosSin() const { return vec2(spotCos, sqrt(1.f - spotCos * spotCos)); }
};
struct LightIrradiance {
vec3 color { 1.f };
float intensity { 1.f };
float falloffRadius { 0.1f };
float cutoffRadius { 0.1f };
float falloffSpot { 1.f };
float spare1;
vec3 getColor() const { return color; }
float getIntensity() const { return intensity; }
vec3 getIrradiance() const { return color * intensity; }
float getFalloffRadius() const { return falloffRadius; }
float getCutoffRadius() const { return cutoffRadius; }
float getFalloffSpot() const { return falloffSpot; }
};
enum Type {
SUN = 0,
POINT,
@ -54,8 +93,8 @@ public:
Light& operator= (const Light& light);
virtual ~Light();
void setType(Type type) { editSchema()._control.x = float(type); }
Type getType() const { return Type((int) getSchema()._control.x); }
void setType(Type type);
Type getType() const { return _type; }
void setPosition(const Vec3& position);
const Vec3& getPosition() const { return _transform.getTranslation(); }
@ -66,86 +105,88 @@ public:
void setOrientation(const Quat& orientation);
const glm::quat& getOrientation() const { return _transform.getRotation(); }
const Color& getColor() const { return getSchema()._color; }
const Color& getColor() const { return _lightSchemaBuffer->irradiance.color; }
void setColor(const Color& color);
float getIntensity() const { return getSchema()._intensity; }
float getIntensity() const { return _lightSchemaBuffer->irradiance.intensity; }
void setIntensity(float intensity);
bool isRanged() const { return (getType() == POINT) || (getType() == SPOT ); }
bool isRanged() const { return (getType() == POINT) || (getType() == SPOT); }
bool hasAmbient() const { return (getType() == SUN); }
// FalloffRradius is the physical radius of the light sphere through which energy shines,
// expressed in meters. It is used only to calculate the falloff curve of the light.
// Actual rendered lights will all have surface radii approaching 0.
void setFalloffRadius(float radius);
float getFalloffRadius() const { return getSchema()._attenuation.x; }
float getFalloffRadius() const { return _lightSchemaBuffer->irradiance.falloffRadius; }
// Maximum radius is the cutoff radius of the light energy, expressed in meters.
// It is used to bound light entities, and *will not* affect the falloff curve of the light.
// Setting it low will result in a noticeable cutoff.
void setMaximumRadius(float radius);
float getMaximumRadius() const { return getSchema()._attenuation.y; }
float getMaximumRadius() const { return _lightSchemaBuffer->volume.radius; }
// Spot properties
bool isSpot() const { return getType() == SPOT; }
void setSpotAngle(float angle);
float getSpotAngle() const { return getSchema()._spot.z; }
glm::vec2 getSpotAngleCosSin() const { return glm::vec2(getSchema()._spot.x, getSchema()._spot.y); }
float getSpotAngle() const { return acos(_lightSchemaBuffer->volume.getSpotAngleCos()); }
glm::vec2 getSpotAngleCosSin() const { return _lightSchemaBuffer->volume.getSpotAngleCosSin(); }
void setSpotExponent(float exponent);
float getSpotExponent() const { return getSchema()._spot.w; }
// For editing purpose, show the light volume contour.
// Set to non 0 to show it, the value is used as the intensity of the contour color
void setShowContour(float show);
float getShowContour() const { return getSchema()._control.z; }
float getSpotExponent() const { return _lightSchemaBuffer->irradiance.falloffSpot; }
// If the light has an ambient (Indirect) component, then the Ambientintensity can be used to control its contribution to the lighting
void setAmbientIntensity(float intensity);
float getAmbientIntensity() const { return getSchema()._ambientIntensity; }
float getAmbientIntensity() const { return _ambientSchemaBuffer->intensity; }
// Spherical Harmonics storing the Ambient lighting approximation used for the Sun typed light
void setAmbientSphere(const gpu::SphericalHarmonics& sphere);
const gpu::SphericalHarmonics& getAmbientSphere() const { return getSchema()._ambientSphere; }
const gpu::SphericalHarmonics& getAmbientSphere() const { return _ambientSchemaBuffer->ambientSphere; }
void setAmbientSpherePreset(gpu::SphericalHarmonics::Preset preset);
void setAmbientMap(gpu::TexturePointer ambientMap);
gpu::TexturePointer getAmbientMap() const { return _ambientMap; }
void setAmbientMapNumMips(uint16_t numMips);
uint16_t getAmbientMapNumMips() const { return (uint16_t) getSchema()._ambientMapNumMips; }
uint16_t getAmbientMapNumMips() const { return (uint16_t) _ambientSchemaBuffer->mapNumMips; }
// Schema to access the attribute values of the light
class Schema {
// Light Schema
class LightSchema {
public:
Vec4 _position{0.0f, 0.0f, 0.0f, 1.0f};
Vec3 _direction{0.0f, 0.0f, -1.0f};
float _ambientIntensity{0.0f};
Color _color{1.0f};
float _intensity{1.0f};
Vec4 _attenuation{0.1f, 1.0f, 0.0f, 0.0f};
Vec4 _spot{0.0f, 0.0f, 0.0f, 0.0f};
Vec4 _shadow{0.0f};
float _ambientMapNumMips{ 0.0f };
Vec3 _control{ 0.0f, 0.0f, 0.0f };
gpu::SphericalHarmonics _ambientSphere;
LightVolume volume;
LightIrradiance irradiance;
};
const UniformBufferView& getSchemaBuffer() const { return _schemaBuffer; }
class AmbientSchema {
public:
float intensity { 0.0f };
float mapNumMips { 0.0f };
float spare1;
float spare2;
gpu::SphericalHarmonics ambientSphere;
};
using LightSchemaBuffer = gpu::StructBuffer<LightSchema>;
using AmbientSchemaBuffer = gpu::StructBuffer<AmbientSchema>;
const LightSchemaBuffer& getLightSchemaBuffer() const { return _lightSchemaBuffer; }
const AmbientSchemaBuffer& getAmbientSchemaBuffer() const { return _ambientSchemaBuffer; }
protected:
Flags _flags{ 0 };
UniformBufferView _schemaBuffer;
LightSchemaBuffer _lightSchemaBuffer;
AmbientSchemaBuffer _ambientSchemaBuffer;
Transform _transform;
gpu::TexturePointer _ambientMap;
const Schema& getSchema() const { return _schemaBuffer.get<Schema>(); }
Schema& editSchema() { return _schemaBuffer.edit<Schema>(); }
Type _type { SUN };
float _spotCos { -1.0f }; // stored here to be able to reset the spot angle when turning the type spot on/off
void updateLightRadius();
};
typedef std::shared_ptr< Light > LightPointer;

View file

@ -11,121 +11,51 @@
<@if not MODEL_LIGHT_SLH@>
<@def MODEL_LIGHT_SLH@>
struct SphericalHarmonics {
vec4 L00;
vec4 L1m1;
vec4 L10;
vec4 L11;
vec4 L2m2;
vec4 L2m1;
vec4 L20;
vec4 L21;
vec4 L22;
<@include model/LightVolume.shared.slh@>
<@include model/LightIrradiance.shared.slh@>
// NOw lets define Light
struct Light {
LightVolume volume;
LightIrradiance irradiance;
};
vec4 evalSphericalLight(SphericalHarmonics sh, vec3 direction ) {
bool light_isSpot(Light l) { return lightVolume_isSpot(l.volume); }
vec3 dir = direction.xyz;
vec3 getLightPosition(Light l) { return lightVolume_getPosition(l.volume); }
vec3 getLightDirection(Light l) { return lightVolume_getDirection(l.volume); }
const float C1 = 0.429043;
const float C2 = 0.511664;
const float C3 = 0.743125;
const float C4 = 0.886227;
const float C5 = 0.247708;
vec4 value = C1 * sh.L22 * (dir.x * dir.x - dir.y * dir.y) +
C3 * sh.L20 * dir.z * dir.z +
C4 * sh.L00 - C5 * sh.L20 +
2.0 * C1 * ( sh.L2m2 * dir.x * dir.y +
sh.L21 * dir.x * dir.z +
sh.L2m1 * dir.y * dir.z ) +
2.0 * C2 * ( sh.L11 * dir.x +
sh.L1m1 * dir.y +
sh.L10 * dir.z ) ;
return value;
}
struct Light {
vec4 _position;
vec4 _direction;
vec4 _color;
vec4 _attenuation;
vec4 _spot;
vec4 _shadow;
vec4 _control;
vec3 getLightColor(Light l) { return lightIrradiance_getColor(l.irradiance); }
float getLightIntensity(Light l) { return lightIrradiance_getIntensity(l.irradiance); }
vec3 getLightIrradiance(Light l) { return lightIrradiance_getIrradiance(l.irradiance); }
// AMbient lighting needs extra info provided from a different Buffer
<@include model/SphericalHarmonics.shared.slh@>
// Light Ambient
struct LightAmbient {
vec4 _ambient;
SphericalHarmonics _ambientSphere;
};
vec3 getLightPosition(Light l) { return l._position.xyz; }
vec3 getLightDirection(Light l) { return l._direction.xyz; } // direction is -Z axis
SphericalHarmonics getLightAmbientSphere(LightAmbient l) { return l._ambientSphere; }
vec3 getLightColor(Light l) { return l._color.rgb; }
float getLightIntensity(Light l) { return l._color.w; }
float getLightAmbientIntensity(Light l) { return l._direction.w; }
float getLightSpotAngleCos(Light l) {
return l._spot.x;
float getLightAmbientIntensity(LightAmbient l) { return l._ambient.x; }
bool getLightHasAmbientMap(LightAmbient l) { return l._ambient.y > 0; }
float getLightAmbientMapNumMips(LightAmbient l) { return l._ambient.y; }
<@func declareLightBuffer(N)@>
<@if N@>
uniform lightBuffer {
Light lightArray[<$N$>];
};
Light getLight(int index) {
return lightArray[index];
}
vec2 getLightSpotOutsideNormal2(Light l) {
return vec2(-l._spot.y, l._spot.x);
}
float evalLightSpotAttenuation(Light l, float cosA) {
return pow(cosA, l._spot.w);
}
float getLightRadius(Light l) {
return l._attenuation.x;
}
float getLightSquareRadius(Light l) {
return getLightRadius(l) * getLightRadius(l);
}
float getLightCutoffRadius(Light l) {
return l._attenuation.z;
}
float getLightCutoffSquareRadius(Light l) {
return getLightCutoffRadius(l) * getLightCutoffRadius(l);
}
float getLightShowContour(Light l) {
return l._control.w;
}
// Light is the light source its self, d is the light's distance calculated as length(unnormalized light vector).
float evalLightAttenuation(Light l, float d) {
float radius = getLightRadius(l);
float denom = d / radius + 1.0;
float attenuation = 1.0 / (denom * denom);
float cutoff = getLightCutoffRadius(l);
// "Fade" the edges of light sources to make things look a bit more attractive.
// Note: this tends to look a bit odd at lower exponents.
attenuation *= min(1.0, max(0.0, -(d - cutoff)));
return attenuation;
}
SphericalHarmonics getLightAmbientSphere(Light l) {
return l._ambientSphere;
}
bool getLightHasAmbientMap(Light l) {
return l._control.x > 0.0;
}
float getLightAmbientMapNumMips(Light l) {
return l._control.x;
}
<@else@>
uniform lightBuffer {
Light light;
};
@ -133,42 +63,39 @@ Light getLight() {
return light;
}
<@endif@>
<@endfunc@>
bool clipFragToLightVolumePoint(Light light, vec3 fragPos, out vec4 fragLightVecLen2) {
fragLightVecLen2.xyz = getLightPosition(light) - fragPos.xyz;
fragLightVecLen2.w = dot(fragLightVecLen2.xyz, fragLightVecLen2.xyz);
// Kill if too far from the light center
if (fragLightVecLen2.w > getLightCutoffSquareRadius(light)) {
return false;
}
return true;
<@func declareLightAmbientBuffer(N)@>
<@if N@>
uniform lightAmbientBuffer {
LightAmbient lightAmbientArray[<$N$>];
};
LightAmbient getLightAmbient(int index) {
return lightAmbientArray[index];
}
bool clipFragToLightVolumeSpot(Light light, vec3 fragPos, out vec4 fragLightVecLen2, out vec4 fragLightDirLen, out float cosSpotAngle) {
fragLightVecLen2.xyz = getLightPosition(light) - fragPos.xyz;
fragLightVecLen2.w = dot(fragLightVecLen2.xyz, fragLightVecLen2.xyz);
<@else@>
uniform lightAmbientBuffer {
LightAmbient lightAmbient;
};
// Kill if too far from the light center
if (fragLightVecLen2.w > getLightCutoffSquareRadius(light)) {
return false;
}
// Allright we re valid in the volume
fragLightDirLen.w = length(fragLightVecLen2.xyz);
fragLightDirLen.xyz = fragLightVecLen2.xyz / fragLightDirLen.w;
// Kill if not in the spot light (ah ah !)
cosSpotAngle = max(-dot(fragLightDirLen.xyz, getLightDirection(light)), 0.0);
if (cosSpotAngle < getLightSpotAngleCos(light)) {
return false;
}
return true;
LightAmbient getLightAmbient() {
return lightAmbient;
}
<@endif@>
<@endfunc@>
<@endif@>

View file

@ -0,0 +1,56 @@
// glsl / C++ compatible source as interface for Light
#ifndef LightIrradiance_Shared_slh
#define LightIrradiance_Shared_slh
//
// Created by Sam Gateau on 14/9/2016.
// Copyright 2014 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
//
#define LightIrradianceConstRef LightIrradiance
struct LightIrradiance {
vec4 colorIntensity;
// falloffRadius, cutoffRadius, falloffSpot, spare
vec4 attenuation;
};
vec3 lightIrradiance_getColor(LightIrradiance li) { return li.colorIntensity.xyz; }
float lightIrradiance_getIntensity(LightIrradiance li) { return li.colorIntensity.w; }
vec3 lightIrradiance_getIrradiance(LightIrradiance li) { return li.colorIntensity.xyz * li.colorIntensity.w; }
float lightIrradiance_getFalloffRadius(LightIrradiance li) { return li.attenuation.x; }
float lightIrradiance_getCutoffRadius(LightIrradiance li) { return li.attenuation.y; }
float lightIrradiance_getFalloffSpot(LightIrradiance li) { return li.attenuation.z; }
// Light is the light source its self, d is the light's distance calculated as length(unnormalized light vector).
float lightIrradiance_evalLightAttenuation(LightIrradiance li, float d) {
float radius = lightIrradiance_getFalloffRadius(li);
float cutoff = lightIrradiance_getCutoffRadius(li);
float denom = (d / radius) + 1.0;
float attenuation = 1.0 / (denom * denom);
// "Fade" the edges of light sources to make things look a bit more attractive.
// Note: this tends to look a bit odd at lower exponents.
attenuation *= min(1, max(0, -(d - cutoff)));
return attenuation;
}
float lightIrradiance_evalLightSpotAttenuation(LightIrradiance li, float cosA) {
return pow(cosA, lightIrradiance_getFalloffSpot(li));
}
#endif
// <@if 1@>
// Trigger Scribe include
// <@endif@> <!def that !>

View file

@ -0,0 +1,66 @@
// glsl / C++ compatible source as interface for Light
#ifndef LightVolume_Shared_slh
#define LightVolume_Shared_slh
// Light.shared.slh
// libraries/model/src/model
//
// Created by Sam Gateau on 14/9/2016.
// Copyright 2014 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
//
#define LightVolumeConstRef LightVolume
struct LightVolume {
vec4 positionRadius;
vec4 directionSpotCos;
};
bool lightVolume_isPoint(LightVolume lv) { return bool(lv.directionSpotCos.w < 0.f); }
bool lightVolume_isSpot(LightVolume lv) { return bool(lv.directionSpotCos.w >= 0.f); }
vec3 lightVolume_getPosition(LightVolume lv) { return lv.positionRadius.xyz; }
float lightVolume_getRadius(LightVolume lv) { return lv.positionRadius.w; }
float lightVolume_getRadiusSquare(LightVolume lv) { return lv.positionRadius.w * lv.positionRadius.w; }
vec3 lightVolume_getDirection(LightVolume lv) { return lv.directionSpotCos.xyz; } // direction is -Z axis
float lightVolume_getSpotAngleCos(LightVolume lv) { return lv.directionSpotCos.w; }
vec2 lightVolume_getSpotOutsideNormal2(LightVolume lv) { return vec2(-sqrt(1.0 - lv.directionSpotCos.w * lv.directionSpotCos.w), lv.directionSpotCos.w); }
bool lightVolume_clipFragToLightVolumePoint(LightVolume lv, vec3 fragPos, out vec4 fragLightVecLen2) {
fragLightVecLen2.xyz = lightVolume_getPosition(lv) - fragPos.xyz;
fragLightVecLen2.w = dot(fragLightVecLen2.xyz, fragLightVecLen2.xyz);
// Kill if too far from the light center
return (fragLightVecLen2.w <= lightVolume_getRadiusSquare(lv));
}
bool lightVolume_clipFragToLightVolumeSpotSide(LightVolume lv, vec4 fragLightDirLen, out float cosSpotAngle) {
// Kill if not in the spot light (ah ah !)
cosSpotAngle = max(-dot(fragLightDirLen.xyz, lightVolume_getDirection(lv)), 0.0);
return (cosSpotAngle >= lightVolume_getSpotAngleCos(lv));
}
bool lightVolume_clipFragToLightVolumeSpot(LightVolume lv, vec3 fragPos, out vec4 fragLightVecLen2, out vec4 fragLightDirLen, out float cosSpotAngle) {
if (!lightVolume_clipFragToLightVolumePoint(lv, fragPos, fragLightVecLen2)) {
return false;
}
// Allright we re valid in the volume
fragLightDirLen.w = length(fragLightVecLen2.xyz);
fragLightDirLen.xyz = fragLightVecLen2.xyz / fragLightDirLen.w;
return lightVolume_clipFragToLightVolumeSpotSide(lv, fragLightDirLen, cosSpotAngle);
}
#endif
// <@if 1@>
// Trigger Scribe include
// <@endif@> <!def that !>

View file

@ -0,0 +1,57 @@
// glsl / C++ compatible source as interface for Light
#ifndef SphericalHarmonics_Shared_slh
#define SphericalHarmonics_Shared_slh
// SphericalHarmonics.shared.slh
// libraries/model/src/model
//
// Created by Sam Gateau on 14/9/2016.
// Copyright 2014 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
//
#define SphericalHarmonicsConstRef SphericalHarmonics
struct SphericalHarmonics {
vec4 L00;
vec4 L1m1;
vec4 L10;
vec4 L11;
vec4 L2m2;
vec4 L2m1;
vec4 L20;
vec4 L21;
vec4 L22;
};
vec4 sphericalHarmonics_evalSphericalLight(SphericalHarmonicsConstRef sh, vec3 direction) {
vec3 dir = direction.xyz;
const float C1 = 0.429043;
const float C2 = 0.511664;
const float C3 = 0.743125;
const float C4 = 0.886227;
const float C5 = 0.247708;
vec4 value = C1 * sh.L22 * (dir.x * dir.x - dir.y * dir.y) +
C3 * sh.L20 * dir.z * dir.z +
C4 * sh.L00 - C5 * sh.L20 +
2.0 * C1 * (sh.L2m2 * dir.x * dir.y +
sh.L21 * dir.x * dir.z +
sh.L2m1 * dir.y * dir.z) +
2.0 * C2 * (sh.L11 * dir.x +
sh.L1m1 * dir.y +
sh.L10 * dir.z);
return value;
}
#endif
// <@if 1@>
// Trigger Scribe include
// <@endif@> <!def that !> End C++ compatible

View file

@ -184,46 +184,46 @@ void AmbientOcclusionEffect::configure(const Config& config) {
const double RADIUS_POWER = 6.0;
const auto& radius = config.radius;
if (radius != _parametersBuffer->getRadius()) {
auto& current = _parametersBuffer->radiusInfo;
auto& current = _parametersBuffer.edit().radiusInfo;
current.x = radius;
current.y = radius * radius;
current.z = (float)(1.0 / pow((double)radius, RADIUS_POWER));
}
if (config.obscuranceLevel != _parametersBuffer->getObscuranceLevel()) {
auto& current = _parametersBuffer->radiusInfo;
auto& current = _parametersBuffer.edit().radiusInfo;
current.w = config.obscuranceLevel;
}
if (config.falloffBias != _parametersBuffer->getFalloffBias()) {
auto& current = _parametersBuffer->ditheringInfo;
auto& current = _parametersBuffer.edit().ditheringInfo;
current.z = config.falloffBias;
}
if (config.edgeSharpness != _parametersBuffer->getEdgeSharpness()) {
auto& current = _parametersBuffer->blurInfo;
auto& current = _parametersBuffer.edit().blurInfo;
current.x = config.edgeSharpness;
}
if (config.blurDeviation != _parametersBuffer->getBlurDeviation()) {
auto& current = _parametersBuffer->blurInfo;
auto& current = _parametersBuffer.edit().blurInfo;
current.z = config.blurDeviation;
shouldUpdateGaussian = true;
}
if (config.numSpiralTurns != _parametersBuffer->getNumSpiralTurns()) {
auto& current = _parametersBuffer->sampleInfo;
auto& current = _parametersBuffer.edit().sampleInfo;
current.z = config.numSpiralTurns;
}
if (config.numSamples != _parametersBuffer->getNumSamples()) {
auto& current = _parametersBuffer->sampleInfo;
auto& current = _parametersBuffer.edit().sampleInfo;
current.x = config.numSamples;
current.y = 1.0f / config.numSamples;
}
if (config.fetchMipsEnabled != _parametersBuffer->isFetchMipsEnabled()) {
auto& current = _parametersBuffer->sampleInfo;
auto& current = _parametersBuffer.edit().sampleInfo;
current.w = (float)config.fetchMipsEnabled;
}
@ -232,26 +232,26 @@ void AmbientOcclusionEffect::configure(const Config& config) {
}
if (config.perspectiveScale != _parametersBuffer->getPerspectiveScale()) {
_parametersBuffer->resolutionInfo.z = config.perspectiveScale;
_parametersBuffer.edit().resolutionInfo.z = config.perspectiveScale;
}
if (config.resolutionLevel != _parametersBuffer->getResolutionLevel()) {
auto& current = _parametersBuffer->resolutionInfo;
auto& current = _parametersBuffer.edit().resolutionInfo;
current.x = (float) config.resolutionLevel;
}
if (config.blurRadius != _parametersBuffer->getBlurRadius()) {
auto& current = _parametersBuffer->blurInfo;
auto& current = _parametersBuffer.edit().blurInfo;
current.y = (float)config.blurRadius;
shouldUpdateGaussian = true;
}
if (config.ditheringEnabled != _parametersBuffer->isDitheringEnabled()) {
auto& current = _parametersBuffer->ditheringInfo;
auto& current = _parametersBuffer.edit().ditheringInfo;
current.x = (float)config.ditheringEnabled;
}
if (config.borderingEnabled != _parametersBuffer->isBorderingEnabled()) {
auto& current = _parametersBuffer->ditheringInfo;
auto& current = _parametersBuffer.edit().ditheringInfo;
current.w = (float)config.borderingEnabled;
}
@ -334,7 +334,7 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getVBlurPipeline() {
}
void AmbientOcclusionEffect::updateGaussianDistribution() {
auto coefs = _parametersBuffer->_gaussianCoefs;
auto coefs = _parametersBuffer.edit()._gaussianCoefs;
GaussianDistribution::evalSampling(coefs, Parameters::GAUSSIAN_COEFS_LENGTH, _parametersBuffer->getBlurRadius(), _parametersBuffer->getBlurDeviation());
}
@ -351,6 +351,10 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext
auto sourceViewport = args->_viewport;
auto occlusionViewport = sourceViewport;
if (!_gpuTimer) {
_gpuTimer = std::make_shared < gpu::RangeTimer>(__FUNCTION__);
}
if (!_framebuffer) {
_framebuffer = std::make_shared<AmbientOcclusionFramebuffer>();
}
@ -384,7 +388,7 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext
gpu::doInBatch(args->_context, [=](gpu::Batch& batch) {
batch.enableStereo(false);
_gpuTimer.begin(batch);
_gpuTimer->begin(batch);
batch.setViewportTransform(occlusionViewport);
batch.setProjectionTransform(glm::mat4());
@ -428,12 +432,12 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext
batch.setResourceTexture(AmbientOcclusionEffect_LinearDepthMapSlot, nullptr);
batch.setResourceTexture(AmbientOcclusionEffect_OcclusionMapSlot, nullptr);
_gpuTimer.end(batch);
_gpuTimer->end(batch);
});
// Update the timer
auto config = std::static_pointer_cast<Config>(renderContext->jobConfig);
config->setGPUBatchRunTime(_gpuTimer.getGPUAverage(), _gpuTimer.getBatchAverage());
config->setGPUBatchRunTime(_gpuTimer->getGPUAverage(), _gpuTimer->getBatchAverage());
}
@ -447,7 +451,7 @@ void DebugAmbientOcclusion::configure(const Config& config) {
auto cursorPos = glm::vec2(_parametersBuffer->pixelInfo);
if (cursorPos != config.debugCursorTexcoord) {
_parametersBuffer->pixelInfo = glm::vec4(config.debugCursorTexcoord, 0.0f, 0.0f);
_parametersBuffer.edit().pixelInfo = glm::vec4(config.debugCursorTexcoord, 0.0f, 0.0f);
}
}

View file

@ -12,6 +12,7 @@
#ifndef hifi_AmbientOcclusionEffect_h
#define hifi_AmbientOcclusionEffect_h
#include <string>
#include <DependencyManager.h>
#include "render/DrawTask.h"
@ -103,26 +104,6 @@ signals:
void dirty();
};
namespace gpu {
template <class T> class UniformBuffer : public gpu::BufferView {
public:
static BufferPointer makeBuffer() {
T t;
return std::make_shared<gpu::Buffer>(sizeof(T), (const gpu::Byte*) &t);
}
~UniformBuffer<T>() {};
UniformBuffer<T>() : gpu::BufferView(makeBuffer()) {}
const T* operator ->() const { return &get<T>(); }
T* operator ->() {
return &edit<T>(0);
}
};
}
class AmbientOcclusionEffect {
public:
using Inputs = render::VaryingSet3<DeferredFrameTransformPointer, DeferredFramebufferPointer, LinearDepthFramebufferPointer>;
@ -171,7 +152,7 @@ public:
bool isDitheringEnabled() const { return ditheringInfo.x; }
bool isBorderingEnabled() const { return ditheringInfo.w; }
};
using ParametersBuffer = gpu::UniformBuffer<Parameters>;
using ParametersBuffer = gpu::StructBuffer<Parameters>;
private:
void updateGaussianDistribution();
@ -188,7 +169,7 @@ private:
AmbientOcclusionFramebufferPointer _framebuffer;
gpu::RangeTimer _gpuTimer;
gpu::RangeTimerPointer _gpuTimer;
friend class DebugAmbientOcclusion;
};
@ -231,7 +212,7 @@ private:
Parameters() {}
};
gpu::UniformBuffer<Parameters> _parametersBuffer;
gpu::StructBuffer<Parameters> _parametersBuffer;
const gpu::PipelinePointer& getDebugPipeline();

View file

@ -396,7 +396,6 @@ void DebugDeferredBuffer::run(const SceneContextPointer& sceneContext, const Ren
const auto geometryBuffer = DependencyManager::get<GeometryCache>();
const auto framebufferCache = DependencyManager::get<FramebufferCache>();
const auto textureCache = DependencyManager::get<TextureCache>();
const auto& lightStage = DependencyManager::get<DeferredLightingEffect>()->getLightStage();
glm::mat4 projMat;
Transform viewMat;
@ -418,8 +417,13 @@ void DebugDeferredBuffer::run(const SceneContextPointer& sceneContext, const Ren
batch.setResourceTexture(Depth, deferredFramebuffer->getPrimaryDepthTexture());
batch.setResourceTexture(Lighting, deferredFramebuffer->getLightingTexture());
}
if (!lightStage.lights.empty()) {
batch.setResourceTexture(Shadow, lightStage.lights[0]->shadow.framebuffer->getDepthStencilBuffer());
auto deferredLightingEffect = DependencyManager::get<DeferredLightingEffect>();
assert(deferredLightingEffect->getLightStage()->getNumLights() > 0);
auto lightAndShadow = deferredLightingEffect->getLightStage()->getLightAndShadow(0);
const auto& globalShadow = lightAndShadow.second;
if (globalShadow) {
batch.setResourceTexture(Shadow, globalShadow->map);
}
if (linearDepthTarget) {

View file

@ -24,6 +24,7 @@ uniform sampler2D specularMap;
// the depth texture
uniform sampler2D depthMap;
uniform sampler2D linearZeyeMap;
// the obscurance texture
uniform sampler2D obscuranceMap;
@ -86,6 +87,40 @@ DeferredFragment unpackDeferredFragmentNoPosition(vec2 texcoord) {
}
DeferredFragment unpackDeferredFragmentNoPositionNoAmbient(vec2 texcoord) {
vec4 normalVal;
vec4 diffuseVal;
DeferredFragment frag;
frag.depthVal = -1;
normalVal = texture(normalMap, texcoord);
diffuseVal = texture(albedoMap, texcoord);
// Unpack the normal from the map
frag.normal = unpackNormal(normalVal.xyz);
frag.roughness = normalVal.a;
// Diffuse color and unpack the mode and the metallicness
frag.albedo = diffuseVal.xyz;
frag.scattering = 0.0;
unpackModeMetallic(diffuseVal.w, frag.mode, frag.metallic);
//frag.emissive = specularVal.xyz;
frag.obscurance = 1.0;
if (frag.metallic <= 0.5) {
frag.metallic = 0.0;
frag.fresnel = vec3(0.03); // Default Di-electric fresnel value
} else {
frag.fresnel = vec3(diffuseVal.xyz);
frag.metallic = 1.0;
}
return frag;
}
<@include DeferredTransform.slh@>
<$declareDeferredFrameTransform()$>
@ -103,6 +138,19 @@ vec4 unpackDeferredPosition(DeferredFrameTransform deferredTransform, float dept
return vec4(evalEyePositionFromZeye(side, Zeye, texcoord), 1.0);
}
vec4 unpackDeferredPositionFromZeye(vec2 texcoord) {
float Zeye = -texture(linearZeyeMap, texcoord).x;
int side = 0;
if (isStereo()) {
if (texcoord.x > 0.5) {
texcoord.x -= 0.5;
side = 1;
}
texcoord.x *= 2.0;
}
return vec4(evalEyePositionFromZeye(side, Zeye, texcoord), 1.0);
}
DeferredFragment unpackDeferredFragment(DeferredFrameTransform deferredTransform, vec2 texcoord) {
float depthValue = texture(depthMap, texcoord).r;

View file

@ -14,6 +14,8 @@
<@include model/Light.slh@>
<@include LightingModel.slh@>
<$declareLightBuffer()$>
<$declareLightAmbientBuffer()$>
<@include LightAmbient.slh@>
<@include LightDirectional.slh@>
@ -28,7 +30,11 @@
// Get light
Light light = getLight();
LightAmbient lightAmbient = getLightAmbient();
vec3 lightDirection = getLightDirection(light);
vec3 lightIrradiance = getLightIrradiance(light);
vec3 color = vec3(0.0);
<@endfunc@>
@ -37,7 +43,7 @@
<@func declareEvalAmbientGlobalColor()@>
vec3 evalAmbientGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 fresnel, float metallic, float roughness) {
<$prepareGlobalLight()$>
color += albedo * getLightColor(light) * obscurance * getLightAmbientIntensity(light);
color += albedo * getLightColor(light) * obscurance * getLightAmbientIntensity(lightAmbient);
return color;
}
<@endfunc@>
@ -62,7 +68,7 @@ vec3 albedo, vec3 fresnel, float metallic, float roughness
// Ambient
vec3 ambientDiffuse;
vec3 ambientSpecular;
evalLightingAmbient(ambientDiffuse, ambientSpecular, light, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance
<@if supportScattering@>
,scattering, midNormalCurvature, lowNormalCurvature
<@endif@> );
@ -73,7 +79,7 @@ vec3 albedo, vec3 fresnel, float metallic, float roughness
// Directional
vec3 directionalDiffuse;
vec3 directionalSpecular;
evalLightingDirectional(directionalDiffuse, directionalSpecular, light, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation
<@if supportScattering@>
,scattering, midNormalCurvature, lowNormalCurvature
<@endif@> );
@ -106,7 +112,7 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu
// Ambient
vec3 ambientDiffuse;
vec3 ambientSpecular;
evalLightingAmbient(ambientDiffuse, ambientSpecular, light, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance
<@if supportScattering@>
,scattering, midNormalCurvature, lowNormalCurvature
<@endif@>
@ -118,7 +124,7 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu
// Directional
vec3 directionalDiffuse;
vec3 directionalSpecular;
evalLightingDirectional(directionalDiffuse, directionalSpecular, light, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation
<@if supportScattering@>
,scattering, midNormalCurvature, lowNormalCurvature
<@endif@>
@ -134,6 +140,7 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu
<@func declareEvalLightmappedColor()@>
vec3 evalLightmappedColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 normal, vec3 albedo, vec3 lightmap) {
Light light = getLight();
LightAmbient ambient = getLightAmbient();
// Catch normals perpendicular to the projection plane, hence the magic number for the threshold
// It should be just 0, but we have inaccuracy so we overshoot
@ -149,7 +156,7 @@ vec3 evalLightmappedColor(mat4 invViewMat, float shadowAttenuation, float obscur
vec3 diffuseLight = lightAttenuation * lightmap;
// Ambient light is the lightmap when in shadow
vec3 ambientLight = (1.0 - lightAttenuation) * lightmap * getLightAmbientIntensity(light);
vec3 ambientLight = (1.0 - lightAttenuation) * lightmap * getLightAmbientIntensity(ambient);
return isLightmapEnabled() * obscurance * albedo * (diffuseLight + ambientLight);
}
@ -171,7 +178,7 @@ vec3 evalGlobalLightingAlphaBlended(mat4 invViewMat, float shadowAttenuation, fl
// Ambient
vec3 ambientDiffuse;
vec3 ambientSpecular;
evalLightingAmbient(ambientDiffuse, ambientSpecular, light, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance);
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance);
color += ambientDiffuse;
color += ambientSpecular / opacity;
@ -179,7 +186,7 @@ vec3 evalGlobalLightingAlphaBlended(mat4 invViewMat, float shadowAttenuation, fl
// Directional
vec3 directionalDiffuse;
vec3 directionalSpecular;
evalLightingDirectional(directionalDiffuse, directionalSpecular, light, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation);
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation);
color += directionalDiffuse;
color += directionalSpecular / opacity;

View file

@ -24,7 +24,7 @@
#include "FramebufferCache.h"
#include "deferred_light_vert.h"
#include "deferred_light_limited_vert.h"
#include "deferred_light_point_vert.h"
#include "deferred_light_spot_vert.h"
#include "directional_light_frag.h"
@ -35,6 +35,8 @@
#include "directional_ambient_light_shadow_frag.h"
#include "directional_skybox_light_shadow_frag.h"
#include "local_lights_shading_frag.h"
#include "local_lights_drawOutline_frag.h"
#include "point_light_frag.h"
#include "spot_light_frag.h"
@ -42,11 +44,10 @@ using namespace render;
struct LightLocations {
int radius{ -1 };
int ambientSphere{ -1 };
int lightBufferUnit{ -1 };
int ambientBufferUnit { -1 };
int lightIndexBufferUnit { -1 };
int texcoordFrameTransform{ -1 };
int sphereParam{ -1 };
int coneParam{ -1 };
int deferredFrameTransformBuffer{ -1 };
int subsurfaceScatteringParametersBuffer{ -1 };
int shadowTransformBuffer{ -1 };
@ -60,6 +61,7 @@ enum DeferredShader_MapSlot {
DEFERRED_BUFFER_OBSCURANCE_UNIT = 4,
SHADOW_MAP_UNIT = 5,
SKYBOX_MAP_UNIT = 6,
DEFERRED_BUFFER_LINEAR_DEPTH_UNIT,
DEFERRED_BUFFER_CURVATURE_UNIT,
DEFERRED_BUFFER_DIFFUSED_CURVATURE_UNIT,
SCATTERING_LUT_UNIT,
@ -71,9 +73,26 @@ enum DeferredShader_BufferSlot {
SCATTERING_PARAMETERS_BUFFER_SLOT,
LIGHTING_MODEL_BUFFER_SLOT = render::ShapePipeline::Slot::LIGHTING_MODEL,
LIGHT_GPU_SLOT = render::ShapePipeline::Slot::LIGHT,
LIGHT_AMBIENT_SLOT,
LIGHT_INDEX_GPU_SLOT,
LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT,
LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT,
LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT,
};
static void loadLightProgram(const char* vertSource, const char* fragSource, bool lightVolume, gpu::PipelinePointer& program, LightLocationsPtr& locations);
static void loadLightVolumeProgram(const char* vertSource, const char* fragSource, bool front, gpu::PipelinePointer& program, LightLocationsPtr& locations);
const char no_light_frag[] =
R"SCRIBE(
out vec4 _fragColor;
void main(void) {
_fragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
)SCRIBE"
;
void DeferredLightingEffect::init() {
_directionalLightLocations = std::make_shared<LightLocations>();
@ -84,6 +103,8 @@ void DeferredLightingEffect::init() {
_directionalAmbientSphereLightShadowLocations = std::make_shared<LightLocations>();
_directionalSkyboxLightShadowLocations = std::make_shared<LightLocations>();
_localLightLocations = std::make_shared<LightLocations>();
_localLightOutlineLocations = std::make_shared<LightLocations>();
_pointLightLocations = std::make_shared<LightLocations>();
_spotLightLocations = std::make_shared<LightLocations>();
@ -95,26 +116,44 @@ void DeferredLightingEffect::init() {
loadLightProgram(deferred_light_vert, directional_ambient_light_shadow_frag, false, _directionalAmbientSphereLightShadow, _directionalAmbientSphereLightShadowLocations);
loadLightProgram(deferred_light_vert, directional_skybox_light_shadow_frag, false, _directionalSkyboxLightShadow, _directionalSkyboxLightShadowLocations);
loadLightProgram(deferred_light_limited_vert, point_light_frag, true, _pointLight, _pointLightLocations);
loadLightProgram(deferred_light_spot_vert, spot_light_frag, true, _spotLight, _spotLightLocations);
loadLightProgram(deferred_light_vert, local_lights_shading_frag, true, _localLight, _localLightLocations);
loadLightProgram(deferred_light_vert, local_lights_drawOutline_frag, true, _localLightOutline, _localLightOutlineLocations);
loadLightVolumeProgram(deferred_light_point_vert, no_light_frag, false, _pointLightBack, _pointLightLocations);
loadLightVolumeProgram(deferred_light_point_vert, no_light_frag, true, _pointLightFront, _pointLightLocations);
loadLightVolumeProgram(deferred_light_spot_vert, no_light_frag, false, _spotLightBack, _spotLightLocations);
loadLightVolumeProgram(deferred_light_spot_vert, no_light_frag, true, _spotLightFront, _spotLightLocations);
// Light Stage and clusters
_lightStage = std::make_shared<LightStage>();
// Allocate a global light representing the Global Directional light casting shadow (the sun) and the ambient light
_globalLights.push_back(0);
_allocatedLights.push_back(std::make_shared<model::Light>());
model::LightPointer lp = _allocatedLights[0];
lp->setType(model::Light::SUN);
// Add the global light to the light stage (for later shadow rendering)
_lightStage.addLight(lp);
lp->setDirection(glm::vec3(-1.0f));
lp->setColor(glm::vec3(1.0f));
lp->setIntensity(1.0f);
lp->setType(model::Light::SUN);
lp->setAmbientSpherePreset(gpu::SphericalHarmonics::Preset::OLD_TOWN_SQUARE);
// Add the global light to the light stage (for later shadow rendering)
_globalLights.push_back(_lightStage->addLight(lp));
_lightStage->addShadow(_globalLights[0]);
}
void DeferredLightingEffect::addLight(const model::LightPointer& light) {
assert(light);
auto lightID = _lightStage->addLight(light);
if (light->getType() == model::Light::POINT) {
_pointLights.push_back(lightID);
} else {
_spotLights.push_back(lightID);
}
}
void DeferredLightingEffect::addPointLight(const glm::vec3& position, float radius, const glm::vec3& color,
float intensity, float falloffRadius) {
addSpotLight(position, radius, color, intensity, falloffRadius);
@ -148,12 +187,15 @@ void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radiu
}
}
void DeferredLightingEffect::setupKeyLightBatch(gpu::Batch& batch, int lightBufferUnit, int skyboxCubemapUnit) {
void DeferredLightingEffect::setupKeyLightBatch(gpu::Batch& batch, int lightBufferUnit, int ambientBufferUnit, int skyboxCubemapUnit) {
PerformanceTimer perfTimer("DLE->setupBatch()");
auto keyLight = _allocatedLights[_globalLights.front()];
if (lightBufferUnit >= 0) {
batch.setUniformBuffer(lightBufferUnit, keyLight->getSchemaBuffer());
batch.setUniformBuffer(lightBufferUnit, keyLight->getLightSchemaBuffer());
}
if (keyLight->hasAmbient() && (ambientBufferUnit >= 0)) {
batch.setUniformBuffer(ambientBufferUnit, keyLight->getAmbientSchemaBuffer());
}
if (keyLight->getAmbientMap() && (skyboxCubemapUnit >= 0)) {
@ -161,7 +203,22 @@ void DeferredLightingEffect::setupKeyLightBatch(gpu::Batch& batch, int lightBuff
}
}
static void loadLightProgram(const char* vertSource, const char* fragSource, bool lightVolume, gpu::PipelinePointer& pipeline, LightLocationsPtr& locations) {
void DeferredLightingEffect::unsetKeyLightBatch(gpu::Batch& batch, int lightBufferUnit, int ambientBufferUnit, int skyboxCubemapUnit) {
auto keyLight = _allocatedLights[_globalLights.front()];
if (lightBufferUnit >= 0) {
batch.setUniformBuffer(lightBufferUnit, nullptr);
}
if (keyLight->hasAmbient() && (ambientBufferUnit >= 0)) {
batch.setUniformBuffer(ambientBufferUnit, nullptr);
}
if (keyLight->getAmbientMap() && (skyboxCubemapUnit >= 0)) {
batch.setResourceTexture(skyboxCubemapUnit, nullptr);
}
}
static gpu::ShaderPointer makeLightProgram(const char* vertSource, const char* fragSource, LightLocationsPtr& locations) {
auto VS = gpu::Shader::createVertex(std::string(vertSource));
auto PS = gpu::Shader::createPixel(std::string(fragSource));
@ -176,6 +233,7 @@ static void loadLightProgram(const char* vertSource, const char* fragSource, boo
slotBindings.insert(gpu::Shader::Binding(std::string("shadowMap"), SHADOW_MAP_UNIT));
slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), SKYBOX_MAP_UNIT));
slotBindings.insert(gpu::Shader::Binding(std::string("linearZeyeMap"), DEFERRED_BUFFER_LINEAR_DEPTH_UNIT));
slotBindings.insert(gpu::Shader::Binding(std::string("curvatureMap"), DEFERRED_BUFFER_CURVATURE_UNIT));
slotBindings.insert(gpu::Shader::Binding(std::string("diffusedCurvatureMap"), DEFERRED_BUFFER_DIFFUSED_CURVATURE_UNIT));
slotBindings.insert(gpu::Shader::Binding(std::string("scatteringLUT"), SCATTERING_LUT_UNIT));
@ -187,37 +245,53 @@ static void loadLightProgram(const char* vertSource, const char* fragSource, boo
slotBindings.insert(gpu::Shader::Binding(std::string("lightingModelBuffer"), LIGHTING_MODEL_BUFFER_SLOT));
slotBindings.insert(gpu::Shader::Binding(std::string("subsurfaceScatteringParametersBuffer"), SCATTERING_PARAMETERS_BUFFER_SLOT));
slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), LIGHT_GPU_SLOT));
slotBindings.insert(gpu::Shader::Binding(std::string("lightAmbientBuffer"), LIGHT_AMBIENT_SLOT));
slotBindings.insert(gpu::Shader::Binding(std::string("lightIndexBuffer"), LIGHT_INDEX_GPU_SLOT));
slotBindings.insert(gpu::Shader::Binding(std::string("frustumGridBuffer"), LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT));
slotBindings.insert(gpu::Shader::Binding(std::string("clusterGridBuffer"), LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT));
slotBindings.insert(gpu::Shader::Binding(std::string("clusterContentBuffer"), LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT));
gpu::Shader::makeProgram(*program, slotBindings);
locations->radius = program->getUniforms().findLocation("radius");
locations->ambientSphere = program->getUniforms().findLocation("ambientSphere.L00");
locations->texcoordFrameTransform = program->getUniforms().findLocation("texcoordFrameTransform");
locations->sphereParam = program->getUniforms().findLocation("sphereParam");
locations->coneParam = program->getUniforms().findLocation("coneParam");
locations->lightBufferUnit = program->getBuffers().findLocation("lightBuffer");
locations->ambientBufferUnit = program->getBuffers().findLocation("lightAmbientBuffer");
locations->lightIndexBufferUnit = program->getBuffers().findLocation("lightIndexBuffer");
locations->deferredFrameTransformBuffer = program->getBuffers().findLocation("deferredFrameTransformBuffer");
locations->subsurfaceScatteringParametersBuffer = program->getBuffers().findLocation("subsurfaceScatteringParametersBuffer");
locations->shadowTransformBuffer = program->getBuffers().findLocation("shadowTransformBuffer");
return program;
}
static void loadLightProgram(const char* vertSource, const char* fragSource, bool lightVolume, gpu::PipelinePointer& pipeline, LightLocationsPtr& locations) {
gpu::ShaderPointer program = makeLightProgram(vertSource, fragSource, locations);
auto state = std::make_shared<gpu::State>();
state->setColorWriteMask(true, true, true, false);
// Stencil test all the light passes for objects pixels only, not the background
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
if (lightVolume) {
state->setStencilTest(true, 0x00, gpu::State::StencilTest(1, 0xFF, gpu::LESS_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
state->setCullMode(gpu::State::CULL_BACK);
state->setDepthTest(true, false, gpu::LESS_EQUAL);
// state->setCullMode(gpu::State::CULL_FRONT);
// state->setDepthTest(true, false, gpu::GREATER_EQUAL);
//state->setDepthClampEnable(true);
// TODO: We should use DepthClamp and avoid changing geometry for inside /outside cases
// additive blending
state->setBlendFunction(true, gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
} else {
// Stencil test all the light passes for objects pixels only, not the background
state->setStencilTest(true, 0x00, gpu::State::StencilTest(0, 0x01, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
state->setCullMode(gpu::State::CULL_BACK);
// additive blending
state->setBlendFunction(true, gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
@ -226,6 +300,39 @@ static void loadLightProgram(const char* vertSource, const char* fragSource, boo
}
static void loadLightVolumeProgram(const char* vertSource, const char* fragSource, bool front, gpu::PipelinePointer& pipeline, LightLocationsPtr& locations) {
gpu::ShaderPointer program = makeLightProgram(vertSource, fragSource, locations);
auto state = std::make_shared<gpu::State>();
// Stencil test all the light passes for objects pixels only, not the background
if (front) {
state->setCullMode(gpu::State::CULL_BACK);
state->setDepthTest(true, false, gpu::LESS_EQUAL);
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_DECR, gpu::State::STENCIL_OP_KEEP));
// state->setDepthClampEnable(true);
// TODO: We should use DepthClamp and avoid changing geometry for inside /outside cases
// additive blending
// state->setBlendFunction(true, gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
//state->setColorWriteMask(true, true, true, false);
state->setColorWriteMask(false, false, false, false);
} else {
state->setCullMode(gpu::State::CULL_FRONT);
state->setDepthTest(true, false, gpu::LESS_EQUAL);
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_INCR, gpu::State::STENCIL_OP_KEEP));
// additive blending
// state->setBlendFunction(true, gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
// state->setColorWriteMask(true, true, true, false);
state->setColorWriteMask(false, false, false, false);
}
pipeline = gpu::Pipeline::create(program, state);
}
void DeferredLightingEffect::setGlobalLight(const model::LightPointer& light) {
auto globalLight = _allocatedLights.front();
globalLight->setDirection(light->getDirection());
@ -236,11 +343,50 @@ void DeferredLightingEffect::setGlobalLight(const model::LightPointer& light) {
globalLight->setAmbientMap(light->getAmbientMap());
}
#include <shared/Shapes.h>
model::MeshPointer DeferredLightingEffect::getPointLightMesh() {
if (!_pointLightMesh) {
_pointLightMesh = std::make_shared<model::Mesh>();
// let's use a icosahedron
auto solid = geometry::icosahedron();
solid.fitDimension(1.05f); // scaled to 1.05 meters, it will be scaled by the shader accordingly to the light size
int verticesSize = (int) (solid.vertices.size() * 3 * sizeof(float));
float* vertexData = (float*) solid.vertices.data();
_pointLightMesh->setVertexBuffer(gpu::BufferView(new gpu::Buffer(verticesSize, (gpu::Byte*) vertexData), gpu::Element::VEC3F_XYZ));
int nbIndices = (int) solid.faces.size() * 3;
gpu::uint16* indexData = new gpu::uint16[nbIndices];
gpu::uint16* index = indexData;
for (auto face : solid.faces) {
*(index++) = face[0];
*(index++) = face[1];
*(index++) = face[2];
}
_pointLightMesh->setIndexBuffer(gpu::BufferView(new gpu::Buffer(sizeof(unsigned short) * nbIndices, (gpu::Byte*) indexData), gpu::Element::INDEX_UINT16));
delete[] indexData;
std::vector<model::Mesh::Part> parts;
parts.push_back(model::Mesh::Part(0, nbIndices, 0, model::Mesh::TRIANGLES));
parts.push_back(model::Mesh::Part(0, nbIndices, 0, model::Mesh::LINE_STRIP)); // outline version
_pointLightMesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part), (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL));
}
return _pointLightMesh;
}
model::MeshPointer DeferredLightingEffect::getSpotLightMesh() {
if (!_spotLightMesh) {
_spotLightMesh = std::make_shared<model::Mesh>();
int slices = 32;
int slices = 16;
int rings = 3;
int vertices = 2 + rings * slices;
int originVertex = vertices - 2;
@ -362,6 +508,7 @@ void PreparePrimaryFramebuffer::run(const SceneContextPointer& sceneContext, con
_primaryFramebuffer->setDepthStencilBuffer(primaryDepthTexture, depthFormat);
}
primaryFramebuffer = _primaryFramebuffer;
}
@ -379,7 +526,6 @@ void PrepareDeferred::run(const SceneContextPointer& sceneContext, const RenderC
outputs.edit0() = _deferredFramebuffer;
outputs.edit1() = _deferredFramebuffer->getLightingFramebuffer();
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
batch.enableStereo(false);
batch.setViewportTransform(args->_viewport);
@ -399,6 +545,11 @@ void PrepareDeferred::run(const SceneContextPointer& sceneContext, const RenderC
// For the rest of the rendering, bind the lighting model
batch.setUniformBuffer(LIGHTING_MODEL_BUFFER_SLOT, lightingModel->getParametersBuffer());
});
// Prepare a fresh Light Frame
auto deferredLightingEffect = DependencyManager::get<DeferredLightingEffect>();
deferredLightingEffect->getLightStage()->_currentFrame.clear();
}
@ -409,10 +560,10 @@ void RenderDeferredSetup::run(const render::SceneContextPointer& sceneContext, c
const SurfaceGeometryFramebufferPointer& surfaceGeometryFramebuffer,
const AmbientOcclusionFramebufferPointer& ambientOcclusionFramebuffer,
const SubsurfaceScatteringResourcePointer& subsurfaceScatteringResource) {
auto args = renderContext->args;
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
auto& batch = (*args->_batch);
{
// Framebuffer copy operations cannot function as multipass stereo operations.
batch.enableStereo(false);
@ -452,6 +603,7 @@ void RenderDeferredSetup::run(const render::SceneContextPointer& sceneContext, c
// Subsurface scattering specific
if (surfaceGeometryFramebuffer) {
batch.setResourceTexture(DEFERRED_BUFFER_LINEAR_DEPTH_UNIT, surfaceGeometryFramebuffer->getLinearDepthTexture());
batch.setResourceTexture(DEFERRED_BUFFER_CURVATURE_UNIT, surfaceGeometryFramebuffer->getCurvatureTexture());
batch.setResourceTexture(DEFERRED_BUFFER_DIFFUSED_CURVATURE_UNIT, surfaceGeometryFramebuffer->getLowCurvatureTexture());
}
@ -463,11 +615,14 @@ void RenderDeferredSetup::run(const render::SceneContextPointer& sceneContext, c
// Global directional light and ambient pass
assert(deferredLightingEffect->getLightStage().lights.size() > 0);
const auto& globalShadow = deferredLightingEffect->getLightStage().lights[0]->shadow;
assert(deferredLightingEffect->getLightStage()->getNumLights() > 0);
auto lightAndShadow = deferredLightingEffect->getLightStage()->getLightAndShadow(0);
const auto& globalShadow = lightAndShadow.second;
// Bind the shadow buffer
batch.setResourceTexture(SHADOW_MAP_UNIT, globalShadow.map);
if (globalShadow) {
batch.setResourceTexture(SHADOW_MAP_UNIT, globalShadow->map);
}
auto& program = deferredLightingEffect->_shadowMapEnabled ? deferredLightingEffect->_directionalLightShadow : deferredLightingEffect->_directionalLight;
LightLocationsPtr locations = deferredLightingEffect->_shadowMapEnabled ? deferredLightingEffect->_directionalLightShadowLocations : deferredLightingEffect->_directionalLightLocations;
@ -498,7 +653,9 @@ void RenderDeferredSetup::run(const render::SceneContextPointer& sceneContext, c
}
if (locations->shadowTransformBuffer >= 0) {
batch.setUniformBuffer(locations->shadowTransformBuffer, globalShadow.getBuffer());
if (globalShadow) {
batch.setUniformBuffer(locations->shadowTransformBuffer, globalShadow->getBuffer());
}
}
batch.setPipeline(program);
}
@ -507,24 +664,28 @@ void RenderDeferredSetup::run(const render::SceneContextPointer& sceneContext, c
auto textureFrameTransform = gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(deferredFramebuffer->getFrameSize(), args->_viewport);
batch._glUniform4fv(locations->texcoordFrameTransform, 1, reinterpret_cast< const float* >(&textureFrameTransform));
{ // Setup the global lighting
deferredLightingEffect->setupKeyLightBatch(batch, locations->lightBufferUnit, SKYBOX_MAP_UNIT);
}
// Setup the global lighting
deferredLightingEffect->setupKeyLightBatch(batch, locations->lightBufferUnit, locations->ambientBufferUnit, SKYBOX_MAP_UNIT);
batch.draw(gpu::TRIANGLE_STRIP, 4);
if (keyLight->getAmbientMap()) {
batch.setResourceTexture(SKYBOX_MAP_UNIT, nullptr);
}
deferredLightingEffect->unsetKeyLightBatch(batch, locations->lightBufferUnit, locations->ambientBufferUnit, SKYBOX_MAP_UNIT);
batch.setResourceTexture(SHADOW_MAP_UNIT, nullptr);
});
}
}
RenderDeferredLocals::RenderDeferredLocals() :
_localLightsBuffer(std::make_shared<gpu::Buffer>()) {
}
void RenderDeferredLocals::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext,
const DeferredFrameTransformPointer& frameTransform,
const DeferredFramebufferPointer& deferredFramebuffer,
const LightingModelPointer& lightingModel) {
const LightingModelPointer& lightingModel,
const SurfaceGeometryFramebufferPointer& surfaceGeometryFramebuffer, const LightClustersPointer& lightClusters) {
bool points = lightingModel->isPointLightEnabled();
bool spots = lightingModel->isSpotLightEnabled();
@ -533,133 +694,62 @@ void RenderDeferredLocals::run(const render::SceneContextPointer& sceneContext,
return;
}
auto args = renderContext->args;
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
auto& batch = (*args->_batch);
{
// 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;
auto viewport = args->_viewport;
// The view frustum is the mono frustum base
auto viewFrustum = args->getViewFrustum();
// Eval the mono projection
mat4 monoProjMat;
viewFrustum.evalProjectionMatrix(monoProjMat);
mat4 projMat;
viewFrustum.evalProjectionMatrix(projMat);
// The mono view transform
Transform monoViewTransform;
viewFrustum.evalViewTransform(monoViewTransform);
// The view transform
Transform viewTransform;
viewFrustum.evalViewTransform(viewTransform);
// THe mono view matrix coming from the mono view transform
glm::mat4 monoViewMat;
monoViewTransform.getMatrix(monoViewMat);
auto geometryCache = DependencyManager::get<GeometryCache>();
auto eyePoint = viewFrustum.getPosition();
float nearRadius = glm::distance(eyePoint, viewFrustum.getNearTopLeft());
auto deferredLightingEffect = DependencyManager::get<DeferredLightingEffect>();
// Render in this side's viewport
batch.setViewportTransform(monoViewport);
batch.setStateScissorRect(monoViewport);
// Render in this viewport
batch.setViewportTransform(viewport);
batch.setStateScissorRect(viewport);
// enlarge the scales slightly to account for tesselation
const float SCALE_EXPANSION = 0.05f;
auto textureFrameTransform = gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(deferredFramebuffer->getFrameSize(), viewport);
auto textureFrameTransform = gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(deferredFramebuffer->getFrameSize(), monoViewport);
batch.setProjectionTransform(monoProjMat);
batch.setViewTransform(monoViewTransform, true);
auto& lightIndices = lightClusters->_visibleLightIndices;
if (!lightIndices.empty() && lightIndices[0] > 0) {
// Bind the global list of lights and the visible lights this frame
batch.setUniformBuffer(deferredLightingEffect->_localLightLocations->lightBufferUnit, lightClusters->_lightStage->_lightArrayBuffer);
// Splat Point lights
if (points && !deferredLightingEffect->_pointLights.empty()) {
// POint light pipeline
batch.setPipeline(deferredLightingEffect->_pointLight);
batch._glUniform4fv(deferredLightingEffect->_pointLightLocations->texcoordFrameTransform, 1, reinterpret_cast< const float* >(&textureFrameTransform));
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT, lightClusters->_frustumGridBuffer);
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT, lightClusters->_clusterGridBuffer);
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT, lightClusters->_clusterContentBuffer);
for (auto lightID : deferredLightingEffect->_pointLights) {
auto& light = deferredLightingEffect->_allocatedLights[lightID];
// IN DEBUG: light->setShowContour(true);
batch.setUniformBuffer(deferredLightingEffect->_pointLightLocations->lightBufferUnit, light->getSchemaBuffer());
// Local light pipeline
batch.setPipeline(deferredLightingEffect->_localLight);
batch._glUniform4fv(deferredLightingEffect->_localLightLocations->texcoordFrameTransform, 1, reinterpret_cast<const float*>(&textureFrameTransform));
float expandedRadius = light->getMaximumRadius() * (1.0f + SCALE_EXPANSION);
glm::vec4 sphereParam(expandedRadius, 0.0f, 0.0f, 1.0f);
batch.draw(gpu::TRIANGLE_STRIP, 4);
// 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) {
sphereParam.w = 0.0f;
batch._glUniform4fv(deferredLightingEffect->_pointLightLocations->sphereParam, 1, reinterpret_cast< const float* >(&sphereParam));
batch.draw(gpu::TRIANGLE_STRIP, 4);
} else {
sphereParam.w = 1.0f;
batch._glUniform4fv(deferredLightingEffect->_pointLightLocations->sphereParam, 1, reinterpret_cast< const float* >(&sphereParam));
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);
}
// Draw outline as well ?
if (lightingModel->isShowLightContourEnabled()) {
batch.setPipeline(deferredLightingEffect->_localLightOutline);
batch._glUniform4fv(deferredLightingEffect->_localLightOutlineLocations->texcoordFrameTransform, 1, reinterpret_cast<const float*>(&textureFrameTransform));
batch.draw(gpu::TRIANGLE_STRIP, 4);
}
}
// Splat spot lights
if (spots && !deferredLightingEffect->_spotLights.empty()) {
// Spot light pipeline
batch.setPipeline(deferredLightingEffect->_spotLight);
batch._glUniform4fv(deferredLightingEffect->_spotLightLocations->texcoordFrameTransform, 1, reinterpret_cast< const float* >(&textureFrameTransform));
// Spot mesh
auto mesh = deferredLightingEffect->getSpotLightMesh();
batch.setIndexBuffer(mesh->getIndexBuffer());
batch.setInputBuffer(0, mesh->getVertexBuffer());
batch.setInputFormat(mesh->getVertexFormat());
auto& conePart = mesh->getPartBuffer().get<model::Mesh::Part>(0);
for (auto lightID : deferredLightingEffect->_spotLights) {
auto light = deferredLightingEffect->_allocatedLights[lightID];
// IN DEBUG:
// light->setShowContour(true);
batch.setUniformBuffer(deferredLightingEffect->_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...
const float OVER_CONSERVATIVE_SCALE = 1.1f;
if ((eyeHalfPlaneDistance > -nearRadius) &&
(glm::distance(eyePoint, glm::vec3(light->getPosition())) < (expandedRadius * OVER_CONSERVATIVE_SCALE) + nearRadius)) {
coneParam.w = 0.0f;
batch._glUniform4fv(deferredLightingEffect->_spotLightLocations->coneParam, 1, reinterpret_cast< const float* >(&coneParam));
batch.draw(gpu::TRIANGLE_STRIP, 4);
} else {
coneParam.w = 1.0f;
batch._glUniform4fv(deferredLightingEffect->_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);
batch.drawIndexed(model::Mesh::topologyToPrimitive(conePart._topology), conePart._numIndices, conePart._startIndex);
}
}
}
});
}
}
void RenderDeferredCleanup::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) {
auto args = renderContext->args;
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
auto& batch = (*args->_batch);
{
// 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);
@ -667,16 +757,22 @@ void RenderDeferredCleanup::run(const render::SceneContextPointer& sceneContext,
batch.setResourceTexture(DEFERRED_BUFFER_DEPTH_UNIT, nullptr);
batch.setResourceTexture(DEFERRED_BUFFER_OBSCURANCE_UNIT, nullptr);
batch.setResourceTexture(DEFERRED_BUFFER_LINEAR_DEPTH_UNIT, nullptr);
batch.setResourceTexture(DEFERRED_BUFFER_CURVATURE_UNIT, nullptr);
batch.setResourceTexture(DEFERRED_BUFFER_DIFFUSED_CURVATURE_UNIT, nullptr);
batch.setResourceTexture(SCATTERING_LUT_UNIT, nullptr);
batch.setResourceTexture(SCATTERING_SPECULAR_UNIT, nullptr);
batch.setUniformBuffer(SCATTERING_PARAMETERS_BUFFER_SLOT, nullptr);
// batch.setUniformBuffer(LIGHTING_MODEL_BUFFER_SLOT, nullptr);
// batch.setUniformBuffer(LIGHTING_MODEL_BUFFER_SLOT, nullptr);
batch.setUniformBuffer(DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT, nullptr);
});
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT, nullptr);
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT, nullptr);
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT, nullptr);
}
auto deferredLightingEffect = DependencyManager::get<DeferredLightingEffect>();
// End of the Lighting pass
@ -697,28 +793,37 @@ void RenderDeferred::configure(const Config& config) {
}
void RenderDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const Inputs& inputs) {
PROFILE_RANGE("DeferredLighting");
auto deferredTransform = inputs.get0();
auto deferredFramebuffer = inputs.get1();
auto lightingModel = inputs.get2();
auto surfaceGeometryFramebuffer = inputs.get3();
auto ssaoFramebuffer = inputs.get4();
auto subsurfaceScatteringResource = inputs.get5();
auto lightClusters = inputs.get6();
auto args = renderContext->args;
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
_gpuTimer.begin(batch);
});
if (!_gpuTimer) {
_gpuTimer = std::make_shared < gpu::RangeTimer>(__FUNCTION__);
}
auto previousBatch = args->_batch;
gpu::Batch batch;
args->_batch = &batch;
_gpuTimer->begin(batch);
setupJob.run(sceneContext, renderContext, deferredTransform, deferredFramebuffer, lightingModel, surfaceGeometryFramebuffer, ssaoFramebuffer, subsurfaceScatteringResource);
lightsJob.run(sceneContext, renderContext, deferredTransform, deferredFramebuffer, lightingModel);
lightsJob.run(sceneContext, renderContext, deferredTransform, deferredFramebuffer, lightingModel, surfaceGeometryFramebuffer, lightClusters);
cleanupJob.run(sceneContext, renderContext);
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
_gpuTimer.end(batch);
});
_gpuTimer->end(batch);
args->_context->appendFrameBatch(batch);
args->_batch = previousBatch;
auto config = std::static_pointer_cast<Config>(renderContext->jobConfig);
config->setGPUBatchRunTime(_gpuTimer.getGPUAverage(), _gpuTimer.getBatchAverage());
config->setGPUBatchRunTime(_gpuTimer->getGPUAverage(), _gpuTimer->getBatchAverage());
}

View file

@ -28,6 +28,8 @@
#include "LightingModel.h"
#include "LightStage.h"
#include "LightClusters.h"
#include "SurfaceGeometryPass.h"
#include "SubsurfaceScattering.h"
#include "AmbientOcclusionEffect.h"
@ -43,6 +45,8 @@ class DeferredLightingEffect : public Dependency {
public:
void init();
void addLight(const model::LightPointer& light);
/// Adds a point light to render for the current frame.
void addPointLight(const glm::vec3& position, float radius, const glm::vec3& color = glm::vec3(0.0f, 0.0f, 0.0f),
float intensity = 0.5f, float falloffRadius = 0.01f);
@ -52,12 +56,14 @@ public:
float intensity = 0.5f, float falloffRadius = 0.01f,
const glm::quat& orientation = glm::quat(), float exponent = 0.0f, float cutoff = PI);
void setupKeyLightBatch(gpu::Batch& batch, int lightBufferUnit, int skyboxCubemapUnit);
void setupKeyLightBatch(gpu::Batch& batch, int lightBufferUnit, int ambientBufferUnit, int skyboxCubemapUnit);
void unsetKeyLightBatch(gpu::Batch& batch, int lightBufferUnit, int ambientBufferUnit, int skyboxCubemapUnit);
// update global lighting
void setGlobalLight(const model::LightPointer& light);
const LightStage& getLightStage() { return _lightStage; }
const LightStagePointer getLightStage() { return _lightStage; }
void setShadowMapEnabled(bool enable) { _shadowMapEnabled = enable; };
void setAmbientOcclusionEnabled(bool enable) { _ambientOcclusionEnabled = enable; }
bool isAmbientOcclusionEnabled() const { return _ambientOcclusionEnabled; }
@ -65,11 +71,13 @@ public:
private:
DeferredLightingEffect() = default;
LightStage _lightStage;
LightStagePointer _lightStage;
bool _shadowMapEnabled{ false };
bool _ambientOcclusionEnabled{ false };
model::MeshPointer _pointLightMesh;
model::MeshPointer getPointLightMesh();
model::MeshPointer _spotLightMesh;
model::MeshPointer getSpotLightMesh();
@ -81,8 +89,13 @@ private:
gpu::PipelinePointer _directionalAmbientSphereLightShadow;
gpu::PipelinePointer _directionalLightShadow;
gpu::PipelinePointer _pointLight;
gpu::PipelinePointer _spotLight;
gpu::PipelinePointer _localLight;
gpu::PipelinePointer _localLightOutline;
gpu::PipelinePointer _pointLightBack;
gpu::PipelinePointer _pointLightFront;
gpu::PipelinePointer _spotLightBack;
gpu::PipelinePointer _spotLightFront;
LightLocationsPtr _directionalSkyboxLightLocations;
LightLocationsPtr _directionalAmbientSphereLightLocations;
@ -92,6 +105,8 @@ private:
LightLocationsPtr _directionalAmbientSphereLightShadowLocations;
LightLocationsPtr _directionalLightShadowLocations;
LightLocationsPtr _localLightLocations;
LightLocationsPtr _localLightOutlineLocations;
LightLocationsPtr _pointLightLocations;
LightLocationsPtr _spotLightLocations;
@ -101,7 +116,8 @@ private:
std::vector<int> _globalLights;
std::vector<int> _pointLights;
std::vector<int> _spotLights;
friend class LightClusteringPass;
friend class RenderDeferredSetup;
friend class RenderDeferredLocals;
friend class RenderDeferredCleanup;
@ -150,7 +166,14 @@ public:
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext,
const DeferredFrameTransformPointer& frameTransform,
const DeferredFramebufferPointer& deferredFramebuffer,
const LightingModelPointer& lightingModel);
const LightingModelPointer& lightingModel,
const SurfaceGeometryFramebufferPointer& surfaceGeometryFramebuffer,
const LightClustersPointer& lightClusters);
gpu::BufferView _localLightsBuffer;
RenderDeferredLocals();
};
@ -165,7 +188,7 @@ using RenderDeferredConfig = render::GPUJobConfig;
class RenderDeferred {
public:
using Inputs = render::VaryingSet6 < DeferredFrameTransformPointer, DeferredFramebufferPointer, LightingModelPointer, SurfaceGeometryFramebufferPointer, AmbientOcclusionFramebufferPointer, SubsurfaceScatteringResourcePointer>;
using Inputs = render::VaryingSet7 < DeferredFrameTransformPointer, DeferredFramebufferPointer, LightingModelPointer, SurfaceGeometryFramebufferPointer, AmbientOcclusionFramebufferPointer, SubsurfaceScatteringResourcePointer, LightClustersPointer>;
using Config = RenderDeferredConfig;
using JobModel = render::Job::ModelI<RenderDeferred, Inputs, Config>;
@ -180,7 +203,9 @@ public:
RenderDeferredCleanup cleanupJob;
protected:
gpu::RangeTimer _gpuTimer;
gpu::RangeTimerPointer _gpuTimer;
};
#endif // hifi_DeferredLightingEffect_h

View file

@ -30,16 +30,16 @@ vec3 fresnelSchlickAmbient(vec3 fresnelColor, vec3 lightDir, vec3 halfDir, float
<$declareSkyboxMap()$>
<@endif@>
vec3 evalAmbientSpecularIrradiance(Light light, vec3 fragEyeDir, vec3 fragNormal, float roughness, vec3 fresnel) {
vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 fragNormal, float roughness, vec3 fresnel) {
vec3 direction = -reflect(fragEyeDir, fragNormal);
vec3 ambientFresnel = fresnelSchlickAmbient(fresnel, fragEyeDir, fragNormal, 1.0 - roughness);
vec3 specularLight;
<@if supportIfAmbientMapElseAmbientSphere@>
if (getLightHasAmbientMap(light))
if (getLightHasAmbientMap(ambient))
<@endif@>
<@if supportAmbientMap@>
{
float levels = getLightAmbientMapNumMips(light);
float levels = getLightAmbientMapNumMips(ambient);
float lod = min(floor((roughness)* levels), levels);
specularLight = evalSkyboxLight(direction, lod).xyz;
}
@ -49,7 +49,7 @@ vec3 evalAmbientSpecularIrradiance(Light light, vec3 fragEyeDir, vec3 fragNormal
<@endif@>
<@if supportAmbientSphere@>
{
specularLight = evalSphericalLight(getLightAmbientSphere(light), direction).xyz;
specularLight = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), direction).xyz;
}
<@endif@>
@ -67,19 +67,18 @@ float curvatureAO(in float k) {
}
<@endif@>
void evalLightingAmbient(out vec3 diffuse, out vec3 specular, Light light, vec3 eyeDir, vec3 normal,
void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambient, vec3 eyeDir, vec3 normal,
float roughness, float metallic, vec3 fresnel, vec3 albedo, float obscurance
<@if supportScattering@>
, float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature
<@endif@>
) {
// Diffuse from ambient
diffuse = (1.0 - metallic) * evalSphericalLight(getLightAmbientSphere(light), normal).xyz;
diffuse = (1.0 - metallic) * sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), normal).xyz;
// Specular highlight from ambient
specular = evalAmbientSpecularIrradiance(light, eyeDir, normal, roughness, fresnel) * obscurance * getLightAmbientIntensity(light);
specular = evalAmbientSpecularIrradiance(ambient, eyeDir, normal, roughness, fresnel) * obscurance * getLightAmbientIntensity(ambient);
<@if supportScattering@>
@ -92,7 +91,7 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, Light light, vec3
if (scattering * isScatteringEnabled() > 0.0) {
// Diffuse from ambient
diffuse = evalSphericalLight(getLightAmbientSphere(light), lowNormalCurvature.xyz).xyz;
diffuse = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), lowNormalCurvature.xyz).xyz;
specular = vec3(0.0);
}
@ -102,7 +101,7 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, Light light, vec3
obscurance = 1.0;
}
float lightEnergy = obscurance * getLightAmbientIntensity(light);
float lightEnergy = obscurance * getLightAmbientIntensity(ambient);
if (isAlbedoEnabled() > 0.0) {
diffuse *= albedo;

View file

@ -0,0 +1,85 @@
<!
// LightClusterGrid.slh
//
// Created by Sam Gateau on 9/8/16.
// 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
!>
<@if not RENDER_LIGHT_CLUSTER_GRID_SLH@>
<@def RENDER_LIGHT_CLUSTER_GRID_SLH@>
struct FrustumGrid {
float frustumNear;
float rangeNear;
float rangeFar;
float frustumFar;
ivec3 dims;
float spare;
mat4 eyeToGridProj;
mat4 worldToEyeMat;
mat4 eyeToWorldMat;
};
uniform frustumGridBuffer {
FrustumGrid frustumGrid;
};
float projection_getNear(mat4 projection) {
float planeC = projection[2][3] + projection[2][2];
float planeD = projection[3][2];
return planeD / planeC;
}
float projection_getFar(mat4 projection) {
//float planeA = projection[0][3] - projection[0][2]; All Zeros
//float planeB = projection[1][3] - projection[1][2]; All Zeros
float planeC = projection[2][3] - projection[2][2];
float planeD = /*projection[3][3]*/ -projection[3][2];
return planeD / planeC;
}
// glsl / C++ compatible source as interface for FrustrumGrid
<@include LightClusterGrid_shared.slh@>
// end of hybrid include
<@if GLPROFILE == MAC_GL @>
#define GRID_NUM_ELEMENTS 4096
#define GRID_INDEX_TYPE ivec4
#define GRID_FETCH_BUFFER(i) i / 4][i % 4
<@else@>
#define GRID_NUM_ELEMENTS 16384
#define GRID_INDEX_TYPE int
#define GRID_FETCH_BUFFER(i) i
<@endif@>
uniform clusterGridBuffer {
GRID_INDEX_TYPE _clusterGridTable[GRID_NUM_ELEMENTS];
};
uniform clusterContentBuffer {
GRID_INDEX_TYPE _clusterGridContent[GRID_NUM_ELEMENTS];
};
ivec3 clusterGrid_getCluster(int index) {
int clusterDesc = _clusterGridTable[GRID_FETCH_BUFFER(index)];
int numPointLights = 0xFF & (clusterDesc >> 16);
int numSpotLights = 0xFF & (clusterDesc >> 24);
int contentOffset = 0xFFFF & (clusterDesc);
return ivec3(numPointLights, numSpotLights, contentOffset);
}
int clusterGrid_getClusterLightId(int index, int offset) {
int elementIndex = offset + index;
/*
int element = _clusterGridContent[GRID_FETCH_BUFFER(elementIndex)];
return element;
*/
int element = _clusterGridContent[GRID_FETCH_BUFFER((elementIndex >> 1))];
return (((elementIndex & 0x00000001) == 1) ? (element >> 16) : element) & 0x0000FFFF;
}
<@endif@>

View file

@ -0,0 +1,194 @@
// glsl / C++ compatible source as interface for FrustrumGrid
#if defined(Q_OS_LINUX)
#define float_exp2 exp2f
#else
#define float_exp2 exp2
#endif
float frustumGrid_depthRampGridToVolume(float ngrid) {
// return ngrid;
// return sqrt(ngrid);
return float_exp2(ngrid) - 1.0f;
}
float frustumGrid_depthRampInverseVolumeToGrid(float nvolume) {
// return nvolume;
// return nvolume * nvolume;
return log2(nvolume + 1.0f);
}
vec3 frustumGrid_gridToVolume(vec3 pos, ivec3 dims) {
vec3 gridScale = vec3(1.0f) / vec3(dims);
vec3 volumePos = pos * gridScale;
volumePos.z = frustumGrid_depthRampGridToVolume(volumePos.z);
return volumePos;
}
float frustumGrid_volumeToGridDepth(float vposZ, ivec3 dims) {
return frustumGrid_depthRampInverseVolumeToGrid(vposZ) * float(dims.z);
}
vec3 frustumGrid_volumeToGrid(vec3 vpos, ivec3 dims) {
vec3 gridPos = vec3(vpos.x, vpos.y, frustumGrid_depthRampInverseVolumeToGrid(vpos.z)) * vec3(dims);
return gridPos;
}
vec4 frustumGrid_volumeToClip(vec3 vpos, float rangeNear, float rangeFar) {
vec3 ndcPos = vec3(-1.0f + 2.0f * vpos.x, -1.0f + 2.0f * vpos.y, vpos.z);
float depth = rangeNear * (1 - ndcPos.z) + rangeFar * (ndcPos.z);
vec4 clipPos = vec4(ndcPos.x * depth, ndcPos.y * depth, 1.0f, depth);
return clipPos;
}
vec3 frustumGrid_clipToEye(vec4 clipPos, mat4 projection) {
return vec3(
(clipPos.x + projection[2][0] * clipPos.w) / projection[0][0],
(clipPos.y + projection[2][1] * clipPos.w) / projection[1][1],
-clipPos.w
//, (clipPos.z - projection[3][3] * clipPos.w) / projection[3][2]
);
}
vec3 frustumGrid_volumeToEye(vec3 vpos, mat4 projection, float rangeNear, float rangeFar) {
return frustumGrid_clipToEye(frustumGrid_volumeToClip(vpos, rangeNear, rangeFar), projection);
}
float frustumGrid_eyeToVolumeDepth(float eposZ, float rangeNear, float rangeFar) {
return (-eposZ - rangeNear) / (rangeFar - rangeNear);
}
vec3 frustumGrid_eyeToVolume(vec3 epos, mat4 projection, float rangeNear, float rangeFar) {
vec4 clipPos = vec4(epos.x * projection[0][0] + epos.z * projection[2][0],
epos.y * projection[1][1] + epos.z * projection[2][1],
epos.z * projection[2][2] + projection[2][3],
-epos.z);
vec4 ndcPos = clipPos / clipPos.w;
vec3 volumePos = vec3(0.5f * (ndcPos.x + 1.0f), 0.5f * (ndcPos.y + 1.0f), (clipPos.w - rangeNear) / (rangeFar - rangeNear));
return volumePos;
}
int frustumGrid_numClusters() {
return frustumGrid.dims.x * frustumGrid.dims.y * (frustumGrid.dims.z + 1);
}
int frustumGrid_clusterToIndex(ivec3 pos) {
return pos.x + (pos.y + pos.z * frustumGrid.dims.y) * frustumGrid.dims.x;
}
ivec3 frustumGrid_indexToCluster(int index) {
ivec3 summedDims = ivec3(frustumGrid.dims.x * frustumGrid.dims.y, frustumGrid.dims.x, 1);
int layer = index / summedDims.x;
int offsetInLayer = index % summedDims.x;
ivec3 clusterPos = ivec3(offsetInLayer % summedDims.y, offsetInLayer / summedDims.y, layer);
return clusterPos;
}
vec3 frustumGrid_clusterPosToEye(vec3 clusterPos) {
vec3 cvpos = clusterPos;
vec3 volumePos = frustumGrid_gridToVolume(cvpos, frustumGrid.dims);
vec3 eyePos = frustumGrid_volumeToEye(volumePos, frustumGrid.eyeToGridProj, frustumGrid.rangeNear, frustumGrid.rangeFar);
return eyePos;
}
vec3 frustumGrid_clusterPosToEye(ivec3 clusterPos, vec3 offset) {
vec3 cvpos = vec3(clusterPos) + offset;
return frustumGrid_clusterPosToEye(cvpos);
}
int frustumGrid_eyeDepthToClusterLayer(float eyeZ) {
if ((eyeZ > -frustumGrid.frustumNear) || (eyeZ < -frustumGrid.frustumFar)) {
return -2;
}
if (eyeZ > -frustumGrid.rangeNear) {
return -1;
}
float volumeZ = frustumGrid_eyeToVolumeDepth(eyeZ, frustumGrid.rangeNear, frustumGrid.rangeFar);
float gridZ = frustumGrid_volumeToGridDepth(volumeZ, frustumGrid.dims);
if (gridZ >= frustumGrid.dims.z) {
gridZ = frustumGrid.dims.z;
}
return int(gridZ);
}
ivec3 frustumGrid_eyeToClusterPos(vec3 eyePos) {
if ((eyePos.z > -frustumGrid.frustumNear) || (eyePos.z < -frustumGrid.frustumFar)) {
return ivec3(-2);
}
if (eyePos.z > -frustumGrid.rangeNear) {
return ivec3(0,0,-1);
}
vec3 volumePos = frustumGrid_eyeToVolume(eyePos, frustumGrid.eyeToGridProj, frustumGrid.rangeNear, frustumGrid.rangeFar);
vec3 gridPos = frustumGrid_volumeToGrid(volumePos, frustumGrid.dims);
if (gridPos.z >= frustumGrid.dims.z) {
gridPos.z = frustumGrid.dims.z;
}
return ivec3(floor(gridPos));
}
int frustumGrid_eyeToClusterDirH(vec3 eyeDir) {
if (eyeDir.z >= 0.0f) {
return (eyeDir.x > 0 ? frustumGrid.dims.x : -1);
}
float eyeDepth = -eyeDir.z;
float nclipDir = eyeDir.x / eyeDepth;
float ndcDir = nclipDir * frustumGrid.eyeToGridProj[0][0] - frustumGrid.eyeToGridProj[2][0];
float volumeDir = 0.5f * (ndcDir + 1.0f);
float gridPos = volumeDir * float(frustumGrid.dims.x);
return int(gridPos);
}
int frustumGrid_eyeToClusterDirV(vec3 eyeDir) {
if (eyeDir.z >= 0.0f) {
return (eyeDir.y > 0 ? frustumGrid.dims.y : -1);
}
float eyeDepth = -eyeDir.z;
float nclipDir = eyeDir.y / eyeDepth;
float ndcDir = nclipDir * frustumGrid.eyeToGridProj[1][1] - frustumGrid.eyeToGridProj[2][1];
float volumeDir = 0.5f * (ndcDir + 1.0f);
float gridPos = volumeDir * float(frustumGrid.dims.y);
return int(gridPos);
}
ivec2 frustumGrid_eyeToClusterDir(vec3 eyeDir) {
return ivec2(frustumGrid_eyeToClusterDirH(eyeDir), frustumGrid_eyeToClusterDirV(eyeDir));
}
vec4 frustumGrid_eyeToWorld(vec4 eyePos) {
return frustumGrid.eyeToWorldMat * eyePos;
}
vec4 frustumGrid_worldToEye(vec4 worldPos) {
return frustumGrid.worldToEyeMat * worldPos;
}
// <@if 1@>
// Trigger Scribe include
// <@endif@> <!def that !> End C++ compatible

View file

@ -0,0 +1,792 @@
//
// LightClusters.cpp
//
// Created by Sam Gateau on 9/7/2016.
// 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 "LightClusters.h"
#include <gpu/Context.h>
#include <gpu/StandardShaderLib.h>
#include "lightClusters_drawGrid_vert.h"
#include "lightClusters_drawGrid_frag.h"
//#include "lightClusters_drawClusterFromDepth_vert.h"
#include "lightClusters_drawClusterFromDepth_frag.h"
#include "lightClusters_drawClusterContent_vert.h"
#include "lightClusters_drawClusterContent_frag.h"
enum LightClusterGridShader_MapSlot {
DEFERRED_BUFFER_LINEAR_DEPTH_UNIT = 0,
DEFERRED_BUFFER_COLOR_UNIT,
DEFERRED_BUFFER_NORMAL_UNIT,
DEFERRED_BUFFER_EMISSIVE_UNIT,
DEFERRED_BUFFER_DEPTH_UNIT,
};
enum LightClusterGridShader_BufferSlot {
LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT = 0,
DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT,
CAMERA_CORRECTION_BUFFER_SLOT,
LIGHT_GPU_SLOT = render::ShapePipeline::Slot::LIGHT,
LIGHT_INDEX_GPU_SLOT,
LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT,
LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT,
};
FrustumGrid::FrustumGrid(const FrustumGrid& source) :
frustumNear(source.frustumNear),
rangeNear(source.rangeNear),
rangeFar(source.rangeFar),
frustumFar(source.frustumFar),
dims(source.dims),
spare(source.spare),
eyeToGridProj(source.eyeToGridProj),
worldToEyeMat(source.worldToEyeMat),
eyeToWorldMat(source.eyeToWorldMat)
{}
void FrustumGrid::generateGridPlanes(Planes& xPlanes, Planes& yPlanes, Planes& zPlanes) {
xPlanes.resize(dims.x + 1);
yPlanes.resize(dims.y + 1);
zPlanes.resize(dims.z + 1);
float centerY = float(dims.y) * 0.5f;
float centerX = float(dims.x) * 0.5f;
for (int z = 0; z < (int) zPlanes.size(); z++) {
ivec3 pos(0, 0, z);
zPlanes[z] = glm::vec4(0.0f, 0.0f, 1.0f, -frustumGrid_clusterPosToEye(pos, vec3(0.0)).z);
}
for (int x = 0; x < (int) xPlanes.size(); x++) {
auto slicePos = frustumGrid_clusterPosToEye(glm::vec3((float)x, centerY, 0.0));
auto sliceDir = glm::normalize(slicePos);
xPlanes[x] = glm::vec4(sliceDir.z, 0.0, -sliceDir.x, 0.0);
}
for (int y = 0; y < (int) yPlanes.size(); y++) {
auto slicePos = frustumGrid_clusterPosToEye(glm::vec3(centerX, (float)y, 0.0));
auto sliceDir = glm::normalize(slicePos);
yPlanes[y] = glm::vec4(0.0, sliceDir.z, -sliceDir.y, 0.0);
}
}
#include "DeferredLightingEffect.h"
#ifdef Q_OS_MAC
const glm::uvec4 LightClusters::MAX_GRID_DIMENSIONS { 16, 16, 16, 16384 };
#else
const glm::uvec4 LightClusters::MAX_GRID_DIMENSIONS { 32, 32, 31, 16384 };
#endif
LightClusters::LightClusters() :
_lightIndicesBuffer(std::make_shared<gpu::Buffer>()),
_clusterGridBuffer(/*std::make_shared<gpu::Buffer>(), */gpu::Element::INDEX_INT32),
_clusterContentBuffer(/*std::make_shared<gpu::Buffer>(), */gpu::Element::INDEX_INT32) {
}
void LightClusters::setDimensions(glm::uvec3 gridDims, uint32_t listBudget) {
ivec3 configDimensions;
auto gridBudget = MAX_GRID_DIMENSIONS.w;
configDimensions.x = std::max(1, (int) std::min(MAX_GRID_DIMENSIONS.x, gridDims.x));
configDimensions.y = std::max(1, (int) std::min(MAX_GRID_DIMENSIONS.y, gridDims.y));
configDimensions.z = std::max(1, (int) std::min(MAX_GRID_DIMENSIONS.z, gridDims.z));
auto sliceCost = configDimensions.x * configDimensions.y;
auto maxNumSlices = (int)(gridBudget / sliceCost) - 1;
configDimensions.z = std::min(maxNumSlices, configDimensions.z);
// Grab the frustumGridBuffer and force it updated
const auto& constFrustumGrid = _frustumGridBuffer.get();
const auto& dims = constFrustumGrid.dims;
if ((dims.x != configDimensions.x) || (dims.y != configDimensions.y) || (dims.z != configDimensions.z)) {
auto& theFrustumGrid = _frustumGridBuffer.edit();
theFrustumGrid.dims = configDimensions;
theFrustumGrid.generateGridPlanes(_gridPlanes[0], _gridPlanes[1], _gridPlanes[2]);
_clusterResourcesInvalid = true;
}
auto configListBudget = std::min(MAX_GRID_DIMENSIONS.w, listBudget);
if (_clusterContentBudget != configListBudget) {
_clusterContentBudget = configListBudget;
_clusterResourcesInvalid = true;
}
}
uint32_t LightClusters::getNumClusters() const {
auto theFrustumGrid = _frustumGridBuffer.get();
return theFrustumGrid.frustumGrid_numClusters();
}
void LightClusters::updateClusterResource() {
if (!_clusterResourcesInvalid) {
return;
}
_clusterResourcesInvalid = false;
auto numClusters = getNumClusters();
if (numClusters != (uint32_t) _clusterGrid.size()) {
_clusterGrid.clear();
_clusterGrid.resize(numClusters, EMPTY_CLUSTER);
_clusterGridBuffer._size = (numClusters * sizeof(uint32_t));
_clusterGridBuffer._buffer = std::make_shared<gpu::Buffer>(_clusterGridBuffer._size, (gpu::Byte*) _clusterGrid.data(), _clusterGridBuffer._size);
}
// Since LightIndex is 2bytes, we can fit 2 in a uint32
auto configListBudget = _clusterContentBudget;
if (sizeof(LightIndex) == 2) {
configListBudget *= 2;
}
if (configListBudget != (uint32_t) _clusterContent.size()) {
_clusterContent.clear();
_clusterContent.resize(configListBudget, INVALID_LIGHT);
_clusterContentBuffer._size = (configListBudget * sizeof(LightIndex));
_clusterContentBuffer._buffer = std::make_shared<gpu::Buffer>(_clusterContentBuffer._size, (gpu::Byte*) _clusterContent.data(), _clusterContentBuffer._size);
}
}
void LightClusters::setRangeNearFar(float rangeNear, float rangeFar) {
bool changed = false;
if (_frustumGridBuffer->rangeNear != rangeNear) {
_frustumGridBuffer.edit().rangeNear = rangeNear;
changed = true;
}
if (_frustumGridBuffer->rangeFar != rangeFar) {
_frustumGridBuffer.edit().rangeFar = rangeFar;
changed = true;
}
if (changed) {
_frustumGridBuffer.edit().generateGridPlanes(_gridPlanes[0], _gridPlanes[1], _gridPlanes[2]);
}
}
void LightClusters::updateFrustum(const ViewFrustum& frustum) {
_frustum = frustum;
_frustumGridBuffer.edit().updateFrustum(frustum);
if (true) {
_frustumGridBuffer.edit().generateGridPlanes(_gridPlanes[0], _gridPlanes[1], _gridPlanes[2]);
}
}
void LightClusters::updateLightStage(const LightStagePointer& lightStage) {
_lightStage = lightStage;
}
void LightClusters::updateLightFrame(const LightStage::Frame& lightFrame, bool points, bool spots) {
// start fresh
_visibleLightIndices.clear();
// Now gather the lights
// gather lights
auto& srcPointLights = lightFrame._pointLights;
auto& srcSpotLights = lightFrame._spotLights;
int numPointLights = (int)srcPointLights.size();
int numSpotLights = (int)srcSpotLights.size();
_visibleLightIndices.resize(numPointLights + numSpotLights + 1);
_visibleLightIndices[0] = 0;
if (points && !srcPointLights.empty()) {
memcpy(_visibleLightIndices.data() + (_visibleLightIndices[0] + 1), srcPointLights.data(), srcPointLights.size() * sizeof(int));
_visibleLightIndices[0] += (int)srcPointLights.size();
}
if (spots && !srcSpotLights.empty()) {
memcpy(_visibleLightIndices.data() + (_visibleLightIndices[0] + 1), srcSpotLights.data(), srcSpotLights.size() * sizeof(int));
_visibleLightIndices[0] += (int)srcSpotLights.size();
}
_lightIndicesBuffer._buffer->setData(_visibleLightIndices.size() * sizeof(int), (const gpu::Byte*) _visibleLightIndices.data());
_lightIndicesBuffer._size = _visibleLightIndices.size() * sizeof(int);
}
float distanceToPlane(const glm::vec3& point, const glm::vec4& plane) {
return plane.x * point.x + plane.y * point.y + plane.z * point.z + plane.w;
}
bool reduceSphereToPlane(const glm::vec4& sphere, const glm::vec4& plane, glm::vec4& reducedSphere) {
float distance = distanceToPlane(glm::vec3(sphere), plane);
if (std::abs(distance) <= sphere.w) {
reducedSphere = glm::vec4(sphere.x - distance * plane.x, sphere.y - distance * plane.y, sphere.z - distance * plane.z, sqrt(sphere.w * sphere.w - distance * distance));
return true;
}
return false;
}
uint32_t scanLightVolumeBoxSlice(FrustumGrid& grid, const FrustumGrid::Planes planes[3], int zSlice, int yMin, int yMax, int xMin, int xMax, LightClusters::LightID lightId, const glm::vec4& eyePosRadius,
std::vector< std::vector<LightClusters::LightIndex>>& clusterGrid) {
glm::ivec3 gridPosToOffset(1, grid.dims.x, grid.dims.x * grid.dims.y);
uint32_t numClustersTouched = 0;
for (auto y = yMin; (y <= yMax); y++) {
for (auto x = xMin; (x <= xMax); x++) {
auto index = x + gridPosToOffset.y * y + gridPosToOffset.z * zSlice;
clusterGrid[index].emplace_back(lightId);
numClustersTouched++;
}
}
return numClustersTouched;
}
uint32_t scanLightVolumeBox(FrustumGrid& grid, const FrustumGrid::Planes planes[3], int zMin, int zMax, int yMin, int yMax, int xMin, int xMax, LightClusters::LightID lightId, const glm::vec4& eyePosRadius,
std::vector< std::vector<LightClusters::LightIndex>>& clusterGrid) {
glm::ivec3 gridPosToOffset(1, grid.dims.x, grid.dims.x * grid.dims.y);
uint32_t numClustersTouched = 0;
for (auto z = zMin; (z <= zMax); z++) {
for (auto y = yMin; (y <= yMax); y++) {
for (auto x = xMin; (x <= xMax); x++) {
auto index = x + gridPosToOffset.y * y + gridPosToOffset.z * z;
clusterGrid[index].emplace_back(lightId);
numClustersTouched++;
}
}
}
return numClustersTouched;
}
uint32_t scanLightVolumeSphere(FrustumGrid& grid, const FrustumGrid::Planes planes[3], int zMin, int zMax, int yMin, int yMax, int xMin, int xMax, LightClusters::LightID lightId, const glm::vec4& eyePosRadius,
std::vector< std::vector<LightClusters::LightIndex>>& clusterGrid) {
glm::ivec3 gridPosToOffset(1, grid.dims.x, grid.dims.x * grid.dims.y);
uint32_t numClustersTouched = 0;
const auto& xPlanes = planes[0];
const auto& yPlanes = planes[1];
const auto& zPlanes = planes[2];
// FInd the light origin cluster
auto centerCluster = grid.frustumGrid_eyeToClusterPos(glm::vec3(eyePosRadius));
int center_z = centerCluster.z;
int center_y = centerCluster.y;
for (auto z = zMin; (z <= zMax); z++) {
auto zSphere = eyePosRadius;
if (z != center_z) {
auto plane = (z < center_z) ? zPlanes[z + 1] : -zPlanes[z];
if (!reduceSphereToPlane(zSphere, plane, zSphere)) {
// pass this slice!
continue;
}
}
for (auto y = yMin; (y <= yMax); y++) {
auto ySphere = zSphere;
if (y != center_y) {
auto plane = (y < center_y) ? yPlanes[y + 1] : -yPlanes[y];
if (!reduceSphereToPlane(ySphere, plane, ySphere)) {
// pass this slice!
continue;
}
}
glm::vec3 spherePoint(ySphere);
auto x = xMin;
for (; (x < xMax); ++x) {
const auto& plane = xPlanes[x + 1];
auto testDistance = distanceToPlane(spherePoint, plane) + ySphere.w;
if (testDistance >= 0.0f) {
break;
}
}
auto xs = xMax;
for (; (xs >= x); --xs) {
auto plane = -xPlanes[xs];
auto testDistance = distanceToPlane(spherePoint, plane) + ySphere.w;
if (testDistance >= 0.0f) {
break;
}
}
for (; (x <= xs); x++) {
auto index = grid.frustumGrid_clusterToIndex(ivec3(x, y, z));
if (index < (int)clusterGrid.size()) {
clusterGrid[index].emplace_back(lightId);
numClustersTouched++;
} else {
qDebug() << "WARNING: LightClusters::scanLightVolumeSphere invalid index found ? numClusters = " << clusterGrid.size() << " index = " << index << " found from cluster xyz = " << x << " " << y << " " << z;
}
}
}
}
return numClustersTouched;
}
glm::ivec3 LightClusters::updateClusters() {
// Make sure resource are in good shape
updateClusterResource();
// Clean up last info
uint32_t numClusters = (uint32_t)_clusterGrid.size();
std::vector< std::vector< LightIndex > > clusterGridPoint(numClusters);
std::vector< std::vector< LightIndex > > clusterGridSpot(numClusters);
_clusterGrid.clear();
_clusterGrid.resize(numClusters, EMPTY_CLUSTER);
uint32_t maxNumIndices = (uint32_t)_clusterContent.size();
_clusterContent.clear();
_clusterContent.resize(maxNumIndices, INVALID_LIGHT);
auto theFrustumGrid(_frustumGridBuffer.get());
glm::ivec3 gridPosToOffset(1, theFrustumGrid.dims.x, theFrustumGrid.dims.x * theFrustumGrid.dims.y);
uint32_t numClusterTouched = 0;
uint32_t numLightsIn = _visibleLightIndices[0];
uint32_t numClusteredLights = 0;
for (size_t lightNum = 1; lightNum < _visibleLightIndices.size(); ++lightNum) {
auto lightId = _visibleLightIndices[lightNum];
auto light = _lightStage->getLight(lightId);
if (!light) {
continue;
}
auto worldOri = light->getPosition();
auto radius = light->getMaximumRadius();
bool isSpot = light->isSpot();
// Bring into frustum eye space
auto eyeOri = theFrustumGrid.frustumGrid_worldToEye(glm::vec4(worldOri, 1.0f));
// Remove light that slipped through and is not in the z range
float eyeZMax = eyeOri.z - radius;
if (eyeZMax > -theFrustumGrid.rangeNear) {
continue;
}
float eyeZMin = eyeOri.z + radius;
bool beyondFar = false;
if (eyeZMin < -theFrustumGrid.rangeFar) {
beyondFar = true;
}
// Get z slices
int zMin = theFrustumGrid.frustumGrid_eyeDepthToClusterLayer(eyeZMin);
int zMax = theFrustumGrid.frustumGrid_eyeDepthToClusterLayer(eyeZMax);
// That should never happen
if (zMin == -2 && zMax == -2) {
continue;
}
// Before Range NEar just apss, range neatr == true near for now
if ((zMin == -1) && (zMax == -1)) {
continue;
}
// CLamp the z range
zMin = std::max(0, zMin);
auto xLeftDistance = radius - distanceToPlane(eyeOri, _gridPlanes[0][0]);
auto xRightDistance = radius + distanceToPlane(eyeOri, _gridPlanes[0].back());
auto yBottomDistance = radius - distanceToPlane(eyeOri, _gridPlanes[1][0]);
auto yTopDistance = radius + distanceToPlane(eyeOri, _gridPlanes[1].back());
if ((xLeftDistance < 0.f) || (xRightDistance < 0.f) || (yBottomDistance < 0.f) || (yTopDistance < 0.f)) {
continue;
}
// find 2D corners of the sphere in grid
int xMin { 0 };
int xMax { theFrustumGrid.dims.x - 1 };
int yMin { 0 };
int yMax { theFrustumGrid.dims.y - 1 };
float radius2 = radius * radius;
auto eyeOriH = glm::vec3(eyeOri);
auto eyeOriV = glm::vec3(eyeOri);
eyeOriH.y = 0.0f;
eyeOriV.x = 0.0f;
float eyeOriLen2H = glm::length2(eyeOriH);
float eyeOriLen2V = glm::length2(eyeOriV);
if ((eyeOriLen2H > radius2)) {
float eyeOriLenH = sqrt(eyeOriLen2H);
auto eyeOriDirH = glm::vec3(eyeOriH) / eyeOriLenH;
float eyeToTangentCircleLenH = sqrt(eyeOriLen2H - radius2);
float eyeToTangentCircleCosH = eyeToTangentCircleLenH / eyeOriLenH;
float eyeToTangentCircleSinH = radius / eyeOriLenH;
// rotate the eyeToOriDir (H & V) in both directions
glm::vec3 leftDir(eyeOriDirH.x * eyeToTangentCircleCosH + eyeOriDirH.z * eyeToTangentCircleSinH, 0.0f, eyeOriDirH.x * -eyeToTangentCircleSinH + eyeOriDirH.z * eyeToTangentCircleCosH);
glm::vec3 rightDir(eyeOriDirH.x * eyeToTangentCircleCosH - eyeOriDirH.z * eyeToTangentCircleSinH, 0.0f, eyeOriDirH.x * eyeToTangentCircleSinH + eyeOriDirH.z * eyeToTangentCircleCosH);
auto lc = theFrustumGrid.frustumGrid_eyeToClusterDirH(leftDir);
if (lc > xMax) {
lc = xMin;
}
auto rc = theFrustumGrid.frustumGrid_eyeToClusterDirH(rightDir);
if (rc < 0) {
rc = xMax;
}
xMin = std::max(xMin, lc);
xMax = std::min(rc, xMax);
assert(xMin <= xMax);
}
if ((eyeOriLen2V > radius2)) {
float eyeOriLenV = sqrt(eyeOriLen2V);
auto eyeOriDirV = glm::vec3(eyeOriV) / eyeOriLenV;
float eyeToTangentCircleLenV = sqrt(eyeOriLen2V - radius2);
float eyeToTangentCircleCosV = eyeToTangentCircleLenV / eyeOriLenV;
float eyeToTangentCircleSinV = radius / eyeOriLenV;
// rotate the eyeToOriDir (H & V) in both directions
glm::vec3 bottomDir(0.0f, eyeOriDirV.y * eyeToTangentCircleCosV + eyeOriDirV.z * eyeToTangentCircleSinV, eyeOriDirV.y * -eyeToTangentCircleSinV + eyeOriDirV.z * eyeToTangentCircleCosV);
glm::vec3 topDir(0.0f, eyeOriDirV.y * eyeToTangentCircleCosV - eyeOriDirV.z * eyeToTangentCircleSinV, eyeOriDirV.y * eyeToTangentCircleSinV + eyeOriDirV.z * eyeToTangentCircleCosV);
auto bc = theFrustumGrid.frustumGrid_eyeToClusterDirV(bottomDir);
auto tc = theFrustumGrid.frustumGrid_eyeToClusterDirV(topDir);
if (bc > yMax) {
bc = yMin;
}
if (tc < 0) {
tc = yMax;
}
yMin = std::max(yMin, bc);
yMax =std::min(tc, yMax);
assert(yMin <= yMax);
}
// now voxelize
auto& clusterGrid = (isSpot ? clusterGridSpot : clusterGridPoint);
if (beyondFar) {
numClusterTouched += scanLightVolumeBoxSlice(theFrustumGrid, _gridPlanes, zMin, yMin, yMax, xMin, xMax, lightId, glm::vec4(glm::vec3(eyeOri), radius), clusterGrid);
} else {
numClusterTouched += scanLightVolumeSphere(theFrustumGrid, _gridPlanes, zMin, zMax, yMin, yMax, xMin, xMax, lightId, glm::vec4(glm::vec3(eyeOri), radius), clusterGrid);
}
numClusteredLights++;
}
// Lights have been gathered now reexpress in terms of 2 sequential buffers
// Start filling from near to far and stops if it overflows
bool checkBudget = false;
if (numClusterTouched > maxNumIndices) {
checkBudget = true;
}
uint16_t indexOffset = 0;
for (int i = 0; i < (int) clusterGridPoint.size(); i++) {
auto& clusterPoint = clusterGridPoint[i];
auto& clusterSpot = clusterGridSpot[i];
uint8_t numLightsPoint = ((uint8_t)clusterPoint.size());
uint8_t numLightsSpot = ((uint8_t)clusterSpot.size());
uint16_t numLights = numLightsPoint + numLightsSpot;
uint16_t offset = indexOffset;
// Check for overflow
if (checkBudget) {
if ((indexOffset + numLights) > (uint16_t) maxNumIndices) {
break;
}
}
// Encode the cluster grid: [ ContentOffset - 16bits, Num Point LIghts - 8bits, Num Spot Lights - 8bits]
_clusterGrid[i] = (uint32_t)((0xFF000000 & (numLightsSpot << 24)) | (0x00FF0000 & (numLightsPoint << 16)) | (0x0000FFFF & offset));
if (numLightsPoint) {
memcpy(_clusterContent.data() + indexOffset, clusterPoint.data(), numLightsPoint * sizeof(LightIndex));
indexOffset += numLightsPoint;
}
if (numLightsSpot) {
memcpy(_clusterContent.data() + indexOffset, clusterSpot.data(), numLightsSpot * sizeof(LightIndex));
indexOffset += numLightsSpot;
}
}
// update the buffers
_clusterGridBuffer._buffer->setData(_clusterGridBuffer._size, (gpu::Byte*) _clusterGrid.data());
_clusterContentBuffer._buffer->setSubData(0, indexOffset * sizeof(LightIndex), (gpu::Byte*) _clusterContent.data());
return glm::ivec3(numLightsIn, numClusteredLights, numClusterTouched);
}
LightClusteringPass::LightClusteringPass() {
}
void LightClusteringPass::configure(const Config& config) {
if (_lightClusters) {
_lightClusters->setRangeNearFar(config.rangeNear, config.rangeFar);
_lightClusters->setDimensions(glm::uvec3(config.dimX, config.dimY, config.dimZ));
}
_freeze = config.freeze;
}
void LightClusteringPass::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& output) {
auto args = renderContext->args;
auto deferredTransform = inputs.get0();
auto lightingModel = inputs.get1();
auto surfaceGeometryFramebuffer = inputs.get2();
if (!_lightClusters) {
_lightClusters = std::make_shared<LightClusters>();
}
// first update the Grid with the new frustum
if (!_freeze) {
_lightClusters->updateFrustum(args->getViewFrustum());
}
// From the LightStage and the current frame, update the light cluster Grid
auto deferredLightingEffect = DependencyManager::get<DeferredLightingEffect>();
auto lightStage = deferredLightingEffect->getLightStage();
_lightClusters->updateLightStage(lightStage);
_lightClusters->updateLightFrame(lightStage->_currentFrame, lightingModel->isPointLightEnabled(), lightingModel->isSpotLightEnabled());
auto clusteringStats = _lightClusters->updateClusters();
output = _lightClusters;
auto config = std::static_pointer_cast<Config>(renderContext->jobConfig);
config->numSceneLights = lightStage->getNumLights();
config->numFreeSceneLights = lightStage->getNumFreeLights();
config->numAllocatedSceneLights = lightStage->getNumAllocatedLights();
config->setNumInputLights(clusteringStats.x);
config->setNumClusteredLights(clusteringStats.y);
config->setNumClusteredLightReferences(clusteringStats.z);
}
DebugLightClusters::DebugLightClusters() {
}
void DebugLightClusters::configure(const Config& config) {
doDrawGrid = config.doDrawGrid;
doDrawClusterFromDepth = config.doDrawClusterFromDepth;
doDrawContent = config.doDrawContent;
}
const gpu::PipelinePointer DebugLightClusters::getDrawClusterGridPipeline() {
if (!_drawClusterGrid) {
auto vs = gpu::Shader::createVertex(std::string(lightClusters_drawGrid_vert));
auto ps = gpu::Shader::createPixel(std::string(lightClusters_drawGrid_frag));
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding(std::string("frustumGridBuffer"), LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT));
slotBindings.insert(gpu::Shader::Binding(std::string("clusterGridBuffer"), LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT));
slotBindings.insert(gpu::Shader::Binding(std::string("clusterContentBuffer"), LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT));
gpu::Shader::makeProgram(*program, slotBindings);
auto state = std::make_shared<gpu::State>();
state->setDepthTest(true, false, gpu::LESS_EQUAL);
// Blend on transparent
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
// Good to go add the brand new pipeline
_drawClusterGrid = gpu::Pipeline::create(program, state);
}
return _drawClusterGrid;
}
const gpu::PipelinePointer DebugLightClusters::getDrawClusterFromDepthPipeline() {
if (!_drawClusterFromDepth) {
// auto vs = gpu::Shader::createVertex(std::string(lightClusters_drawGrid_vert));
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
auto ps = gpu::Shader::createPixel(std::string(lightClusters_drawClusterFromDepth_frag));
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding(std::string("frustumGridBuffer"), LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT));
slotBindings.insert(gpu::Shader::Binding(std::string("clusterGridBuffer"), LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT));
slotBindings.insert(gpu::Shader::Binding(std::string("clusterContentBuffer"), LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT));
slotBindings.insert(gpu::Shader::Binding(std::string("linearZeyeMap"), DEFERRED_BUFFER_LINEAR_DEPTH_UNIT));
slotBindings.insert(gpu::Shader::Binding(std::string("cameraCorrectionBuffer"), CAMERA_CORRECTION_BUFFER_SLOT));
slotBindings.insert(gpu::Shader::Binding(std::string("deferredFrameTransformBuffer"), DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT));
gpu::Shader::makeProgram(*program, slotBindings);
auto state = std::make_shared<gpu::State>();
// Blend on transparent
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
// Good to go add the brand new pipeline
_drawClusterFromDepth = gpu::Pipeline::create(program, state);
}
return _drawClusterFromDepth;
}
const gpu::PipelinePointer DebugLightClusters::getDrawClusterContentPipeline() {
if (!_drawClusterContent) {
// auto vs = gpu::Shader::createVertex(std::string(lightClusters_drawClusterContent_vert));
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
auto ps = gpu::Shader::createPixel(std::string(lightClusters_drawClusterContent_frag));
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), LIGHT_GPU_SLOT));
slotBindings.insert(gpu::Shader::Binding(std::string("frustumGridBuffer"), LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT));
slotBindings.insert(gpu::Shader::Binding(std::string("clusterGridBuffer"), LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT));
slotBindings.insert(gpu::Shader::Binding(std::string("clusterContentBuffer"), LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT));
slotBindings.insert(gpu::Shader::Binding(std::string("linearZeyeMap"), DEFERRED_BUFFER_LINEAR_DEPTH_UNIT));
slotBindings.insert(gpu::Shader::Binding(std::string("cameraCorrectionBuffer"), CAMERA_CORRECTION_BUFFER_SLOT));
slotBindings.insert(gpu::Shader::Binding(std::string("deferredFrameTransformBuffer"), DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT));
gpu::Shader::makeProgram(*program, slotBindings);
auto state = std::make_shared<gpu::State>();
// Blend on transparent
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
// Good to go add the brand new pipeline
_drawClusterContent = gpu::Pipeline::create(program, state);
}
return _drawClusterContent;
}
void DebugLightClusters::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Inputs& inputs) {
if (!(doDrawClusterFromDepth || doDrawContent || doDrawGrid)) {
return;
}
auto deferredTransform = inputs.get0();
auto deferredFramebuffer = inputs.get1();
auto lightingModel = inputs.get2();
auto linearDepthTarget = inputs.get3();
auto lightClusters = inputs.get4();
auto args = renderContext->args;
gpu::Batch batch;
batch.enableStereo(false);
// Assign the camera transform
batch.setViewportTransform(args->_viewport);
glm::mat4 projMat;
Transform viewMat;
args->getViewFrustum().evalProjectionMatrix(projMat);
args->getViewFrustum().evalViewTransform(viewMat);
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat, true);
// Then the actual ClusterGrid attributes
batch.setModelTransform(Transform());
// Bind the Light CLuster data strucutre
batch.setUniformBuffer(LIGHT_GPU_SLOT, lightClusters->_lightStage->_lightArrayBuffer);
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT, lightClusters->_frustumGridBuffer);
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT, lightClusters->_clusterGridBuffer);
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT, lightClusters->_clusterContentBuffer);
if (doDrawClusterFromDepth) {
batch.setPipeline(getDrawClusterFromDepthPipeline());
batch.setUniformBuffer(DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT, deferredTransform->getFrameTransformBuffer());
if (linearDepthTarget) {
batch.setResourceTexture(DEFERRED_BUFFER_LINEAR_DEPTH_UNIT, linearDepthTarget->getLinearDepthTexture());
}
batch.draw(gpu::TRIANGLE_STRIP, 4, 0);
batch.setResourceTexture(DEFERRED_BUFFER_LINEAR_DEPTH_UNIT, nullptr);
batch.setUniformBuffer(DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT, nullptr);
}
if (doDrawContent) {
// bind the one gpu::Pipeline we need
batch.setPipeline(getDrawClusterContentPipeline());
batch.setUniformBuffer(DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT, deferredTransform->getFrameTransformBuffer());
if (linearDepthTarget) {
batch.setResourceTexture(DEFERRED_BUFFER_LINEAR_DEPTH_UNIT, linearDepthTarget->getLinearDepthTexture());
}
batch.draw(gpu::TRIANGLE_STRIP, 4, 0);
batch.setResourceTexture(DEFERRED_BUFFER_LINEAR_DEPTH_UNIT, nullptr);
batch.setUniformBuffer(DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT, nullptr);
}
gpu::Batch drawGridAndCleanBatch;
if (doDrawGrid) {
// bind the one gpu::Pipeline we need
drawGridAndCleanBatch.setPipeline(getDrawClusterGridPipeline());
auto dims = lightClusters->_frustumGridBuffer->dims;
glm::ivec3 summedDims(dims.x*dims.y * dims.z, dims.x*dims.y, dims.x);
drawGridAndCleanBatch.drawInstanced(summedDims.x, gpu::LINES, 24, 0);
}
drawGridAndCleanBatch.setUniformBuffer(LIGHT_GPU_SLOT, nullptr);
drawGridAndCleanBatch.setUniformBuffer(LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT, nullptr);
drawGridAndCleanBatch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT, nullptr);
drawGridAndCleanBatch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT, nullptr);
drawGridAndCleanBatch.setResourceTexture(DEFERRED_BUFFER_COLOR_UNIT, nullptr);
drawGridAndCleanBatch.setResourceTexture(DEFERRED_BUFFER_NORMAL_UNIT, nullptr);
drawGridAndCleanBatch.setResourceTexture(DEFERRED_BUFFER_EMISSIVE_UNIT, nullptr);
drawGridAndCleanBatch.setResourceTexture(DEFERRED_BUFFER_DEPTH_UNIT, nullptr);
args->_context->appendFrameBatch(batch);
args->_context->appendFrameBatch(drawGridAndCleanBatch);
}

View file

@ -0,0 +1,239 @@
//
// LightClusters.h
//
// Created by Sam Gateau on 9/7/2016.
// 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_LightClusters_h
#define hifi_render_utils_LightClusters_h
#include <ViewFrustum.h>
#include <gpu/Buffer.h>
#include <render/Engine.h>
#include "LightStage.h"
#include "DeferredFrameTransform.h"
#include "LightingModel.h"
#include "SurfaceGeometryPass.h"
class FrustumGrid {
public:
float frustumNear { 0.1f };
float rangeNear { 0.1f };
float rangeFar { 200.0f };
float frustumFar { 10000.0f };
glm::ivec3 dims { 1, 1, 1 };
float spare;
glm::mat4 eyeToGridProj;
glm::mat4 worldToEyeMat;
glm::mat4 eyeToWorldMat;
FrustumGrid() = default;
FrustumGrid(const FrustumGrid& source);
void updateFrustum(const ViewFrustum& frustum) {
frustumNear = frustum.getNearClip();
frustumFar = frustum.getFarClip();
eyeToGridProj = frustum.evalProjectionMatrixRange(rangeNear, rangeFar);
Transform view;
frustum.evalViewTransform(view);
eyeToWorldMat = view.getMatrix();
worldToEyeMat = view.getInverseMatrix();
}
// Copy paste of the slh functions
using vec3 = glm::vec3;
using ivec3 = glm::ivec3;
using mat4 = glm::mat4;
#define frustumGrid (*this)
#include "LightClusterGrid_shared.slh"
using Planes = std::vector < glm::vec4 >;
void generateGridPlanes(Planes& xPlanes, Planes& yPlanes, Planes& zPlanes);
};
class LightClusters {
public:
using LightID = LightStage::Index;
static const glm::uvec4 MAX_GRID_DIMENSIONS;
LightClusters();
void setDimensions(glm::uvec3 gridDims, uint32_t listBudget = MAX_GRID_DIMENSIONS.w);
void setRangeNearFar(float rangeNear, float rangeFar);
uint32_t getNumClusters() const;
void updateFrustum(const ViewFrustum& frustum);
void updateLightStage(const LightStagePointer& lightStage);
void updateLightFrame(const LightStage::Frame& lightFrame, bool points = true, bool spots = true);
glm::ivec3 updateClusters();
ViewFrustum _frustum;
LightStagePointer _lightStage;
gpu::StructBuffer<FrustumGrid> _frustumGridBuffer;
FrustumGrid::Planes _gridPlanes[3];
LightStage::LightIndices _visibleLightIndices;
gpu::BufferView _lightIndicesBuffer;
const uint32_t EMPTY_CLUSTER { 0x0000FFFF };
const LightID INVALID_LIGHT { LightStage::INVALID_INDEX };
using LightIndex = uint16_t;
std::vector<uint32_t> _clusterGrid;
std::vector<LightIndex> _clusterContent;
gpu::BufferView _clusterGridBuffer;
gpu::BufferView _clusterContentBuffer;
uint32_t _clusterContentBudget { 0 };
bool _clusterResourcesInvalid { true };
void updateClusterResource();
};
using LightClustersPointer = std::shared_ptr<LightClusters>;
class LightClusteringPassConfig : public render::Job::Config {
Q_OBJECT
Q_PROPERTY(float rangeNear MEMBER rangeNear NOTIFY dirty)
Q_PROPERTY(float rangeFar MEMBER rangeFar NOTIFY dirty)
Q_PROPERTY(int dimX MEMBER dimX NOTIFY dirty)
Q_PROPERTY(int dimY MEMBER dimY NOTIFY dirty)
Q_PROPERTY(int dimZ MEMBER dimZ NOTIFY dirty)
Q_PROPERTY(bool freeze MEMBER freeze NOTIFY dirty)
Q_PROPERTY(int numClusteredLightReferences MEMBER numClusteredLightReferences NOTIFY dirty)
Q_PROPERTY(int numInputLights MEMBER numInputLights NOTIFY dirty)
Q_PROPERTY(int numClusteredLights MEMBER numClusteredLights NOTIFY dirty)
Q_PROPERTY(int numSceneLights MEMBER numSceneLights NOTIFY dirty)
Q_PROPERTY(int numFreeSceneLights MEMBER numFreeSceneLights NOTIFY dirty)
Q_PROPERTY(int numAllocatedSceneLights MEMBER numAllocatedSceneLights NOTIFY dirty)
public:
LightClusteringPassConfig() : render::Job::Config(true){}
float rangeNear{ 0.1f };
float rangeFar{ 200.0f };
int dimX { 14 };
int dimY { 14 };
int dimZ { 14 };
bool freeze{ false };
int numClusteredLightReferences { 0 };
int numInputLights { 0 };
int numClusteredLights { 0 };
void setNumClusteredLightReferences(int numRefs) { numClusteredLightReferences = numRefs; emit dirty(); }
void setNumInputLights(int numLights) { numInputLights = numLights; emit dirty(); }
void setNumClusteredLights(int numLights) { numClusteredLights = numLights; emit dirty(); }
int numSceneLights { 0 };
int numFreeSceneLights { 0 };
int numAllocatedSceneLights { 0 };
signals:
void dirty();
protected:
};
class LightClusteringPass {
public:
using Inputs = render::VaryingSet3<DeferredFrameTransformPointer, LightingModelPointer, LinearDepthFramebufferPointer>;
using Outputs = LightClustersPointer;
using Config = LightClusteringPassConfig;
using JobModel = render::Job::ModelIO<LightClusteringPass, Inputs, Outputs, Config>;
LightClusteringPass();
void configure(const Config& config);
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& output);
protected:
LightClustersPointer _lightClusters;
bool _freeze;
};
class DebugLightClustersConfig : public render::Job::Config {
Q_OBJECT
Q_PROPERTY(bool doDrawGrid MEMBER doDrawGrid NOTIFY dirty)
Q_PROPERTY(bool doDrawClusterFromDepth MEMBER doDrawClusterFromDepth NOTIFY dirty)
Q_PROPERTY(bool doDrawContent MEMBER doDrawContent NOTIFY dirty)
public:
DebugLightClustersConfig() : render::Job::Config(true){}
bool doDrawGrid{ false };
bool doDrawClusterFromDepth { false };
bool doDrawContent { false };
signals:
void dirty();
protected:
};
#include "DeferredFramebuffer.h"
class DebugLightClusters {
public:
using Inputs = render::VaryingSet5 < DeferredFrameTransformPointer, DeferredFramebufferPointer, LightingModelPointer, LinearDepthFramebufferPointer, LightClustersPointer>;
using Config = DebugLightClustersConfig;
using JobModel = render::Job::ModelI<DebugLightClusters, Inputs, Config>;
DebugLightClusters();
void configure(const Config& config);
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Inputs& inputs);
protected:
gpu::BufferPointer _gridBuffer;
gpu::PipelinePointer _drawClusterGrid;
gpu::PipelinePointer _drawClusterFromDepth;
gpu::PipelinePointer _drawClusterContent;
const gpu::PipelinePointer getDrawClusterGridPipeline();
const gpu::PipelinePointer getDrawClusterFromDepthPipeline();
const gpu::PipelinePointer getDrawClusterContentPipeline();
bool doDrawGrid { false };
bool doDrawClusterFromDepth { false };
bool doDrawContent { false };
};
#endif

View file

@ -11,7 +11,7 @@
<@func declareLightingDirectional(supportScattering)@>
void evalLightingDirectional(out vec3 diffuse, out vec3 specular, Light light,
void evalLightingDirectional(out vec3 diffuse, out vec3 specular, vec3 lightDir, vec3 lightIrradiance,
vec3 eyeDir, vec3 normal, float roughness,
float metallic, vec3 fresnel, vec3 albedo, float shadow
<@if supportScattering@>
@ -20,9 +20,9 @@ void evalLightingDirectional(out vec3 diffuse, out vec3 specular, Light light,
) {
// Attenuation
vec3 lightEnergy = shadow * getLightColor(light) * getLightIntensity(light);
vec3 lightEnergy = shadow * lightIrradiance;
evalFragShading(diffuse, specular, normal, -getLightDirection(light), eyeDir, metallic, fresnel, roughness, albedo
evalFragShading(diffuse, specular, normal, -lightDir, eyeDir, metallic, fresnel, roughness, albedo
<@if supportScattering@>
,scattering, midNormalCurvature, lowNormalCurvature
<@endif@>

View file

@ -0,0 +1,83 @@
//
// LightPayload.cpp
//
// Created by Sam Gateau on 9/6/16.
// 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 "LightPayload.h"
#include <gpu/Batch.h>
#include "DeferredLightingEffect.h"
namespace render {
template <> const ItemKey payloadGetKey(const LightPayload::Pointer& payload) {
ItemKey::Builder builder;
builder.withTypeLight();
if (!payload || !payload->isVisible()) {
builder.withInvisible();
}
return builder.build();
}
template <> const Item::Bound payloadGetBound(const LightPayload::Pointer& payload) {
if (payload) {
return payload->editBound();
}
return render::Item::Bound();
}
template <> void payloadRender(const LightPayload::Pointer& payload, RenderArgs* args) {
if (args) {
if (payload) {
payload->render(args);
}
}
}
}
LightPayload::LightPayload() :
_light(std::make_shared<model::Light>())
{
}
LightPayload::~LightPayload() {
if (!LightStage::isIndexInvalid(_index)) {
if (_stage) {
_stage->removeLight(_index);
}
}
}
void LightPayload::render(RenderArgs* args) {
if (!_stage) {
_stage = DependencyManager::get<DeferredLightingEffect>()->getLightStage();
}
// Do we need to allocate the light in the stage ?
if (LightStage::isIndexInvalid(_index)) {
_index = _stage->addLight(_light);
_needUpdate = false;
}
// Need an update ?
if (_needUpdate) {
_stage->updateLightArrayBuffer(_index);
_needUpdate = false;
}
if (isVisible()) {
// FInally, push the light visible in the frame
_stage->_currentFrame.pushLight(_index, _light->getType());
#ifdef WANT_DEBUG
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
batch.setModelTransform(getTransformToCenter());
DependencyManager::get<GeometryCache>()->renderWireSphere(batch, 0.5f, 15, 15, glm::vec4(color, 1.0f));
#endif
}
}

View file

@ -0,0 +1,49 @@
//
// LightPayload.h
//
// Created by Sam Gateau on 9/6/16.
// 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_LightPayload_h
#define hifi_LightPayload_h
#include <model/Light.h>
#include <render/Item.h>
#include "LightStage.h"
class LightPayload {
public:
using Payload = render::Payload<LightPayload>;
using Pointer = Payload::DataPointer;
LightPayload();
~LightPayload();
void render(RenderArgs* args);
model::LightPointer editLight() { _needUpdate = true; return _light; }
render::Item::Bound& editBound() { _needUpdate = true; return _bound; }
void setVisible(bool visible) { _isVisible = visible; }
bool isVisible() const { return _isVisible; }
protected:
model::LightPointer _light;
render::Item::Bound _bound;
LightStagePointer _stage;
LightStage::Index _index { LightStage::INVALID_INDEX };
bool _needUpdate { true };
bool _isVisible{ true };
};
namespace render {
template <> const ItemKey payloadGetKey(const LightPayload::Pointer& payload);
template <> const Item::Bound payloadGetBound(const LightPayload::Pointer& payload);
template <> void payloadRender(const LightPayload::Pointer& payload, RenderArgs* args);
}
#endif

View file

@ -12,20 +12,20 @@
<@func declareLightingPoint(supportScattering)@>
void evalLightingPoint(out vec3 diffuse, out vec3 specular, Light light,
vec3 fragLightVec, vec3 fragEyeDir, vec3 normal, float roughness,
vec4 fragLightDirLen, vec3 fragEyeDir, vec3 normal, float roughness,
float metallic, vec3 fresnel, vec3 albedo, float shadow
<@if supportScattering@>
, float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature
<@endif@>
) {
// Allright we re valid in the volume
float fragLightDistance = length(fragLightVec);
vec3 fragLightDir = fragLightVec / fragLightDistance;
float fragLightDistance = fragLightDirLen.w;
vec3 fragLightDir = fragLightDirLen.xyz;
// Eval attenuation
float radialAttenuation = evalLightAttenuation(light, fragLightDistance);
vec3 lightEnergy = radialAttenuation * shadow * getLightColor(light) * getLightIntensity(light);
float radialAttenuation = lightIrradiance_evalLightAttenuation(light.irradiance, fragLightDistance);
vec3 lightEnergy = radialAttenuation * shadow * getLightIrradiance(light);
// Eval shading
evalFragShading(diffuse, specular, normal, fragLightDir, fragEyeDir, metallic, fresnel, roughness, albedo
@ -39,10 +39,10 @@ void evalLightingPoint(out vec3 diffuse, out vec3 specular, Light light,
if (isShowLightContour() > 0.0) {
// Show edge
float edge = abs(2.0 * ((getLightRadius(light) - fragLightDistance) / (0.1)) - 1.0);
float edge = abs(2.0 * ((lightVolume_getRadius(light.volume) - fragLightDistance) / (0.1)) - 1.0);
if (edge < 1.0) {
float edgeCoord = exp2(-8.0*edge*edge);
diffuse = vec3(edgeCoord * edgeCoord * getLightShowContour(light) * getLightColor(light));
diffuse = vec3(edgeCoord * edgeCoord * getLightColor(light));
}
}
}
@ -50,3 +50,24 @@ void evalLightingPoint(out vec3 diffuse, out vec3 specular, Light light,
<@endfunc@>
<@func declareDrawPointOutline()@>
bool evalLightPointEdge(out vec3 color, Light light, vec4 fragLightDirLen, vec3 fragEyeDir) {
// Allright we re valid in the volume
float fragLightDistance = fragLightDirLen.w;
vec3 fragLightDir = fragLightDirLen.xyz;
// Show edges
float edge = abs(2.0 * ((lightVolume_getRadius(light.volume) - fragLightDistance) / (0.1)) - 1.0);
if (edge < 1) {
float edgeCoord = exp2(-8.0*edge*edge);
color = vec3(edgeCoord * edgeCoord * getLightColor(light));
}
return (edge < 1);
}
<@endfunc@>

View file

@ -23,10 +23,11 @@ void evalLightingSpot(out vec3 diffuse, out vec3 specular, Light light,
float fragLightDistance = fragLightDirLen.w;
vec3 fragLightDir = fragLightDirLen.xyz;
// Eval attenuation
float radialAttenuation = evalLightAttenuation(light, fragLightDistance);
float angularAttenuation = evalLightSpotAttenuation(light, cosSpotAngle);
vec3 lightEnergy = angularAttenuation * radialAttenuation * shadow * getLightColor(light) * getLightIntensity(light);
float radialAttenuation = lightIrradiance_evalLightAttenuation(light.irradiance, fragLightDistance);
float angularAttenuation = lightIrradiance_evalLightSpotAttenuation(light.irradiance, cosSpotAngle);
vec3 lightEnergy = angularAttenuation * radialAttenuation * shadow *getLightIrradiance(light);
// Eval shading
evalFragShading(diffuse, specular, normal, fragLightDir, fragEyeDir, metallic, fresnel, roughness, albedo
@ -40,8 +41,8 @@ void evalLightingSpot(out vec3 diffuse, out vec3 specular, Light light,
if (isShowLightContour() > 0.0) {
// Show edges
float edgeDistR = (getLightRadius(light) - fragLightDistance);
float edgeDistS = dot(fragLightDistance * vec2(cosSpotAngle, sqrt(1.0 - cosSpotAngle * cosSpotAngle)), -getLightSpotOutsideNormal2(light));
float edgeDistR = (lightVolume_getRadius(light.volume) - fragLightDistance);
float edgeDistS = dot(fragLightDistance * vec2(cosSpotAngle, sqrt(1.0 - cosSpotAngle * cosSpotAngle)), -lightVolume_getSpotOutsideNormal2(light.volume));
float edgeDist = min(edgeDistR, edgeDistS);
float edge = abs(2.0 * (edgeDist / (0.1)) - 1.0);
if (edge < 1.0) {
@ -53,4 +54,26 @@ void evalLightingSpot(out vec3 diffuse, out vec3 specular, Light light,
<@endfunc@>
<@func declareDrawSpotOutline()@>
bool evalLightSpotEdge(out vec3 color, Light light, vec4 fragLightDirLen, float cosSpotAngle, vec3 fragEyeDir) {
// Allright we re valid in the volume
float fragLightDistance = fragLightDirLen.w;
vec3 fragLightDir = fragLightDirLen.xyz;
// Show edges
float edgeDistR = (lightVolume_getRadius(light.volume) - fragLightDistance);
float edgeDistS = dot(fragLightDistance * vec2(cosSpotAngle, sqrt(1.0 - cosSpotAngle * cosSpotAngle)), -lightVolume_getSpotOutsideNormal2(light.volume));
float edgeDist = min(edgeDistR, edgeDistS);
float edge = abs(2.0 * (edgeDist / (0.1)) - 1.0);
if (edge < 1) {
float edgeCoord = exp2(-8.0*edge*edge);
color = vec3(edgeCoord * edgeCoord * getLightColor(light));
}
return (edge < 1);
}
<@endfunc@>

View file

@ -87,10 +87,81 @@ 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>(Shadow(light));
stageLight->light = light;
lights.push_back(stageLight);
return stageLight;
LightStage::Index LightStage::findLight(const LightPointer& light) const {
auto found = _lightMap.find(light);
if (found != _lightMap.end()) {
return INVALID_INDEX;
} else {
return (*found).second;
}
}
LightStage::Index LightStage::addLight(const LightPointer& light) {
auto found = _lightMap.find(light);
if (found == _lightMap.end()) {
auto lightId = _lights.newElement(light);
// Avoid failing to allocate a light, just pass
if (lightId != INVALID_INDEX) {
// Allocate the matching Desc to the light
if (lightId >= (Index) _descs.size()) {
_descs.emplace_back(Desc());
} else {
_descs.emplace(_descs.begin() + lightId, Desc());
}
// INsert the light and its index in the reverese map
_lightMap.insert(LightMap::value_type(light, lightId));
updateLightArrayBuffer(lightId);
}
return lightId;
} else {
return (*found).second;
}
}
LightStage::Index LightStage::addShadow(Index lightIndex) {
auto light = getLight(lightIndex);
Index shadowId = INVALID_INDEX;
if (light) {
shadowId = _shadows.newElement(std::make_shared<Shadow>(light));
_descs[lightIndex].shadowId = shadowId;
}
return shadowId;
}
LightStage::LightPointer LightStage::removeLight(Index index) {
LightPointer removed = _lights.freeElement(index);
if (removed) {
_lightMap.erase(removed);
_descs[index] = Desc();
}
return removed;
}
void LightStage::updateLightArrayBuffer(Index lightId) {
auto lightSize = sizeof(model::Light::LightSchema);
if (!_lightArrayBuffer) {
_lightArrayBuffer = std::make_shared<gpu::Buffer>(lightSize);
}
assert(checkLightId(lightId));
if (lightId > (Index)_lightArrayBuffer->getNumTypedElements<model::Light::LightSchema>()) {
_lightArrayBuffer->resize(lightSize * (lightId + 10));
}
// lightArray is big enough so we can remap
auto light = _lights._elements[lightId];
if (light) {
const auto& lightSchema = light->getLightSchemaBuffer().get();
_lightArrayBuffer->setSubData<model::Light::LightSchema>(lightId, lightSchema);
} else {
// this should not happen ?
}
}

View file

@ -12,6 +12,10 @@
#ifndef hifi_render_utils_LightStage_h
#define hifi_render_utils_LightStage_h
#include <set>
#include <unordered_map>
#include <render/IndexedContainer.h>
#include "gpu/Framebuffer.h"
#include "model/Light.h"
@ -21,6 +25,16 @@ class ViewFrustum;
// Light stage to set up light-related rendering tasks
class LightStage {
public:
using Index = render::indexed_container::Index;
static const Index INVALID_INDEX { render::indexed_container::INVALID_INDEX };
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
using LightPointer = model::LightPointer;
using Lights = render::indexed_container::IndexedPointerVector<model::Light>;
using LightMap = std::unordered_map<LightPointer, Index>;
using LightIndices = std::vector<Index>;
class Shadow {
public:
using UniformBufferView = gpu::BufferView;
@ -56,21 +70,79 @@ public:
friend class Light;
};
using ShadowPointer = std::shared_ptr<Shadow>;
using Shadows = render::indexed_container::IndexedPointerVector<Shadow>;
class Light {
public:
Light(Shadow&& shadow) : shadow{ shadow } {}
model::LightPointer light;
Shadow shadow;
struct Desc {
Index shadowId { INVALID_INDEX };
};
using LightPointer = std::shared_ptr<Light>;
using Lights = std::vector<LightPointer>;
using Descs = std::vector<Desc>;
const LightPointer addLight(model::LightPointer light);
// TODO: removeLight
Lights lights;
Index findLight(const LightPointer& light) const;
Index addLight(const LightPointer& light);
Index addShadow(Index lightIndex);
LightPointer removeLight(Index index);
bool checkLightId(Index index) const { return _lights.checkIndex(index); }
Index getNumLights() const { return _lights.getNumElements(); }
Index getNumFreeLights() const { return _lights.getNumFreeIndices(); }
Index getNumAllocatedLights() const { return _lights.getNumAllocatedIndices(); }
LightPointer getLight(Index lightId) const {
return _lights.get(lightId);
}
Index getShadowId(Index lightId) const {
if (checkLightId(lightId)) {
return _descs[lightId].shadowId;
} else {
return INVALID_INDEX;
}
}
ShadowPointer getShadow(Index lightId) const {
return _shadows.get(getShadowId(lightId));
}
using LightAndShadow = std::pair<LightPointer, ShadowPointer>;
LightAndShadow getLightAndShadow(Index lightId) const {
return LightAndShadow(getLight(lightId), getShadow(lightId));
}
Lights _lights;
LightMap _lightMap;
Descs _descs;
class Frame {
public:
Frame() {}
void clear() { _pointLights.clear(); _spotLights.clear(); }
void pushLight(LightStage::Index index, model::Light::Type type) {
switch (type) {
case model::Light::POINT: { pushPointLight(index); break; }
case model::Light::SPOT: { pushSpotLight(index); break; }
default: { break; }
}
}
void pushPointLight(LightStage::Index index) { _pointLights.emplace_back(index); }
void pushSpotLight(LightStage::Index index) { _spotLights.emplace_back(index); }
LightStage::LightIndices _pointLights;
LightStage::LightIndices _spotLights;
};
Frame _currentFrame;
gpu::BufferPointer _lightArrayBuffer;
void updateLightArrayBuffer(Index lightId);
Shadows _shadows;
};
using LightStagePointer = std::shared_ptr<LightStage>;
#endif

View file

@ -143,7 +143,7 @@ public:
bool enablePointLight{ true };
bool enableSpotLight{ true };
bool showLightContour{ false }; // false by default
bool showLightContour { false }; // false by default
signals:
void dirty();

View file

@ -82,12 +82,6 @@ float fetchSpecularBeckmann(float ndoth, float roughness) {
return pow(2.0 * texture(scatteringSpecularBeckmann, vec2(ndoth, roughness)).r, 10.0);
}
float fresnelSchlickScalar(float fresnelColor, vec3 lightDir, vec3 halfDir) {
float base = 1.0 - clamp(dot(lightDir, halfDir), 0.0, 1.0);
float exponential = pow(base, 5.0);
return (exponential)+fresnelColor * (1.0 - exponential);
}
vec2 skinSpecular(vec3 N, vec3 L, vec3 V, float roughness, float intensity) {
vec2 result = vec2(0.0, 1.0);
float ndotl = dot(N, L);
@ -110,15 +104,33 @@ vec2 skinSpecular(vec3 N, vec3 L, vec3 V, float roughness, float intensity) {
vec3 fresnelSchlickColor(vec3 fresnelColor, vec3 lightDir, vec3 halfDir) {
float base = 1.0 - clamp(dot(lightDir, halfDir), 0.0, 1.0);
float exponential = pow(base, 5.0);
//float exponential = pow(base, 5.0);
float base2 = base * base;
float exponential = base * base2 * base2;
return vec3(exponential) + fresnelColor * (1.0 - exponential);
}
float fresnelSchlickScalar(float fresnelScalar, vec3 lightDir, vec3 halfDir) {
float base = 1.0 - clamp(dot(lightDir, halfDir), 0.0, 1.0);
//float exponential = pow(base, 5.0);
float base2 = base * base;
float exponential = base * base2 * base2;
return (exponential) + fresnelScalar * (1.0 - exponential);
}
float specularDistribution(float roughness, vec3 normal, vec3 halfDir) {
float ndoth = clamp(dot(halfDir, normal), 0.0, 1.0);
float gloss2 = pow(0.001 + roughness, 4.0);
// float gloss2 = pow(0.001 + roughness, 4);
float gloss2 = (0.001 + roughness);
gloss2 *= gloss2; // pow 2
gloss2 *= gloss2; // pow 4
float denom = (ndoth * ndoth*(gloss2 - 1.0) + 1.0);
float power = gloss2 / (3.14159 * denom * denom);
return power;
}
float specularDistributionGloss(float gloss2, vec3 normal, vec3 halfDir) {
float ndoth = clamp(dot(halfDir, normal), 0.0, 1.0);
// float gloss2 = pow(0.001 + roughness, 4);
float denom = (ndoth * ndoth*(gloss2 - 1.0) + 1.0);
float power = gloss2 / (3.14159 * denom * denom);
return power;
@ -140,10 +152,52 @@ vec4 evalPBRShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float m
vec3 halfDir = normalize(fragEyeDir + fragLightDir);
vec3 fresnelColor = fresnelSchlickColor(fresnel, fragLightDir, halfDir);
float power = specularDistribution(roughness, fragNormal, halfDir);
vec3 specular = power * fresnelColor * diffuse;
vec3 specular = fresnelColor * power * diffuse;
return vec4(specular, (1.0 - metallic) * diffuse * (1 - fresnelColor.x));
}
// Frag Shading returns the diffuse amount as W and the specular rgb as xyz
vec4 evalPBRShadingDielectric(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float roughness, float fresnel) {
// Diffuse Lighting
float diffuse = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0);
// Specular Lighting
vec3 halfDir = normalize(fragEyeDir + fragLightDir);
float fresnelScalar = fresnelSchlickScalar(fresnel, fragLightDir, halfDir);
float power = specularDistribution(roughness, fragNormal, halfDir);
vec3 specular = vec3(fresnelScalar) * power * diffuse;
return vec4(specular, diffuse * (1 - fresnelScalar));
}
vec4 evalPBRShadingMetallic(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float roughness, vec3 fresnel) {
// Diffuse Lighting
float diffuse = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0);
// Specular Lighting
vec3 halfDir = normalize(fragEyeDir + fragLightDir);
vec3 fresnelColor = fresnelSchlickColor(fresnel, fragLightDir, halfDir);
float power = specularDistribution(roughness, fragNormal, halfDir);
vec3 specular = fresnelColor * power * diffuse;
return vec4(specular, 0.f);
}
// Frag Shading returns the diffuse amount as W and the specular rgb as xyz
vec4 evalPBRShadingGloss(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float metallic, vec3 fresnel, float gloss2) {
// Diffuse Lighting
float diffuse = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0);
// Specular Lighting
vec3 halfDir = normalize(fragEyeDir + fragLightDir);
vec3 fresnelColor = fresnelSchlickColor(fresnel, fragLightDir, halfDir);
float power = specularDistributionGloss(gloss2, fragNormal, halfDir);
vec3 specular = fresnelColor * power * diffuse;
return vec4(specular, (1.0 - metallic) * diffuse * (1.0 - fresnelColor.x));
}
<@endfunc@>
@ -187,14 +241,40 @@ void evalFragShading(out vec3 diffuse, out vec3 specular,
diffuse *= specularBrdf.y;
specular = vec3(specularBrdf.x);
} else {
vec4 shading = evalPBRShading(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, roughness);
vec4 shading = evalPBRShadingGloss(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, roughness);
diffuse = vec3(shading.w);
specular = shading.xyz;
}
if (isAlbedoEnabled() > 0.0) {
diffuse *= albedo;
}
diffuse *= mix(vec3(1.0), albedo, isAlbedoEnabled());
}
void evalFragShadingScattering(out vec3 diffuse, out vec3 specular,
vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir,
float metallic, vec3 fresnel, float roughness, vec3 albedo
,float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature
) {
vec3 brdf = evalSkinBRDF(fragLightDir, fragNormal, midNormalCurvature.xyz, lowNormalCurvature.xyz, lowNormalCurvature.w);
float NdotL = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0);
diffuse = mix(vec3(NdotL), brdf, scattering);
// Specular Lighting
vec3 halfDir = normalize(fragEyeDir + fragLightDir);
vec2 specularBrdf = skinSpecular(fragNormal, fragLightDir, fragEyeDir, roughness, 1.0);
diffuse *= specularBrdf.y;
specular = vec3(specularBrdf.x);
diffuse *= mix(vec3(1.0), albedo, isAlbedoEnabled());
}
void evalFragShadingGloss(out vec3 diffuse, out vec3 specular,
vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir,
float metallic, vec3 fresnel, float gloss, vec3 albedo
) {
vec4 shading = evalPBRShadingGloss(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, gloss);
diffuse = vec3(shading.w);
diffuse *= mix(vec3(1.0), albedo, isAlbedoEnabled());
specular = shading.xyz;
}
<@endif@>

View file

@ -101,7 +101,7 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) {
const auto primaryFramebuffer = addJob<PreparePrimaryFramebuffer>("PreparePrimaryBuffer");
// const auto fullFrameRangeTimer = addJob<BeginGPURangeTimer>("BeginRangeTimer");
const auto opaqueRangeTimer = addJob<BeginGPURangeTimer>("BeginOpaqueRangeTimer");
const auto opaqueRangeTimer = addJob<BeginGPURangeTimer>("BeginOpaqueRangeTimer", "DrawOpaques");
const auto prepareDeferredInputs = PrepareDeferred::Inputs(primaryFramebuffer, lightingModel).hasVarying();
const auto prepareDeferredOutputs = addJob<PrepareDeferred>("PrepareDeferred", prepareDeferredInputs);
@ -142,24 +142,38 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) {
const auto ambientOcclusionFramebuffer = ambientOcclusionOutputs.getN<AmbientOcclusionEffect::Outputs>(0);
const auto ambientOcclusionUniforms = ambientOcclusionOutputs.getN<AmbientOcclusionEffect::Outputs>(1);
// Draw Lights just add the lights to the current list of lights to deal with. NOt really gpu job for now.
addJob<DrawLight>("DrawLight", lights);
const auto deferredLightingInputs = RenderDeferred::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel,
surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, scatteringResource).hasVarying();
// Light Clustering
// Create the cluster grid of lights, cpu job for now
const auto lightClusteringPassInputs = LightClusteringPass::Inputs(deferredFrameTransform, lightingModel, linearDepthTarget).hasVarying();
const auto lightClusters = addJob<LightClusteringPass>("LightClustering", lightClusteringPassInputs);
// DeferredBuffer is complete, now let's shade it into the LightingBuffer
const auto deferredLightingInputs = RenderDeferred::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel,
surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, scatteringResource, lightClusters).hasVarying();
addJob<RenderDeferred>("RenderDeferred", deferredLightingInputs);
// Use Stencil and draw background in Lighting buffer to complete filling in the opaque
const auto backgroundInputs = DrawBackgroundDeferred::Inputs(background, lightingModel).hasVarying();
addJob<DrawBackgroundDeferred>("DrawBackgroundDeferred", backgroundInputs);
// Render transparent objects forward in LightingBuffer
const auto transparentsInputs = DrawDeferred::Inputs(transparents, lightingModel).hasVarying();
addJob<DrawDeferred>("DrawTransparentDeferred", transparentsInputs, shapePlumber);
const auto toneAndPostRangeTimer = addJob<BeginGPURangeTimer>("BeginToneAndPostRangeTimer");
// LIght Cluster Grid Debuging job
{
const auto debugLightClustersInputs = DebugLightClusters::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel, linearDepthTarget, lightClusters).hasVarying();
addJob<DebugLightClusters>("DebugLightClusters", debugLightClustersInputs);
}
const auto toneAndPostRangeTimer = addJob<BeginGPURangeTimer>("BeginToneAndPostRangeTimer", "PostToneOverlaysAntialiasing");
// Lighting Buffer ready for tone mapping
const auto toneMappingInputs = render::Varying(ToneMappingDeferred::Inputs(lightingFramebuffer, primaryFramebuffer));
@ -174,11 +188,13 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) {
// Debugging stages
{
// Debugging Deferred buffer job
const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer));
addJob<DebugDeferredBuffer>("DebugDeferredBuffer", debugFramebuffers);
// Debugging Deferred buffer job
const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer));
addJob<DebugDeferredBuffer>("DebugDeferredBuffer", debugFramebuffers);
addJob<DebugSubsurfaceScattering>("DebugScattering", deferredLightingInputs);
const auto debugSubsurfaceScatteringInputs = DebugSubsurfaceScattering::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel,
surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, scatteringResource).hasVarying();
addJob<DebugSubsurfaceScattering>("DebugScattering", debugSubsurfaceScatteringInputs);
const auto debugAmbientOcclusionInputs = DebugAmbientOcclusion::Inputs(deferredFrameTransform, deferredFramebuffer, linearDepthTarget, ambientOcclusionUniforms).hasVarying();
addJob<DebugAmbientOcclusion>("DebugAmbientOcclusion", debugAmbientOcclusionInputs);
@ -390,7 +406,7 @@ gpu::PipelinePointer DrawStencilDeferred::getOpaquePipeline() {
auto state = std::make_shared<gpu::State>();
state->setDepthTest(true, false, gpu::LESS_EQUAL);
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(STENCIL_OPAQUE, 0xFF, gpu::ALWAYS, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_REPLACE));
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(STENCIL_OPAQUE, 0xFF, gpu::ALWAYS, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_KEEP));
state->setColorWriteMask(0);
_opaquePipeline = gpu::Pipeline::create(program, state);

View file

@ -21,7 +21,7 @@ class BeginGPURangeTimer {
public:
using JobModel = render::Job::ModelO<BeginGPURangeTimer, gpu::RangeTimerPointer>;
BeginGPURangeTimer() : _gpuTimer(std::make_shared<gpu::RangeTimer>()) {}
BeginGPURangeTimer(const std::string& name) : _gpuTimer(std::make_shared<gpu::RangeTimer>(name)) {}
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, gpu::RangeTimerPointer& timer);
@ -146,7 +146,7 @@ public:
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Inputs& inputs);
protected:
gpu::RangeTimer _gpuTimer;
gpu::RangeTimerPointer _gpuTimer;
};
class DrawOverlay3DConfig : public render::Job::Config {
@ -205,7 +205,7 @@ public:
using JobModel = Model<RenderDeferredTask, Config>;
protected:
gpu::RangeTimer _gpuTimer;
gpu::RangeTimerPointer _gpuTimer;
};
#endif // hifi_RenderDeferredTask_h

View file

@ -78,6 +78,7 @@ void lightBatchSetter(const ShapePipeline& pipeline, gpu::Batch& batch) {
if (pipeline.locations->lightBufferUnit >= 0) {
DependencyManager::get<DeferredLightingEffect>()->setupKeyLightBatch(batch,
pipeline.locations->lightBufferUnit,
pipeline.locations->lightAmbientBufferUnit,
pipeline.locations->lightAmbientMapUnit);
}
}

View file

@ -36,10 +36,15 @@ void RenderShadowMap::run(const render::SceneContextPointer& sceneContext, const
assert(renderContext->args);
assert(renderContext->args->hasViewFrustum());
const auto& lightStage = DependencyManager::get<DeferredLightingEffect>()->getLightStage();
const auto globalLight = lightStage.lights[0];
const auto& shadow = globalLight->shadow;
const auto& fbo = shadow.framebuffer;
auto lightStage = DependencyManager::get<DeferredLightingEffect>()->getLightStage();
LightStage::Index globalLightIndex { 0 };
const auto globalLight = lightStage->getLight(globalLightIndex);
const auto shadow = lightStage->getShadow(globalLightIndex);
if (!shadow) return;
const auto& fbo = shadow->framebuffer;
RenderArgs* args = renderContext->args;
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
@ -54,8 +59,8 @@ void RenderShadowMap::run(const render::SceneContextPointer& sceneContext, const
gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH,
vec4(vec3(1.0, 1.0, 1.0), 0.0), 1.0, 0, true);
batch.setProjectionTransform(shadow.getProjection());
batch.setViewTransform(shadow.getView(), false);
batch.setProjectionTransform(shadow->getProjection());
batch.setViewTransform(shadow->getView(), false);
auto shadowPipeline = _shapePlumber->pickPipeline(args, ShapeKey());
auto shadowSkinnedPipeline = _shapePlumber->pickPipeline(args, ShapeKey::Builder().withSkinned());
@ -139,11 +144,11 @@ void RenderShadowTask::run(const SceneContextPointer& sceneContext, const render
return;
}
const auto& lightStage = DependencyManager::get<DeferredLightingEffect>()->getLightStage();
const auto globalLight = lightStage.lights[0];
auto lightStage = DependencyManager::get<DeferredLightingEffect>()->getLightStage();
const auto globalShadow = lightStage->getShadow(0);
// If the global light is not set, bail
if (!globalLight) {
if (!globalShadow) {
return;
}
@ -153,10 +158,10 @@ void RenderShadowTask::run(const SceneContextPointer& sceneContext, const render
auto nearClip = args->getViewFrustum().getNearClip();
float nearDepth = -args->_boomOffset.z;
const int SHADOW_FAR_DEPTH = 20;
globalLight->shadow.setKeylightFrustum(args->getViewFrustum(), nearDepth, nearClip + SHADOW_FAR_DEPTH);
globalShadow->setKeylightFrustum(args->getViewFrustum(), nearDepth, nearClip + SHADOW_FAR_DEPTH);
// Set the keylight render args
args->pushViewFrustum(*(globalLight->shadow.getFrustum()));
args->pushViewFrustum(*(globalShadow->getFrustum()));
args->_renderMode = RenderArgs::SHADOW_RENDER_MODE;
// TODO: Allow runtime manipulation of culling ShouldRenderFunctor

View file

@ -533,7 +533,7 @@ void DebugSubsurfaceScattering::run(const render::SceneContextPointer& sceneCont
const auto theLight = DependencyManager::get<DeferredLightingEffect>()->getLightStage().lights[0];
const auto light = DependencyManager::get<DeferredLightingEffect>()->getLightStage()->getLight(0);
gpu::doInBatch(args->_context, [=](gpu::Batch& batch) {
batch.enableStereo(false);
@ -567,8 +567,8 @@ void DebugSubsurfaceScattering::run(const render::SceneContextPointer& sceneCont
batch.setUniformBuffer(ScatteringTask_FrameTransformSlot, frameTransform->getFrameTransformBuffer());
batch.setUniformBuffer(ScatteringTask_ParamSlot, scatteringResource->getParametersBuffer());
if (theLight->light) {
batch.setUniformBuffer(ScatteringTask_LightSlot, theLight->light->getSchemaBuffer());
if (light) {
batch.setUniformBuffer(ScatteringTask_LightSlot, light->getLightSchemaBuffer());
}
batch.setResourceTexture(ScatteringTask_ScatteringTableSlot, scatteringTable);
batch.setResourceTexture(ScatteringTask_CurvatureMapSlot, curvatureFramebuffer->getRenderBuffer(0));

View file

@ -61,9 +61,9 @@ void LinearDepthFramebuffer::updatePrimaryDepth(const gpu::TexturePointer& depth
void LinearDepthFramebuffer::clear() {
_linearDepthFramebuffer.reset();
_linearDepthTexture.reset();
_downsampleFramebuffer.reset();
_halfLinearDepthTexture.reset();
_halfNormalTexture.reset();
_downsampleFramebuffer.reset();
_halfLinearDepthTexture.reset();
_halfNormalTexture.reset();
}
void LinearDepthFramebuffer::allocate() {
@ -142,6 +142,10 @@ void LinearDepthPass::run(const render::SceneContextPointer& sceneContext, const
const auto frameTransform = inputs.get0();
const auto deferredFramebuffer = inputs.get1();
if (!_gpuTimer) {
_gpuTimer = std::make_shared < gpu::RangeTimer>(__FUNCTION__);
}
if (!_linearDepthFramebuffer) {
_linearDepthFramebuffer = std::make_shared<LinearDepthFramebuffer>();
}
@ -171,7 +175,7 @@ void LinearDepthPass::run(const render::SceneContextPointer& sceneContext, const
float clearLinearDepth = args->getViewFrustum().getFarClip() * 2.0f;
gpu::doInBatch(args->_context, [=](gpu::Batch& batch) {
_gpuTimer.begin(batch);
_gpuTimer->begin(batch);
batch.enableStereo(false);
batch.setViewportTransform(depthViewport);
@ -197,11 +201,11 @@ void LinearDepthPass::run(const render::SceneContextPointer& sceneContext, const
batch.setPipeline(downsamplePipeline);
batch.draw(gpu::TRIANGLE_STRIP, 4);
_gpuTimer.end(batch);
_gpuTimer->end(batch);
});
auto config = std::static_pointer_cast<Config>(renderContext->jobConfig);
config->setGPUBatchRunTime(_gpuTimer.getGPUAverage(), _gpuTimer.getBatchAverage());
config->setGPUBatchRunTime(_gpuTimer->getGPUAverage(), _gpuTimer->getBatchAverage());
}
@ -406,6 +410,10 @@ void SurfaceGeometryPass::run(const render::SceneContextPointer& sceneContext, c
RenderArgs* args = renderContext->args;
if (!_gpuTimer) {
_gpuTimer = std::make_shared < gpu::RangeTimer>(__FUNCTION__);
}
const auto frameTransform = inputs.get0();
const auto deferredFramebuffer = inputs.get1();
const auto linearDepthFramebuffer = inputs.get2();
@ -458,7 +466,7 @@ void SurfaceGeometryPass::run(const render::SceneContextPointer& sceneContext, c
gpu::doInBatch(args->_context, [=](gpu::Batch& batch) {
_gpuTimer.begin(batch);
_gpuTimer->begin(batch);
batch.enableStereo(false);
batch.setProjectionTransform(glm::mat4());
@ -519,12 +527,12 @@ void SurfaceGeometryPass::run(const render::SceneContextPointer& sceneContext, c
batch.setResourceTexture(BlurTask_DepthSlot, nullptr);
batch.setUniformBuffer(BlurTask_ParamsSlot, nullptr);
_gpuTimer.end(batch);
_gpuTimer->end(batch);
});
auto config = std::static_pointer_cast<Config>(renderContext->jobConfig);
config->setGPUBatchRunTime(_gpuTimer.getGPUAverage(), _gpuTimer.getBatchAverage());
config->setGPUBatchRunTime(_gpuTimer->getGPUAverage(), _gpuTimer->getBatchAverage());
}

View file

@ -87,7 +87,7 @@ private:
const gpu::PipelinePointer& getDownsamplePipeline();
gpu::PipelinePointer _downsamplePipeline;
gpu::RangeTimer _gpuTimer;
gpu::RangeTimerPointer _gpuTimer;
};
@ -202,7 +202,7 @@ private:
render::BlurGaussianDepthAware _diffusePass;
gpu::RangeTimer _gpuTimer;
gpu::RangeTimerPointer _gpuTimer;
};
#endif // hifi_SurfaceGeometryPass_h

View file

@ -0,0 +1,54 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// deferred_light_limited.vert
// vertex shader
//
// Created by Sam Gateau on 6/16/16.
// Copyright 2014 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/Transform.slh@>
<@include gpu/Inputs.slh@>
<$declareStandardTransform()$>
<@include model/Light.slh@>
<$declareLightBuffer(256)$>
uniform lightIndexBuffer {
int lightIndex[256];
};
out vec4 _texCoord0;
void main(void) {
int instanceID = lightIndex[gl_InstanceID];
Light light = getLight(instanceID);
vec4 sphereVertex = inPosition;
vec3 lightOrigin = getLightPosition(light);
vec4 sphereParam = vec4(1.0); // = getLightVolumeGeometry(light);
sphereVertex.xyz *= sphereParam.w;
sphereVertex.xyz += lightOrigin;
// standard transform
TransformCamera cam = getTransformCamera();
<$transformWorldToClipPos(cam, sphereVertex, gl_Position)$>;
vec4 projected = gl_Position / gl_Position.w;
projected.xy = (projected.xy + 1.0) * 0.5;
if (cam_isStereo()) {
projected.x = 0.5 * (projected.x + cam_getStereoSide());
}
_texCoord0 = vec4(projected.xy, 0.0, 1.0) * gl_Position.w;
}

View file

@ -18,33 +18,44 @@
<$declareStandardTransform()$>
uniform vec4 coneParam;
<@include model/Light.slh@>
<$declareLightBuffer(256)$>
uniform lightIndexBuffer {
int lightIndex[256];
};
out vec4 _texCoord0;
void main(void) {
vec4 coneVertex = inPosition;
if (coneParam.w != 0.0) {
if(coneVertex.z >= 0.0) {
// Evaluate the true position of the spot volume
vec2 dir = float(coneVertex.z < 0.5f) * (coneParam.xy
+ vec2(coneParam.y, -coneParam.x) * coneParam.z * float(coneVertex.z > 0.0f))
+ float(coneVertex.z > 0.5f) * (vec2(1.0, 0.0)
+ vec2(0.0, coneParam.z) * float(coneVertex.z < 1.0f));
int instanceID = lightIndex[gl_InstanceID];
Light light = getLight(instanceID);
vec3 lightPos = getLightPosition(light);
vec4 coneParam = vec4(1.0); // = getLightVolumeGeometry(light);
coneVertex.xy *= dir.y;
coneVertex.z = -dir.x;
} else {
coneVertex.z = 0.0;
}
if(coneVertex.z >= 0.0) {
// Evaluate the true position of the spot volume
vec2 dir = float(coneVertex.z < 0.5f) * (coneParam.xy
+ vec2(coneParam.y, -coneParam.x) * coneParam.z * float(coneVertex.z > 0.0f))
+ float(coneVertex.z > 0.5f) * (vec2(1.0, 0.0)
+ vec2(0.0, coneParam.z) * float(coneVertex.z < 1.0f));
coneVertex.xy *= dir.y;
coneVertex.z = -dir.x;
} else {
coneVertex.z = 0.0;
}
// standard transform
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
<$transformModelToClipPos(cam, obj, coneVertex, gl_Position)$>;
vec4 projected = gl_Position / gl_Position.w;
projected.xy = (projected.xy + 1.0) * 0.5;
coneVertex.xyz *= coneParam.w;
coneVertex.xyz += lightPos;
// standard transform
TransformCamera cam = getTransformCamera();
<$transformWorldToClipPos(cam, coneVertex, gl_Position)$>;
vec4 projected = gl_Position / gl_Position.w;
projected.xy = (projected.xy + 1.0) * 0.5;
#ifdef GPU_TRANSFORM_IS_STEREO
#ifdef GPU_TRANSFORM_STEREO_SPLIT_SCREEN
@ -54,35 +65,5 @@ void main(void) {
}
#endif
#endif
_texCoord0 = vec4(projected.xy, 0.0, 1.0) * gl_Position.w;
} else {
const float depth = -1.0; //Draw at near plane
const vec4 UNIT_QUAD[4] = vec4[4](
vec4(-1.0, -1.0, depth, 1.0),
vec4(1.0, -1.0, depth, 1.0),
vec4(-1.0, 1.0, depth, 1.0),
vec4(1.0, 1.0, depth, 1.0)
);
vec4 pos = UNIT_QUAD[gl_VertexID];
#ifdef GPU_TRANSFORM_IS_STEREO
#ifdef GPU_TRANSFORM_STEREO_SPLIT_SCREEN
TransformCamera cam = getTransformCamera();
<$transformStereoClipsSpace(cam, pos)$>
#endif
#endif
_texCoord0 = vec4((pos.xy + 1.0) * 0.5, 0.0, 1.0);
#ifdef GPU_TRANSFORM_IS_STEREO
#ifdef GPU_TRANSFORM_STEREO_SPLIT_SCREEN
#else
if (cam_isStereo()) {
_texCoord0.x = 0.5 * (_texCoord0.x + cam_getStereoSide());
}
#endif
#endif
gl_Position = pos;
}
_texCoord0 = vec4(projected.xy, 0.0, 1.0) * gl_Position.w;
}

View file

@ -0,0 +1,95 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
// lightClusters_drawClusterFro Depth.slf
//
// Created by Sam Gateau on 9/8/2016.
// 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
//
// Everything about deferred buffer
<@include DeferredBufferRead.slh@>
<@include model/Light.slh@>
<$declareLightBuffer(256)$>
<@include LightClusterGrid.slh@>
<@include gpu/Color.slh@>
<$declareColorWheel()$>
in vec2 varTexCoord0;
out vec4 _fragColor;
void main(void) {
// Grab the fragment data from the uv
vec2 texCoord = varTexCoord0.st;
vec4 fragEyePos = unpackDeferredPositionFromZeye(texCoord);
vec4 fragWorldPos = getViewInverse() * fragEyePos;
// From frag world pos find the cluster
vec4 clusterEyePos = frustumGrid_worldToEye(fragWorldPos);
ivec3 clusterPos = frustumGrid_eyeToClusterPos(clusterEyePos.xyz);
int clusterIndex = frustumGrid_clusterToIndex(clusterPos);
ivec3 cluster = clusterGrid_getCluster(clusterIndex);
int numLights = cluster.x + cluster.y;
float numLightsScale = clamp(numLights * 0.05, 0.01, 1.0);
int clusterOffset = cluster.z;
ivec3 dims = frustumGrid.dims.xyz;
dims.z +=1;
ivec3 summedDims = ivec3(dims.x * dims.y, dims.x, 1);
if (clusterPos.x < 0 || clusterPos.x >= dims.x) {
_fragColor = vec4(0.0);
return;
}
if (clusterPos.y < 0 || clusterPos.y >= dims.y) {
_fragColor = vec4(0.0);
return;
}
if (clusterPos.z < 0 || clusterPos.z > dims.z) {
_fragColor = vec4(0.0);
return;
}
int numLightTouching = 0;
for (int i = 0; i < numLights; i++) {
// Need the light now
int theLightIndex = clusterGrid_getClusterLightId(i, clusterOffset);
Light light = getLight(theLightIndex);
// Clip againgst the light volume and Make the Light vector going from fragment to light center in world space
vec4 fragLightVecLen2;
vec4 fragLightDirLen;
float cosSpotAngle;
if (!lightVolume_clipFragToLightVolumePoint(light.volume, fragWorldPos.xyz, fragLightVecLen2)) {
continue;
}
// Allright we re in the light sphere volume
fragLightDirLen.w = length(fragLightVecLen2.xyz);
fragLightDirLen.xyz = fragLightVecLen2.xyz / fragLightDirLen.w;
// Check spot
if ((i >= cluster.x) && !lightVolume_clipFragToLightVolumeSpotSide(light.volume, fragLightDirLen, cosSpotAngle)) {
continue;
}
numLightTouching++;
}
_fragColor = vec4(colorRamp(1.0 - (numLightTouching / 12.0f)), (numLightTouching > 0 ? 0.5 + 0.5 * numLightsScale : 0.0));
}

View file

@ -0,0 +1,73 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// lightClusters_drawClusterContent.slv
// Vertex shader
//
// Created by Sam Gateau on 9/8/2016
// 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 gpu/Transform.slh@>
<$declareStandardTransform()$>
<@include LightClusterGrid.slh@>
<@include gpu/Color.slh@>
<$declareColorWheel()$>
out vec4 varColor;
void main(void) {
const vec4 UNIT_BOX[8] = vec4[8](
vec4(0.0, 0.0, 0.0, 1.0),
vec4(1.0, 0.0, 0.0, 1.0),
vec4(0.0, 1.0, 0.0, 1.0),
vec4(1.0, 1.0, 0.0, 1.0),
vec4(0.0, 0.0, 1.0, 1.0),
vec4(1.0, 0.0, 1.0, 1.0),
vec4(0.0, 1.0, 1.0, 1.0),
vec4(1.0, 1.0, 1.0, 1.0)
);
const int UNIT_BOX_LINE_INDICES[24] = int[24](
0, 1,
1, 3,
3, 2,
2, 0,
4, 5,
5, 7,
7, 6,
6, 4,
2, 6,
3, 7,
0, 4,
1, 5
);
vec4 pos = UNIT_BOX[UNIT_BOX_LINE_INDICES[gl_VertexID]];
ivec3 cluster = clusterGrid_getCluster(gpu_InstanceID);
int numLights = cluster.x + cluster.y;
float numLightsScale = clamp(numLights * 0.1, 0.0, 1.0);
ivec3 clusterPos = frustumGrid_indexToCluster(gpu_InstanceID);
float boxScale = 0.99;
vec3 eyePos = frustumGrid_clusterPosToEye(clusterPos, vec3((1.0 - boxScale) * 0.5 + (1.0 - numLightsScale) * boxScale * 0.5) + numLightsScale * boxScale * pos.xyz);
vec4 worldPos = frustumGrid_eyeToWorld(vec4(eyePos.xyz, 1.0));
// standard transform
TransformCamera cam = getTransformCamera();
<$transformWorldToClipPos(cam, worldPos, gl_Position)$>
varColor = vec4(colorWheel(fract(float(gpu_InstanceID) / float(frustumGrid_numClusters()))), (numLights >0 ? 0.9 : 0.1));
}

View file

@ -0,0 +1,73 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
// lightClusters_drawClusterFro Depth.slf
//
// Created by Sam Gateau on 9/8/2016.
// 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
//
// Everything about deferred buffer
<@include DeferredBufferRead.slh@>
<@include LightClusterGrid.slh@>
<@include gpu/Color.slh@>
<$declareColorWheel()$>
in vec2 varTexCoord0;
out vec4 _fragColor;
void main(void) {
// Grab the fragment data from the uv
vec2 texCoord = varTexCoord0.st;
vec4 fragEyePos = unpackDeferredPositionFromZeye(texCoord);
vec4 fragWorldPos = getViewInverse() * fragEyePos;
// From frag world pos find the cluster
vec4 clusterEyePos = frustumGrid_worldToEye(fragWorldPos);
ivec3 clusterPos = frustumGrid_eyeToClusterPos(clusterEyePos.xyz);
ivec3 cluster = clusterGrid_getCluster(frustumGrid_clusterToIndex(clusterPos));
int numLights = cluster.x + cluster.y;
float numLightsScale = clamp(numLights * 0.05, 0.01, 1.0);
ivec3 dims = frustumGrid.dims.xyz;
dims.z +=1;
ivec3 summedDims = ivec3(dims.x * dims.y, dims.x, 1);
if (clusterPos.x < 0 || clusterPos.x >= dims.x) {
_fragColor = vec4(0.0);
return;
}
if (clusterPos.y < 0 || clusterPos.y >= dims.y) {
_fragColor = vec4(0.0);
return;
}
if (clusterPos.z < 0 || clusterPos.z > dims.z) {
_fragColor = vec4(0.0);
return;
}
float relClusterId = float(clusterPos.z * summedDims.x + clusterPos.y * summedDims.y + clusterPos.x) / float(frustumGrid_numClusters());
if (relClusterId < 0.0) {
_fragColor = vec4(0.0);
} else if (relClusterId >= 1.0) {
_fragColor = vec4(vec3(1.0), 0.2);
} else {
_fragColor = vec4(colorWheel(fract(relClusterId)), (numLights > 0 ? 0.05 + 0.95 * numLightsScale : 0.0));
}
}

View file

@ -0,0 +1,66 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// lightClusters_drawClusterFrom Depth.slv
// Vertex shader
//
// Created by Sam Gateau on 9/8/2016
// 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 gpu/Transform.slh@>
<$declareStandardTransform()$>
<@include LightClusterGrid.slh@>
<@include gpu/Color.slh@>
<$declareColorWheel()$>
out vec4 varColor;
void main(void) {
const vec4 UNIT_BOX[8] = vec4[8](
vec4(0.0, 0.0, 0.0, 1.0),
vec4(1.0, 0.0, 0.0, 1.0),
vec4(0.0, 1.0, 0.0, 1.0),
vec4(1.0, 1.0, 0.0, 1.0),
vec4(0.0, 0.0, 1.0, 1.0),
vec4(1.0, 0.0, 1.0, 1.0),
vec4(0.0, 1.0, 1.0, 1.0),
vec4(1.0, 1.0, 1.0, 1.0)
);
const int UNIT_BOX_LINE_INDICES[24] = int[24](
0, 1,
1, 3,
3, 2,
2, 0,
4, 5,
5, 7,
7, 6,
6, 4,
2, 6,
3, 7,
0, 4,
1, 5
);
vec4 pos = UNIT_BOX[UNIT_BOX_LINE_INDICES[gl_VertexID]];
ivec3 clusterPos = frustumGrid_indexToCluster(gpu_InstanceID);
vec3 eyePos = frustumGrid_clusterPosToEye(clusterPos, vec3(0.05) + 0.9 * pos.xyz);
vec4 worldPos = frustumGrid_eyeToWorld(vec4(eyePos.xyz, 1.0));
// standard transform
TransformCamera cam = getTransformCamera();
<$transformWorldToClipPos(cam, worldPos, gl_Position)$>
varColor = vec4(colorWheel(fract(float(gpu_InstanceID) / float(frustumGrid_numClusters()))), 0.9);
}

View file

@ -0,0 +1,19 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
// lightClusters_drawGrid.slf
//
// Created by Sam Gateau on 9/8/2016.
// 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
//
in vec4 varColor;
out vec4 outFragColor;
void main(void) {
outFragColor = varColor;
}

View file

@ -0,0 +1,73 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// lightClusters_drawGrid.slv
// Vertex shader
//
// Created by Sam Gateau on 9/8/2016
// 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 gpu/Transform.slh@>
<$declareStandardTransform()$>
<@include LightClusterGrid.slh@>
<@include gpu/Color.slh@>
<$declareColorWheel()$>
out vec4 varColor;
void main(void) {
const vec4 UNIT_BOX[8] = vec4[8](
vec4(0.0, 0.0, 0.0, 1.0),
vec4(1.0, 0.0, 0.0, 1.0),
vec4(0.0, 1.0, 0.0, 1.0),
vec4(1.0, 1.0, 0.0, 1.0),
vec4(0.0, 0.0, 1.0, 1.0),
vec4(1.0, 0.0, 1.0, 1.0),
vec4(0.0, 1.0, 1.0, 1.0),
vec4(1.0, 1.0, 1.0, 1.0)
);
const int UNIT_BOX_LINE_INDICES[24] = int[24](
0, 1,
1, 3,
3, 2,
2, 0,
4, 5,
5, 7,
7, 6,
6, 4,
2, 6,
3, 7,
0, 4,
1, 5
);
vec4 pos = UNIT_BOX[UNIT_BOX_LINE_INDICES[gl_VertexID]];
ivec3 cluster = clusterGrid_getCluster(gpu_InstanceID);
int numLights = cluster.x + cluster.y;
ivec3 clusterPos = frustumGrid_indexToCluster(gpu_InstanceID);
float boxScale = 1.0;
vec3 eyePos = frustumGrid_clusterPosToEye(clusterPos, vec3(1.0 - boxScale) * 0.5 + boxScale * pos.xyz);
vec4 worldPos = frustumGrid_eyeToWorld(vec4(eyePos.xyz, 1.0));
// standard transform
TransformCamera cam = getTransformCamera();
<$transformWorldToClipPos(cam, worldPos, gl_Position)$>
varColor = vec4(colorWheel(fract(float(gpu_InstanceID) / float(frustumGrid_numClusters()))), (numLights > 0 ? 0.9 : 0.0));
}

View file

@ -0,0 +1,153 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// local_lights_drawOutline.frag
// fragment shader
//
// Created by Sam Gateau on 9/6/2016.
// Copyright 2014 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 DeferredBufferRead.slh@>
<$declareDeferredCurvature()$>
// Everything about light
<@include model/Light.slh@>
<$declareLightBuffer(128)$>
<@include LightingModel.slh@>
<@include LightPoint.slh@>
<$declareDrawPointOutline()$>
<@include LightSpot.slh@>
<$declareDrawSpotOutline()$>
<@include LightClusterGrid.slh@>
in vec2 _texCoord0;
out vec4 _fragColor;
void main(void) {
// Grab the fragment data from the uv
vec2 texCoord = _texCoord0.st;
vec4 fragPosition = unpackDeferredPositionFromZeye(texCoord);
DeferredFragment frag = unpackDeferredFragmentNoPosition(texCoord);
if (frag.mode == FRAG_MODE_UNLIT) {
discard;
}
frag.position = fragPosition;
// Frag pos in world
mat4 invViewMat = getViewInverse();
vec4 fragPos = invViewMat * fragPosition;
// From frag world pos find the cluster
vec4 clusterEyePos = frustumGrid_worldToEye(fragPos);
ivec3 clusterPos = frustumGrid_eyeToClusterPos(clusterEyePos.xyz);
ivec3 cluster = clusterGrid_getCluster(frustumGrid_clusterToIndex(clusterPos));
int numLights = cluster.x + cluster.y;
if (numLights <= 0) {
discard;
}
int lightClusterOffset = cluster.z;
ivec3 dims = frustumGrid.dims.xyz;
if (clusterPos.x < 0 || clusterPos.x >= dims.x) {
discard;
}
if (clusterPos.y < 0 || clusterPos.y >= dims.y) {
discard;
}
if (clusterPos.z < 0 || clusterPos.z > dims.z) {
discard;
}
// Frag to eye vec
vec4 fragEyeVector = invViewMat * vec4(-frag.position.xyz, 0.0);
vec3 fragEyeDir = normalize(fragEyeVector.xyz);
int numLightTouching = 0;
for (int i = 0; i < cluster.x; i++) {
// Need the light now
int theLightIndex = clusterGrid_getClusterLightId(i, lightClusterOffset);
Light light = getLight(theLightIndex);
// Clip againgst the light volume and Make the Light vector going from fragment to light center in world space
vec4 fragLightVecLen2;
vec4 fragLightDirLen;
if (!lightVolume_clipFragToLightVolumePoint(light.volume, fragPos.xyz, fragLightVecLen2)) {
continue;
}
// Allright we re in the light sphere volume
fragLightDirLen.w = length(fragLightVecLen2.xyz);
fragLightDirLen.xyz = fragLightVecLen2.xyz / fragLightDirLen.w;
if (dot(frag.normal, fragLightDirLen.xyz) < 0.0) {
continue;
}
numLightTouching++;
// Allright we re valid in the volume
float fragLightDistance = fragLightDirLen.w;
vec3 fragLightDir = fragLightDirLen.xyz;
vec3 color = vec3(0.0);
if (evalLightPointEdge(color, light, fragLightDirLen, fragEyeDir)) {
_fragColor.rgb += color;
}
}
for (int i = cluster.x; i < numLights; i++) {
// Need the light now
int theLightIndex = clusterGrid_getClusterLightId(i, lightClusterOffset);
Light light = getLight(theLightIndex);
// Clip againgst the light volume and Make the Light vector going from fragment to light center in world space
vec4 fragLightVecLen2;
vec4 fragLightDirLen;
float cosSpotAngle;
if (!lightVolume_clipFragToLightVolumePoint(light.volume, fragPos.xyz, fragLightVecLen2)) {
continue;
}
// Allright we re in the light sphere volume
fragLightDirLen.w = length(fragLightVecLen2.xyz);
fragLightDirLen.xyz = fragLightVecLen2.xyz / fragLightDirLen.w;
if (dot(frag.normal, fragLightDirLen.xyz) < 0.0) {
continue;
}
// Check spot
if (!lightVolume_clipFragToLightVolumeSpotSide(light.volume, fragLightDirLen, cosSpotAngle)) {
continue;
}
numLightTouching++;
vec3 color = vec3(0.0);
if (evalLightSpotEdge(color, light, fragLightDirLen, cosSpotAngle, fragEyeDir)) {
_fragColor.rgb += color;
}
}
}

View file

@ -0,0 +1,200 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// local_lights_shading.frag
// fragment shader
//
// Created by Sam Gateau on 9/6/2016.
// Copyright 2014 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 DeferredBufferRead.slh@>
<$declareDeferredCurvature()$>
// Everything about light
<@include model/Light.slh@>
<$declareLightBuffer(256)$>
<@include LightingModel.slh@>
<@include LightPoint.slh@>
<$declareLightingPoint(supportScattering)$>
<@include LightSpot.slh@>
<$declareLightingSpot(supportScattering)$>
<@include LightClusterGrid.slh@>
in vec2 _texCoord0;
out vec4 _fragColor;
void main(void) {
// Grab the fragment data from the uv
vec2 texCoord = _texCoord0.st;
vec4 fragPosition = unpackDeferredPositionFromZeye(texCoord);
DeferredFragment frag = unpackDeferredFragmentNoPosition(texCoord);
if (frag.mode == FRAG_MODE_UNLIT) {
discard;
}
frag.position = fragPosition;
// Frag pos in world
mat4 invViewMat = getViewInverse();
vec4 fragPos = invViewMat * fragPosition;
// From frag world pos find the cluster
vec4 clusterEyePos = frustumGrid_worldToEye(fragPos);
ivec3 clusterPos = frustumGrid_eyeToClusterPos(clusterEyePos.xyz);
ivec3 cluster = clusterGrid_getCluster(frustumGrid_clusterToIndex(clusterPos));
int numLights = cluster.x + cluster.y;
if (numLights <= 0) {
discard;
}
int lightClusterOffset = cluster.z;
ivec3 dims = frustumGrid.dims.xyz;
if (clusterPos.x < 0 || clusterPos.x >= dims.x) {
discard;
}
if (clusterPos.y < 0 || clusterPos.y >= dims.y) {
discard;
}
if (clusterPos.z < 0 || clusterPos.z > dims.z) {
discard;
}
vec4 midNormalCurvature;
vec4 lowNormalCurvature;
if (frag.mode == FRAG_MODE_SCATTERING) {
unpackMidLowNormalCurvature(texCoord, midNormalCurvature, lowNormalCurvature);
}
// Frag to eye vec
vec4 fragEyeVector = invViewMat * vec4(-frag.position.xyz, 0.0);
vec3 fragEyeDir = normalize(fragEyeVector.xyz);
// COmpute the rougness into gloss2 once:
float fragGloss2 = pow(frag.roughness + 0.001, 2.0);
bool withScattering = (frag.scattering * isScatteringEnabled() > 0.0);
int numLightTouching = 0;
for (int i = 0; i < cluster.x; i++) {
// Need the light now
int theLightIndex = clusterGrid_getClusterLightId(i, lightClusterOffset);
Light light = getLight(theLightIndex);
// Clip againgst the light volume and Make the Light vector going from fragment to light center in world space
vec4 fragLightVecLen2;
vec4 fragLightDirLen;
if (!lightVolume_clipFragToLightVolumePoint(light.volume, fragPos.xyz, fragLightVecLen2)) {
continue;
}
// Allright we re in the light sphere volume
fragLightDirLen.w = length(fragLightVecLen2.xyz);
fragLightDirLen.xyz = fragLightVecLen2.xyz / fragLightDirLen.w;
if (dot(frag.normal, fragLightDirLen.xyz) < 0.0) {
continue;
}
numLightTouching++;
vec3 diffuse = vec3(1.0);
vec3 specular = vec3(0.1);
// Allright we re valid in the volume
float fragLightDistance = fragLightDirLen.w;
vec3 fragLightDir = fragLightDirLen.xyz;
// Eval attenuation
float radialAttenuation = lightIrradiance_evalLightAttenuation(light.irradiance, fragLightDistance);
vec3 lightEnergy = radialAttenuation * getLightIrradiance(light);
// Eval shading
if (withScattering) {
evalFragShadingScattering(diffuse, specular, frag.normal, fragLightDir, fragEyeDir, frag.metallic, frag.fresnel, frag.roughness, frag.albedo
,frag.scattering, midNormalCurvature, lowNormalCurvature );
} else {
evalFragShadingGloss(diffuse, specular, frag.normal, fragLightDir, fragEyeDir, frag.metallic, frag.fresnel, fragGloss2, frag.albedo);
}
diffuse *= lightEnergy * isDiffuseEnabled();
specular *= lightEnergy * isSpecularEnabled();
_fragColor.rgb += diffuse;
_fragColor.rgb += specular;
}
for (int i = cluster.x; i < numLights; i++) {
// Need the light now
int theLightIndex = clusterGrid_getClusterLightId(i, lightClusterOffset);
Light light = getLight(theLightIndex);
// Clip againgst the light volume and Make the Light vector going from fragment to light center in world space
vec4 fragLightVecLen2;
vec4 fragLightDirLen;
float cosSpotAngle;
if (!lightVolume_clipFragToLightVolumePoint(light.volume, fragPos.xyz, fragLightVecLen2)) {
continue;
}
// Allright we re in the light sphere volume
fragLightDirLen.w = length(fragLightVecLen2.xyz);
fragLightDirLen.xyz = fragLightVecLen2.xyz / fragLightDirLen.w;
if (dot(frag.normal, fragLightDirLen.xyz) < 0.0) {
continue;
}
// Check spot
if (!lightVolume_clipFragToLightVolumeSpotSide(light.volume, fragLightDirLen, cosSpotAngle)) {
continue;
}
numLightTouching++;
vec3 diffuse = vec3(1.0);
vec3 specular = vec3(0.1);
// Allright we re valid in the volume
float fragLightDistance = fragLightDirLen.w;
vec3 fragLightDir = fragLightDirLen.xyz;
// Eval attenuation
float radialAttenuation = lightIrradiance_evalLightAttenuation(light.irradiance, fragLightDistance);
float angularAttenuation = lightIrradiance_evalLightSpotAttenuation(light.irradiance, cosSpotAngle);
vec3 lightEnergy = radialAttenuation * angularAttenuation * getLightIrradiance(light);
// Eval shading
if (withScattering) {
evalFragShadingScattering(diffuse, specular, frag.normal, fragLightDir, fragEyeDir, frag.metallic, frag.fresnel, frag.roughness, frag.albedo
,frag.scattering, midNormalCurvature, lowNormalCurvature );
} else {
evalFragShadingGloss(diffuse, specular, frag.normal, fragLightDir, fragEyeDir, frag.metallic, frag.fresnel, fragGloss2, frag.albedo);
}
diffuse *= lightEnergy * isDiffuseEnabled();
specular *= lightEnergy * isSpecularEnabled();
_fragColor.rgb += diffuse;
_fragColor.rgb += specular;
}
}

View file

@ -13,6 +13,8 @@
<@include model/Light.slh@>
<$declareLightBuffer()$>
<$declareLightAmbientBuffer()$>
<@include LightingModel.slh@>
@ -26,6 +28,11 @@ vec4 evalGlobalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 a
// Need the light now
Light light = getLight();
vec3 lightDirection = getLightDirection(light);
vec3 lightIrradiance = getLightIrradiance(light);
LightAmbient ambient = getLightAmbient();
TransformCamera cam = getTransformCamera();
vec3 fragNormal;
<$transformEyeToWorldDir(cam, normal, fragNormal)$>
@ -33,12 +40,12 @@ vec4 evalGlobalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 a
vec3 fragEyeDir;
<$transformEyeToWorldDir(cam, fragEyeVectorView, fragEyeDir)$>
vec3 color = opacity * albedo * getLightColor(light) * getLightAmbientIntensity(light);
vec3 color = opacity * albedo * getLightColor(light) * getLightAmbientIntensity(ambient);
// Directional
vec3 directionalDiffuse;
vec3 directionalSpecular;
evalLightingDirectional(directionalDiffuse, directionalSpecular, light, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation);
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation);
color += directionalDiffuse * isDiffuseEnabled() * isDirectionalEnabled();
color += directionalSpecular * isSpecularEnabled() * isDirectionalEnabled();

View file

@ -13,6 +13,8 @@
//
<@include model/Light.slh@>
<$declareLightBuffer()$>
<$declareLightAmbientBuffer()$>
<@include LightingModel.slh@>
@ -26,6 +28,11 @@ vec4 evalGlobalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 a
// Need the light now
Light light = getLight();
vec3 lightDirection = getLightDirection(light);
vec3 lightIrradiance = getLightIrradiance(light);
LightAmbient ambient = getLightAmbient();
TransformCamera cam = getTransformCamera();
vec3 fragNormal;
<$transformEyeToWorldDir(cam, normal, fragNormal)$>
@ -33,12 +40,12 @@ vec4 evalGlobalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 a
vec3 fragEyeDir;
<$transformEyeToWorldDir(cam, fragEyeVectorView, fragEyeDir)$>
vec3 color = opacity * albedo * getLightColor(light) * getLightAmbientIntensity(light);
vec3 color = opacity * albedo * getLightColor(light) * getLightAmbientIntensity(ambient);
// Directional
vec3 directionalDiffuse;
vec3 directionalSpecular;
evalLightingDirectional(directionalDiffuse, directionalSpecular, light, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation);
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation);
color += directionalDiffuse;
color += directionalSpecular / opacity;

View file

@ -11,7 +11,7 @@
// 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 DeferredBufferRead.slh@>
@ -19,6 +19,7 @@
// Everything about light
<@include model/Light.slh@>
<$declareLightBuffer()$>
<@include LightingModel.slh@>
@ -28,10 +29,13 @@
uniform vec4 texcoordFrameTransform;
in vec4 _texCoord0;
in vec4 _texCoord0;!>
out vec4 _fragColor;
void main(void) {
_fragColor = vec4(1.0, 1.0, 1.0, 1.0);
<!
DeferredFrameTransform deferredTransform = getDeferredFrameTransform();
// Grab the fragment data from the uv
@ -45,12 +49,6 @@ void main(void) {
discard;
}
// Kill if in front of the light volume
float depth = frag.depthVal;
if (depth < gl_FragCoord.z) {
discard;
}
// Need the light now
Light light = getLight();
@ -60,7 +58,7 @@ void main(void) {
// Clip againgst the light volume and Make the Light vector going from fragment to light center in world space
vec4 fragLightVecLen2;
if (!clipFragToLightVolumePoint(light, fragPos.xyz, fragLightVecLen2)) {
if (!lightVolume_clipFragToLightVolumePoint(light.volume, fragPos.xyz, fragLightVecLen2)) {
discard;
}
@ -83,4 +81,5 @@ void main(void) {
_fragColor.rgb += diffuse;
_fragColor.rgb += specular;
!>
}

View file

@ -13,77 +13,103 @@
//
// Everything about deferred buffer
<@include DeferredBufferRead.slh@>
<!<@include DeferredBufferRead.slh@>
<$declareDeferredCurvature()$>
// Everything about light
<@include model/Light.slh@>
<$declareLightBuffer(256)$>
uniform lightIndexBuffer {
int lightIndex[256];
};
<@include LightingModel.slh@>
<@include LightPoint.slh@>
<$declareLightingPoint(supportScattering)$>
<@include LightSpot.slh@>
<$declareLightingSpot(supportScattering)$>
uniform vec4 texcoordFrameTransform;
//uniform vec4 texcoordFrameTransform;
!>
in vec4 _texCoord0;
//in vec4 _texCoord0;
//flat in int instanceID;
out vec4 _fragColor;
void main(void) {
_fragColor = vec4(1.0, 1.0, 1.0, 1.0);
DeferredFrameTransform deferredTransform = getDeferredFrameTransform();
// DeferredFrameTransform deferredTransform = getDeferredFrameTransform();
// Grab the fragment data from the uv
vec2 texCoord = _texCoord0.st / _texCoord0.q;
texCoord *= texcoordFrameTransform.zw;
texCoord += texcoordFrameTransform.xy;
DeferredFragment frag = unpackDeferredFragment(deferredTransform, texCoord);
//vec2 texCoord = _texCoord0.st;/* / _texCoord0.q;
/*texCoord *= texcoordFrameTransform.zw;
texCoord += texcoordFrameTransform.xy;*/
/*
vec4 fragPosition = unpackDeferredPositionFromZeye(texCoord);
DeferredFragment frag = unpackDeferredFragmentNoPosition(texCoord);
if (frag.mode == FRAG_MODE_UNLIT) {
discard;
}
// Kill if in front of the light volume
float depth = frag.depthVal;
if (depth < gl_FragCoord.z) {
discard;
}
// Need the light now
Light light = getLight();
// frag.depthVal = depthValue;
frag.position = fragPosition;
// Frag pos in world
mat4 invViewMat = getViewInverse();
vec4 fragPos = invViewMat * frag.position;
// Clip againgst the light volume and Make the Light vector going from fragment to light center in world space
vec4 fragLightVecLen2;
vec4 fragLightDirLen;
float cosSpotAngle;
if (!clipFragToLightVolumeSpot(light, fragPos.xyz, fragLightVecLen2, fragLightDirLen, cosSpotAngle)) {
discard;
}
// Frag to eye vec
vec4 fragEyeVector = invViewMat * vec4(-frag.position.xyz, 0.0);
vec3 fragEyeDir = normalize(fragEyeVector.xyz);
vec3 diffuse;
vec3 specular;
vec4 midNormalCurvature;
vec4 lowNormalCurvature;
if (frag.mode == FRAG_MODE_SCATTERING) {
unpackMidLowNormalCurvature(texCoord, midNormalCurvature, lowNormalCurvature);
}
evalLightingSpot(diffuse, specular, light,
fragLightDirLen.xyzw, cosSpotAngle, fragEyeDir, frag.normal, frag.roughness,
frag.metallic, frag.fresnel, frag.albedo, 1.0,
frag.scattering, midNormalCurvature, lowNormalCurvature);
_fragColor.rgb += diffuse;
_fragColor.rgb += specular;
// Frag pos in world
mat4 invViewMat = getViewInverse();
vec4 fragPos = invViewMat * fragPosition;
// Frag to eye vec
vec4 fragEyeVector = invViewMat * vec4(-frag.position.xyz, 0.0);
vec3 fragEyeDir = normalize(fragEyeVector.xyz);
int numLights = lightIndex[0];
for (int i = 0; i < numLights; i++) {
// Need the light now
Light light = getLight(lightIndex[i + 1]);
bool isSpot = light_isSpot(light);
// Clip againgst the light volume and Make the Light vector going from fragment to light center in world space
vec4 fragLightVecLen2;
vec4 fragLightDirLen;
float cosSpotAngle;
if (isSpot) {
if (!clipFragToLightVolumeSpot(light, fragPos.xyz, fragLightVecLen2, fragLightDirLen, cosSpotAngle)) {
continue;
}
} else {
if (!clipFragToLightVolumePoint(light, fragPos.xyz, fragLightVecLen2)) {
continue;
}
}
vec3 diffuse;
vec3 specular;
if (isSpot) {
evalLightingSpot(diffuse, specular, light,
fragLightDirLen.xyzw, cosSpotAngle, fragEyeDir, frag.normal, frag.roughness,
frag.metallic, frag.fresnel, frag.albedo, 1.0,
frag.scattering, midNormalCurvature, lowNormalCurvature);
} else {
evalLightingPoint(diffuse, specular, light,
fragLightVecLen2.xyz, fragEyeDir, frag.normal, frag.roughness,
frag.metallic, frag.fresnel, frag.albedo, 1.0,
frag.scattering, midNormalCurvature, lowNormalCurvature);
}
_fragColor.rgb += diffuse;
_fragColor.rgb += specular;
}
*/
}

View file

@ -13,6 +13,7 @@
<@include DeferredBufferRead.slh@>
<@include model/Light.slh@>
<$declareLightBuffer()$>
<$declareDeferredCurvature()$>

View file

@ -0,0 +1,156 @@
//
// IndexedContainer.h
// render
//
// Created by Sam Gateau on 9/6/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_render_IndexedContainer_h
#define hifi_render_IndexedContainer_h
#include <vector>
namespace render {
namespace indexed_container {
using Index = int32_t;
const Index MAXIMUM_INDEX { 1 << 30 };
const Index INVALID_INDEX { -1 };
using Indices = std::vector< Index >;
template <Index MaxNumElements = MAXIMUM_INDEX>
class Allocator {
public:
Allocator() {}
Indices _freeIndices;
Index _nextNewIndex { 0 };
bool checkIndex(Index index) const { return ((index >= 0) && (index < _nextNewIndex)); }
Index getNumIndices() const { return _nextNewIndex - (Index) _freeIndices.size(); }
Index getNumFreeIndices() const { return (Index) _freeIndices.size(); }
Index getNumAllocatedIndices() const { return _nextNewIndex; }
Index allocateIndex() {
if (_freeIndices.empty()) {
Index index = _nextNewIndex;
if (index >= MaxNumElements) {
// abort! we are trying to go overboard with the total number of allocated elements
assert(false);
// This should never happen because Bricks are allocated along with the cells and there
// is already a cap on the cells allocation
return INVALID_INDEX;
}
_nextNewIndex++;
return index;
} else {
Index index = _freeIndices.back();
_freeIndices.pop_back();
return index;
}
}
void freeIndex(Index index) {
if (checkIndex(index)) {
_freeIndices.push_back(index);
}
}
void clear() {
_freeIndices.clear();
_nextNewIndex = 0;
}
};
template <class T, Index MaxNumElements = MAXIMUM_INDEX>
class IndexedVector {
Allocator<MaxNumElements> _allocator;
public:
using Element = T;
using Elements = std::vector<T>;
Elements _elements;
bool checkIndex(Index index) const { return _allocator.checkIndex(index); };
Index getNumElements() const { return _allocator.getNumIndices(); }
Index getNumFreeIndices() const { return _allocator.getNumFreeIndices(); }
Index getNumAllocatedIndices() const { return _allocator.getNumAllocatedIndices(); }
Index newElement(const Element& e) {
Index index = _allocator.allocateIndex();
if (index != INVALID_INDEX) {
if (index < (Index) _elements.size()) {
_elements[index] = e;
} else {
assert(index == _elements.size());
_elements.emplace_back(e);
}
}
return index;
}
const Element& freeElement(Index index) {
_allocator.freeIndex(index);
return _elements[index];
}
const Element& get(Index index) const {
return _elements[index];
}
Element& edit(Index index) {
return _elements[index];
}
};
template <class T, Index MaxNumElements = MAXIMUM_INDEX>
class IndexedPointerVector {
Allocator<MaxNumElements> _allocator;
public:
using Data = T;
using ElementPtr = std::shared_ptr<Data>;
using Elements = std::vector<ElementPtr>;
Elements _elements;
bool checkIndex(Index index) const { return _allocator.checkIndex(index); };
Index getNumElements() const { return _allocator.getNumIndices(); }
Index getNumFreeIndices() const { return _allocator.getNumFreeIndices(); }
Index getNumAllocatedIndices() const { return _allocator.getNumAllocatedIndices(); }
Index newElement(const ElementPtr& e) {
Index index = _allocator.allocateIndex();
if (index != INVALID_INDEX) {
if (index < (Index) _elements.size()) {
_elements[index] = e;
} else {
assert(index == (Index) _elements.size());
_elements.emplace_back(e);
}
}
return index;
}
ElementPtr freeElement(Index index) {
ElementPtr freed;
if (checkIndex(index)) {
_allocator.freeIndex(index);
freed = _elements[index];
_elements[index].reset(); // really forget it
}
return freed;
}
ElementPtr get(Index index) const {
if (checkIndex(index)) {
return _elements[index];
} else {
return ElementPtr();
}
}
};
};
}
#endif

View file

@ -63,6 +63,7 @@ void ShapePlumber::addPipeline(const Filter& filter, const gpu::ShaderPointer& p
slotBindings.insert(gpu::Shader::Binding(std::string("occlusionMap"), Slot::MAP::OCCLUSION));
slotBindings.insert(gpu::Shader::Binding(std::string("scatteringMap"), Slot::MAP::SCATTERING));
slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), Slot::BUFFER::LIGHT));
slotBindings.insert(gpu::Shader::Binding(std::string("lightAmbientBuffer"), Slot::BUFFER::LIGHT_AMBIENT_BUFFER));
slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), Slot::MAP::LIGHT_AMBIENT));
slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), Slot::NORMAL_FITTING));
@ -85,8 +86,9 @@ void ShapePlumber::addPipeline(const Filter& filter, const gpu::ShaderPointer& p
locations->materialBufferUnit = program->getBuffers().findLocation("materialBuffer");
locations->texMapArrayBufferUnit = program->getBuffers().findLocation("texMapArrayBuffer");
locations->lightBufferUnit = program->getBuffers().findLocation("lightBuffer");
locations->lightAmbientBufferUnit = program->getBuffers().findLocation("lightAmbientBuffer");
locations->lightAmbientMapUnit = program->getTextures().findLocation("skyboxMap");
ShapeKey key{filter._flags};
auto gpuPipeline = gpu::Pipeline::create(program, state);
auto shapePipeline = std::make_shared<Pipeline>(gpuPipeline, locations, batchSetter);

View file

@ -201,6 +201,7 @@ public:
TEXMAPARRAY,
LIGHTING_MODEL,
LIGHT,
LIGHT_AMBIENT_BUFFER,
};
enum MAP {
@ -231,6 +232,7 @@ public:
int materialBufferUnit;
int texMapArrayBufferUnit;
int lightBufferUnit;
int lightAmbientBufferUnit;
int lightAmbientMapUnit;
};
using LocationsPointer = std::shared_ptr<Locations>;

View file

@ -254,6 +254,40 @@ public:
Varying hasVarying() const { return Varying((*this)); }
};
template <class T0, class T1, class T2, class T3, class T4, class T5, class T6>
class VaryingSet7 : public std::tuple<Varying, Varying, Varying, Varying, Varying, Varying, Varying>{
public:
using Parent = std::tuple<Varying, Varying, Varying, Varying, Varying, Varying, Varying>;
VaryingSet7() : Parent(Varying(T0()), Varying(T1()), Varying(T2()), Varying(T3()), Varying(T4()), Varying(T5()), Varying(T6())) {}
VaryingSet7(const VaryingSet7& src) : Parent(std::get<0>(src), std::get<1>(src), std::get<2>(src), std::get<3>(src), std::get<4>(src), std::get<5>(src), std::get<6>(src)) {}
VaryingSet7(const Varying& first, const Varying& second, const Varying& third, const Varying& fourth, const Varying& fifth, const Varying& sixth, const Varying& seventh) : Parent(first, second, third, fourth, fifth, sixth, seventh) {}
const T0& get0() const { return std::get<0>((*this)).template get<T0>(); }
T0& edit0() { return std::get<0>((*this)).template edit<T0>(); }
const T1& get1() const { return std::get<1>((*this)).template get<T1>(); }
T1& edit1() { return std::get<1>((*this)).template edit<T1>(); }
const T2& get2() const { return std::get<2>((*this)).template get<T2>(); }
T2& edit2() { return std::get<2>((*this)).template edit<T2>(); }
const T3& get3() const { return std::get<3>((*this)).template get<T3>(); }
T3& edit3() { return std::get<3>((*this)).template edit<T3>(); }
const T4& get4() const { return std::get<4>((*this)).template get<T4>(); }
T4& edit4() { return std::get<4>((*this)).template edit<T4>(); }
const T5& get5() const { return std::get<5>((*this)).template get<T5>(); }
T5& edit5() { return std::get<5>((*this)).template edit<T5>(); }
const T6& get6() const { return std::get<6>((*this)).template get<T6>(); }
T6& edit6() { return std::get<6>((*this)).template edit<T6>(); }
Varying hasVarying() const { return Varying((*this)); }
};
template < class T, int NUM >
class VaryingArray : public std::array<Varying, NUM> {
public:

View file

@ -657,6 +657,26 @@ void ViewFrustum::evalProjectionMatrix(glm::mat4& proj) const {
proj = _projection;
}
glm::mat4 ViewFrustum::evalProjectionMatrixRange(float rangeNear, float rangeFar) const {
// make sure range near far make sense
assert(rangeNear > 0.0);
assert(rangeFar > rangeNear);
// recreate a projection matrix for only a range of depth of this frustum.
// take the current projection
glm::mat4 rangeProj = _projection;
float A = -(rangeFar + rangeNear) / (rangeFar - rangeNear);
float B = -2 * rangeFar*rangeNear / ((rangeFar - rangeNear));
rangeProj[2][2] = A;
rangeProj[3][2] = B;
return rangeProj;
}
void ViewFrustum::evalViewTransform(Transform& view) const {
view.setTranslation(getPosition());
view.setRotation(getOrientation());

View file

@ -129,6 +129,9 @@ public:
float distanceToCamera(const glm::vec3& point) const;
void evalProjectionMatrix(glm::mat4& proj) const;
glm::mat4 evalProjectionMatrixRange(float rangeNear, float rangeFar) const;
void evalViewTransform(Transform& view) const;
enum PlaneIndex { TOP_PLANE = 0, BOTTOM_PLANE, LEFT_PLANE, RIGHT_PLANE, NEAR_PLANE, FAR_PLANE, NUM_PLANES };

View file

@ -21,8 +21,27 @@ bool nsightActive() {
return nsightLaunched;
}
uint64_t ProfileRange::beginRange(const char* name, uint32_t argbColor) {
nvtxEventAttributes_t eventAttrib = { 0 };
eventAttrib.version = NVTX_VERSION;
eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE;
eventAttrib.colorType = NVTX_COLOR_ARGB;
eventAttrib.color = argbColor;
eventAttrib.messageType = NVTX_MESSAGE_TYPE_ASCII;
eventAttrib.message.ascii = name;
return nvtxRangeStartEx(&eventAttrib);
// return nvtxRangePushEx(&eventAttrib);
}
void ProfileRange::endRange(uint64_t rangeId) {
nvtxRangeEnd(rangeId);
// nvtxRangePop();
}
ProfileRange::ProfileRange(const char *name) {
_rangeId = nvtxRangeStart(name);
// _rangeId = nvtxRangeStart(name);
_rangeId = nvtxRangePush(name);
}
ProfileRange::ProfileRange(const char *name, uint32_t argbColor, uint64_t payload) {
@ -36,11 +55,13 @@ ProfileRange::ProfileRange(const char *name, uint32_t argbColor, uint64_t payloa
eventAttrib.payload.llValue = payload;
eventAttrib.payloadType = NVTX_PAYLOAD_TYPE_UNSIGNED_INT64;
_rangeId = nvtxRangeStartEx(&eventAttrib);
//_rangeId = nvtxRangeStartEx(&eventAttrib);
_rangeId = nvtxRangePushEx(&eventAttrib);
}
ProfileRange::~ProfileRange() {
nvtxRangeEnd(_rangeId);
// nvtxRangeEnd(_rangeId);
nvtxRangePop();
}
#else

View file

@ -19,15 +19,27 @@ public:
ProfileRange(const char *name);
ProfileRange(const char *name, uint32_t argbColor, uint64_t payload);
~ProfileRange();
static uint64_t beginRange(const char* name, uint32_t argbColor);
static void endRange(uint64_t rangeId);
private:
uint64_t _rangeId{ 0 };
};
#define PROFILE_RANGE(name) ProfileRange profileRangeThis(name);
#define PROFILE_RANGE_EX(name, argbColor, payload) ProfileRange profileRangeThis(name, argbColor, (uint64_t)payload);
#define PROFILE_RANGE_BEGIN(rangeId, name, argbColor) rangeId = ProfileRange::beginRange(name, argbColor)
#define PROFILE_RANGE_END(rangeId) ProfileRange::endRange(rangeId)
#else
#define PROFILE_RANGE(name)
#define PROFILE_RANGE_EX(name, argbColor, payload)
#define PROFILE_RANGE_BEGIN(rangeId, name, argbColor)
#define PROFILE_RANGE_END(rangeId)
#endif
#endif

View file

@ -0,0 +1,21 @@
//
// lightClustering.js
// examples/utilities/tools/render
//
// Sam Gateau, created on 9/9/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
//
// Set up the qml ui
var qml = Script.resolvePath('lightClustering.qml');
var window = new OverlayWindow({
title: 'Light Clustering',
source: qml,
width: 400,
height: 300
});
window.setPosition(Window.innerWidth - 420, 50 + 250 + 50 + 250 + 50 );
window.closed.connect(function() { Script.stop(); });

View file

@ -0,0 +1,158 @@
//
// lightClustering.qml
//
// Created by Sam Gateau on 9/9/2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import "configSlider"
import "../lib/plotperf"
Column {
spacing: 8
Column {
id: lightClustering
spacing: 10
Column{
PlotPerf {
title: "Light CLustering Timing"
height: 50
object: Render.getConfig("LightClustering")
valueUnit: "ms"
valueScale: 1
valueNumDigits: "4"
plots: [
{
object: Render.getConfig("LightClustering"),
prop: "cpuRunTime",
label: "time",
scale: 1,
color: "#FFFFFF"
}
]
}
PlotPerf {
title: "Lights"
height: 50
object: Render.getConfig("LightClustering")
valueUnit: ""
valueScale: 1
valueNumDigits: "0"
plots: [
{
object: Render.getConfig("LightClustering"),
prop: "numClusteredLights",
label: "visible",
color: "#D959FE"
},
{
object: Render.getConfig("LightClustering"),
prop: "numInputLights",
label: "input",
color: "#FED959"
}
]
}
PlotPerf {
title: "Scene Lights"
height: 80
object: Render.getConfig("LightClustering")
valueUnit: ""
valueScale: 1
valueNumDigits: "0"
plots: [
{
object: Render.getConfig("LightClustering"),
prop: "numSceneLights",
label: "current",
color: "#00B4EF"
},
{
object: Render.getConfig("LightClustering"),
prop: "numFreeSceneLights",
label: "free",
color: "#1AC567"
},
{
object: Render.getConfig("LightClustering"),
prop: "numAllocatedSceneLights",
label: "allocated",
color: "#9495FF"
}
]
}
ConfigSlider {
label: qsTr("Range Near [m]")
integral: false
config: Render.getConfig("LightClustering")
property: "rangeNear"
max: 20.0
min: 0.1
}
ConfigSlider {
label: qsTr("Range Far [m]")
integral: false
config: Render.getConfig("LightClustering")
property: "rangeFar"
max: 500.0
min: 100.0
}
ConfigSlider {
label: qsTr("Grid X")
integral: true
config: Render.getConfig("LightClustering")
property: "dimX"
max: 32
min: 1
}
ConfigSlider {
label: qsTr("Grid Y")
integral: true
config: Render.getConfig("LightClustering")
property: "dimY"
max: 32
min: 1
}
ConfigSlider {
label: qsTr("Grid Z")
integral: true
config: Render.getConfig("LightClustering")
property: "dimZ"
max: 31
min: 1
}
CheckBox {
text: "Freeze"
checked: Render.getConfig("LightClustering")["freeze"]
onCheckedChanged: { Render.getConfig("LightClustering")["freeze"] = checked }
}
CheckBox {
text: "Draw Grid"
checked: Render.getConfig("DebugLightClusters")["doDrawGrid"]
onCheckedChanged: { Render.getConfig("DebugLightClusters")["doDrawGrid"] = checked }
}
CheckBox {
text: "Draw Cluster From Depth"
checked: Render.getConfig("DebugLightClusters")["doDrawClusterFromDepth"]
onCheckedChanged: { Render.getConfig("DebugLightClusters")["doDrawClusterFromDepth"] = checked }
}
CheckBox {
text: "Draw Content"
checked: Render.getConfig("DebugLightClusters")["doDrawContent"]
onCheckedChanged: { Render.getConfig("DebugLightClusters")["doDrawContent"] = checked }
}
Label {
text: "Num Cluster Items = " + Render.getConfig("LightClustering")["numClusteredLightReferences"].toFixed(0)
}
}
}
}

View file

@ -478,7 +478,6 @@ public:
_octree->init();
// Prevent web entities from rendering
REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, WebEntityItem::factory);
REGISTER_ENTITY_TYPE_WITH_FACTORY(Light, LightEntityItem::factory);
DependencyManager::set<ParentFinder>(_octree->getTree());
getEntities()->setViewFrustum(_viewFrustum);

View file

@ -28,7 +28,8 @@
#include <render-utils/simple_textured_unlit_frag.h>
#include <render-utils/deferred_light_vert.h>
#include <render-utils/deferred_light_limited_vert.h>
#include <render-utils/deferred_light_point_vert.h>
#include <render-utils/deferred_light_spot_vert.h>
#include <render-utils/directional_light_frag.h>
#include <render-utils/directional_ambient_light_frag.h>
@ -157,8 +158,8 @@ void QTestWindow::draw() {
testShaderBuild(deferred_light_vert, directional_light_frag);
testShaderBuild(deferred_light_vert, directional_ambient_light_frag);
testShaderBuild(deferred_light_vert, directional_skybox_light_frag);
testShaderBuild(deferred_light_limited_vert, point_light_frag);
testShaderBuild(deferred_light_limited_vert, spot_light_frag);
testShaderBuild(deferred_light_point_vert, point_light_frag);
testShaderBuild(deferred_light_spot_vert, spot_light_frag);
testShaderBuild(standardTransformPNTC_vert, standardDrawTexture_frag);
testShaderBuild(standardTransformPNTC_vert, DrawTextureOpaque_frag);