|
|
@@ -69,13 +69,35 @@ void Lf::initInternal(const RendererInitializer& initializer)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- // Load the shader
|
|
|
+ maxFlaresPerLight = initializer.pps.lf.maxFlaresPerLight;
|
|
|
+ maxLightsWithFlares = initializer.pps.lf.maxLightsWithFlares;
|
|
|
+
|
|
|
+ // Load program 1
|
|
|
std::string pps = "#define TEX_DIMENSIONS vec2("
|
|
|
+ std::to_string(r->getPps().getHdr().getFai().getWidth()) + ".0, "
|
|
|
+ std::to_string(r->getPps().getHdr().getFai().getHeight()) + ".0)\n";
|
|
|
|
|
|
- drawProg.load(ShaderProgramResource::createSrcCodeToCache(
|
|
|
- "shaders/PpsLfPass.glsl", pps.c_str()).c_str());
|
|
|
+ pseudoProg.load(ShaderProgramResource::createSrcCodeToCache(
|
|
|
+ "shaders/PpsLfPseudoPass.glsl", pps.c_str()).c_str());
|
|
|
+
|
|
|
+ // Load program 2
|
|
|
+ pps = "#define MAX_FLARES "
|
|
|
+ + std::to_string(maxFlaresPerLight * maxLightsWithFlares) + "\n";
|
|
|
+ std::string fname = ShaderProgramResource::createSrcCodeToCache(
|
|
|
+ "shaders/PpsLfSpritePass.glsl", pps.c_str());
|
|
|
+ realProg.load(fname.c_str());
|
|
|
+
|
|
|
+ ublock = &realProg->findUniformBlock("flaresBlock");
|
|
|
+ ublock->setBinding(0);
|
|
|
+
|
|
|
+ PtrSize blockSize = sizeof(Flare) * maxFlaresPerLight * maxLightsWithFlares;
|
|
|
+ if(ublock->getSize() != blockSize)
|
|
|
+ {
|
|
|
+ throw ANKI_EXCEPTION("Incorrect block size");
|
|
|
+ }
|
|
|
+
|
|
|
+ // Init UBO
|
|
|
+ flareDataUbo.create(blockSize, nullptr);
|
|
|
|
|
|
// Create the FAI
|
|
|
fai.create2dFai(r->getPps().getHdr().getFai().getWidth(),
|
|
|
@@ -98,13 +120,17 @@ void Lf::run()
|
|
|
{
|
|
|
ANKI_ASSERT(enabled);
|
|
|
|
|
|
+ //
|
|
|
+ // First pass
|
|
|
+ //
|
|
|
+
|
|
|
// Set the common state
|
|
|
const Texture& inTex = r->getPps().getHdr().getFai();
|
|
|
|
|
|
fbo.bind();
|
|
|
- drawProg->bind();
|
|
|
- drawProg->findUniformVariable("tex").set(inTex);
|
|
|
- drawProg->findUniformVariable("lensDirtTex").set(*lensDirtTex);
|
|
|
+ pseudoProg->bind();
|
|
|
+ pseudoProg->findUniformVariable("tex").set(inTex);
|
|
|
+ pseudoProg->findUniformVariable("lensDirtTex").set(*lensDirtTex);
|
|
|
|
|
|
GlStateSingleton::get().setViewport(
|
|
|
0, 0, inTex.getWidth(), inTex.getHeight());
|
|
|
@@ -112,6 +138,154 @@ void Lf::run()
|
|
|
GlStateSingleton::get().disable(GL_BLEND);
|
|
|
|
|
|
r->drawQuad();
|
|
|
+
|
|
|
+ //
|
|
|
+ // Rest of the passes
|
|
|
+ //
|
|
|
+
|
|
|
+ // Retrieve some things
|
|
|
+ SceneGraph& scene = r->getSceneGraph();
|
|
|
+ Camera& cam = scene.getActiveCamera();
|
|
|
+ VisibilityTestResults& vi = *cam.getVisibilityTestResults();
|
|
|
+
|
|
|
+ // Iterate the visible light and get those that have lens flare
|
|
|
+ Array<Light*, ANKI_MAX_LIGHTS_WITH_FLARE> lights;
|
|
|
+ U lightsCount = 0;
|
|
|
+ for(auto it = vi.getLightsBegin(); it != vi.getLightsEnd(); ++it)
|
|
|
+ {
|
|
|
+ SceneNode& sn = *(*it).node;
|
|
|
+ ANKI_ASSERT(sn.getLight());
|
|
|
+ Light* light = sn.getLight();
|
|
|
+
|
|
|
+ if(light->hasLensFlare())
|
|
|
+ {
|
|
|
+ lights[lightsCount % maxLightsWithFlares] = light;
|
|
|
+ ++lightsCount;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Early exit
|
|
|
+ if(lightsCount == 0)
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ lightsCount = lightsCount % (maxLightsWithFlares + 1);
|
|
|
+
|
|
|
+ // Sort the lights using their lens flare texture
|
|
|
+ std::sort(lights.begin(), lights.begin() + lightsCount, LightSortFunctor());
|
|
|
+
|
|
|
+ // Write the UBO and get the groups
|
|
|
+ //
|
|
|
+ Array<Flare, ANKI_MAX_FLARES> flares;
|
|
|
+ U flaresCount = 0;
|
|
|
+
|
|
|
+ // Contains the number of flares per flare texture
|
|
|
+ Array<U, ANKI_MAX_LIGHTS_WITH_FLARE> groups;
|
|
|
+ Array<const Texture*, ANKI_MAX_LIGHTS_WITH_FLARE> texes;
|
|
|
+ U groupsCount = 0;
|
|
|
+
|
|
|
+ GLuint lastTexId = 0;
|
|
|
+
|
|
|
+ // Iterate all lights and update the flares as well as the groups
|
|
|
+ while(lightsCount-- != 0)
|
|
|
+ {
|
|
|
+ Light& light = *lights[lightsCount];
|
|
|
+ const Texture& tex = light.getLensFlareTexture();
|
|
|
+ const U depth = tex.getDepth();
|
|
|
+
|
|
|
+ // Transform
|
|
|
+ Vec3 posWorld = light.getWorldTransform().getOrigin();
|
|
|
+ Vec4 posClip = cam.getViewProjectionMatrix() * Vec4(posWorld, 1.0);
|
|
|
+
|
|
|
+ if(posClip.x() > posClip.w() || posClip.x() < -posClip.w()
|
|
|
+ || posClip.y() > posClip.w() || posClip.y() < -posClip.w())
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ Vec2 posNdc = posClip.xy() / posClip.w();
|
|
|
+
|
|
|
+ Vec2 dir = -posNdc;
|
|
|
+ F32 len = dir.getLength();
|
|
|
+ dir /= len; // Normalize dir
|
|
|
+
|
|
|
+ // New group?
|
|
|
+ if(lastTexId != tex.getGlId())
|
|
|
+ {
|
|
|
+ texes[groupsCount] = &tex;
|
|
|
+ groups[groupsCount] = 0;
|
|
|
+ lastTexId = tex.getGlId();
|
|
|
+
|
|
|
+ ++groupsCount;
|
|
|
+ }
|
|
|
+
|
|
|
+ // First flare
|
|
|
+ F32 stretchFactor = 1.0 - posNdc.getLength();
|
|
|
+ stretchFactor *= stretchFactor;
|
|
|
+
|
|
|
+ Vec2 stretch = light.getLensFlaresStretchMultiplier() * stretchFactor;
|
|
|
+
|
|
|
+ flares[flaresCount].pos = posNdc;
|
|
|
+ flares[flaresCount].scale =
|
|
|
+ light.getLensFlaresSize() * Vec2(1.0, r->getAspectRatio())
|
|
|
+ * stretch;
|
|
|
+ flares[flaresCount].depth = 0.0;
|
|
|
+ flares[flaresCount].alpha = light.getLensFlaresAlpha() * stretchFactor;
|
|
|
+ ++flaresCount;
|
|
|
+ ++groups[groupsCount - 1];
|
|
|
+
|
|
|
+ // The rest of the flares
|
|
|
+ for(U d = 1; d < depth; d++)
|
|
|
+ {
|
|
|
+ // Write the "flares"
|
|
|
+ F32 factor = d / ((F32)depth - 1.0);
|
|
|
+
|
|
|
+ F32 flen = len * 2.0 * factor;
|
|
|
+
|
|
|
+ flares[flaresCount].pos = posNdc + dir * flen;
|
|
|
+
|
|
|
+ flares[flaresCount].scale =
|
|
|
+ light.getLensFlaresSize() * Vec2(1.0, r->getAspectRatio())
|
|
|
+ * ((len - flen) * 2.0);
|
|
|
+
|
|
|
+ flares[flaresCount].depth = d;
|
|
|
+
|
|
|
+ flares[flaresCount].alpha = light.getLensFlaresAlpha();
|
|
|
+
|
|
|
+ // Advance
|
|
|
+ ++flaresCount;
|
|
|
+ ++groups[groupsCount - 1];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Time to render
|
|
|
+ //
|
|
|
+
|
|
|
+ // Write the buffer
|
|
|
+ flareDataUbo.write(&flares[0], 0, sizeof(Flare) * flaresCount);
|
|
|
+
|
|
|
+ // Set the common state
|
|
|
+ realProg->bind();
|
|
|
+ GlStateSingleton::get().disable(GL_DEPTH_TEST);
|
|
|
+ GlStateSingleton::get().enable(GL_BLEND);
|
|
|
+ GlStateSingleton::get().setBlendFunctions(GL_ONE, GL_ONE);
|
|
|
+
|
|
|
+ PtrSize offset = 0;
|
|
|
+ for(U i = 0; i < groupsCount; i++)
|
|
|
+ {
|
|
|
+ const Texture& tex = *texes[i];
|
|
|
+ U instances = groups[i];
|
|
|
+ PtrSize buffSize = sizeof(Flare) * instances;
|
|
|
+
|
|
|
+ realProg->findUniformVariable("images").set(tex);
|
|
|
+
|
|
|
+ flareDataUbo.setBindingRange(0, offset, buffSize);
|
|
|
+
|
|
|
+ r->drawQuadInstanced(instances);
|
|
|
+
|
|
|
+ offset += buffSize;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
} // end namespace anki
|