mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge pull request #8560 from samcake/hdr
Introducing Clustered Lighting and improve Lighting performances
This commit is contained in:
commit
54543935be
80 changed files with 4249 additions and 692 deletions
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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@>
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 };
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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); }
|
||||
};
|
||||
|
||||
|
|
|
@ -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$>);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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@>
|
||||
|
|
56
libraries/model/src/model/LightIrradiance.shared.slh
Normal file
56
libraries/model/src/model/LightIrradiance.shared.slh
Normal 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 !>
|
66
libraries/model/src/model/LightVolume.shared.slh
Normal file
66
libraries/model/src/model/LightVolume.shared.slh
Normal 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 !>
|
57
libraries/model/src/model/SphericalHarmonics.shared.slh
Normal file
57
libraries/model/src/model/SphericalHarmonics.shared.slh
Normal 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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
85
libraries/render-utils/src/LightClusterGrid.slh
Normal file
85
libraries/render-utils/src/LightClusterGrid.slh
Normal 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@>
|
194
libraries/render-utils/src/LightClusterGrid_shared.slh
Normal file
194
libraries/render-utils/src/LightClusterGrid_shared.slh
Normal 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
|
792
libraries/render-utils/src/LightClusters.cpp
Normal file
792
libraries/render-utils/src/LightClusters.cpp
Normal 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);
|
||||
}
|
239
libraries/render-utils/src/LightClusters.h
Normal file
239
libraries/render-utils/src/LightClusters.h
Normal 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
|
|
@ -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@>
|
||||
|
|
83
libraries/render-utils/src/LightPayload.cpp
Normal file
83
libraries/render-utils/src/LightPayload.cpp
Normal 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
|
||||
}
|
||||
}
|
||||
|
49
libraries/render-utils/src/LightPayload.h
Normal file
49
libraries/render-utils/src/LightPayload.h
Normal 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
|
|
@ -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@>
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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@>
|
||||
|
||||
|
||||
|
|
|
@ -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 ?
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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@>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
54
libraries/render-utils/src/deferred_light_point.slv
Normal file
54
libraries/render-utils/src/deferred_light_point.slv
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
19
libraries/render-utils/src/lightClusters_drawGrid.slf
Normal file
19
libraries/render-utils/src/lightClusters_drawGrid.slf
Normal 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;
|
||||
}
|
73
libraries/render-utils/src/lightClusters_drawGrid.slv
Normal file
73
libraries/render-utils/src/lightClusters_drawGrid.slv
Normal 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));
|
||||
}
|
153
libraries/render-utils/src/local_lights_drawOutline.slf
Normal file
153
libraries/render-utils/src/local_lights_drawOutline.slf
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
200
libraries/render-utils/src/local_lights_shading.slf
Normal file
200
libraries/render-utils/src/local_lights_shading.slf
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
!>
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
<@include DeferredBufferRead.slh@>
|
||||
<@include model/Light.slh@>
|
||||
<$declareLightBuffer()$>
|
||||
|
||||
<$declareDeferredCurvature()$>
|
||||
|
||||
|
|
156
libraries/render/src/render/IndexedContainer.h
Normal file
156
libraries/render/src/render/IndexedContainer.h
Normal 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
|
|
@ -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);
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
21
scripts/developer/utilities/render/lightClustering.js
Normal file
21
scripts/developer/utilities/render/lightClustering.js
Normal 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(); });
|
158
scripts/developer/utilities/render/lightClustering.qml
Normal file
158
scripts/developer/utilities/render/lightClustering.qml
Normal 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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue