Fix render order between particles and grass clusters

This commit is contained in:
Vsevolod Kremianskii 2021-05-19 01:22:05 +07:00
parent c2fdc464dd
commit 95f9fce3c4
10 changed files with 129 additions and 110 deletions

View file

@ -304,6 +304,7 @@ set(SCENE_HEADERS
src/engine/scene/pipeline/control.h
src/engine/scene/pipeline/world.h
src/engine/scene/scenegraph.h
src/engine/scene/sceneleaf.h
src/engine/scene/types.h)
set(SCENE_SOURCES

View file

@ -533,10 +533,11 @@ void Area::fill(SceneGraph &sceneGraph) {
glm::vec3 baryPosition(getRandomBarycentric());
glm::vec3 position(aabbTransform * glm::vec4(barycentricToCartesian(vertices[0], vertices[1], vertices[2], baryPosition), 1.0f));
glm::vec2 lightmapUV(aabbNode->mesh()->mesh->getTriangleTexCoords2(face, baryPosition));
GrassSceneNode::Cluster cluster;
cluster.position = move(position);
cluster.variant = getRandomGrassVariant();
cluster.lightmapUV = move(lightmapUV);
auto cluster = make_shared<GrassSceneNode::Cluster>();
cluster->parent = grass.get();
cluster->position = move(position);
cluster->variant = getRandomGrassVariant();
cluster->lightmapUV = move(lightmapUV);
grass->addCluster(move(cluster));
}
}

View file

