Is it ok to calculate differences for every shadow light type as vec3s or it’s better to use if statements?

I’m implementing shadows in my game but I also want my shader to be as general as possible.

My first implementation had 3 different functions:

layout(binding = 0) uniform sampler2DArrayShadow u_dirShadowMap;
layout(binding = 1) uniform sampler2DArrayShadow u_pointShadowMap;
layout(binding = 2) uniform sampler2DArrayShadow u_spotShadowMap;

#define CASCADE_COUNT 4
#define POINT_LIGHT_FACE_COUNT 6

float DirShadow(const uint lightIndex, const vec3 worldPosition, const vec3 geoNormal, const vec3 lightDir, const float NoL)
{
    const uint textureID = (lightIndex * CASCADE_COUNT) + getShadowCascade(worldPosition);
    const mat4 vp = u_shadowData.viewProjection[textureID];
    const vec3 dirShadowMapCoords = getShadowUV(vp, worldPosition, geoNormal, lightDir, NoL, getDirTexelSizeWorldSpace(vp));
    return PCF_Shadow(dirShadowMapCoords.xy, dirShadowMapCoords.z, u_dirShadowMap, textureID);
}

float PointShadow(const uint lightIndex, const vec3 worldPosition, const vec3 lightPosition, const vec3 geoNormal, const float NoL)
{
    vec3 r = worldPosition - lightPosition;
    const uint face = getPointLightFace(r);
    const uint dirTextureCount = getDirLightCount() * CASCADE_COUNT;
    const uint shadowIndex = face + (lightIndex * POINT_LIGHT_FACE_COUNT);
    const uint matrixIndex = dirTextureCount + shadowIndex;

    const vec3 dir = normalize(lightPosition - worldPosition);
    const float texelSizeWorldSpace = getSpotTexelSizeWorldSpace(45.0f * DEG_TO_RAD);
    const vec3 pointShadowMapCoords = getShadowUV(u_shadowData.viewProjection[matrixIndex], worldPosition, geoNormal, dir, NoL, texelSizeWorldSpace);

    return PCF_Shadow(pointShadowMapCoords.xy, pointShadowMapCoords.z, u_pointShadowMap, shadowIndex);
}

float SpotShadow(const uint lightIndex, const vec3 worldPosition, const vec3 geoNormal, const vec3 lightDir, const float NoL, const float angle)
{
    const uint dirTextureCount = getDirLightCount() * CASCADE_COUNT;
    const uint pointTextureCount = getPointLightCount() * POINT_LIGHT_FACE_COUNT;
    const uint matrixIndex = dirTextureCount + pointTextureCount + lightIndex;
    const float texelSizeWorldSpace = getSpotTexelSizeWorldSpace(angle);

    const vec3 spotShadowMapCoords = getShadowUV(u_shadowData.viewProjection[matrixIndex], worldPosition, geoNormal, lightDir, NoL, texelSizeWorldSpace);

    return PCF_Shadow(spotShadowMapCoords.xy, spotShadowMapCoords.z, u_spotShadowMap, lightIndex);
}

but there’s a lot of similarities in these functions and I thought of having only one function

    float Shadow(const ShadowParams shadowParams, const sampler2DArrayShadow shadowMap)
    {
        return PCF_Shadow(shadowParams.uv.xy, shadowParams.uv.z, shadowMap, shadowParams.shadowIndex);
    }

and now there’s a problem. There are 3 types of lights: directional, point, and spot. Each one calculates their shadow map face, texel world size, and shadow matrix index differently.
I’m not sure how I should handle this.
I could do the if statements to check for which light type this shadow function should work, but
if the scene has only directional lights, the shader would also do the point/spot light part due to branching. The same happens if only points or spotlights are in the scene.

It should not be a problem if there are all types of lights, but I want this shader to be as general as possible.

Thus I thought of calculating “differences” (shadow map face, texel world size, and shadow matrix index) as vec3s and then choosing the desired value by index like this:

#define SHADOW_TYPE_DIR 0
#define SHADOW_TYPE_POINT 1
#define SHADOW_TYPE_SPOT 2

#define CASCADE_COUNT 4
#define POINT_LIGHT_FACE_COUNT 6
#define SPOT_LIGHT_FACE_COUNT 1

