- moved gl_spritelight out of gl/. This required a few more changes:
* split gl_shadowmap.cpp into a GL dependent and an API independent part. * gl_drawinfo must be kept around for the HUD sprite because it connects the renderer with the hardware indpendent part of the engine.
This commit is contained in:
parent
819ea8f937
commit
634b3cf413
12 changed files with 256 additions and 193 deletions
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
// 1D dynamic shadow maps
|
||||
// 1D dynamic shadow maps (OpenGL dependent part)
|
||||
// Copyright(C) 2017 Magnus Norddahl
|
||||
// All rights reserved.
|
||||
//
|
||||
|
|
@ -32,77 +32,6 @@
|
|||
#include "hwrenderer/dynlights/hw_dynlightdata.h"
|
||||
#include "stats.h"
|
||||
|
||||
/*
|
||||
The 1D shadow maps are stored in a 1024x1024 texture as float depth values (R32F).
|
||||
|
||||
Each line in the texture is assigned to a single light. For example, to grab depth values for light 20
|
||||
the fragment shader (main.fp) needs to sample from row 20. That is, the V texture coordinate needs
|
||||
to be 20.5/1024.
|
||||
|
||||
The texel row for each light is split into four parts. One for each direction, like a cube texture,
|
||||
but then only in 2D where this reduces itself to a square. When main.fp samples from the shadow map
|
||||
it first decides in which direction the fragment is (relative to the light), like cubemap sampling does
|
||||
for 3D, but once again just for the 2D case.
|
||||
|
||||
Texels 0-255 is Y positive, 256-511 is X positive, 512-767 is Y negative and 768-1023 is X negative.
|
||||
|
||||
Generating the shadow map itself is done by FShadowMap::Update(). The shadow map texture's FBO is
|
||||
bound and then a screen quad is drawn to make a fragment shader cover all texels. For each fragment
|
||||
it shoots a ray and collects the distance to what it hit.
|
||||
|
||||
The shadowmap.fp shader knows which light and texel it is processing by mapping gl_FragCoord.y back
|
||||
to the light index, and it knows which direction to ray trace by looking at gl_FragCoord.x. For
|
||||
example, if gl_FragCoord.y is 20.5, then it knows its processing light 20, and if gl_FragCoord.x is
|
||||
127.5, then it knows we are shooting straight ahead for the Y positive direction.
|
||||
|
||||
Ray testing is done by uploading two GPU storage buffers - one holding AABB tree nodes, and one with
|
||||
the line segments at the leaf nodes of the tree. The fragment shader then performs a test same way
|
||||
as on the CPU, except everything uses indexes as pointers are not allowed in GLSL.
|
||||
*/
|
||||
|
||||
namespace
|
||||
{
|
||||
cycle_t UpdateCycles;
|
||||
int LightsProcessed;
|
||||
int LightsShadowmapped;
|
||||
}
|
||||
|
||||
ADD_STAT(shadowmap)
|
||||
{
|
||||
FString out;
|
||||
out.Format("upload=%04.2f ms lights=%d shadowmapped=%d", UpdateCycles.TimeMS(), LightsProcessed, LightsShadowmapped);
|
||||
return out;
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, gl_shadowmap_quality, 512, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
switch (self)
|
||||
{
|
||||
case 128:
|
||||
case 256:
|
||||
case 512:
|
||||
case 1024:
|
||||
break;
|
||||
default:
|
||||
self = 128;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CUSTOM_CVAR (Bool, gl_light_shadowmap, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
// Unset any residual shadow map indices in the light actors.
|
||||
TThinkerIterator<ADynamicLight> it(STAT_DLIGHT);
|
||||
while (auto light = it.Next())
|
||||
{
|
||||
light->mShadowmapIndex = 1024;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FShadowMap::Update()
|
||||
{
|
||||
UpdateCycles.Reset();
|
||||
|
|
@ -145,53 +74,10 @@ void FShadowMap::Update()
|
|||
UpdateCycles.Unclock();
|
||||
}
|
||||
|
||||
bool FShadowMap::ShadowTest(ADynamicLight *light, const DVector3 &pos)
|
||||
{
|
||||
if (light->shadowmapped && light->radius > 0.0 && IsEnabled() && mAABBTree)
|
||||
return mAABBTree->RayTest(light->Pos(), pos) >= 1.0f;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FShadowMap::IsEnabled() const
|
||||
{
|
||||
return gl_light_shadowmap && !!(gl.flags & RFL_SHADER_STORAGE_BUFFER);
|
||||
}
|
||||
|
||||
void FShadowMap::UploadLights()
|
||||
{
|
||||
if (mLights.Size() != 1024 * 4) mLights.Resize(1024 * 4);
|
||||
int lightindex = 0;
|
||||
|
||||
// Todo: this should go through the blockmap in a spiral pattern around the player so that closer lights are preferred.
|
||||
TThinkerIterator<ADynamicLight> it(STAT_DLIGHT);
|
||||
while (auto light = it.Next())
|
||||
{
|
||||
LightsProcessed++;
|
||||
if (light->shadowmapped && lightindex < 1024 * 4)
|
||||
{
|
||||
LightsShadowmapped++;
|
||||
|
||||
light->mShadowmapIndex = lightindex >> 2;
|
||||
|
||||
mLights[lightindex] = light->X();
|
||||
mLights[lightindex+1] = light->Y();
|
||||
mLights[lightindex+2] = light->Z();
|
||||
mLights[lightindex+3] = light->GetRadius();
|
||||
lightindex += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
light->mShadowmapIndex = 1024;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (; lightindex < 1024 * 4; lightindex++)
|
||||
{
|
||||
mLights[lightindex] = 0;
|
||||
}
|
||||
|
||||
CollectLights();
|
||||
|
||||
if (mLightList == 0)
|
||||
glGenBuffers(1, (GLuint*)&mLightList);
|
||||
|
||||
|
|
@ -204,28 +90,21 @@ void FShadowMap::UploadLights()
|
|||
|
||||
void FShadowMap::UploadAABBTree()
|
||||
{
|
||||
// Just comparing the level info is not enough. If two MAPINFO-less levels get played after each other,
|
||||
// they can both refer to the same default level info.
|
||||
if (level.info != mLastLevel && (level.nodes.Size() != mLastNumNodes || level.segs.Size() != mLastNumSegs))
|
||||
Clear();
|
||||
if (!ValidateAABBTree())
|
||||
{
|
||||
int oldBinding = 0;
|
||||
glGetIntegerv(GL_SHADER_STORAGE_BUFFER_BINDING, &oldBinding);
|
||||
|
||||
if (mAABBTree)
|
||||
return;
|
||||
glGenBuffers(1, (GLuint*)&mNodesBuffer);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, mNodesBuffer);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(hwrenderer::AABBTreeNode) * mAABBTree->nodes.Size(), &mAABBTree->nodes[0], GL_STATIC_DRAW);
|
||||
|
||||
mAABBTree.reset(new hwrenderer::LevelAABBTree());
|
||||
glGenBuffers(1, (GLuint*)&mLinesBuffer);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, mLinesBuffer);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(hwrenderer::AABBTreeLine) * mAABBTree->lines.Size(), &mAABBTree->lines[0], GL_STATIC_DRAW);
|
||||
|
||||
int oldBinding = 0;
|
||||
glGetIntegerv(GL_SHADER_STORAGE_BUFFER_BINDING, &oldBinding);
|
||||
|
||||
glGenBuffers(1, (GLuint*)&mNodesBuffer);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, mNodesBuffer);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(hwrenderer::AABBTreeNode) * mAABBTree->nodes.Size(), &mAABBTree->nodes[0], GL_STATIC_DRAW);
|
||||
|
||||
glGenBuffers(1, (GLuint*)&mLinesBuffer);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, mLinesBuffer);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(hwrenderer::AABBTreeLine) * mAABBTree->lines.Size(), &mAABBTree->lines[0], GL_STATIC_DRAW);
|
||||
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, oldBinding);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, oldBinding);
|
||||
}
|
||||
}
|
||||
|
||||
void FShadowMap::Clear()
|
||||
|
|
@ -247,10 +126,4 @@ void FShadowMap::Clear()
|
|||
glDeleteBuffers(1, (GLuint*)&mLinesBuffer);
|
||||
mLinesBuffer = 0;
|
||||
}
|
||||
|
||||
mAABBTree.reset();
|
||||
|
||||
mLastLevel = level.info;
|
||||
mLastNumNodes = level.nodes.Size();
|
||||
mLastNumSegs = level.segs.Size();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue