Skip to content

Commit

Permalink
Gloom: Promote lights for shading during global lighting pass
Browse files Browse the repository at this point in the history
  • Loading branch information
skyjake committed Sep 1, 2019
1 parent 43b6c75 commit ca3b123
Show file tree
Hide file tree
Showing 6 changed files with 242 additions and 181 deletions.
29 changes: 1 addition & 28 deletions doomsday/tests/test_gloom/gloom/gloomworld.cpp
Expand Up @@ -226,18 +226,8 @@ DENG2_PIMPL(GloomWorld), public Asset
void update(const TimeSpan &elapsed)
{
currentTime += elapsed;

renderContext.uCurrentTime = float(currentTime);

/*
for (auto i = map.planes().begin(), end = map.planes().end(); i != end; ++i)
{
const float planeY = float(initialPlaneY[i.key()]) +
std::sin(i.key() + float(currentTime) * .1f);
i.value().point.y = planeY;
}
*/

updateEntities(elapsed);
}

Expand All @@ -251,23 +241,6 @@ DENG2_PIMPL(GloomWorld), public Asset
ent.setPosition(pos);
}
}

// void positionOnGround(Entity &ent, Vec2f const &surfacePos)
// {
// ent.setPosition(Vec3f(surfacePos.x,
// height.heightAtPosition(surfacePos) + .05f,
// surfacePos.y));
// }

// bool isFlatSurface(Vec2f const &pos) const
// {
// return (height.normalAtPosition(pos).y < -.9 &&
// height.normalAtPosition(pos + Vec2f(-1, -1)).y < -.9 &&
// height.normalAtPosition(pos + Vec2f(1, -1)).y < -.9 &&
// height.normalAtPosition(pos + Vec2f(-1, 1)).y < -.9 &&
// height.normalAtPosition(pos + Vec2f(1, 1)).y < -.9);
// }

};

GloomWorld::GloomWorld() : d(new Impl(this))
Expand Down Expand Up @@ -381,7 +354,7 @@ void GloomWorld::render(const ICamera &camera)
d->debugQuad.render();
}

#if 0
#if 1
{
auto &perfTimer = GLWindow::main().timer();
for (int i = 0; i < PerfTimerCount; ++i)
Expand Down
155 changes: 115 additions & 40 deletions doomsday/tests/test_gloom/gloom/render/lightrender.cpp
Expand Up @@ -47,7 +47,8 @@ internal::AttribSpec const LightData::_spec[5] = {
};
LIBGUI_VERTEX_FORMAT_SPEC(LightData, 11 * 4)

static constexpr int MAX_SHADOWS = 6;
static constexpr int MAX_OMNI_LIGHTS = 6;
static constexpr int MAX_OMNI_SHADOWS = 6;

DENG2_PIMPL(LightRender)
{
Expand All @@ -57,7 +58,7 @@ DENG2_PIMPL(LightRender)
std::unique_ptr<Light> skyLight;
Lights lights;
QSet<Light *> activeLights;
QSet<Light *> shadowCasters; // up to MAX_SHADOWS
QSet<Light *> shadowCasters; // up to MAX_OMNI_SHADOWS
RenderFunc callback;
GLState shadowState;
GLProgram shadingProgram;
Expand All @@ -75,16 +76,37 @@ DENG2_PIMPL(LightRender)
GLUniform uShadowSize {"uShadowSize", GLUniform::Vec2};
GLUniform uShadowMap {"uShadowMap", GLUniform::Sampler2D}; // <----TESTING-----

GLUniform uShadowMaps[MAX_SHADOWS] {
GLUniform uShadowMaps[MAX_OMNI_SHADOWS] {
{"uShadowMaps[0]", GLUniform::SamplerCube},
{"uShadowMaps[1]", GLUniform::SamplerCube},
{"uShadowMaps[2]", GLUniform::SamplerCube},
{"uShadowMaps[3]", GLUniform::SamplerCube},
{"uShadowMaps[4]", GLUniform::SamplerCube},
{"uShadowMaps[5]", GLUniform::SamplerCube}
};
GLUniform uOmniLightCount{"uOmniLightCount", GLUniform::Int};
struct OmniLight {
GLUniform origin;
GLUniform intensity;
GLUniform falloffRadius;
GLUniform shadowIndex;
};
OmniLight uOmniLights[MAX_OMNI_LIGHTS] { // TODO: Shader storage buffer would be nice (GLSL 4)
#define OMNI_LIGHT_MEMBERS(idx) { \
{"uOmniLights["#idx"].origin", GLUniform::Vec3}, \
{"uOmniLights["#idx"].intensity", GLUniform::Vec3}, \
{"uOmniLights["#idx"].falloffRadius", GLUniform::Float}, \
{"uOmniLights["#idx"].shadowIndex", GLUniform::Int} }
OMNI_LIGHT_MEMBERS(0),
OMNI_LIGHT_MEMBERS(1),
OMNI_LIGHT_MEMBERS(2),
OMNI_LIGHT_MEMBERS(3),
OMNI_LIGHT_MEMBERS(4),
OMNI_LIGHT_MEMBERS(5)
#undef OMNI_LIGHT_MEMBERS
};
std::unique_ptr<Shadow> dirShadow;
std::unique_ptr<Shadow> omniShadows[MAX_SHADOWS];
std::unique_ptr<Shadow> omniShadows[MAX_OMNI_SHADOWS];
QHash<const Light *, const Shadow *> activeShadows;

Impl(Public *i) : Base(i)
Expand Down Expand Up @@ -126,7 +148,7 @@ DENG2_PIMPL(LightRender)
// Create shadow maps. These will be assigned to lights as needed.
{
dirShadow.reset(new Shadow(Light::Directional));
for (int i = 0; i < MAX_SHADOWS; ++i)
for (int i = 0; i < MAX_OMNI_SHADOWS; ++i)
{
omniShadows[i].reset(new Shadow(Light::Omni));
}
Expand Down Expand Up @@ -166,9 +188,24 @@ DENG2_PIMPL(LightRender)
<< uViewSpaceLightDir
<< uLightIntensity
<< uViewToLightMatrix
<< ctx.uLightMatrix;
<< ctx.uLightMatrix
<< uOmniLightCount
<< uShadowMaps[0]
<< uShadowMaps[1]
<< uShadowMaps[2]
<< uShadowMaps[3]
<< uShadowMaps[4]
<< uShadowMaps[5];
ctx.bindGBuffer(giQuad.program());
// ctx.bindMaterials(giQuad.program());

for (int i = 0; i < MAX_OMNI_LIGHTS; ++i)
{
giQuad.program() << uOmniLightCount
<< uOmniLights[i].origin
<< uOmniLights[i].intensity
<< uOmniLights[i].falloffRadius
<< uOmniLights[i].shadowIndex;
}

// Generate a sphere for light bounds.
{
Expand Down Expand Up @@ -237,14 +274,14 @@ DENG2_PIMPL(LightRender)
auto &ctx = self().context();
if (ctx.view.camera)
{
using ProxEntry = std::pair<double, Light *>;
const Vec3d camPos = ctx.view.camera->cameraPosition(); // TODO: Get complete frustum.

shadowCasters << skyLight.get();

using ProxEntry = std::pair<double, Light *>;
QVector<ProxEntry> proxLights;
proxLights.reserve(activeLights.size());

const Vec3d camPos = ctx.view.camera->cameraPosition();

// The remaining shadows will be assigned based on proximity.
for (Light *light : activeLights)
{
Expand All @@ -254,9 +291,9 @@ DENG2_PIMPL(LightRender)
}
}

std::sort(proxLights.begin(), proxLights.end(), [](const ProxEntry &a, const ProxEntry &b) {
return a.first < b.first;
});
std::sort(proxLights.begin(),
proxLights.end(),
[](const ProxEntry &a, const ProxEntry &b) { return a.first < b.first; });

for (const ProxEntry &proxEntry : proxLights)
{
Expand All @@ -265,13 +302,37 @@ DENG2_PIMPL(LightRender)

shadowCasters << proxEntry.second;

if (shadowCasters.size() == MAX_SHADOWS + 1)
if (shadowCasters.size() == MAX_OMNI_SHADOWS + 1)
{
break; // skyLight has a separate shadow map
}
}
}
}

int assignOmniLights(std::function<bool (const Light *, int)> assignLight)
{
int totalOmnis = 0;
int counter = 0;
for (const auto *light : activeLights)
{
if (light->type() == Light::Omni)
{
totalOmnis++;

// Assign shadow maps.
int shadowIndex = -1;
if (activeShadows.contains(light) && counter < MAX_OMNI_SHADOWS)
{
shadowIndex = counter++;
uShadowMaps[shadowIndex] = activeShadows[light]->shadowMap();
}

if (!assignLight(light, shadowIndex)) break;
}
}
return totalOmnis;
}
};

LightRender::LightRender()
Expand Down Expand Up @@ -307,7 +368,7 @@ void LightRender::render()
}
else
{
if (shadowIndex == MAX_SHADOWS) continue;
if (shadowIndex == MAX_OMNI_SHADOWS) continue;
shadow = d->omniShadows[shadowIndex++].get();
}

Expand Down Expand Up @@ -354,8 +415,7 @@ void LightRender::advanceTime(TimeSpan elapsed)

// Testing.
{
Vec3d rotPos =
Mat4f::rotate(float(elapsed), Vec3f(0, 1, 0)) * d->skyLight->origin();
Vec3d rotPos = Mat4f::rotate(float(elapsed), Vec3f(0, 1, 0)) * d->skyLight->origin();
d->skyLight->setOrigin(rotPos);
d->skyLight->setDirection(-rotPos);
}
Expand All @@ -376,12 +436,38 @@ void LightRender::renderLighting()
d->uViewSpaceLightDir = ctx.view.uWorldToViewRotate.toMat3f() * d->skyLight->direction();
d->uViewSpaceLightOrigin = ctx.view.camera->cameraModelView() * d->skyLight->origin();
d->uViewToLightMatrix = lightMatrix * ctx.view.camera->cameraModelView().inverse();

if (d->activeShadows.contains(d->skyLight.get()))
{
d->uShadowMap = d->activeShadows[d->skyLight.get()]->shadowMap();
}
}

// Select omni lights for global pass.
QSet<const Light *> promoted;
{
d->assignOmniLights([this, &ctx, &promoted](const Light *light, int shadowIndex)
{
// Only promote lights if the camera is well within the falloff volume.
const Vec3d camPos = ctx.view.camera->cameraPosition();
if ((light->origin() - camPos).length() < double(light->falloffDistance()))
{
if (promoted.size() == MAX_OMNI_LIGHTS) return false;

const int promIdx = promoted.size();
auto & omni = d->uOmniLights[promIdx];
omni.origin = ctx.view.camera->cameraModelView() * light->origin();
omni.intensity = light->intensity();
omni.falloffRadius = light->falloffDistance();
omni.shadowIndex = shadowIndex;

promoted.insert(light);
}
return true;
});
d->uOmniLightCount = promoted.size();
}

// Global illumination.
d->giQuad.state()
.setBlend(false)
Expand All @@ -392,34 +478,23 @@ void LightRender::renderLighting()

typedef GLBufferT<LightData> LightBuf;

// Individual light sources.
// Individual unlimited light sources.
LightBuf::Vertices lightData;
int counter = 0;

for (const auto *light : d->activeLights)
d->assignOmniLights([&lightData, &promoted](const Light *light, int shadowIndex)
{
if (light->type() == Light::Directional)
if (!promoted.contains(light))
{
// Already shaded during GI pass.
continue;
LightData instance{light->origin(),
light->intensity(),
light->direction(),
light->falloffDistance(),
shadowIndex};
lightData << instance;
}

// Assign shadow maps.
int shadowIndex = -1;
//if (light->type() == Light::Omni && light->castShadows())
if (d->activeShadows.contains(light))
{
shadowIndex = counter++;
d->uShadowMaps[shadowIndex] = d->activeShadows[light]->shadowMap();
}

LightData instance{light->origin(),
light->intensity(),
light->direction(),
light->falloffDistance(),
shadowIndex};
lightData << instance;
}
// Keep going through all the lights.
return true;
});

// The G-buffer depths are used as-is.
context().gbuffer->framebuf().blit(target, GLFramebuffer::Depth);
Expand Down
Expand Up @@ -37,9 +37,9 @@ vec3 Gloom_BlinnPhong(Light light, SurfacePoint surf) {

vec3 specular = vec3(0.0);
if (surf.specGloss.rgb != vec3(0.0)) {
float shininess = 1024.0; // TODO: <= specGloss.a
float gloss = max(1.0, 1024.0 * surf.specGloss.a);
// Blinn-Phong.
float spec = abs(pow(max(dot(surf.normal, halfwayDir), 0.0), shininess));
float spec = abs(pow(max(dot(surf.normal, halfwayDir), 0.0), gloss));
specular = surf.specGloss.rgb * light.intensity * spec * edgeFalloff;
}
return (light.intensity * falloff * diffuse * surf.diffuse) + specular;
Expand Down

0 comments on commit ca3b123

Please sign in to comment.