@ -144,7 +144,7 @@ void EmitterSceneNode::doSpawnParticle() {
glm::vec3 velocity((_velocity + random(0.0f, _randomVelocity)) * dir);
auto particle = make_shared<Particle>();
particle->emitter = this;
particle->parent = this;
particle->position = move(position);
particle->velocity = move(velocity);
particle->frame = _frameStart;
@ -191,7 +191,7 @@ void EmitterSceneNode::spawnLightningParticles() {
glm::vec3 endToStart(segment.second - segment.first);
glm::vec3 center(0.5f * (segment.first + segment.second));
auto particle = make_shared<Particle>();
particle->emitter = this;
particle->parent = this;
particle->position = move(center);
particle->dir = _absTransform * glm::vec4(glm::normalize(endToStart), 0.0f);
particle->size = glm::vec2(_lightningScale, glm::length(endToStart));
@ -248,8 +248,11 @@ void EmitterSceneNode::detonate() {
doSpawnParticle();
}
void EmitterSceneNode::drawParticles(const vector<Particle *> &particles) {
if (particles.empty()) return;
void EmitterSceneNode::drawLeafs(const vector<shared_ptr<SceneLeaf>> &leafs, int count) {
if (leafs.empty()) return;
if (count == -1) {
count = static_cast<int>(leafs.size());
}
shared_ptr<ModelNode::Emitter> emitter(_modelNode->emitter());
shared_ptr<Texture> texture(emitter->texture);
@ -260,22 +263,22 @@ void EmitterSceneNode::drawParticles(const vector<Particle *> &particles) {
uniforms.particles->gridSize = emitter->gridSize;
uniforms.particles->render = static_cast<int>(emitter->renderMode);
for (size_t i = 0; i < particles.size(); ++i) {
const Particle &particle = *particles[i];
for (int i = 0; i < count; ++i) {
auto particle = static_pointer_cast<Particle>(leafs[i]);
glm::mat4 transform(_absTransform);
transform = glm::translate(transform, particles[i]->position);
transform = glm::translate(transform, particle->position);
if (emitter->renderMode == ModelNode::Emitter::RenderMode::MotionBlur) {
transform = glm::scale(transform, glm::vec3((1.0f + kMotionBlurStrength * kProjectileSpeed) * particle.size.x, particle.size.y, 1.0f));
transform = glm::scale(transform, glm::vec3((1.0f + kMotionBlurStrength * kProjectileSpeed) * particle->size.x, particle->size.y, 1.0f));
} else {
transform = glm::scale(transform, glm::vec3(particle.size, 1.0f));
transform = glm::scale(transform, glm::vec3(particle->size, 1.0f));
}
uniforms.particles->particles[i].transform = move(transform);
uniforms.particles->particles[i].dir = glm::vec4(particle.dir, 1.0f);
uniforms.particles->particles[i].color = glm::vec4(particle.color, particle.alpha);
uniforms.particles->particles[i].size = glm::vec2(particle.size);
uniforms.particles->particles[i].frame = particle.frame;
uniforms.particles->particles[i].dir = glm::vec4(particle->dir, 1.0f);
uniforms.particles->particles[i].color = glm::vec4(particle->color, particle->alpha);
uniforms.particles->particles[i].size = glm::vec2(particle->size);
uniforms.particles->particles[i].frame = particle->frame;
}
Shaders::instance().activate(ShaderProgram::ParticleParticle, uniforms);
@ -285,11 +288,11 @@ void EmitterSceneNode::drawParticles(const vector<Particle *> &particles) {
bool lighten = emitter->blendMode == ModelNode::Emitter::BlendMode::Lighten;
if (lighten) {
withLightenBlending([&particles]() {
Meshes::instance().getBillboard()->drawInstanced(static_cast<int>(particles.size()));
withLightenBlending([&count]() {
Meshes::instance().getBillboard()->drawInstanced(count);
});
} else {
Meshes::instance().getBillboard()->drawInstanced(static_cast<int>(particles.size()));
Meshes::instance().getBillboard()->drawInstanced(count);
}
}

View file

@ -23,6 +23,8 @@
#include "../../graphics/beziercurve.h"
#include "../../graphics/model/modelnode.h"
#include "../sceneleaf.h"
#include "modelnodescenenode.h"
namespace reone {
@ -33,8 +35,7 @@ class ModelSceneNode;
class EmitterSceneNode : public ModelNodeSceneNode {
public:
struct Particle {
EmitterSceneNode *emitter { nullptr };
struct Particle : public SceneLeaf {
glm::vec3 position { 0.0f };
glm::vec3 dir { 0.0f }; // used in Linked render mode
glm::vec3 color { 1.0f };
@ -49,7 +50,7 @@ public:
EmitterSceneNode(const ModelSceneNode *model, std::shared_ptr<graphics::ModelNode> modelNode, SceneGraph *sceneGraph);
void update(float dt) override;
void drawParticles(const std::vector<Particle *> &particles);
void drawLeafs(const std::vector<std::shared_ptr<SceneLeaf>> &leafs, int count) override;
void detonate();

View file

@ -48,11 +48,16 @@ void GrassSceneNode::clear() {
_clusters.clear();
}
void GrassSceneNode::addCluster(Cluster cluster) {
void GrassSceneNode::addCluster(shared_ptr<Cluster> cluster) {
_clusters.push_back(move(cluster));
}
void GrassSceneNode::drawClusters(const vector<Cluster> &clusters) {
void GrassSceneNode::drawLeafs(const vector<shared_ptr<SceneLeaf>> &leafs, int count) {
if (leafs.empty()) return;
if (count == -1) {
count = static_cast<int>(leafs.size());
}
setActiveTextureUnit(TextureUnits::diffuseMap);
_texture->bind();
@ -66,15 +71,15 @@ void GrassSceneNode::drawClusters(const vector<Cluster> &clusters) {
uniforms.combined.featureMask |= UniformFeatureFlags::lightmap;
}
int numClusters = static_cast<int>(clusters.size());
for (int i = 0; i < numClusters; ++i) {
for (int i = 0; i < count; ++i) {
auto cluster = static_pointer_cast<GrassSceneNode::Cluster>(leafs[i]);
uniforms.grass->quadSize = _quadSize;
uniforms.grass->clusters[i].positionVariant = glm::vec4(clusters[i].position, static_cast<float>(clusters[i].variant));
uniforms.grass->clusters[i].lightmapUV = clusters[i].lightmapUV;
uniforms.grass->clusters[i].positionVariant = glm::vec4(cluster->position, static_cast<float>(cluster->variant));
uniforms.grass->clusters[i].lightmapUV = cluster->lightmapUV;
}
Shaders::instance().activate(ShaderProgram::GrassGrass, uniforms);
Meshes::instance().getGrass()->drawInstanced(numClusters);
Meshes::instance().getGrass()->drawInstanced(count);
}
} // namespace scene

View file

@ -19,6 +19,8 @@
#include "../../graphics/texture/texture.h"
#include "../sceneleaf.h"
#include "scenenode.h"
namespace reone {
@ -27,7 +29,7 @@ namespace scene {
class GrassSceneNode : public SceneNode {
public:
struct Cluster {
struct Cluster : public SceneLeaf {
glm::vec3 position { 0.0f };
glm::vec2 lightmapUV { 0.0f };
int variant { 0 };
@ -36,17 +38,17 @@ public:
GrassSceneNode(std::string name, glm::vec2 quadSize, std::shared_ptr<graphics::Texture> texture, std::shared_ptr<graphics::Texture> lightmap, SceneGraph *graph);
void clear();
void addCluster(Cluster cluster);
void addCluster(std::shared_ptr<Cluster> cluster);
void drawClusters(const std::vector<Cluster> &clusters);
void drawLeafs(const std::vector<std::shared_ptr<SceneLeaf>> &leafs, int count) override;
const std::vector<Cluster> &clusters() const { return _clusters; }
const std::vector<std::shared_ptr<Cluster>> &clusters() const { return _clusters; }
private:
glm::vec2 _quadSize { 0.0f };
std::shared_ptr<graphics::Texture> _texture;
std::shared_ptr<graphics::Texture> _lightmap;
std::vector<Cluster> _clusters;
std::vector<std::shared_ptr<Cluster>> _clusters;
};
} // namespace scene

View file

@ -28,6 +28,7 @@
#include "../../graphics/aabb.h"
#include "../sceneleaf.h"
#include "../types.h"
namespace reone {
@ -44,6 +45,8 @@ public:
virtual void update(float dt);
virtual void draw();
virtual void drawLeafs(const std::vector<std::shared_ptr<SceneLeaf>> &leafs, int count = -1) {}
bool isVisible() const { return _visible; }
bool isCullable() const { return _cullable; }
bool isCulled() const { return _culled; }

View file

@ -74,8 +74,7 @@ void SceneGraph::update(float dt) {
updateLighting();
updateShadows(dt);
prepareTransparentMeshes();
prepareParticles();
prepareGrass();
prepareLeafs();
}
}
@ -226,78 +225,53 @@ void SceneGraph::prepareTransparentMeshes() {
});
}
void SceneGraph::prepareParticles() {
void SceneGraph::prepareLeafs() {
static glm::vec4 viewport(-1.0f, -1.0f, 1.0f, 1.0f);
// Extract particles from all emitters, sort them by depth
vector<pair<EmitterSceneNode::Particle *, float>> particlesZ;
vector<pair<shared_ptr<SceneLeaf>, float>> leafs;
glm::vec3 cameraPos(_activeCamera->absoluteTransform()[3]);
// Add grass clusters
for (auto &grass : _grass) {
float grassDistance2 = kMaxGrassDistance * kMaxGrassDistance;
size_t numLeafs = leafs.size();
for (auto &cluster : grass->clusters()) {
float distance2 = glm::distance2(cameraPos, cluster->position);
if (distance2 <= grassDistance2) {
glm::vec3 screen(glm::project(cluster->position, _activeCamera->view(), _activeCamera->projection(), viewport));
if (screen.z >= 0.5f && glm::abs(screen.x) <= 1.0f && glm::abs(screen.y) <= 1.0f) {
leafs.push_back(make_pair(cluster, screen.z));
}
}
}
}
// Add particles
for (auto &emitter : _emitters) {
glm::mat4 modelView(_activeCamera->view() * emitter->absoluteTransform());
for (auto &particle : emitter->particles()) {
glm::vec3 screen(glm::project(particle->position, modelView, _activeCamera->projection(), viewport));
if (screen.z >= 0.5f && glm::abs(screen.x) <= 1.0f && glm::abs(screen.y) <= 1.0f) {
particlesZ.push_back(make_pair(particle.get(), screen.z));
leafs.push_back(make_pair(particle, screen.z));
}
}
}
sort(particlesZ.begin(), particlesZ.end(), [](auto &left, auto &right) {
return left.second > right.second;
});
// Map (particle, Z) pairs to (emitter, particles)
_particles.clear();
EmitterSceneNode *emitter = nullptr;
vector<EmitterSceneNode::Particle *> emitterParticles;
for (auto &pair : particlesZ) {
if (pair.first->emitter != emitter) {
flushEmitterParticles(emitter, emitterParticles);
emitter = pair.first->emitter;
// Sort leafs back to front
sort(leafs.begin(), leafs.end(), [](auto &left, auto &right) { return left.second > right.second; });
// Group leafs into buckets
_leafs.clear();
vector<shared_ptr<SceneLeaf>> nodeLeafs;
for (auto &leafDepth : leafs) {
if (!nodeLeafs.empty()) {
_leafs.push_back(make_pair(nodeLeafs[0]->parent, nodeLeafs));
nodeLeafs.clear();
}
emitterParticles.push_back(pair.first);
nodeLeafs.push_back(leafDepth.first);
}
flushEmitterParticles(emitter, emitterParticles);
}
void SceneGraph::flushEmitterParticles(EmitterSceneNode *emitter, vector<EmitterSceneNode::Particle *> &particles) {
if (emitter && !particles.empty()) {
_particles.push_back(make_pair(emitter, particles));
particles.clear();
}
}
void SceneGraph::prepareGrass() {
static glm::vec4 viewport(-1.0f, -1.0f, 1.0f, 1.0f);
_grassClusters.clear();
if (_activeCamera && !_grass.empty()) {
glm::vec3 cameraPos(_activeCamera->absoluteTransform()[3]);
float grassDistance2 = kMaxGrassDistance * kMaxGrassDistance;
for (auto &node : _grass) {
vector<GrassSceneNode::Cluster> clusters;
vector<pair<GrassSceneNode::Cluster, float>> clustersZ;
for (auto &cluster : node->clusters()) {
float distance2 = glm::distance2(cameraPos, cluster.position);
if (distance2 <= grassDistance2) {
glm::vec3 screen(glm::project(cluster.position, _activeCamera->view(), _activeCamera->projection(), viewport));
if (screen.z >= 0.5f && glm::abs(screen.x) <= 1.0f && glm::abs(screen.y) <= 1.0f) {
clustersZ.push_back(make_pair(cluster, screen.z));
}
}
}
sort(clustersZ.begin(), clustersZ.end(), [](auto &left, auto &right) {
return left.second > right.second;
});
int numClustersToErase = glm::max(0, static_cast<int>(clustersZ.size()) - kMaxGrassClusters);
if (numClustersToErase > 0) {
clustersZ.erase(clustersZ.begin(), clustersZ.begin() + numClustersToErase);
}
for (auto &cluster : clustersZ) {
clusters.push_back(move(cluster.first));
}
_grassClusters.push_back(make_pair(node, move(clusters)));
}
if (!nodeLeafs.empty()) {
_leafs.push_back(make_pair(nodeLeafs[0]->parent, nodeLeafs));
}
}
@ -339,14 +313,12 @@ void SceneGraph::draw(bool shadowPass) {
setBackFaceCullingEnabled(false);
// Render grass
for (auto &grass : _grassClusters) {
grass.first->drawClusters(grass.second);
}
// Render particles
for (auto &pair : _particles) {
pair.first->drawParticles(pair.second);
// Render particles and grass clusters
for (auto &nodeLeaf : _leafs) {
int count = nodeLeaf.first->type() == SceneNodeType::Grass && nodeLeaf.second.size() > kMaxGrassClusters ?
kMaxGrassClusters :
-1;
nodeLeaf.first->drawLeafs(nodeLeaf.second, count);
}
// Render lens flares

View file

@ -121,8 +121,7 @@ private:
std::vector<LightSceneNode *> _lights;
std::vector<EmitterSceneNode *> _emitters;
std::vector<GrassSceneNode *> _grass;
std::vector<std::pair<EmitterSceneNode *, std::vector<EmitterSceneNode::Particle *>>> _particles;
std::vector<std::pair<GrassSceneNode *, std::vector<GrassSceneNode::Cluster>>> _grassClusters;
std::vector<std::pair<SceneNode *, std::vector<std::shared_ptr<SceneLeaf>>>> _leafs;
uint32_t _textureId { 0 };
bool _updateRoots { true };
@ -157,10 +156,7 @@ private:
void refreshShadowLight();
void prepareTransparentMeshes();
void prepareParticles();
void prepareGrass();
inline void flushEmitterParticles(EmitterSceneNode *emitter, std::vector<EmitterSceneNode::Particle *> &particles);
void prepareLeafs();
};
} // namespace scene

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2020-2021 The reone project contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
namespace reone {
namespace scene {
class SceneNode;
/**
* Base class for emitter particles and grass clusters.
*/
struct SceneLeaf {
SceneNode *parent { nullptr };
};
} // namespace scene
} // namespace reone