float getDirTexelSizeWorldSpace(const mat4 viewProjection) 
{
    const vec3 p = vec3(0.5f, 0.5f, 0.0f);

    const float n = viewProjection[0][0];
    const float A = viewProjection[1][1];
    const float B = viewProjection[3][1];

    const float dz = A - p.y;
    const float j = -B / (n * dz * dz);

    const mat3 J = mat3(
        j * dz,  0.0f,    0.0f,
        j * p.x, j * n,   0.0f,
        0.0f,    j * p.z, j * dz
    );

    const float ures = getShadowTexelSize();
    const float vres = getShadowTexelSize();

    const vec3 Jx = J[0] * ures;
    const vec3 Jy = J[1] * vres;
    return max(length(Jx), length(Jy));
}

float getSpotTexelSizeWorldSpace(const float angle)
{
    return 2.0f * tan(angle * DEG_TO_RAD) * getShadowTexelSize();
}

uint getShadowCascade(const vec3 worldPosition)
{
    float z = MulMat4x4Float3(getView(), worldPosition).z;
    uvec4 greaterZ = uvec4(greaterThan(getCascadeSplits(), vec4(z)));
    return clamp(greaterZ.x + greaterZ.y + greaterZ.z + greaterZ.w, 0, getCascadeCount() - 1);
}

uint getPointLightFace(const vec3 r) 
{
    vec4 tc;
    float rx = abs(r.x);
    float ry = abs(r.y);
    float rz = abs(r.z);
    float d = max(rx, max(ry, rz));
    if (d == rx) {
        return (r.x >= 0.0 ? 0 : 1);
    } else if (d == ry) {
        return (r.y >= 0.0 ? 2 : 3);
    } else {
        return (r.z >= 0.0 ? 4 : 5);
    }
}

uvec3 getMatrixOffset()
{
    uvec3 v;
    v.x = 0;
    v.y = getDirLightCount() * CASCADE_COUNT;
    v.z = v.y + getPointLightCount() * POINT_LIGHT_FACE_COUNT;
    return v;
}

uvec3 getShadowFace(const vec3 worldPosition, const vec3 lightPosition)
{
    uvec3 face;

    face.x = getShadowCascade(worldPosition);
    face.y = getPointLightFace(worldPosition - lightPosition);
    face.z = 0;

    return face;
}

vec3 getTexelSizeWorldSpace(const mat4 viewProjection, const float angle)
{
    vec3 tsws;

    tsws.x = getDirTexelSizeWorldSpace(viewProjection);
    tsws.y = getSpotTexelSizeWorldSpace(45.0f);
    tsws.z = getSpotTexelSizeWorldSpace(angle);

    return tsws;
}

const uvec3 SHADOW_FACE_COUNT = uvec3(CASCADE_COUNT, POINT_LIGHT_FACE_COUNT, SPOT_LIGHT_FACE_COUNT);

struct ShadowParams
{
    vec3 uv;
    uint shadowIndex;
};

ShadowParams getShadowParams(const Light light, const ShadingParams shadingParams, const uint shadowType)
{
    const uint face = getShadowFace(shadingParams.worldPosition, light.worldPosition)[shadowType];
    const uint shadowIndex = face + (light.index * SHADOW_FACE_COUNT[shadowType]);
    const uint matrixOffset = getMatrixOffset()[shadowType];
    const mat4 shadowMatrix = u_shadowData.viewProjection[shadowIndex + matrixOffset];
    const float texelSizeWorldSpace = getTexelSizeWorldSpace(shadowMatrix, light.angle)[shadowType];

    ShadowParams params;
    params.uv = getShadowUV(shadowMatrix, shadingParams, light, texelSizeWorldSpace);
    params.shadowIndex = shadowIndex;
    return params;
}

and in shading function I could do this (pseudocode):

vec3 Shading()
{
    {
        const ShadowParams shadowParams = getShadowParams(
            light,
            params,
            SHADOW_TYPE_DIR
        );
        float visibility = Shadow(shadowParams, u_dirShadowMap);

        ....
    }

    {
        const ShadowParams shadowParams = getShadowParams(
            light,
            params,
            SHADOW_TYPE_POINT
        );
        float visibility = Shadow(shadowParams, u_pointShadowMap);

        ....
    }

    {
        const ShadowParams shadowParams = getShadowParams(
            light,
            params,
            SHADOW_TYPE_SPOT
        );
        float visibility = Shadow(shadowParams, u_spotShadowMap);

        ....
    }
}

but now the shader calculates these differences everytime, for every light type, even though some of these values won’t be used.
I’m not sure which option might be better:

  1. if statements
  2. vec3s

What do you think about it? How would you handle this concern?
How would you rate the readability and the performance of these 2 options?
I’m interested more in hypothetical performance cases because for accurate results we’d have to test it.

New contributor

Gerrard is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật