mirror of
https://github.com/overte-org/overte.git
synced 2025-08-08 04:37:23 +02:00
v3 procedurals and shader fixes
This commit is contained in:
parent
fd87aad3c2
commit
2acb6b6857
9 changed files with 249 additions and 140 deletions
|
@ -38,18 +38,10 @@ ShapeEntityRenderer::ShapeEntityRenderer(const EntityItemPointer& entity) : Pare
|
||||||
// FIXME: Setup proper uniform slots and use correct pipelines for forward rendering
|
// FIXME: Setup proper uniform slots and use correct pipelines for forward rendering
|
||||||
_procedural._opaqueFragmentSource = gpu::Shader::Source::get(shader::render_utils::fragment::simple);
|
_procedural._opaqueFragmentSource = gpu::Shader::Source::get(shader::render_utils::fragment::simple);
|
||||||
_procedural._transparentFragmentSource = gpu::Shader::Source::get(shader::render_utils::fragment::simple_transparent);
|
_procedural._transparentFragmentSource = gpu::Shader::Source::get(shader::render_utils::fragment::simple_transparent);
|
||||||
_procedural._opaqueState->setCullMode(gpu::State::CULL_NONE);
|
|
||||||
_procedural._opaqueState->setDepthTest(true, true, gpu::LESS_EQUAL);
|
// TODO: move into Procedural.cpp
|
||||||
PrepareStencil::testMaskDrawShape(*_procedural._opaqueState);
|
PrepareStencil::testMaskDrawShape(*_procedural._opaqueState);
|
||||||
_procedural._opaqueState->setBlendFunction(false,
|
|
||||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
|
||||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
|
||||||
_procedural._transparentState->setCullMode(gpu::State::CULL_BACK);
|
|
||||||
_procedural._transparentState->setDepthTest(true, true, gpu::LESS_EQUAL);
|
|
||||||
PrepareStencil::testMask(*_procedural._transparentState);
|
PrepareStencil::testMask(*_procedural._transparentState);
|
||||||
_procedural._transparentState->setBlendFunction(true,
|
|
||||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
|
||||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShapeEntityRenderer::needsRenderUpdate() const {
|
bool ShapeEntityRenderer::needsRenderUpdate() const {
|
||||||
|
@ -212,7 +204,10 @@ ShapeKey ShapeEntityRenderer::getShapeKey() {
|
||||||
return builder.build();
|
return builder.build();
|
||||||
} else {
|
} else {
|
||||||
ShapeKey::Builder builder;
|
ShapeKey::Builder builder;
|
||||||
if (_procedural.isReady()) {
|
bool proceduralReady = resultWithReadLock<bool>([&] {
|
||||||
|
return _procedural.isReady();
|
||||||
|
});
|
||||||
|
if (proceduralReady) {
|
||||||
builder.withOwnPipeline();
|
builder.withOwnPipeline();
|
||||||
}
|
}
|
||||||
if (isTransparent()) {
|
if (isTransparent()) {
|
||||||
|
@ -242,7 +237,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
|
||||||
if (_procedural.isReady()) {
|
if (_procedural.isReady()) {
|
||||||
outColor = _procedural.getColor(outColor);
|
outColor = _procedural.getColor(outColor);
|
||||||
outColor.a *= _procedural.isFading() ? Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) : 1.0f;
|
outColor.a *= _procedural.isFading() ? Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) : 1.0f;
|
||||||
_procedural.prepare(batch, _position, _dimensions, _orientation, outColor);
|
_procedural.prepare(batch, _position, _dimensions, _orientation, ProceduralProgramKey(outColor.a < 1.0f));
|
||||||
proceduralRender = true;
|
proceduralRender = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,8 +38,6 @@ protected:
|
||||||
StatePointer _state;
|
StatePointer _state;
|
||||||
|
|
||||||
Pipeline();
|
Pipeline();
|
||||||
Pipeline(const Pipeline& pipeline); // deep copy of the sysmem shader
|
|
||||||
Pipeline& operator=(const Pipeline& pipeline); // deep copy of the sysmem texture
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef Pipeline::Pointer PipelinePointer;
|
typedef Pipeline::Pointer PipelinePointer;
|
||||||
|
|
|
@ -39,8 +39,10 @@ static const std::string PROCEDURAL_BLOCK = "//PROCEDURAL_BLOCK";
|
||||||
static const std::string PROCEDURAL_VERSION = "//PROCEDURAL_VERSION";
|
static const std::string PROCEDURAL_VERSION = "//PROCEDURAL_VERSION";
|
||||||
|
|
||||||
bool operator==(const ProceduralData& a, const ProceduralData& b) {
|
bool operator==(const ProceduralData& a, const ProceduralData& b) {
|
||||||
return ((a.version == b.version) && (a.shaderUrl == b.shaderUrl) && (a.uniforms == b.uniforms) &&
|
return ((a.version == b.version) &&
|
||||||
(a.channels == b.channels));
|
(a.shaderUrl == b.shaderUrl) &&
|
||||||
|
(a.uniforms == b.uniforms) &&
|
||||||
|
(a.channels == b.channels));
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonValue ProceduralData::getProceduralData(const QString& proceduralJson) {
|
QJsonValue ProceduralData::getProceduralData(const QString& proceduralJson) {
|
||||||
|
@ -57,9 +59,9 @@ QJsonValue ProceduralData::getProceduralData(const QString& proceduralJson) {
|
||||||
return doc.object()[PROCEDURAL_USER_DATA_KEY];
|
return doc.object()[PROCEDURAL_USER_DATA_KEY];
|
||||||
}
|
}
|
||||||
|
|
||||||
ProceduralData ProceduralData::parse(const QString& userDataJson) {
|
ProceduralData ProceduralData::parse(const QString& proceduralData) {
|
||||||
ProceduralData result;
|
ProceduralData result;
|
||||||
result.parse(getProceduralData(userDataJson).toObject());
|
result.parse(getProceduralData(proceduralData).toObject());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +75,7 @@ void ProceduralData::parse(const QJsonObject& proceduralData) {
|
||||||
if (versionJson.isDouble()) {
|
if (versionJson.isDouble()) {
|
||||||
version = (uint8_t)(floor(versionJson.toDouble()));
|
version = (uint8_t)(floor(versionJson.toDouble()));
|
||||||
// invalid version
|
// invalid version
|
||||||
if (!(version == 1 || version == 2)) {
|
if (!(version == 1 || version == 2 || version == 3)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -102,20 +104,27 @@ void ProceduralData::parse(const QJsonObject& proceduralData) {
|
||||||
//}
|
//}
|
||||||
|
|
||||||
Procedural::Procedural() {
|
Procedural::Procedural() {
|
||||||
_transparentState->setCullMode(gpu::State::CULL_NONE);
|
_opaqueState->setCullMode(gpu::State::CULL_BACK);
|
||||||
|
_opaqueState->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||||
|
_opaqueState->setBlendFunction(false,
|
||||||
|
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||||
|
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||||
|
|
||||||
|
_transparentState->setCullMode(gpu::State::CULL_BACK);
|
||||||
_transparentState->setDepthTest(true, true, gpu::LESS_EQUAL);
|
_transparentState->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||||
_transparentState->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
_transparentState->setBlendFunction(true,
|
||||||
|
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||||
|
|
||||||
_standardInputsBuffer = std::make_shared<gpu::Buffer>(sizeof(StandardInputs), nullptr);
|
_standardInputsBuffer = std::make_shared<gpu::Buffer>(sizeof(StandardInputs), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Procedural::setProceduralData(const ProceduralData& proceduralData) {
|
void Procedural::setProceduralData(const ProceduralData& proceduralData) {
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
if (proceduralData == _data) {
|
if (proceduralData == _data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_dirty = true;
|
|
||||||
_enabled = false;
|
_enabled = false;
|
||||||
|
|
||||||
if (proceduralData.version != _data.version) {
|
if (proceduralData.version != _data.version) {
|
||||||
|
@ -124,6 +133,10 @@ void Procedural::setProceduralData(const ProceduralData& proceduralData) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (proceduralData.uniforms != _data.uniforms) {
|
if (proceduralData.uniforms != _data.uniforms) {
|
||||||
|
// If the uniform keys changed, we need to recreate the whole shader to handle the reflection
|
||||||
|
if (proceduralData.uniforms.keys() != _data.uniforms.keys()) {
|
||||||
|
_shaderDirty = true;
|
||||||
|
}
|
||||||
_data.uniforms = proceduralData.uniforms;
|
_data.uniforms = proceduralData.uniforms;
|
||||||
_uniformsDirty = true;
|
_uniformsDirty = true;
|
||||||
}
|
}
|
||||||
|
@ -147,16 +160,14 @@ void Procedural::setProceduralData(const ProceduralData& proceduralData) {
|
||||||
|
|
||||||
if (proceduralData.shaderUrl != _data.shaderUrl) {
|
if (proceduralData.shaderUrl != _data.shaderUrl) {
|
||||||
_data.shaderUrl = proceduralData.shaderUrl;
|
_data.shaderUrl = proceduralData.shaderUrl;
|
||||||
_shaderDirty = true;
|
|
||||||
const auto& shaderUrl = _data.shaderUrl;
|
const auto& shaderUrl = _data.shaderUrl;
|
||||||
|
|
||||||
|
_shaderDirty = true;
|
||||||
_networkShader.reset();
|
_networkShader.reset();
|
||||||
_shaderPath.clear();
|
_shaderPath.clear();
|
||||||
|
_shaderSource.clear();
|
||||||
|
|
||||||
if (shaderUrl.isEmpty()) {
|
if (shaderUrl.isEmpty() || !shaderUrl.isValid()) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!shaderUrl.isValid()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,6 +191,8 @@ bool Procedural::isReady() const {
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
|
||||||
if (!_enabled) {
|
if (!_enabled) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -209,10 +222,11 @@ bool Procedural::isReady() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Procedural::prepare(gpu::Batch& batch,
|
void Procedural::prepare(gpu::Batch& batch,
|
||||||
const glm::vec3& position,
|
const glm::vec3& position,
|
||||||
const glm::vec3& size,
|
const glm::vec3& size,
|
||||||
const glm::quat& orientation,
|
const glm::quat& orientation,
|
||||||
const glm::vec4& color) {
|
const ProceduralProgramKey key) {
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
_entityDimensions = size;
|
_entityDimensions = size;
|
||||||
_entityPosition = position;
|
_entityPosition = position;
|
||||||
_entityOrientation = glm::mat3_cast(orientation);
|
_entityOrientation = glm::mat3_cast(orientation);
|
||||||
|
@ -225,62 +239,56 @@ void Procedural::prepare(gpu::Batch& batch,
|
||||||
_shaderDirty = true;
|
_shaderDirty = true;
|
||||||
_shaderModified = lastModified;
|
_shaderModified = lastModified;
|
||||||
}
|
}
|
||||||
} else if (_networkShader && _networkShader->isLoaded()) {
|
} else if (_shaderSource.isEmpty() && _networkShader && _networkShader->isLoaded()) {
|
||||||
_shaderSource = _networkShader->_source;
|
_shaderSource = _networkShader->_source;
|
||||||
|
_shaderDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_opaquePipeline || !_transparentPipeline || _shaderDirty) {
|
if (_shaderDirty) {
|
||||||
|
_proceduralPipelines.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& pipeline = _proceduralPipelines.find(key);
|
||||||
|
bool recompiledShader = false;
|
||||||
|
if (pipeline == _proceduralPipelines.end()) {
|
||||||
if (!_vertexShader) {
|
if (!_vertexShader) {
|
||||||
_vertexShader = gpu::Shader::createVertex(_vertexSource);
|
_vertexShader = gpu::Shader::createVertex(_vertexSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gpu::Shader::Source& fragmentSource = (key.isTransparent() && _transparentFragmentSource.valid()) ? _transparentFragmentSource : _opaqueFragmentSource;
|
||||||
|
|
||||||
// Build the fragment shader
|
// Build the fragment shader
|
||||||
_opaqueFragmentSource.replacements.clear();
|
fragmentSource.replacements.clear();
|
||||||
if (_data.version == 1) {
|
fragmentSource.replacements[PROCEDURAL_VERSION] = "#define PROCEDURAL_V" + std::to_string(_data.version);
|
||||||
_opaqueFragmentSource.replacements[PROCEDURAL_VERSION] = "#define PROCEDURAL_V1 1";
|
fragmentSource.replacements[PROCEDURAL_BLOCK] = _shaderSource.toStdString();
|
||||||
} else if (_data.version == 2) {
|
|
||||||
_opaqueFragmentSource.replacements[PROCEDURAL_VERSION] = "#define PROCEDURAL_V2 1";
|
|
||||||
}
|
|
||||||
_opaqueFragmentSource.replacements[PROCEDURAL_BLOCK] = _shaderSource.toStdString();
|
|
||||||
_transparentFragmentSource.replacements = _opaqueFragmentSource.replacements;
|
|
||||||
|
|
||||||
// Set any userdata specified uniforms
|
// Set any userdata specified uniforms
|
||||||
int customSlot = procedural::slot::uniform::Custom;
|
int customSlot = procedural::slot::uniform::Custom;
|
||||||
for (const auto& key : _data.uniforms.keys()) {
|
for (const auto& key : _data.uniforms.keys()) {
|
||||||
std::string uniformName = key.toLocal8Bit().data();
|
std::string uniformName = key.toLocal8Bit().data();
|
||||||
_opaqueFragmentSource.reflection.uniforms[uniformName] = customSlot;
|
fragmentSource.reflection.uniforms[uniformName] = customSlot;
|
||||||
_transparentFragmentSource.reflection.uniforms[uniformName] = customSlot;
|
|
||||||
++customSlot;
|
++customSlot;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Leave this here for debugging
|
// Leave this here for debugging
|
||||||
// qCDebug(procedural) << "FragmentShader:\n" << fragmentShaderSource.c_str();
|
//qCDebug(proceduralLog) << "FragmentShader:\n" << fragmentSource.getSource(shader::Dialect::glsl450, shader::Variant::Mono).c_str();
|
||||||
|
|
||||||
|
gpu::ShaderPointer fragmentShader = gpu::Shader::createPixel(fragmentSource);
|
||||||
|
gpu::ShaderPointer program = gpu::Shader::createProgram(_vertexShader, fragmentShader);
|
||||||
|
|
||||||
|
_proceduralPipelines[key] = gpu::Pipeline::create(program, key.isTransparent() ? _transparentState : _opaqueState);
|
||||||
|
|
||||||
// TODO: THis is a simple fix, we need a cleaner way to provide the "hosting" program for procedural custom shaders to be defined together with the required bindings.
|
|
||||||
_opaqueFragmentShader = gpu::Shader::createPixel(_opaqueFragmentSource);
|
|
||||||
_opaqueShader = gpu::Shader::createProgram(_vertexShader, _opaqueFragmentShader);
|
|
||||||
_opaquePipeline = gpu::Pipeline::create(_opaqueShader, _opaqueState);
|
|
||||||
if (_transparentFragmentSource.valid()) {
|
|
||||||
_transparentFragmentShader = gpu::Shader::createPixel(_transparentFragmentSource);
|
|
||||||
_transparentShader = gpu::Shader::createProgram(_vertexShader, _transparentFragmentShader);
|
|
||||||
_transparentPipeline = gpu::Pipeline::create(_transparentShader, _transparentState);
|
|
||||||
} else {
|
|
||||||
_transparentFragmentShader = _opaqueFragmentShader;
|
|
||||||
_transparentShader = _opaqueShader;
|
|
||||||
_transparentPipeline = _opaquePipeline;
|
|
||||||
}
|
|
||||||
_start = usecTimestampNow();
|
_start = usecTimestampNow();
|
||||||
_frameCount = 0;
|
_frameCount = 0;
|
||||||
|
recompiledShader = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool transparent = color.a < 1.0f;
|
batch.setPipeline(_proceduralPipelines[key]);
|
||||||
batch.setPipeline(transparent ? _transparentPipeline : _opaquePipeline);
|
|
||||||
|
|
||||||
if (_shaderDirty || _uniformsDirty || _prevTransparent != transparent) {
|
if (_shaderDirty || _uniformsDirty) {
|
||||||
setupUniforms(transparent);
|
setupUniforms();
|
||||||
}
|
}
|
||||||
|
|
||||||
_prevTransparent = transparent;
|
|
||||||
_shaderDirty = _uniformsDirty = false;
|
_shaderDirty = _uniformsDirty = false;
|
||||||
|
|
||||||
for (auto lambda : _uniforms) {
|
for (auto lambda : _uniforms) {
|
||||||
|
@ -290,8 +298,7 @@ void Procedural::prepare(gpu::Batch& batch,
|
||||||
static gpu::Sampler sampler;
|
static gpu::Sampler sampler;
|
||||||
static std::once_flag once;
|
static std::once_flag once;
|
||||||
std::call_once(once, [&] {
|
std::call_once(once, [&] {
|
||||||
gpu::Sampler::Desc desc;
|
sampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR);
|
||||||
desc._filter = gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
for (size_t i = 0; i < MAX_PROCEDURAL_TEXTURE_CHANNELS; ++i) {
|
for (size_t i = 0; i < MAX_PROCEDURAL_TEXTURE_CHANNELS; ++i) {
|
||||||
|
@ -301,19 +308,17 @@ void Procedural::prepare(gpu::Batch& batch,
|
||||||
gpuTexture->setSampler(sampler);
|
gpuTexture->setSampler(sampler);
|
||||||
gpuTexture->setAutoGenerateMips(true);
|
gpuTexture->setAutoGenerateMips(true);
|
||||||
}
|
}
|
||||||
batch.setResourceTexture((gpu::uint32)i, gpuTexture);
|
batch.setResourceTexture(procedural::slot::texture::Channel0 + i, gpuTexture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Procedural::setupUniforms(bool transparent) {
|
void Procedural::setupUniforms() {
|
||||||
_uniforms.clear();
|
_uniforms.clear();
|
||||||
auto customUniformCount = _data.uniforms.keys().size();
|
|
||||||
// Set any userdata specified uniforms
|
// Set any userdata specified uniforms
|
||||||
for (int i = 0; i < customUniformCount; ++i) {
|
int slot = procedural::slot::uniform::Custom;
|
||||||
int slot = procedural::slot::uniform::Custom + i;
|
for (const auto& key : _data.uniforms.keys()) {
|
||||||
QString key = _data.uniforms.keys().at(i);
|
|
||||||
std::string uniformName = key.toLocal8Bit().data();
|
std::string uniformName = key.toLocal8Bit().data();
|
||||||
QJsonValue value = _data.uniforms[key];
|
QJsonValue value = _data.uniforms[key];
|
||||||
if (value.isDouble()) {
|
if (value.isDouble()) {
|
||||||
|
@ -360,6 +365,7 @@ void Procedural::setupUniforms(bool transparent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
slot++;
|
||||||
}
|
}
|
||||||
|
|
||||||
_uniforms.push_back([=](gpu::Batch& batch) {
|
_uniforms.push_back([=](gpu::Batch& batch) {
|
||||||
|
@ -398,7 +404,7 @@ void Procedural::setupUniforms(bool transparent) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec4 Procedural::getColor(const glm::vec4& entityColor) {
|
glm::vec4 Procedural::getColor(const glm::vec4& entityColor) const {
|
||||||
if (_data.version == 1) {
|
if (_data.version == 1) {
|
||||||
return glm::vec4(1);
|
return glm::vec4(1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef hifi_RenderableProcedrualItem_h
|
|
||||||
#define hifi_RenderableProcedrualItem_h
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
|
@ -32,7 +30,6 @@ const size_t MAX_PROCEDURAL_TEXTURE_CHANNELS{ 4 };
|
||||||
struct ProceduralData {
|
struct ProceduralData {
|
||||||
static QJsonValue getProceduralData(const QString& proceduralJson);
|
static QJsonValue getProceduralData(const QString& proceduralJson);
|
||||||
static ProceduralData parse(const QString& userDataJson);
|
static ProceduralData parse(const QString& userDataJson);
|
||||||
// This should only be called from the render thread, as it shares data with Procedural::prepare
|
|
||||||
void parse(const QJsonObject&);
|
void parse(const QJsonObject&);
|
||||||
|
|
||||||
// Rendering object descriptions, from userData
|
// Rendering object descriptions, from userData
|
||||||
|
@ -42,10 +39,40 @@ struct ProceduralData {
|
||||||
QJsonArray channels;
|
QJsonArray channels;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ProceduralProgramKey {
|
||||||
|
public:
|
||||||
|
enum FlagBit {
|
||||||
|
IS_TRANSPARENT = 0,
|
||||||
|
NUM_FLAGS
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::bitset<NUM_FLAGS> Flags;
|
||||||
|
|
||||||
|
Flags _flags;
|
||||||
|
|
||||||
|
bool isTransparent() const { return _flags[IS_TRANSPARENT]; }
|
||||||
|
|
||||||
|
ProceduralProgramKey(bool transparent = false) {
|
||||||
|
if (transparent) {
|
||||||
|
_flags.set(IS_TRANSPARENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
namespace std {
|
||||||
|
template <>
|
||||||
|
struct hash<ProceduralProgramKey> {
|
||||||
|
size_t operator()(const ProceduralProgramKey& key) const {
|
||||||
|
return std::hash<std::bitset<ProceduralProgramKey::FlagBit::NUM_FLAGS>>()(key._flags);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
inline bool operator==(const ProceduralProgramKey& a, const ProceduralProgramKey& b) {
|
||||||
|
return a._flags == b._flags;
|
||||||
|
}
|
||||||
|
inline bool operator!=(const ProceduralProgramKey& a, const ProceduralProgramKey& b) {
|
||||||
|
return a._flags != b._flags;
|
||||||
|
}
|
||||||
|
|
||||||
// WARNING with threaded rendering it is the RESPONSIBILITY OF THE CALLER to ensure that
|
|
||||||
// calls to `setProceduralData` happen on the main thread and that calls to `ready` and `prepare`
|
|
||||||
// are treated atomically, and that they cannot happen concurrently with calls to `setProceduralData`
|
|
||||||
// FIXME better encapsulation
|
// FIXME better encapsulation
|
||||||
// FIXME better mechanism for extending to things rendered using shaders other than simple.slv
|
// FIXME better mechanism for extending to things rendered using shaders other than simple.slv
|
||||||
struct Procedural {
|
struct Procedural {
|
||||||
|
@ -55,10 +82,9 @@ public:
|
||||||
|
|
||||||
bool isReady() const;
|
bool isReady() const;
|
||||||
bool isEnabled() const { return _enabled; }
|
bool isEnabled() const { return _enabled; }
|
||||||
void prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size, const glm::quat& orientation, const glm::vec4& color = glm::vec4(1));
|
void prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size, const glm::quat& orientation, const ProceduralProgramKey key = ProceduralProgramKey());
|
||||||
const gpu::ShaderPointer& getOpaqueShader() const { return _opaqueShader; }
|
|
||||||
|
|
||||||
glm::vec4 getColor(const glm::vec4& entityColor);
|
glm::vec4 getColor(const glm::vec4& entityColor) const;
|
||||||
quint64 getFadeStartTime() const { return _fadeStartTime; }
|
quint64 getFadeStartTime() const { return _fadeStartTime; }
|
||||||
bool isFading() const { return _doesFade && _isFading; }
|
bool isFading() const { return _doesFade && _isFading; }
|
||||||
void setIsFading(bool isFading) { _isFading = isFading; }
|
void setIsFading(bool isFading) { _isFading = isFading; }
|
||||||
|
@ -108,22 +134,19 @@ protected:
|
||||||
QString _shaderPath;
|
QString _shaderPath;
|
||||||
quint64 _shaderModified { 0 };
|
quint64 _shaderModified { 0 };
|
||||||
NetworkShaderPointer _networkShader;
|
NetworkShaderPointer _networkShader;
|
||||||
bool _dirty { false };
|
|
||||||
bool _shaderDirty { true };
|
bool _shaderDirty { true };
|
||||||
bool _uniformsDirty { true };
|
bool _uniformsDirty { true };
|
||||||
|
|
||||||
// Rendering objects
|
// Rendering objects
|
||||||
UniformLambdas _uniforms;
|
UniformLambdas _uniforms;
|
||||||
NetworkTexturePointer _channels[MAX_PROCEDURAL_TEXTURE_CHANNELS];
|
NetworkTexturePointer _channels[MAX_PROCEDURAL_TEXTURE_CHANNELS];
|
||||||
gpu::PipelinePointer _opaquePipeline;
|
|
||||||
gpu::PipelinePointer _transparentPipeline;
|
std::unordered_map<ProceduralProgramKey, gpu::PipelinePointer> _proceduralPipelines;
|
||||||
|
|
||||||
|
gpu::ShaderPointer _vertexShader;
|
||||||
|
|
||||||
StandardInputs _standardInputs;
|
StandardInputs _standardInputs;
|
||||||
gpu::BufferPointer _standardInputsBuffer;
|
gpu::BufferPointer _standardInputsBuffer;
|
||||||
gpu::ShaderPointer _vertexShader;
|
|
||||||
gpu::ShaderPointer _opaqueFragmentShader;
|
|
||||||
gpu::ShaderPointer _transparentFragmentShader;
|
|
||||||
gpu::ShaderPointer _opaqueShader;
|
|
||||||
gpu::ShaderPointer _transparentShader;
|
|
||||||
|
|
||||||
// Entity metadata
|
// Entity metadata
|
||||||
glm::vec3 _entityDimensions;
|
glm::vec3 _entityDimensions;
|
||||||
|
@ -131,14 +154,11 @@ protected:
|
||||||
glm::mat3 _entityOrientation;
|
glm::mat3 _entityOrientation;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// This should only be called from the render thread, as it shares data with Procedural::prepare
|
void setupUniforms();
|
||||||
void setupUniforms(bool transparent);
|
|
||||||
|
|
||||||
mutable quint64 _fadeStartTime { 0 };
|
mutable quint64 _fadeStartTime { 0 };
|
||||||
mutable bool _hasStartedFade { false };
|
mutable bool _hasStartedFade { false };
|
||||||
mutable bool _isFading { false };
|
mutable bool _isFading { false };
|
||||||
bool _doesFade { true };
|
bool _doesFade { true };
|
||||||
bool _prevTransparent { false };
|
mutable std::mutex _mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -8,11 +8,8 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
<@include gpu/Transform.slh@>
|
|
||||||
<@include gpu/Noise.slh@>
|
<@include gpu/Noise.slh@>
|
||||||
<@include procedural/ShaderConstants.h@>
|
<@include procedural/ShaderConstants.h@>
|
||||||
|
|
||||||
<$declareStandardCameraTransform()$>
|
|
||||||
|
|
||||||
LAYOUT(binding=PROCEDURAL_TEXTURE_CHANNEL0) uniform sampler2D iChannel0;
|
LAYOUT(binding=PROCEDURAL_TEXTURE_CHANNEL0) uniform sampler2D iChannel0;
|
||||||
LAYOUT(binding=PROCEDURAL_TEXTURE_CHANNEL1) uniform sampler2D iChannel1;
|
LAYOUT(binding=PROCEDURAL_TEXTURE_CHANNEL1) uniform sampler2D iChannel1;
|
||||||
|
@ -59,6 +56,19 @@ LAYOUT_STD140(binding=0) uniform standardInputsBuffer {
|
||||||
#define iChannelResolution standardInputs.channelResolution
|
#define iChannelResolution standardInputs.channelResolution
|
||||||
#define iWorldOrientation standardInputs.worldOrientation
|
#define iWorldOrientation standardInputs.worldOrientation
|
||||||
|
|
||||||
|
struct ProceduralFragmentData {
|
||||||
|
vec3 position;
|
||||||
|
vec3 normal;
|
||||||
|
vec3 diffuse;
|
||||||
|
vec3 specular;
|
||||||
|
vec3 emissive;
|
||||||
|
float alpha;
|
||||||
|
float roughness;
|
||||||
|
float metallic;
|
||||||
|
float occlusion;
|
||||||
|
float scattering;
|
||||||
|
};
|
||||||
|
|
||||||
// Unimplemented uniforms
|
// Unimplemented uniforms
|
||||||
// Resolution doesn't make sense in the VR context
|
// Resolution doesn't make sense in the VR context
|
||||||
const vec3 iResolution = vec3(1.0);
|
const vec3 iResolution = vec3(1.0);
|
||||||
|
@ -69,8 +79,6 @@ const float iSampleRate = 1.0;
|
||||||
// No support for video input
|
// No support for video input
|
||||||
const vec4 iChannelTime = vec4(0.0);
|
const vec4 iChannelTime = vec4(0.0);
|
||||||
|
|
||||||
#define PROCEDURAL 1
|
|
||||||
|
|
||||||
//PROCEDURAL_VERSION
|
//PROCEDURAL_VERSION
|
||||||
|
|
||||||
// hack comment for extra whitespace
|
// hack comment for extra whitespace
|
||||||
|
|
|
@ -35,7 +35,6 @@ bool ProceduralSkybox::empty() {
|
||||||
void ProceduralSkybox::clear() {
|
void ProceduralSkybox::clear() {
|
||||||
// Parse and prepare a procedural with no shaders to release textures
|
// Parse and prepare a procedural with no shaders to release textures
|
||||||
parse(QString());
|
parse(QString());
|
||||||
_procedural.isReady();
|
|
||||||
|
|
||||||
Skybox::clear();
|
Skybox::clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,9 +14,9 @@
|
||||||
<@include DefaultMaterials.slh@>
|
<@include DefaultMaterials.slh@>
|
||||||
|
|
||||||
<@include ForwardGlobalLight.slh@>
|
<@include ForwardGlobalLight.slh@>
|
||||||
<@include gpu/Transform.slh@>
|
|
||||||
|
|
||||||
<$declareEvalSkyboxGlobalColor()$>
|
<$declareEvalSkyboxGlobalColor()$>
|
||||||
|
|
||||||
|
<@include gpu/Transform.slh@>
|
||||||
<$declareStandardCameraTransform()$>
|
<$declareStandardCameraTransform()$>
|
||||||
|
|
||||||
// the interpolated normal
|
// the interpolated normal
|
||||||
|
|
|
@ -14,6 +14,9 @@
|
||||||
|
|
||||||
<@include DeferredBufferWrite.slh@>
|
<@include DeferredBufferWrite.slh@>
|
||||||
|
|
||||||
|
<@include gpu/Transform.slh@>
|
||||||
|
<$declareStandardCameraTransform()$>
|
||||||
|
|
||||||
<@include render-utils/ShaderConstants.h@>
|
<@include render-utils/ShaderConstants.h@>
|
||||||
|
|
||||||
// the interpolated normal
|
// the interpolated normal
|
||||||
|
@ -45,27 +48,64 @@ float getProceduralColors(inout vec3 diffuse, inout vec3 specular, inout float s
|
||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float getProceduralFragment(inout ProceduralFragmentData proceduralData) {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
//PROCEDURAL_BLOCK_END
|
//PROCEDURAL_BLOCK_END
|
||||||
|
|
||||||
#line 2030
|
#line 2030
|
||||||
void main(void) {
|
void main(void) {
|
||||||
vec3 normal = normalize(_normalWS.xyz);
|
vec3 normal = normalize(_normalWS.xyz);
|
||||||
vec3 diffuse = _color.rgb;
|
vec3 diffuse = _color.rgb;
|
||||||
|
float roughness = DEFAULT_ROUGHNESS;
|
||||||
|
float metallic = DEFAULT_METALLIC;
|
||||||
|
vec3 emissive = DEFAULT_EMISSIVE;
|
||||||
|
float occlusion = DEFAULT_OCCLUSION;
|
||||||
|
float scattering = DEFAULT_SCATTERING;
|
||||||
|
|
||||||
|
float emissiveAmount = 0.0;
|
||||||
|
|
||||||
|
#if defined(PROCEDURAL_V1)
|
||||||
|
diffuse = getProceduralColor().rgb;
|
||||||
|
emissiveAmount = 1.0;
|
||||||
|
emissive = vec3(1.0);
|
||||||
|
#elif defined(PROCEDURAL_V2)
|
||||||
vec3 specular = DEFAULT_SPECULAR;
|
vec3 specular = DEFAULT_SPECULAR;
|
||||||
float shininess = DEFAULT_SHININESS;
|
float shininess = DEFAULT_SHININESS;
|
||||||
float emissiveAmount = 0.0;
|
|
||||||
|
|
||||||
#ifdef PROCEDURAL
|
|
||||||
|
|
||||||
#ifdef PROCEDURAL_V1
|
|
||||||
diffuse = getProceduralColor().rgb;
|
|
||||||
// Procedural Shaders are expected to be Gamma corrected so let's bring back the RGB in linear space for the rest of the pipeline
|
|
||||||
//diffuse = pow(diffuse, vec3(2.2));
|
|
||||||
emissiveAmount = 1.0;
|
|
||||||
#else
|
|
||||||
emissiveAmount = getProceduralColors(diffuse, specular, shininess);
|
emissiveAmount = getProceduralColors(diffuse, specular, shininess);
|
||||||
#endif
|
roughness = max(0.0, 1.0 - shininess / 128.0);
|
||||||
|
metallic = length(specular);
|
||||||
|
emissive = vec3(clamp(emissiveAmount, 0.0, 1.0));
|
||||||
|
#elif defined(PROCEDURAL_V3)
|
||||||
|
TransformCamera cam = getTransformCamera();
|
||||||
|
vec4 position = cam._viewInverse * _positionES;
|
||||||
|
ProceduralFragmentData proceduralData = {
|
||||||
|
position.xyz,
|
||||||
|
normal,
|
||||||
|
vec3(0.0),
|
||||||
|
DEFAULT_SPECULAR,
|
||||||
|
DEFAULT_EMISSIVE,
|
||||||
|
1.0,
|
||||||
|
DEFAULT_ROUGHNESS,
|
||||||
|
DEFAULT_METALLIC,
|
||||||
|
DEFAULT_OCCLUSION,
|
||||||
|
DEFAULT_SCATTERING
|
||||||
|
};
|
||||||
|
emissiveAmount = getProceduralFragment(proceduralData);
|
||||||
|
normal = proceduralData.normal;
|
||||||
|
diffuse = proceduralData.diffuse;
|
||||||
|
roughness = proceduralData.roughness;
|
||||||
|
metallic = proceduralData.metallic;
|
||||||
|
emissive = proceduralData.emissive;
|
||||||
|
occlusion = proceduralData.occlusion;
|
||||||
|
scattering = proceduralData.scattering;
|
||||||
|
|
||||||
|
position = vec4(proceduralData.position, 1.0);
|
||||||
|
vec4 posClip = cam._projection * (cam._view * position);
|
||||||
|
float far = gl_DepthRange.far;
|
||||||
|
float near = gl_DepthRange.near;
|
||||||
|
gl_FragDepth = 0.5 * ((far - near) * posClip.z / posClip.w + near + far);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (emissiveAmount > 0.0) {
|
if (emissiveAmount > 0.0) {
|
||||||
|
@ -73,18 +113,18 @@ void main(void) {
|
||||||
normal,
|
normal,
|
||||||
1.0,
|
1.0,
|
||||||
diffuse,
|
diffuse,
|
||||||
max(0.0, 1.0 - shininess / 128.0),
|
roughness,
|
||||||
DEFAULT_METALLIC,
|
metallic,
|
||||||
vec3(clamp(emissiveAmount, 0.0, 1.0)));
|
emissive);
|
||||||
} else {
|
} else {
|
||||||
packDeferredFragment(
|
packDeferredFragment(
|
||||||
normal,
|
normal,
|
||||||
1.0,
|
1.0,
|
||||||
diffuse,
|
diffuse,
|
||||||
max(0.0, 1.0 - shininess / 128.0),
|
roughness,
|
||||||
length(specular),
|
metallic,
|
||||||
DEFAULT_EMISSIVE,
|
emissive,
|
||||||
DEFAULT_OCCLUSION,
|
occlusion,
|
||||||
DEFAULT_SCATTERING);
|
scattering);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,9 @@
|
||||||
<@include DeferredGlobalLight.slh@>
|
<@include DeferredGlobalLight.slh@>
|
||||||
<$declareEvalGlobalLightingAlphaBlendedWithHaze()$>
|
<$declareEvalGlobalLightingAlphaBlendedWithHaze()$>
|
||||||
|
|
||||||
|
<@include gpu/Transform.slh@>
|
||||||
|
<$declareStandardCameraTransform()$>
|
||||||
|
|
||||||
<@include render-utils/ShaderConstants.h@>
|
<@include render-utils/ShaderConstants.h@>
|
||||||
|
|
||||||
// the interpolated normal
|
// the interpolated normal
|
||||||
|
@ -50,46 +53,86 @@ float getProceduralColors(inout vec3 diffuse, inout vec3 specular, inout float s
|
||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float getProceduralFragment(inout ProceduralFragmentData proceduralData) {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
//PROCEDURAL_BLOCK_END
|
//PROCEDURAL_BLOCK_END
|
||||||
|
|
||||||
#line 2030
|
#line 2030
|
||||||
void main(void) {
|
void main(void) {
|
||||||
vec3 normal = normalize(_normalWS.xyz);
|
vec3 normal = normalize(_normalWS.xyz);
|
||||||
vec3 diffuse = _color.rgb;
|
vec3 diffuse = _color.rgb;
|
||||||
vec3 specular = DEFAULT_SPECULAR;
|
float alpha = _color.a;
|
||||||
float shininess = DEFAULT_SHININESS;
|
float occlusion = DEFAULT_OCCLUSION;
|
||||||
|
vec3 fresnel = DEFAULT_FRESNEL;
|
||||||
|
float metallic = DEFAULT_METALLIC;
|
||||||
|
vec3 emissive = DEFAULT_EMISSIVE;
|
||||||
|
float roughness = DEFAULT_ROUGHNESS;
|
||||||
|
|
||||||
float emissiveAmount = 0.0;
|
float emissiveAmount = 0.0;
|
||||||
|
|
||||||
#ifdef PROCEDURAL
|
TransformCamera cam = getTransformCamera();
|
||||||
|
vec3 posEye = _positionES.xyz;
|
||||||
|
|
||||||
#ifdef PROCEDURAL_V1
|
#ifdef PROCEDURAL_V1
|
||||||
diffuse = getProceduralColor().rgb;
|
diffuse = getProceduralColor().rgb;
|
||||||
// Procedural Shaders are expected to be Gamma corrected so let's bring back the RGB in linear space for the rest of the pipeline
|
|
||||||
//diffuse = pow(diffuse, vec3(2.2));
|
|
||||||
emissiveAmount = 1.0;
|
emissiveAmount = 1.0;
|
||||||
#else
|
emissive = vec3(1.0);
|
||||||
|
#elif defined(PROCEDURAL_V2)
|
||||||
|
vec3 specular = DEFAULT_SPECULAR;
|
||||||
|
float shininess = DEFAULT_SHININESS;
|
||||||
emissiveAmount = getProceduralColors(diffuse, specular, shininess);
|
emissiveAmount = getProceduralColors(diffuse, specular, shininess);
|
||||||
#endif
|
roughness = max(0.0, 1.0 - shininess / 128.0);
|
||||||
|
metallic = length(specular);
|
||||||
|
emissive = vec3(clamp(emissiveAmount, 0.0, 1.0));
|
||||||
|
#elif defined(PROCEDURAL_V3)
|
||||||
|
vec4 position = cam._viewInverse * _positionES;
|
||||||
|
ProceduralFragmentData proceduralData = {
|
||||||
|
position.xyz,
|
||||||
|
normal,
|
||||||
|
vec3(0.0),
|
||||||
|
DEFAULT_SPECULAR,
|
||||||
|
DEFAULT_EMISSIVE,
|
||||||
|
1.0,
|
||||||
|
DEFAULT_ROUGHNESS,
|
||||||
|
DEFAULT_METALLIC,
|
||||||
|
DEFAULT_OCCLUSION,
|
||||||
|
DEFAULT_SCATTERING
|
||||||
|
};
|
||||||
|
emissiveAmount = getProceduralFragment(proceduralData);
|
||||||
|
position = vec4(proceduralData.position, 1.0);
|
||||||
|
vec4 posEye4 = cam._view * position;
|
||||||
|
posEye = vec3(posEye4);
|
||||||
|
occlusion = proceduralData.occlusion;
|
||||||
|
normal = proceduralData.normal;
|
||||||
|
diffuse = proceduralData.diffuse;
|
||||||
|
alpha = proceduralData.alpha;
|
||||||
|
fresnel = proceduralData.specular;
|
||||||
|
metallic = proceduralData.metallic;
|
||||||
|
emissive = proceduralData.emissive;
|
||||||
|
roughness = proceduralData.roughness;
|
||||||
|
|
||||||
|
vec4 posClip = cam._projection * posEye4;
|
||||||
|
float far = gl_DepthRange.far;
|
||||||
|
float near = gl_DepthRange.near;
|
||||||
|
gl_FragDepth = 0.5 * ((far - near) * posClip.z / posClip.w + near + far);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
TransformCamera cam = getTransformCamera();
|
|
||||||
vec3 fragPosition = _positionES.xyz;
|
|
||||||
|
|
||||||
if (emissiveAmount > 0.0) {
|
if (emissiveAmount > 0.0) {
|
||||||
_fragColor0 = vec4(diffuse, _color.a);
|
_fragColor0 = vec4(diffuse, alpha);
|
||||||
} else {
|
} else {
|
||||||
_fragColor0 = vec4(evalGlobalLightingAlphaBlendedWithHaze(
|
_fragColor0 = vec4(evalGlobalLightingAlphaBlendedWithHaze(
|
||||||
cam._viewInverse,
|
cam._viewInverse,
|
||||||
1.0,
|
1.0,
|
||||||
DEFAULT_OCCLUSION,
|
occlusion,
|
||||||
fragPosition,
|
posEye,
|
||||||
normal,
|
normal,
|
||||||
diffuse,
|
diffuse,
|
||||||
DEFAULT_FRESNEL,
|
fresnel,
|
||||||
length(specular),
|
metallic,
|
||||||
DEFAULT_EMISSIVE,
|
emissive,
|
||||||
max(0.0, 1.0 - shininess / 128.0), _color.a),
|
roughness, alpha),
|
||||||
_color.a);
|
alpha);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue