Skip to content

Commit

Permalink
Decals fixes part#2 (#1355)
Browse files Browse the repository at this point in the history
* Fix and sanitize decals code a little bit.
  • Loading branch information
lhog authored Mar 10, 2024
1 parent f35333a commit 7036aef
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 58 deletions.
70 changes: 49 additions & 21 deletions cont/base/springcontent/shaders/GLSL/GroundDecalsFragProg.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,17 @@ uniform mat4 shadowMatrix;

uniform float curAdjustedFrame;

flat in vec3 vPosTL;
flat in vec3 vPosTR;
flat in vec3 vPosBL;
flat in vec3 vPosBR;
flat in vec4 vPosTL;
flat in vec4 vPosTR;
flat in vec4 vPosBL;
flat in vec4 vPosBR;

flat in vec4 vuvMain;
flat in vec4 vuvNorm;
flat in vec4 midPoint;
in vec4 misc; //misc.x - alpha & glow, misc.y - height, misc.z - uvWrapDistance, misc.w - distance from left // can't be flat because of misc.x
flat in vec4 misc2; // groundNormal.xyz, decalTypeAsFloat
flat in vec3 xDir;

#define groundNormal misc2.xyz
flat in vec4 misc2; // {3xempty}, decalTypeAsFloat
flat in mat3 rotMat;

out vec4 fragColor;

Expand All @@ -65,7 +63,7 @@ out vec4 fragColor;
#define NORM2SNORM(value) (value * 2.0 - 1.0)
#define SNORM2NORM(value) (value * 0.5 + 0.5)

#line 200068
#line 200066

vec3 GetTriangleBarycentric(vec3 p, vec3 p0, vec3 p1, vec3 p2) {
vec3 v0 = p2 - p0;
Expand Down Expand Up @@ -262,6 +260,28 @@ vec3 RotateByNormalVector(vec3 p, vec3 newUpDir, vec3 rotAxis) {
#undef nz
}

/*
bool IsInEllipsoid(vec3 xyz, vec3 abc) {
#if 1
float A = xyz.x * abc.y * abc.z;
float B = xyz.y * abc.x * abc.z;
float C = xyz.z * abc.x * abc.y;
float D = abc.x * abc.y * abc.z;
return A * A + B * B + C * C <= D * D;
#else
return ((xyz.x*xyz.x) / (abc.x*abc.x) + (xyz.y*xyz.y) / (abc.y*abc.y) + (xyz.z*xyz.z) / (abc.z*abc.z) <= 1.0);
#endif
}
*/
float EllipsoidRedunction(vec3 xyz, vec3 abc) {
float s = (xyz.x*xyz.x) / (abc.x*abc.x) + (xyz.y*xyz.y) / (abc.y*abc.y) + (xyz.z*xyz.z) / (abc.z*abc.z);
return clamp(2.0 - max(s, 1.0), 0.0, 1.0);
}


const float DECAL_EXPLOSION = 2.0;

const float SMF_INTENSITY_MULT = 210.0 / 255.0;
const float SMF_SHALLOW_WATER_DEPTH = 10.0;
const float SMF_SHALLOW_WATER_DEPTH_INV = 1.0 / SMF_SHALLOW_WATER_DEPTH;
Expand All @@ -278,7 +298,11 @@ void main() {

vec3 worldPos = GetWorldPos(gl_FragCoord.xy * screenSizeInverse, depthZO);

vec3 worldPosProj = worldPos - dot(worldPos - midPoint.xyz, groundNormal) * groundNormal;
//vec3 rotWorldPos = transpose(rotMat) * (worldPos - midPoint.xyz);
//if (misc2.w == DECAL_EXPLOSION && !IsInEllipsoid(rotWorldPos, vec3(vPosTL.w, misc.y, vPosTR.w)))
// discard;

vec3 worldPosProj = worldPos - dot(worldPos - midPoint.xyz, rotMat[1]) * rotMat[1];

vec4 uvBL = vec4(uvMainBL, uvNormBL);
vec4 uvTL = vec4(uvMainTL, uvNormTL);
Expand All @@ -287,14 +311,13 @@ void main() {

float u = 1.0;
if (misc.z > 0.0) {
u = distance((vPosTL + vPosBL) * 0.5, (vPosBR + vPosTR) * 0.5) / misc.z;
//u = distance(vPosTL, vPosTR) / misc.z;
u = distance((vPosTL.xyz + vPosBL.xyz) * 0.5, (vPosBR.xyz + vPosTR.xyz) * 0.5) / misc.z;
}

vec4 relUV;
bool disc = true;
{
vec3 bc = GetTriangleBarycentric(worldPosProj, vPosBL, vPosTL, vPosTR);
vec3 bc = GetTriangleBarycentric(worldPosProj, vPosBL.xyz, vPosTL.xyz, vPosTR.xyz);
if (all(greaterThanEqual(bc, all0)) && all(lessThanEqual(bc, all1))) {
disc = false;
//uv = bc.x * uvBL + bc.y * uvTL + bc.z * uvTR;
Expand All @@ -303,7 +326,7 @@ void main() {
}
if (disc)
{
vec3 bc = GetTriangleBarycentric(worldPosProj, vPosTR, vPosBR, vPosBL);
vec3 bc = GetTriangleBarycentric(worldPosProj, vPosTR.xyz, vPosBR.xyz, vPosBL.xyz);
if (all(greaterThanEqual(bc, all0)) && all(lessThanEqual(bc, all1))) {
disc = false;
//uv = bc.x * uvTR + bc.y * uvBR + bc.z * uvBL;
Expand Down Expand Up @@ -361,13 +384,13 @@ void main() {
#else
vec3 mapDecalMix = 2.0 * mainCol.rgb * mapDiffuse.rgb;
#endif
mainCol.rgb = mix(mainCol.rgb, mapDecalMix, float(misc2.w == 2.0/*DECAL_EXPLOSION*/)); //only apply mapDecalMix for explosions
mainCol.rgb = mix(mainCol.rgb, mapDecalMix, float(misc2.w == DECAL_EXPLOSION)); //only apply mapDecalMix for explosions

vec3 N = GetFragmentNormal(worldPos.xz);

#if 0
// Expensive processing
vec3 T = xDir; //tangent if N was (0,1,0)
vec3 T = rotMat[0]; //tangent if N was (0,1,0)

if (1.0 - N.y > 0.01) {
// rotAxis is cross(Upvector, N), but Upvector is known to be (0, 1, 0), so simplify
Expand All @@ -376,7 +399,7 @@ void main() {
}
#else
// Cheaper Gramm-Schmidt
vec3 T = normalize(xDir - N * dot(xDir, N));
vec3 T = normalize(rotMat[0] - N * dot(rotMat[0], N));
#endif

vec3 B = normalize(cross(N, T));
Expand Down Expand Up @@ -420,9 +443,14 @@ void main() {
#endif

fragColor.a = mainCol.a * alpha;

vec3 rotWorldPos = transpose(rotMat) * (worldPos - midPoint.xyz);
//fragColor.xyz=vec3(1);
fragColor.a *= mix(1.0, EllipsoidRedunction(rotWorldPos, vec3(vPosTL.w, misc.y, vPosTR.w)), float(misc2.w == DECAL_EXPLOSION));
// artistic adjustments
//fragColor *= pow(max(dot(groundNormal, N), 0.0), 1.5); // MdotL^1.5 is arbitrary
fragColor.a *=
smoothstep(0.0, EPS, relUV.x) * (1.0 - smoothstep(1.0 - EPS, 1.0, relUV.x)) *
smoothstep(0.0, EPS, relUV.y) * (1.0 - smoothstep(1.0 - EPS, 1.0, relUV.y));
//fragColor *= pow(max(dot(rotMat[1], N), 0.0), 1.5); // MdotL^1.5 is arbitrary
//fragColor = vec4(1);
//fragColor.a *=
// smoothstep(0.0, EPS, relUV.x) * (1.0 - smoothstep(1.0 - EPS, 1.0, relUV.x)) *
// smoothstep(0.0, EPS, relUV.y) * (1.0 - smoothstep(1.0 - EPS, 1.0, relUV.y));
}
56 changes: 30 additions & 26 deletions cont/base/springcontent/shaders/GLSL/GroundDecalsVertProg.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,17 @@ uniform sampler2D groundNormalTex;
uniform vec4 mapDims; //mapxy; 1.0 / mapxy
uniform float curAdjustedFrame;

flat out vec3 vPosTL;
flat out vec3 vPosTR;
flat out vec3 vPosBL;
flat out vec3 vPosBR;
flat out vec4 vPosTL;
flat out vec4 vPosTR;
flat out vec4 vPosBL;
flat out vec4 vPosBR;

flat out vec4 vuvMain;
flat out vec4 vuvNorm;
flat out vec4 midPoint;
out vec4 misc; //misc.x - alpha & glow, misc.y - height, misc.z - uvWrapDistance, misc.w - uvOffset // can't be flat because of misc.x
flat out vec4 misc2; // groundNormal.xyz, decalTypeAsFloat
flat out vec3 xDir;

#define groundNormal misc2.xyz
flat out vec4 misc2; // {3xempty}, decalTypeAsFloat
flat out mat3 rotMat;

#define NORM2SNORM(value) (value * 2.0 - 1.0)
#define SNORM2NORM(value) (value * 0.5 + 0.5)
Expand Down Expand Up @@ -135,6 +133,8 @@ vec3 RotateByNormalVector(vec3 p, vec3 newUpDir, vec3 rotAxis) {
#undef nz
}

const float DECAL_EXPLOSION = 2.0;

// uv ==> L, T, R, B
void main() {
vec3 relPos = CUBE_VERT[gl_VertexID];
Expand All @@ -148,14 +148,14 @@ void main() {
misc2.w = float(typeAndId & 0xFu); //copy type only, don't care about ID

// emulate explosion fade in for the first 6 frames, asjusted by the initial alpha (less fadein for already weak scars)
misc.x *= mix(1.0, smoothstep(0.0, 6.0 * info.x, curAdjustedFrame - thisVertexCreateFrame), float(misc2.w == 2.0/*DECAL_EXPLOSION*/));
misc.x *= mix(1.0, smoothstep(0.0, 6.0 * info.x, curAdjustedFrame - thisVertexCreateFrame), float(misc2.w == DECAL_EXPLOSION));

#if 1
if (alphaMax <= 0.0 || misc2.w <= 0.0) {
vPosTL = vec3(0);
vPosTR = vec3(0);
vPosBL = vec3(0);
vPosBR = vec3(0);
vPosTL = vec4(0);
vPosTR = vec4(0);
vPosBL = vec4(0);
vPosBR = vec4(0);
gl_Position = vec4(2.0, 2.0, 2.0, 1.0); //place outside of [-1;1]^3 NDC, basically cull out from the further rendering
return;
}
Expand All @@ -173,6 +173,7 @@ void main() {
midPoint.w *= 1.22474;

// groundNormal
vec3 groundNormal = vec3(0);
if (dot(forcedNormalAndAlphaMult.xyz, forcedNormalAndAlphaMult.xyz) == 0.0) {
groundNormal = vec3(0.0);
groundNormal += 2.0 * GetFragmentNormal(midPoint.xz);
Expand All @@ -186,26 +187,29 @@ void main() {
}

// get 2D orthonormal system
xDir = vec3( ca, 0.0, sa);
vec3 zDir = vec3(-sa, 0.0, ca);
vec3 xDir = vec3( ca, 0.0, sa);

// don't rotate almost vertical cubes
if (1.0 - groundNormal.y > 0.05) {
// rotAxis is cross(Upvector, N), but Upvector is known to be (0, 1, 0), so simplify
vec3 rotAxis = normalize(vec3(groundNormal.z, 0.0, -groundNormal.x));
xDir = RotateByNormalVector(xDir, groundNormal, rotAxis);
zDir = normalize(cross(xDir, groundNormal));
}
vec3 zDir = normalize(cross(xDir, groundNormal));

// orthonormal system
mat3 ONS = mat3(xDir, groundNormal, zDir);
//ONS = mat3(1);
rotMat = mat3(xDir, groundNormal, zDir);

// absolute world coords, already rotated in all possible ways
vPosTL = ONS * (vec3(posT.x, 0.0, posT.y) - vec3(midPoint.x, 0.0, midPoint.z)) + midPoint.xyz;
vPosTR = ONS * (vec3(posT.z, 0.0, posT.w) - vec3(midPoint.x, 0.0, midPoint.z)) + midPoint.xyz;
vPosBR = ONS * (vec3(posB.x, 0.0, posB.y) - vec3(midPoint.x, 0.0, midPoint.z)) + midPoint.xyz;
vPosBL = ONS * (vec3(posB.z, 0.0, posB.w) - vec3(midPoint.x, 0.0, midPoint.z)) + midPoint.xyz;
vPosTL.xyz = rotMat * (vec3(posT.x, 0.0, posT.y) - vec3(midPoint.x, 0.0, midPoint.z)) + midPoint.xyz;
vPosTR.xyz = rotMat * (vec3(posT.z, 0.0, posT.w) - vec3(midPoint.x, 0.0, midPoint.z)) + midPoint.xyz;
vPosBR.xyz = rotMat * (vec3(posB.x, 0.0, posB.y) - vec3(midPoint.x, 0.0, midPoint.z)) + midPoint.xyz;
vPosBL.xyz = rotMat * (vec3(posB.z, 0.0, posB.w) - vec3(midPoint.x, 0.0, midPoint.z)) + midPoint.xyz;

vPosTL.w = distance(posT.zw, posT.xy) * 0.5;
vPosTR.w = distance(posB.xy, posT.zw) * 0.5;
vPosBR.w = distance(posB.zw, posB.xy) * 0.5;
vPosBL.w = distance(posT.xy, posB.zw) * 0.5;

vuvMain = uvMain;
vuvNorm = uvNorm;
Expand All @@ -218,10 +222,10 @@ void main() {
);

vec3 worldPos = vec3(0);
worldPos.xyz += testResults.x * vPosTL;
worldPos.xyz += testResults.y * vPosTR;
worldPos.xyz += testResults.z * vPosBR;
worldPos.xyz += testResults.w * vPosBL;
worldPos.xyz += testResults.x * vPosTL.xyz;
worldPos.xyz += testResults.y * vPosTR.xyz;
worldPos.xyz += testResults.z * vPosBR.xyz;
worldPos.xyz += testResults.w * vPosBL.xyz;

// effect's height
misc.y = info.w;
Expand Down
2 changes: 1 addition & 1 deletion rts/Rendering/Env/Decals/GroundDecal.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ struct GroundDecal {
DECAL_LUA = 4
};
public:
bool IsValid() const { return info.type > Type::DECAL_NONE || alpha > 0.0f; }
bool IsValid() const { return info.type > Type::DECAL_NONE; }
void MarkInvalid() { info.type = Type::DECAL_NONE; }
public:
float2 posTL;
Expand Down
27 changes: 17 additions & 10 deletions rts/Rendering/Env/Decals/GroundDecalHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,9 @@ void CGroundDecalHandler::AddExplosion(float3 pos, float3 explNormalVec, float d
if (radius < 5.0f)
return;

if (damage < 5.0f)
return;

damage = std::min(damage, radius * 30.0f);
damage *= (radius / (radius + altitude));
radius = std::min(radius, damage * 0.25f);
Expand All @@ -524,7 +527,7 @@ void CGroundDecalHandler::AddExplosion(float3 pos, float3 explNormalVec, float d
const auto normName = IntToString(scarIdx, "normscar_%i");

const auto createFrame = static_cast<float>(std::max(gs->frameNum, 0));
const auto height = argmax(size, maxHeightDiff) + 50.0f; // 50.0f is a leeway here
const auto height = argmax(size, maxHeightDiff);

const auto& decal = decals.emplace_back(GroundDecal{
.posTL = posTL,
Expand Down Expand Up @@ -1156,8 +1159,8 @@ void CGroundDecalHandler::CompactDecalsVector(int frameNum)
numToDelete++;
continue;
}
const auto targetExpirationFrame = static_cast<int>(decal.alpha / decal.alphaFalloff);
if (frameNum - decal.createFrameMax > targetExpirationFrame) {
const auto targetExpirationFrame = static_cast<int>(decal.alpha / std::max(decal.alphaFalloff, 1e-6f));
if (decal.info.type != GroundDecal::Type::DECAL_LUA && frameNum - decal.createFrameMax > targetExpirationFrame) {
decal.MarkInvalid();
numToDelete++;
}
Expand All @@ -1176,18 +1179,22 @@ void CGroundDecalHandler::CompactDecalsVector(int frameNum)
// after the compaction is complete
spring::unordered_map<uint32_t, DecalOwner> tmpOwnerToId;

// Remove owners of expired items
for (auto doIt = decalOwners.begin(); doIt != decalOwners.end(); /*NOOP*/) {
if (const auto& decal = decals[doIt->second]; decal.IsValid()) {
const uint32_t id = decals.at(doIt->second).info.id; //can't use bitfield directly below
tmpOwnerToId.emplace(id, doIt->first);
// avoid erasing while iterating
std::vector<DecalOwner> eraseList;

doIt++;
// Remove owners of expired items
for (const auto& [owner, pos] : decalOwners) {
if (const auto& decal = decals.at(pos); decal.IsValid()) {
const uint32_t id = decal.info.id; //can't use bitfield directly below
tmpOwnerToId.emplace(id, owner);
}
else {
doIt = decalOwners.erase(doIt);
eraseList.emplace_back(owner);
}
}
for (const auto& owner : eraseList) {
decalOwners.erase(owner);
}

// group all expired items towards the end of the vector
// Lua items are not considered expired
Expand Down

0 comments on commit 7036aef

Please sign in to comment.