Unity 6.1, URP 17, deferred rendering, shader. Это шейдер для вычисления капсульных отражений. Сейчас они получаются чёткими, но нужны мягкие отражения. Поменяй шейдер так, чтобы сделать отражения мягкими.

struct PrimitiveReflectionData { float3 positionWS; float3 directionWS; half4 color; float radius; float height; };

int _PrimitiveReflectionsCount; StructuredBuffer<PrimitiveReflectionData> _PrimitiveReflections;

float IntersectCapsule(float3 rayOrigin, float3 rayDirection, PrimitiveReflectionData capsule) { float3 capsuleStart = capsule.positionWS - capsule.directionWS * (capsule.height * 0.5); float3 capsuleEnd = capsule.positionWS + capsule.directionWS * (capsule.height * 0.5);

// Вектор от начала к концу капсулы
float3 capsuleAxis = capsuleEnd - capsuleStart;
float capsuleLength = length(capsuleAxis);

capsuleAxis /= capsuleLength;

// Проекция луча на ось капсулы
float3 oc = rayOrigin - capsuleStart;
float a = dot(rayDirection, rayDirection) - dot(rayDirection, capsuleAxis) * dot(
    rayDirection, capsuleAxis);
float b = 2.0 * (dot(oc, rayDirection) - dot(oc, capsuleAxis) * dot(rayDirection, capsuleAxis));
float c = dot(oc, oc) - dot(oc, capsuleAxis) * dot(oc, capsuleAxis) - capsule.radius * capsule.radius;
float discriminant = b * b - 4.0 * a * c;

if (discriminant < 0.0)
    return -1.0;

float t1 = (-b - sqrt(discriminant)) / (2.0 * a);
float t2 = (-b + sqrt(discriminant)) / (2.0 * a);

// Проверяем, попадает ли пересечение в пределы капсулы
UNITY_UNROLL
for (int i = 0; i < 2; i++)
{
    float t = i == 0 ? t1 : t2;

    if (t > 0.001)
    {
        float3 hitPoint = rayOrigin + rayDirection * t;
        float3 toHit = hitPoint - capsuleStart;
        float projection = dot(toHit, capsuleAxis);

        // Проверяем, находится ли точка в пределах цилиндрической части
        if (projection >= 0.0 && projection <= capsuleLength)
            return t;

        // Проверяем пересечение со сферическими заглушками
        float3 sphereCenter = projection < 0.0 ? capsuleStart : capsuleEnd;
        float3 toSphere = rayOrigin - sphereCenter;

        float aSphere = dot(rayDirection, rayDirection);
        float bSphere = 2.0 * dot(toSphere, rayDirection);
        float cSphere = dot(toSphere, toSphere) - capsule.radius * capsule.radius;
        float sphereDisc = bSphere * bSphere - 4.0 * aSphere * cSphere;

        if (sphereDisc >= 0.0)
        {
            float tSphere = (-bSphere - sqrt(sphereDisc)) / (2.0 * aSphere);

            if (tSphere > 0.001)
                return tSphere;
        }
    }
}

return -1.0;
}

float CalculateWeight(float distance) { float maxDistance = 64.0; float weight = 1.0 - saturate(distance / maxDistance);

return weight * weight;
}

float3 CalculateCapsuleReflection(float3 positionWS, float3 viewDirectionWS, float3 normalWS) { float3 reflectionDirection = reflect(-viewDirectionWS, normalWS); float3 finalReflection = 0; float totalWeight = 0;

UNITY_LOOP
for (int i = 0; i < _PrimitiveReflectionsCount; i++)
{
    PrimitiveReflectionData capsule = _PrimitiveReflections[i];
    float t = IntersectCapsule(positionWS, reflectionDirection, capsule);

    if (t > 0.0)
    {
        float3 hitPoint = positionWS + reflectionDirection * t;
        float distanceToHit = distance(positionWS, hitPoint);
        float weight = CalculateWeight(distanceToHit);
        float3 reflection = capsule.color * weight;

        finalReflection += reflection * weight;
        totalWeight += weight;
    }
}

return totalWeight > 0.0 ? finalReflection / totalWeight : 0.0;
}