Newer
Older
rpg / v2 / packages / engine / src / render / __tests__ / shadowSoftness.test.ts
/**
 * Тесты мягкости теней: чистая функция перевода мягкости 0..1 в радиус
 * размытия shadow map (тексели) — мировое размытие не зависит от фрустума.
 */
import { describe, expect, it } from 'vitest';
import { SHADOW_BLUR_MAX, shadowBlurTexels } from '../voxelRenderer';

describe('shadowBlurTexels (мягкость теней)', () => {
    it('мягкость 0 — резкая тень (1 тексель) при любом фрустуме', () => {
        expect(shadowBlurTexels(0, 165, 2048)).toBe(1);
        expect(shadowBlurTexels(0, 700, 2048)).toBe(1);
    });

    it('мировое размытие постоянно: рост фрустума растит радиус в текселях', () => {
        // тексель = 2·frustumHalf/mapSize; SHADOW_BLUR_MAX мировых юнитов
        // при фрустуме 165 — это 3/(330/2048) ≈ 18.6 → 19 текселей
        const noon = shadowBlurTexels(1, 165, 2048);
        expect(noon).toBe(Math.round(SHADOW_BLUR_MAX / (330 / 2048)));
        // низкое солнце — фрустум шире, тексель крупнее, текселей меньше,
        // а мировое размытие то же
        const dawn = shadowBlurTexels(1, 676, 2048);
        expect(dawn).toBeLessThan(noon);
        expect(dawn).toBeGreaterThanOrEqual(1);
    });

    it('монотонность по мягкости и кламп 0..1', () => {
        let prev = shadowBlurTexels(0, 165, 2048);
        for (let k = 0.1; k <= 1.001; k += 0.1) {
            const cur = shadowBlurTexels(Math.min(k, 1), 165, 2048);
            expect(cur).toBeGreaterThanOrEqual(prev);
            prev = cur;
        }
        // клампы: отрицательная и >1 мягкость — крайние значения
        expect(shadowBlurTexels(-2, 165, 2048)).toBe(1);
        expect(shadowBlurTexels(5, 165, 2048)).toBe(shadowBlurTexels(1, 165, 2048));
    });

    it('дегенерат: нулевой фрустум не делит на ноль', () => {
        expect(shadowBlurTexels(1, 0, 2048)).toBe(1);
        expect(shadowBlurTexels(1, 165, 0)).toBeGreaterThanOrEqual(1);
    });
});