- Backend update from GZDoom.

This commit is contained in:
Christoph Oelckers 2023-01-15 09:30:01 +01:00
parent 8001c4041f
commit fca0bdf379
16 changed files with 310 additions and 59 deletions

View file

@ -422,7 +422,7 @@ void FFont::ReadSheetFont(TArray<FolderEntry> &folderdata, int width, int height
{
for (int x = 0; x < numtex_x; x++)
{
auto image = new FSheetTexture(sheetBitmaps.Size() - 1, x * width, y * width, width, height);
auto image = new FSheetTexture(sheetBitmaps.Size() - 1, x * width, y * height, width, height);
FImageTexture *imgtex = new FImageTexture(image);
auto gtex = MakeGameTexture(imgtex, nullptr, ETextureType::FontChar);
gtex->SetWorldPanning(true);

View file

@ -235,6 +235,8 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char *
float uClipHeight;
float uClipHeightDirection;
int uShadowmapFilter;
int uLightBlendMode;
};
uniform int uTextureMode;
@ -328,6 +330,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char *
uniform sampler2D texture9;
uniform sampler2D texture10;
uniform sampler2D texture11;
uniform sampler2D texture12;
// timer data
uniform float timer;

View file

@ -91,6 +91,7 @@ void HWViewpointBuffer::Set2D(FRenderState &di, int width, int height, int pll)
matrices.mPalLightLevels = pll;
matrices.mClipLine.X = -10000000.0f;
matrices.mShadowmapFilter = gl_shadowmap_filter;
matrices.mLightBlendMode = 0;
matrices.mProjectionMatrix.ortho(0, (float)width, (float)height, 0, -1.0f, 1.0f);
matrices.CalcDependencies();

View file

@ -4,6 +4,15 @@
struct HWDrawInfo;
enum class ELightBlendMode : uint8_t
{
CLAMP = 0,
CLAMP_COLOR = 1,
NOCLAMP = 2,
DEFAULT = CLAMP,
};
struct HWViewpointUniforms
{
VSMatrix mProjectionMatrix;
@ -19,6 +28,8 @@ struct HWViewpointUniforms
float mClipHeightDirection = 0.f;
int mShadowmapFilter = 1;
int mLightBlendMode = 0;
void CalcDependencies()
{
mNormalViewMatrix.computeNormalMatrix(mViewMatrix);

View file

@ -570,7 +570,8 @@ void PPColormap::Render(PPRenderState *renderstate, int fixedcm, float flash)
void PPTonemap::UpdateTextures()
{
if (gl_tonemap == Palette && !PaletteTexture.Data)
// level.info->tonemap cannot be ETonemapMode::Palette, so it's fine to only check gl_tonemap here
if (ETonemapMode((int)gl_tonemap) == ETonemapMode::Palette && !PaletteTexture.Data)
{
std::shared_ptr<void> data(new uint32_t[512 * 512], [](void *p) { delete[](uint32_t*)p; });
@ -598,7 +599,9 @@ void PPTonemap::UpdateTextures()
void PPTonemap::Render(PPRenderState *renderstate)
{
if (gl_tonemap == 0)
ETonemapMode current_tonemap = (level_tonemap != ETonemapMode::None) ? level_tonemap : ETonemapMode((int)gl_tonemap);
if (current_tonemap == ETonemapMode::None)
{
return;
}
@ -606,14 +609,14 @@ void PPTonemap::Render(PPRenderState *renderstate)
UpdateTextures();
PPShader *shader = nullptr;
switch (gl_tonemap)
switch (current_tonemap)
{
default:
case Linear: shader = &LinearShader; break;
case Reinhard: shader = &ReinhardShader; break;
case HejlDawson: shader = &HejlDawsonShader; break;
case Uncharted2: shader = &Uncharted2Shader; break;
case Palette: shader = &PaletteShader; break;
case ETonemapMode::Linear: shader = &LinearShader; break;
case ETonemapMode::Reinhard: shader = &ReinhardShader; break;
case ETonemapMode::HejlDawson: shader = &HejlDawsonShader; break;
case ETonemapMode::Uncharted2: shader = &Uncharted2Shader; break;
case ETonemapMode::Palette: shader = &PaletteShader; break;
}
renderstate->PushGroup("tonemap");
@ -622,7 +625,7 @@ void PPTonemap::Render(PPRenderState *renderstate)
renderstate->Shader = shader;
renderstate->Viewport = screen->mScreenViewport;
renderstate->SetInputCurrent(0);
if (gl_tonemap == Palette)
if (current_tonemap == ETonemapMode::Palette)
renderstate->SetInputTexture(1, &PaletteTexture);
renderstate->SetOutputNext();
renderstate->SetNoBlend();

View file

@ -13,6 +13,19 @@ typedef IntRect PPViewport;
class PPTexture;
class PPShader;
enum class ETonemapMode : uint8_t
{
None,
Uncharted2,
HejlDawson,
Reinhard,
Linear,
Palette,
NumTonemapModes
};
enum class PPFilterMode { Nearest, Linear };
enum class PPWrapMode { Clamp, Repeat };
enum class PPTextureType { CurrentPipelineTexture, NextPipelineTexture, PPTexture, SceneColor, SceneFog, SceneNormal, SceneDepth, SwapChain, ShadowMap };
@ -541,6 +554,7 @@ private:
class PPTonemap
{
public:
void SetTonemapMode(ETonemapMode tm) { level_tonemap = tm; }
void Render(PPRenderState *renderstate);
void ClearTonemapPalette() { PaletteTexture = {}; }
@ -554,17 +568,7 @@ private:
PPShader HejlDawsonShader = { "shaders/pp/tonemap.fp", "#define HEJLDAWSON\n", {} };
PPShader Uncharted2Shader = { "shaders/pp/tonemap.fp", "#define UNCHARTED2\n", {} };
PPShader PaletteShader = { "shaders/pp/tonemap.fp", "#define PALETTE\n", {} };
enum TonemapMode
{
None,
Uncharted2,
HejlDawson,
Reinhard,
Linear,
Palette,
NumTonemapModes
};
ETonemapMode level_tonemap = ETonemapMode::None;
};
/////////////////////////////////////////////////////////////////////////////
@ -840,8 +844,11 @@ public:
PPCustomShaders customShaders;
void SetTonemapMode(ETonemapMode tm) { tonemap.SetTonemapMode(tm); }
void Pass1(PPRenderState *state, int fixedcm, int sceneWidth, int sceneHeight);
void Pass2(PPRenderState* state, int fixedcm, float flash, int sceneWidth, int sceneHeight);
};
extern Postprocess hw_postprocess;

View file

@ -175,6 +175,8 @@ static const char *shaderBindings = R"(
float uClipHeight;
float uClipHeightDirection;
int uShadowmapFilter;
int uLightBlendMode;
};
layout(set = 1, binding = 1, std140) uniform MatricesUBO {
@ -244,6 +246,7 @@ static const char *shaderBindings = R"(
layout(set = 2, binding = 8) uniform sampler2D texture9;
layout(set = 2, binding = 9) uniform sampler2D texture10;
layout(set = 2, binding = 10) uniform sampler2D texture11;
layout(set = 2, binding = 11) uniform sampler2D texture12;
// This must match the PushConstants struct
layout(push_constant) uniform PushConstants

View file

@ -438,10 +438,12 @@ template<typename I> void MapIteratorSetValue(I * self, expand_types_vm<typename
PARAM_SELF_STRUCT_PROLOGUE( FMapIterator_I32_Str ); \
ACTION_RETURN_INT( MapIteratorGetKey(self) ); \
} \
DEFINE_ACTION_FUNCTION_NATIVE( FMapIterator_I32_Str , GetValue , MapIteratorGetValue< FMapIterator_I32_Str > ) \
DEFINE_ACTION_FUNCTION_NATIVE( FMapIterator_I32_Str , GetValue , MapIteratorGetValueString< FMapIterator_I32_Str > ) \
{ \
PARAM_SELF_STRUCT_PROLOGUE( FMapIterator_I32_Str ); \
ACTION_RETURN_STRING( MapIteratorGetValue(self) ); \
FString out; \
MapIteratorGetValueString(self , out); \
ACTION_RETURN_STRING( out ); \
}
#define DEF_MAP_IT_S_S() \

View file

@ -133,13 +133,16 @@ FMaterial::FMaterial(FGameTexture * tx, int scaleflags)
if (index >= FIRST_USER_SHADER)
{
const UserShaderDesc& usershader = usershaders[index - FIRST_USER_SHADER];
if (tx->Layers && usershader.shaderType == mShaderIndex) // Only apply user shader if it matches the expected material
if (usershader.shaderType == mShaderIndex) // Only apply user shader if it matches the expected material
{
if (tx->Layers)
{
for (auto& texture : tx->Layers->CustomShaderTextures)
{
if (texture == nullptr) continue;
mTextureLayers.Push({ texture.get(), 0 }); // scalability should be user-definable.
}
}
mShaderIndex = index;
}
}

View file

@ -418,13 +418,16 @@ void MakeRemap(uint32_t* BaseColors, const uint32_t* colors, uint8_t* remap, con
// color, so find a duplicate pair of palette entries, make one of them a
// duplicate of color 0, and remap every graphic so that it uses that entry
// instead of entry 0.
void MakeGoodRemap(uint32_t* BaseColors, uint8_t* Remap)
void MakeGoodRemap(uint32_t* BaseColors, uint8_t* Remap, const uint8_t* lastcolormap)
{
for (int i = 0; i < 256; i++) Remap[i] = i;
PalEntry color0 = BaseColors[0];
int i;
// First try for an exact match of color 0. Only Hexen does not have one.
if (!lastcolormap)
{
for (i = 1; i < 256; ++i)
{
if (BaseColors[i] == color0)
@ -433,6 +436,18 @@ void MakeGoodRemap(uint32_t* BaseColors, uint8_t* Remap)
break;
}
}
}
else
{
for (i = 1; i < 256; ++i)
{
if ((BaseColors[i] == color0) && (lastcolormap[i] == lastcolormap[0]))
{
Remap[0] = i;
break;
}
}
}
// If there is no duplicate of color 0, find the first set of duplicate
// colors and make one of them a duplicate of color 0. In Hexen's PLAYPAL
@ -448,6 +463,8 @@ void MakeGoodRemap(uint32_t* BaseColors, uint8_t* Remap)
sortcopy[i] = (BaseColors[i] & 0xffffff) | (i << 24);
}
qsort(sortcopy, 256, 4, sortforremap);
if (!lastcolormap)
{
for (i = 255; i > 0; --i)
{
if ((sortcopy[i] & 0xFFFFFF) == (sortcopy[i - 1] & 0xFFFFFF))
@ -466,6 +483,27 @@ void MakeGoodRemap(uint32_t* BaseColors, uint8_t* Remap)
}
}
}
else
{
for (i = 255; i > 0; --i)
{
if (((sortcopy[i] & 0xFFFFFF) == (sortcopy[i - 1] & 0xFFFFFF)) && (lastcolormap[sortcopy[i].a] == lastcolormap[sortcopy[i - 1].a]))
{
int new0 = sortcopy[i].a;
int dup = sortcopy[i - 1].a;
if (new0 > dup)
{
// Make the lower-numbered entry a copy of color 0. (Just because.)
std::swap(new0, dup);
}
Remap[0] = new0;
Remap[new0] = dup;
BaseColors[new0] = color0;
break;
}
}
}
}
// If there were no duplicates, InitPalette() will remap color 0 to the
// closest matching color. Hopefully nobody will use a palette where all

View file

@ -14,7 +14,7 @@ void DoBlending(const PalEntry* from, PalEntry* to, int count, int r, int g, int
// Given an array of colors, fills in remap with values to remap the
// passed array of colors to BaseColors. Used for loading palette downconversions of PNGs.
void MakeRemap(uint32_t* BaseColors, const uint32_t* colors, uint8_t* remap, const uint8_t* useful, int numcolors);
void MakeGoodRemap(uint32_t* BaseColors, uint8_t* Remap);
void MakeGoodRemap(uint32_t* BaseColors, uint8_t* Remap, const uint8_t* cmapdata = nullptr);
// Colorspace conversion RGB <-> HSV
void RGBtoHSV (float r, float g, float b, float *h, float *s, float *v);

View file

@ -250,6 +250,7 @@ void HWDrawInfo::SetupView(FRenderState &state, float vx, float vy, float vz, bo
SetViewMatrix(vp.HWAngles, vx, vy, vz, mirror, planemirror);
SetCameraPos(vp.Pos);
VPUniforms.CalcDependencies();
VPUniforms.mLightBlendMode = 0;
vpIndex = screen->mViewpoints->SetViewpoint(state, &VPUniforms);
}

View file

@ -325,7 +325,7 @@ float R_DoomLightingEquation(float light)
// This is a lot more primitive than Doom's lighting...
float numShades = float(uPalLightLevels & 255);
float curshade = (1.0 - light) * (numShades - 1.0);
float visibility = max(uGlobVis * uLightFactor * abs(z), 0.0);
float visibility = max(uGlobVis * uLightFactor * z, 0.0);
float shade = clamp((curshade + visibility), 0.0, numShades - 1.0);
return clamp(shade * uLightDist, 0.0, 1.0);
}
@ -344,10 +344,86 @@ float R_DoomLightingEquation(float light)
//===========================================================================
//
// Check if light is in shadow according to its 1D shadow map
// Check if light is in shadow
//
//===========================================================================
#ifdef SUPPORTS_RAYTRACING
bool traceHit(vec3 origin, vec3 direction, float dist)
{
rayQueryEXT rayQuery;
rayQueryInitializeEXT(rayQuery, TopLevelAS, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF, origin, 0.01f, direction, dist);
while(rayQueryProceedEXT(rayQuery)) { }
return rayQueryGetIntersectionTypeEXT(rayQuery, true) != gl_RayQueryCommittedIntersectionNoneEXT;
}
vec2 softshadow[9 * 3] = vec2[](
vec2( 0.0, 0.0),
vec2(-2.0,-2.0),
vec2( 2.0, 2.0),
vec2( 2.0,-2.0),
vec2(-2.0, 2.0),
vec2(-1.0,-1.0),
vec2( 1.0, 1.0),
vec2( 1.0,-1.0),
vec2(-1.0, 1.0),
vec2( 0.0, 0.0),
vec2(-1.5,-1.5),
vec2( 1.5, 1.5),
vec2( 1.5,-1.5),
vec2(-1.5, 1.5),
vec2(-0.5,-0.5),
vec2( 0.5, 0.5),
vec2( 0.5,-0.5),
vec2(-0.5, 0.5),
vec2( 0.0, 0.0),
vec2(-1.25,-1.75),
vec2( 1.75, 1.25),
vec2( 1.25,-1.75),
vec2(-1.75, 1.75),
vec2(-0.75,-0.25),
vec2( 0.25, 0.75),
vec2( 0.75,-0.25),
vec2(-0.25, 0.75)
);
float shadowAttenuation(vec4 lightpos, float lightcolorA)
{
float shadowIndex = abs(lightcolorA) - 1.0;
if (shadowIndex >= 1024.0)
return 1.0; // Don't cast rays for this light
vec3 origin = pixelpos.xzy;
vec3 target = lightpos.xzy + 0.01; // nudge light position slightly as Doom maps tend to have their lights perfectly aligned with planes
vec3 direction = normalize(target - origin);
float dist = distance(origin, target);
if (uShadowmapFilter <= 0)
{
return traceHit(origin, direction, dist) ? 0.0 : 1.0;
}
else
{
vec3 v = (abs(direction.x) > abs(direction.y)) ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);
vec3 xdir = normalize(cross(direction, v));
vec3 ydir = cross(direction, xdir);
float sum = 0.0;
int step_count = uShadowmapFilter * 9;
for (int i = 0; i <= step_count; i++)
{
vec3 pos = target + xdir * softshadow[i].x + ydir * softshadow[i].y;
sum += traceHit(origin, normalize(pos - origin), dist) ? 0.0 : 1.0;
}
return sum / step_count;
}
}
#else
#ifdef SUPPORTS_SHADOWMAPS
float shadowDirToU(vec2 dir)
@ -491,6 +567,7 @@ float shadowAttenuation(vec4 lightpos, float lightcolorA)
return 1.0;
}
#endif
#endif
float spotLightAttenuation(vec4 lightpos, vec3 spotdir, float lightCosInnerAngle, float lightCosOuterAngle)

View file

@ -5,11 +5,15 @@ layout(location = 2) in vec4 aColor;
layout(location = 0) out vec4 vTexCoord;
layout(location = 1) out vec4 vColor;
layout(location = 9) out vec3 vLightmap;
#ifndef SIMPLE // we do not need these for simple shaders
layout(location = 3) in vec4 aVertex2;
layout(location = 4) in vec4 aNormal;
layout(location = 5) in vec4 aNormal2;
layout(location = 6) in vec3 aLightmap;
layout(location = 7) in vec4 aBoneWeight;
layout(location = 8) in uvec4 aBoneSelector;
layout(location = 2) out vec4 pixelpos;
layout(location = 3) out vec3 glowdist;
@ -23,6 +27,14 @@ layout(location = 7) out vec4 ClipDistanceA;
layout(location = 8) out vec4 ClipDistanceB;
#endif
struct BonesResult
{
vec3 Normal;
vec4 Position;
};
BonesResult ApplyBones();
void main()
{
float ClipDistance0, ClipDistance1, ClipDistance2, ClipDistance3, ClipDistance4;
@ -30,8 +42,10 @@ void main()
vec2 parmTexCoord;
vec4 parmPosition;
BonesResult bones = ApplyBones();
parmTexCoord = aTexCoord;
parmPosition = aPosition;
parmPosition = bones.Position;
#ifndef SIMPLE
vec4 worldcoord = ModelMatrix * mix(parmPosition, aVertex2, uInterpolationFactor);
@ -51,6 +65,8 @@ void main()
#endif
#ifndef SIMPLE
vLightmap = aLightmap;
pixelpos.xyz = worldcoord.xyz;
pixelpos.w = -eyeCoordPos.z/eyeCoordPos.w;
@ -78,14 +94,7 @@ void main()
ClipDistance4 = worldcoord.y - ((uSplitBottomPlane.w + uSplitBottomPlane.x * worldcoord.x + uSplitBottomPlane.y * worldcoord.z) * uSplitBottomPlane.z);
}
#ifdef HAS_UNIFORM_VERTEX_DATA
if ((useVertexData & 2) == 0)
vWorldNormal = NormalModelMatrix * vec4(normalize(uVertexNormal.xyz), 1.0);
else
vWorldNormal = NormalModelMatrix * vec4(normalize(mix(aNormal.xyz, aNormal2.xyz, uInterpolationFactor)), 1.0);
#else
vWorldNormal = NormalModelMatrix * vec4(normalize(mix(aNormal.xyz, aNormal2.xyz, uInterpolationFactor)), 1.0);
#endif
vWorldNormal = NormalModelMatrix * vec4(normalize(bones.Normal), 1.0);
vEyeNormal = NormalViewMatrix * vec4(normalize(vWorldNormal.xyz), 1.0);
#endif
@ -142,3 +151,66 @@ void main()
gl_PointSize = 1.0;
}
#if !defined(SIMPLE)
vec3 GetAttrNormal()
{
#ifdef HAS_UNIFORM_VERTEX_DATA
if ((useVertexData & 2) == 0)
return uVertexNormal.xyz;
else
return mix(aNormal.xyz, aNormal2.xyz, uInterpolationFactor);
#else
return mix(aNormal.xyz, aNormal2.xyz, uInterpolationFactor);
#endif
}
void AddWeightedBone(uint boneIndex, float weight, inout vec4 position, inout vec3 normal)
{
if (weight != 0.0)
{
mat4 transform = bones[uBoneIndexBase + int(boneIndex)];
mat3 rotation = mat3(transform);
position += (transform * aPosition) * weight;
normal += (rotation * aNormal.xyz) * weight;
}
}
BonesResult ApplyBones()
{
BonesResult result;
if (uBoneIndexBase >= 0 && aBoneWeight != vec4(0.0))
{
result.Position = vec4(0.0);
result.Normal = vec3(0.0);
// We use low precision input for our bone weights. Rescale so the sum still is 1.0
float totalWeight = aBoneWeight.x + aBoneWeight.y + aBoneWeight.z + aBoneWeight.w;
float weightMultiplier = 1.0 / totalWeight;
vec4 boneWeight = aBoneWeight * weightMultiplier;
AddWeightedBone(aBoneSelector.x, boneWeight.x, result.Position, result.Normal);
AddWeightedBone(aBoneSelector.y, boneWeight.y, result.Position, result.Normal);
AddWeightedBone(aBoneSelector.z, boneWeight.z, result.Position, result.Normal);
AddWeightedBone(aBoneSelector.w, boneWeight.w, result.Position, result.Normal);
result.Position.w = 1.0; // For numerical stability
}
else
{
result.Position = aPosition;
result.Normal = GetAttrNormal();
}
return result;
}
#else
BonesResult ApplyBones()
{
BonesResult result;
result.Position = aPosition;
return result;
}
#endif

View file

@ -59,7 +59,21 @@ vec3 ProcessMaterialLight(Material material, vec3 color)
}
}
vec3 frag = material.Base.rgb * clamp(color + desaturate(dynlight).rgb, 0.0, 1.4);
vec3 frag;
if ( uLightBlendMode == 1 )
{ // COLOR_CORRECT_CLAMPING
vec3 lightcolor = color + desaturate(dynlight).rgb;
frag = material.Base.rgb * ((lightcolor / max(max(max(lightcolor.r, lightcolor.g), lightcolor.b), 1.4) * 1.4));
}
else if ( uLightBlendMode == 2 )
{ // UNCLAMPED
frag = material.Base.rgb * (color + desaturate(dynlight).rgb);
}
else
{
frag = material.Base.rgb * clamp(color + desaturate(dynlight).rgb, 0.0, 1.4);
}
if (uLightIndex >= 0)
{

View file

@ -67,8 +67,24 @@ vec3 ProcessMaterialLight(Material material, vec3 color)
}
}
if ( uLightBlendMode == 1 )
{ // COLOR_CORRECT_CLAMPING
dynlight.rgb = color + desaturate(dynlight).rgb;
specular.rgb = desaturate(specular).rgb;
dynlight.rgb = ((dynlight.rgb / max(max(max(dynlight.r, dynlight.g), dynlight.b), 1.4) * 1.4));
specular.rgb = ((specular.rgb / max(max(max(specular.r, specular.g), specular.b), 1.4) * 1.4));
}
else if ( uLightBlendMode == 2 )
{ // UNCLAMPED
dynlight.rgb = color + desaturate(dynlight).rgb;
specular.rgb = desaturate(specular).rgb;
}
else
{
dynlight.rgb = clamp(color + desaturate(dynlight).rgb, 0.0, 1.4);
specular.rgb = clamp(desaturate(specular).rgb, 0.0, 1.4);
}
vec3 frag = material.Base.rgb * dynlight.rgb + material.Specular * specular.rgb;