Skip to content

Commit

Permalink
Merge pull request #844 from HifiExperiments/opaqueParticles
Browse files Browse the repository at this point in the history
Support opaque (and black) particles
  • Loading branch information
ksuprynowicz authored Mar 10, 2024
2 parents bd14f39 + 7b4c26b commit 634dc64
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,48 @@ using namespace render::entities;

static uint8_t CUSTOM_PIPELINE_NUMBER = 0;
static gpu::Stream::FormatPointer _vertexFormat;
static std::weak_ptr<gpu::Pipeline> _texturedPipeline;
// forward, transparent, shadow, wireframe
static std::map<std::tuple<bool, bool, bool, bool>, gpu::PipelinePointer> _pipelines;

static ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const ShapeKey& key, RenderArgs* args) {
auto texturedPipeline = _texturedPipeline.lock();
if (!texturedPipeline) {
auto state = std::make_shared<gpu::State>();
state->setCullMode(gpu::State::CULL_BACK);
state->setDepthTest(true, false, gpu::LESS_EQUAL);
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
PrepareStencil::testMask(*state);

auto program = gpu::Shader::createProgram(shader::entities_renderer::program::textured_particle);
_texturedPipeline = texturedPipeline = gpu::Pipeline::create(program, state);
if (_pipelines.empty()) {
using namespace shader::entities_renderer::program;

// forward, translucent, shadow
static const std::vector<std::tuple<bool, bool, bool, uint32_t>> keys = {
std::make_tuple(false, false, false, textured_particle),
std::make_tuple(true, false, false, textured_particle_forward),
std::make_tuple(false, true, false, textured_particle_translucent),
std::make_tuple(true, true, false, textured_particle_translucent_forward),
std::make_tuple(false, false, true, textured_particle_shadow),
// no such thing as shadow and forward/translucent
};

for (auto& key : keys) {
for (int i = 0; i < 2; ++i) {
bool transparent = std::get<1>(key);
bool wireframe = i == 0;

auto state = std::make_shared<gpu::State>();
state->setCullMode(gpu::State::CULL_BACK);

if (wireframe) {
state->setFillMode(gpu::State::FILL_LINE);
}

state->setDepthTest(true, !transparent, gpu::LESS_EQUAL);
state->setBlendFunction(transparent, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
transparent ? PrepareStencil::testMask(*state) : PrepareStencil::testMaskDrawShape(*state);

auto program = gpu::Shader::createProgram(std::get<3>(key));
_pipelines[std::make_tuple(std::get<0>(key), transparent, std::get<2>(key), wireframe)] = gpu::Pipeline::create(program, state);
}
}
}

return std::make_shared<render::ShapePipeline>(texturedPipeline, nullptr, nullptr, nullptr);
return std::make_shared<render::ShapePipeline>(_pipelines[std::make_tuple(args->_renderMethod == Args::RenderMethod::FORWARD, key.isTranslucent(),
args->_renderMode == Args::RenderMode::SHADOW_RENDER_MODE, key.isWireframe())], nullptr, nullptr, nullptr);
}

struct GpuParticle {
Expand Down Expand Up @@ -138,26 +163,25 @@ void ParticleEffectEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEn
_uniformBuffer.edit<ParticleUniforms>() = particleUniforms;
}

ItemKey ParticleEffectEntityRenderer::getKey() {
// FIXME: implement isTransparent() for particles and an opaque pipeline
auto builder = ItemKey::Builder::transparentShape().withTagBits(getTagMask()).withLayer(getHifiRenderLayer());
bool ParticleEffectEntityRenderer::isTransparent() const {
bool particleTransparent = _particleProperties.getColorStart().a < 1.0f || _particleProperties.getColorMiddle().a < 1.0f ||
_particleProperties.getColorFinish().a < 1.0f || _particleProperties.getColorSpread().a > 0.0f ||
_pulseProperties.getAlphaMode() != PulseMode::NONE || (_textureLoaded && _networkTexture && _networkTexture->getGPUTexture() &&
_networkTexture->getGPUTexture()->getUsage().isAlpha() && !_networkTexture->getGPUTexture()->getUsage().isAlphaMask());
return particleTransparent || Parent::isTransparent();
}

if (!_visible) {
builder.withInvisible();
}
ShapeKey ParticleEffectEntityRenderer::getShapeKey() {
auto builder = ShapeKey::Builder().withCustom(CUSTOM_PIPELINE_NUMBER);

if (_cullWithParent) {
builder.withSubMetaCulled();
if (isTransparent()) {
builder.withTranslucent();
}

return builder.build();
}

ShapeKey ParticleEffectEntityRenderer::getShapeKey() {
auto builder = ShapeKey::Builder().withCustom(CUSTOM_PIPELINE_NUMBER).withTranslucent();
if (_primitiveMode == PrimitiveMode::LINES) {
builder.withWireframe();
}

return builder.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class ParticleEffectEntityRenderer : public TypedEntityRenderer<ParticleEffectEn
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;

virtual ItemKey getKey() override;
bool isTransparent() const override;
virtual ShapeKey getShapeKey() override;
virtual Item::Bound getBound(RenderArgs* args) override;
virtual void doRender(RenderArgs* args) override;
Expand Down
49 changes: 20 additions & 29 deletions libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1719,21 +1719,28 @@ using namespace render;
using namespace render::entities;

static uint8_t CUSTOM_PIPELINE_NUMBER;
static std::map<std::tuple<bool, bool, bool>, ShapePipelinePointer> _pipelines;
// forward, shadow, fade, wireframe
static std::map<std::tuple<bool, bool, bool, bool>, ShapePipelinePointer> _pipelines;
static gpu::Stream::FormatPointer _vertexFormat;

ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const ShapeKey& key, RenderArgs* args) {
// FIXME: custom pipelines like this don't handle shadows or renderLayers correctly

if (_pipelines.empty()) {
using namespace shader::entities_renderer::program;

static const std::vector<std::tuple<bool, bool, uint32_t>> keys = {
std::make_tuple(false, false, polyvox), std::make_tuple(true, false, polyvox_forward)
// forward, shadow, fade
static const std::vector<std::tuple<bool, bool, bool, uint32_t>> keys = {
std::make_tuple(false, false, false, polyvox),
std::make_tuple(true, false, false, polyvox_forward),
std::make_tuple(false, true, false, polyvox_shadow),
// no such thing as forward + shadow
#ifdef POLYVOX_ENTITY_USE_FADE_EFFECT
, std::make_tuple(false, true, polyvox_fade), std::make_tuple(true, true, polyvox_forward_fade)
std::make_tuple(false, false, true, polyvox_fade),
std::make_tuple(false, true, true, polyvox_shadow_fade),
// no such thing as forward + fade/shadow
#else
, std::make_tuple(false, true, polyvox), std::make_tuple(true, true, polyvox_forward)
std::make_tuple(false, false, true, polyvox),
std::make_tuple(false, true, true, polyvox_shadow),
// no such thing as forward + fade/shadow
#endif
};
for (auto& key : keys) {
Expand All @@ -1749,19 +1756,19 @@ ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const Sha
state->setFillMode(gpu::State::FILL_LINE);
}

auto pipeline = gpu::Pipeline::create(gpu::Shader::createProgram(std::get<2>(key)), state);
if (std::get<1>(key)) {
_pipelines[std::make_tuple(std::get<0>(key), std::get<1>(key), wireframe)] = std::make_shared<render::ShapePipeline>(pipeline, nullptr, nullptr, nullptr);
auto pipeline = gpu::Pipeline::create(gpu::Shader::createProgram(std::get<3>(key)), state);
if (!std::get<2>(key)) {
_pipelines[std::make_tuple(std::get<0>(key), std::get<1>(key), std::get<2>(key), wireframe)] = std::make_shared<render::ShapePipeline>(pipeline, nullptr, nullptr, nullptr);
} else {
const auto& fadeEffect = DependencyManager::get<FadeEffect>();
_pipelines[std::make_tuple(std::get<0>(key), std::get<1>(key), wireframe)] = std::make_shared<render::ShapePipeline>(pipeline, nullptr,
_pipelines[std::make_tuple(std::get<0>(key), std::get<1>(key), std::get<2>(key), wireframe)] = std::make_shared<render::ShapePipeline>(pipeline, nullptr,
fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter());
}
}
}
}

return _pipelines[std::make_tuple(args->_renderMethod == Args::RenderMethod::FORWARD, key.isFaded(), key.isWireframe())];
return _pipelines[std::make_tuple(args->_renderMethod == Args::RenderMethod::FORWARD, args->_renderMode == Args::RenderMode::SHADOW_RENDER_MODE, key.isFaded(), key.isWireframe())];
}

PolyVoxEntityRenderer::PolyVoxEntityRenderer(const EntityItemPointer& entity) : Parent(entity) {
Expand All @@ -1775,16 +1782,6 @@ PolyVoxEntityRenderer::PolyVoxEntityRenderer(const EntityItemPointer& entity) :
_params = std::make_shared<gpu::Buffer>(sizeof(glm::vec4), nullptr);
}

ItemKey PolyVoxEntityRenderer::getKey() {
auto builder = ItemKey::Builder::opaqueShape().withTagBits(getTagMask()).withLayer(getHifiRenderLayer());

if (_cullWithParent) {
builder.withSubMetaCulled();
}

return builder.build();
}

ShapeKey PolyVoxEntityRenderer::getShapeKey() {
auto builder = ShapeKey::Builder().withCustom(CUSTOM_PIPELINE_NUMBER);
if (_primitiveMode == PrimitiveMode::LINES) {
Expand Down Expand Up @@ -1866,13 +1863,7 @@ void PolyVoxEntityRenderer::doRender(RenderArgs* args) {
batch.setModelTransform(transform);

batch.setInputFormat(_vertexFormat);
batch.setInputBuffer(gpu::Stream::POSITION, _mesh->getVertexBuffer()._buffer, 0,
sizeof(PolyVox::PositionMaterialNormal));

// TODO -- should we be setting this?
// batch.setInputBuffer(gpu::Stream::NORMAL, mesh->getVertexBuffer()._buffer,
// 12,
// sizeof(PolyVox::PositionMaterialNormal));
batch.setInputBuffer(gpu::Stream::POSITION, _mesh->getVertexBuffer()._buffer, 0, sizeof(PolyVox::PositionMaterialNormal));
batch.setIndexBuffer(gpu::UINT32, _mesh->getIndexBuffer()._buffer, 0);

for (size_t i = 0; i < _xyzTextures.size(); ++i) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,6 @@ class PolyVoxEntityRenderer : public TypedEntityRenderer<RenderablePolyVoxEntity
}

protected:
virtual ItemKey getKey() override;
virtual ShapeKey getShapeKey() override;
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DEFINES (translucent:f forward:f)/shadow:f
30 changes: 28 additions & 2 deletions libraries/entities-renderer/src/textured_particle.slf
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,34 @@ LAYOUT(binding=0) uniform sampler2D colorMap;
layout(location=0) in vec4 varColor;
layout(location=1) in vec2 varTexcoord;

layout(location=0) out vec4 outFragColor;
<@if HIFI_USE_FORWARD or HIFI_USE_SHADOW@>
layout(location=0) out vec4 _fragColor0;
<@else@>
<@include DeferredBufferWrite.slh@>
<@endif@>

void main(void) {
outFragColor = texture(colorMap, varTexcoord.xy) * varColor;
vec4 albedo = texture(colorMap, varTexcoord.xy) * varColor;

<@if HIFI_USE_FORWARD or HIFI_USE_SHADOW@>
<@if not HIFI_USE_TRANSLUCENT@>
// to reduce texel flickering for floating point error we discard when alpha is "almost one"
if (albedo.a < 0.999999) {
discard;
}
<@endif@>

<@if HIFI_USE_FORWARD@>
_fragColor0 = albedo;
<@else@>
_fragColor0 = vec4(1.0);
<@endif@>
<@else@>
vec3 NORMAL = vec3(1.0, 0.0, 0.0);
<@if not HIFI_USE_TRANSLUCENT@>
packDeferredFragmentUnlit(NORMAL, albedo.a, albedo.rgb);
<@else@>
packDeferredFragmentTranslucent(NORMAL, albedo.a, albedo.rgb, DEFAULT_ROUGHNESS);
<@endif@>
<@endif@>
}
11 changes: 11 additions & 0 deletions libraries/render-utils/src/RenderCommonTask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ void DrawLayered3D::run(const RenderContextPointer& renderContext, const Inputs&
}

if (!inItems.empty()) {
auto deferredLightingEffect = DependencyManager::get<DeferredLightingEffect>();

// Render the items
gpu::doInBatch("DrawLayered3D::main", args->_context, [&](gpu::Batch& batch) {
args->_batch = &batch;
Expand All @@ -108,11 +110,20 @@ void DrawLayered3D::run(const RenderContextPointer& renderContext, const Inputs&
batch.setUniformBuffer(graphics::slot::buffer::Buffer::HazeParams, haze->getHazeParametersBuffer());
}

// Set the light
deferredLightingEffect->setupKeyLightBatch(args, batch);

auto renderMethod = args->_renderMethod;
args->_renderMethod = Args::RenderMethod::FORWARD;
if (_opaquePass) {
renderStateSortShapes(renderContext, _shapePlumber, inItems, _maxDrawn);
} else {
renderShapes(renderContext, _shapePlumber, inItems, _maxDrawn);
}

deferredLightingEffect->unsetLocalLightsBatch(batch);

args->_renderMethod = renderMethod;
args->_batch = nullptr;
});
}
Expand Down
26 changes: 18 additions & 8 deletions libraries/render-utils/src/RenderPipelines.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state, const re

void sortAndRenderZPassShapes(const ShapePlumberPointer& shapePlumber, const render::RenderContextPointer& renderContext, const render::ShapeBounds& inShapes, render::ItemBounds &itemBounds) {
std::unordered_map<ShapeKey, std::vector<ShapeKey>, ShapeKey::Hash, ShapeKey::KeyEqual> sortedShapeKeys;
std::unordered_map<uint8_t, std::unordered_map<ShapeKey, std::vector<ShapeKey>, ShapeKey::Hash, ShapeKey::KeyEqual>> sortedCustomShapeKeys;
std::unordered_map<ShapeKey, std::vector<ShapeKey>, ShapeKey::Hash, ShapeKey::KeyEqual> sortedOwnPipelineShapeKeys;

for (const auto& items : inShapes) {
Expand Down Expand Up @@ -444,26 +445,35 @@ void sortAndRenderZPassShapes(const ShapePlumberPointer& shapePlumber, const ren

if (items.first.hasOwnPipeline()) {
sortedOwnPipelineShapeKeys[variantKey.build()].push_back(items.first);
} else if (items.first.isCustom()) {
const uint8_t custom = items.first.getCustom();
variantKey.withCustom(custom);
sortedCustomShapeKeys[custom][variantKey.build()].push_back(items.first);
} else {
sortedShapeKeys[variantKey.build()].push_back(items.first);
}
}

// Render non-withOwnPipeline things
for (auto& variantAndKeys : sortedShapeKeys) {
if (variantAndKeys.second.size() > 0) {
// Render non-withCustom, non-withOwnPipeline things
for (const auto& variantAndKeys : sortedShapeKeys) {
for (const auto& key : variantAndKeys.second) {
renderShapes(renderContext, shapePlumber, inShapes.at(key));
}
}

// Render withCustom things
for (const auto& customAndSortedCustomKeys : sortedCustomShapeKeys) {
for (const auto& variantAndKeys : customAndSortedCustomKeys.second) {
for (const auto& key : variantAndKeys.second) {
renderShapes(renderContext, shapePlumber, inShapes.at(key));
}
}
}

// Render withOwnPipeline things
for (auto& variantAndKeys : sortedOwnPipelineShapeKeys) {
if (variantAndKeys.second.size() > 0) {
for (const auto& key : variantAndKeys.second) {
renderShapes(renderContext, shapePlumber, inShapes.at(key));
}
for (const auto& variantAndKeys : sortedOwnPipelineShapeKeys) {
for (const auto& key : variantAndKeys.second) {
renderShapes(renderContext, shapePlumber, inShapes.at(key));
}
}

Expand Down
4 changes: 2 additions & 2 deletions libraries/render/src/render/ShapePipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ ShapeKey::Filter::Builder::Builder() {
}

void ShapePlumber::addPipelineHelper(const Filter& filter, ShapeKey key, int bit, const PipelinePointer& pipeline) const {
// Iterate over all keys
if (bit < (int)ShapeKey::FlagBit::NUM_FLAGS) {
// Iterate over all non-custom keys
if (bit < (int)ShapeKey::FlagBit::NUM_NON_CUSTOM - 1) {
addPipelineHelper(filter, key, bit + 1, pipeline);
if (!filter._mask[bit]) {
// Toggle bits set as insignificant in filter._mask
Expand Down
3 changes: 2 additions & 1 deletion libraries/render/src/render/ShapePipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class ShapeKey {
CUSTOM_7,

NUM_FLAGS, // Not a valid flag
NUM_NON_CUSTOM = INVALID,

CUSTOM_MASK = (0xFF << CUSTOM_0),

Expand Down Expand Up @@ -112,7 +113,7 @@ class ShapeKey {
Builder& withOwnPipeline() { _flags.set(OWN_PIPELINE); return (*this); }
Builder& invalidate() { _flags.set(INVALID); return (*this); }

Builder& withCustom(uint8_t custom) { _flags &= (~CUSTOM_MASK); _flags |= (custom << CUSTOM_0); return (*this); }
Builder& withCustom(uint8_t custom) { _flags &= (~CUSTOM_MASK); _flags |= (custom << CUSTOM_0); return (*this); }

static const ShapeKey ownPipeline() { return Builder().withOwnPipeline(); }
static const ShapeKey invalid() { return Builder().invalidate(); }
Expand Down

0 comments on commit 634dc64

Please sign in to comment.