DirectX11 Работа с светом
Проблема с освещением в моей сцене остается нерешенной. Несмотря на то, что нормали в модели корректны, и освещение в других программах (как в 3D редакторах, так и в Unity) выглядит безупречно, мой собственный шейдер выдает непредсказуемые результаты.
Я перепробовал множество подходов, но ни один из них не дал желаемого эффекта. Модель демонстрирует резкие перепады освещенности, причем свет неожиданно фокусируется на одной стороне, в то время как другие стороны остаются в тени. Это особенно заметно, когда модель вращается, и свет резко "прыгает" на новую сторону. К тому же, спереди и сзади модели практически отсутствует свет, что делает ее внешний вид полностью черным.
Я не могу понять, что является причиной этой проблемы. Возможно, дело в неправильной настройке нормалей в шейдере, или же я упускаю какой-то важный аспект в расчете освещения. Буду благодарен за любую помощь в решении этой задачи.
cbuffer ConstantBuffer : register(b0)
{
matrix World;
matrix View;
matrix Projection;
float4 lightDirection;
float4 lightColor;
float4 ambientColor;
}
struct VS_INPUT
{
float4 Pos : POSITION;
float2 Tex : TEXCOORD0;
float3 Normal : NORMAL;
};
struct PS_INPUT
{
float4 Pos : SV_POSITION;
float2 Tex : TEXCOORD0;
float3 Normal : TEXCOORD1;
};
PS_INPUT VS(VS_INPUT input)
{
PS_INPUT output;
output.Pos = mul(input.Pos, World);
output.Pos = mul(output.Pos, View);
output.Pos = mul(output.Pos, Projection);
output.Tex = float2(input.Tex.x, -input.Tex.y);
output.Normal = mul(float4(input.Normal, 1), World).xyz;
return output;
}
Texture2D txDiffuse : register(t0);
SamplerState samLinear : register(s0);
float4 PS(PS_INPUT input) : SV_Target
{
float4 finalColor = 0;
finalColor += saturate(dot((float3) -lightDirection, input.Normal) * lightColor);
finalColor.a = 1;
return finalColor;
}
Ответы (2 шт):
При передаче из вершинного шейдера в пиксельный значения атрибутов интерполируются. При интерполяции не сохраняется длинна вектора, поэтому нормали стоит нормализовать повторно в пиксельном шейдере:
finalColor += saturate(dot(-lightDirection.xyz, normalize(input.Normal)) * lightColor);
Кроме того, если матрица World в вершинном шейдере имеет Translation (т.е. 4 строка имеет ненулевые компоненты xyz), то нормали будут не просто повернуты, а сдвинуты и повернуты, и это их сломает. Нужно умножать нормали на матрицу с нулевым сдвигом, либо сделать новую матрицу:
float3x3 WorldNoTranslation = (float3x3)World;
output.Normal = mul(float4(input.Normal, 1), WorldNoTranslation).xyz;
Это медленнее, чем просто передать в константу еще одну матрицу мирового преобразования, без сдвига, но для проверки сойдет.
Проблема была совсем в другом месте. В шейдер важно отправлять порядок входящих элементов:
D3D11_INPUT_ELEMENT_DESC layout[] = {
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};
Сейчас вертексы, нормали и текстурные координаты. А были сначала вертексы, текстурные координаты и нормали. Я поменял их местами, и всё заработало. Освещение корректно показывает тени и модель.