Próbuje obliczyć macież widoku oraz wartości near i far dla światła kierunkowego do cienowania.

Na razie mam taki kod:

//gdzies tam w klasie Camera
static constexpr std::array<glm::vec4, 8> NDC_FRUSTUM_CORNERS = {
	glm::vec4(-1.0f, -1.0f, 0.0f, 1.0f),
	glm::vec4(1.0f, -1.0f, 0.0f, 1.0f),
	glm::vec4(-1.0f, 1.0f, 0.0f, 1.0f),
	glm::vec4(1.0f, 1.0f, 0.0f, 1.0f),

	glm::vec4(-1.0f, -1.0f, 1.0f, 1.0f),
	glm::vec4(1.0f, -1.0f, 1.0f, 1.0f),
	glm::vec4(-1.0f, 1.0f, 1.0f, 1.0f),
	glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)
};


glm::mat4 CalculateViewProj(const Camera& camera, glm::vec3 lightDirection)
{
    lightDirection = -glm::normalize(lightDirection);

	float shadowDistance = 10.0f;
	float margin = 10.0f;

	auto frustumCorners = Camera::NDC_FRUSTUM_CORNERS;

	const glm::mat4 cameraInverseView = camera.getModel();
	const glm::mat4 cameraInverseProjection = glm::inverse(glm::perspectiveFov(camera.fov, camera.width, camera.height, shadowDistance, 0.01f));
	const glm::mat4 cameraInverseViewProjection = glm::mat4(glm::mat3(cameraInverseView)) * cameraInverseProjection;

	for (auto& fc : frustumCorners) {
		fc = cameraInverseViewProjection * fc;
		fc /= fc.w;
	}

	glm::vec3 min = glm::vec3(std::numeric_limits<float>::max());
	glm::vec3 max = glm::vec3(std::numeric_limits<float>::min());
	for (const auto& fc : frustumCorners) {
		min = glm::min(min, glm::vec3(fc));
		max = glm::max(max, glm::vec3(fc));
	}
	const float h = glm::distance(min, max);
	const float projectedRectangleSize = h * 0.5f;
	
	const glm::mat4 lightView = ????

	for (auto& fc : frustumCorners) {
		fc = lightView * fc;
		fc /= fc.w;
	}
	float lightMinZ = std::numeric_limits<float>::max();
	float lightMaxZ = std::numeric_limits<float>::min();
	for (const auto& fc : frustumCorners) {
		lightMinZ = glm::min(lightMinZ, fc.z);
		lightMaxZ = glm::max(lightMaxZ, fc.z);
	}			
	const float z = glm::abs(lightMaxZ - lightMinZ) + margin;
	glm::mat4 lightProjection = glm::orthoZO(-projectedRectangleSize, projectedRectangleSize, -projectedRectangleSize, projectedRectangleSize, z, -z);

	const glm::mat4 lightSpaceMatrix = lightProjection * lightView;

	return lightSpaceMatrix;
}

Nie wiem w jaki sposób poprawnie obliczyć macież lightView

Przejrzałem dużo wątków na ten temat w internecie, ale nie znalazłem niczego co pomogłoby rozwiązać mój problem.

Do tej pory, próbowałem 3 opcji

  1. najprostsza
const glm::mat4 lightView = glm::lookAt(lightDirection,
	glm::vec3(0.0f, 0.0f, 0.0f),
	glm::vec3(0.0f, 1.0f, 0.0f));

ale ten sposób nie działa dobrze w połączeniu z obliczeniem near i far dla ortho. Żeby pokazały się cienie, dla testu, ustawiłem te wartości na sztywno na 10.0f i -1.0f const glm::mat4 lightProjection = glm::orthoZO(-projectedRectangleSize, projectedRectangleSize, -projectedRectangleSize, projectedRectangleSize, 10.0f, -1.0f);. Cienie się teraz pokazują, ale nie jest to poprawne rozwiązanie, bo nie skaluje się to z frustum kamery.

  1. Obliczenie środka frustum
glm::vec3 frustumCenter = glm::vec3(0.0f);
for (const auto& fc : frustumCorners) {
	frustumCenter += glm::vec3(fc);
}
frustumCenter /= 8.0f;

const glm::mat4 lightView = glm::lookAt(frustumCenter - lightDirection * -projectedRectangleSize, frustumCenter, Camera::WORLD_UP);

Gdzieś w internecie widziałem propozycje zdefiniowania widoku w ten sposób. Renderowałem scene z perspektywy światła, wygląda dobrze, ale z perspektywy głównej kamery cieni brak. Jest po prostu zwykła, oświetlona scena. Znów ustawiłem na sztywno near i far, cienie się pokazały.

  1. Obliczenie pozycji światła
glm::vec3 newCenter = camera.getPosition() + shadowDistance * 0.5f * camera.getForward();

const float pixelsSize = projectedRectangleSize / 1024;
const float invPixelSize = 1.0f / pixelsSize;

newCenter.x = std::floorf(newCenter.x * invPixelSize) * pixelsSize;
newCenter.y = std::floorf(newCenter.y * invPixelSize) * pixelsSize;

const glm::vec3 lightPosition = (shadowDistance * 0.5f + margin) * lightDirection + newCenter;

const glm::mat4 lightView = glm::lookAt(lightPosition,
	lightDirection,
	glm::vec3(0.0f, 1.0f, 0.0f));

W tym przypadku w ogóle cienie zmieniają swój kierunek po obrocie gównej kamery, co nie jest poprawnym działaniem.

Próbuje od tygodnia to ogarnąć i już nie mam pomysłów.

Depth buffor jest wyczyszczony na 0 ze względu na odwróconą głębie.

Sampler description:

D3D11_SAMPLER_DESC
{
	.Filter = D3D11_FILTER_COMPARISON_MIN_MAG_MIP_POINT,
	.AddressU = D3D11_TEXTURE_ADDRESS_BORDER,
	.AddressV = D3D11_TEXTURE_ADDRESS_BORDER,
	.AddressW = D3D11_TEXTURE_ADDRESS_BORDER,
	.ComparisonFunc = D3D11_COMPARISON_GREATER,
	.BorderColor = 1.0f,
}

depth shader:

cbuffer LightVp: register(b0)
{
    row_major float4x4 lightViewProjection;
};

struct VertexInput
{
    float3 position : POSITION;
    
    nointerpolation float4 modelToWorld1 : I_MODEL_TO_WORLD_ONE;
    nointerpolation float4 modelToWorld2 : I_MODEL_TO_WORLD_TWO;
    nointerpolation float4 modelToWorld3 : I_MODEL_TO_WORLD_THREE;
    nointerpolation float4 modelToWorld4 : I_MODEL_TO_WORLD_FOUR;
};

float4 VSMain(VertexInput input) : SV_POSITION
{
    const float4x4 model = float4x4(input.modelToWorld1, input.modelToWorld2, input.modelToWorld3, input.modelToWorld4);
    const float4 worldPosition = mul(float4(input.position, 1.0f), model);
    
    return mul(worldPosition, lightViewProjection);
}

float PSMain(float4 position : SV_POSITION) : SV_Depth
{
    return position.z;
}

Nie wydaje mi się, że jest tu coś źle, ale dla pewności wrzuce. Próbowałem też odpalić w ogóle bez shadera pixeli, sam vertex shader. NIe ma różnicy.

Funkcja cieniująca

Texture2D dirShadowMap : register(t0);
SamplerComparisonState g_shadowSampler : register(s0);

cbuffer LightVp: register(b0)
{
    row_major float4x4 lightViewProjection;
};

float DirShadows(float3 worldPosition)
{
    float4 fragPosLightSpace = mul(float4(worldPosition, 1.0f), lightViewProjection);

    float3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
    projCoords = projCoords * 0.5 + 0.5;

    return dirShadowMap.SampleCmpLevelZero(g_shadowSampler, projCoords.xy, projCoords.z);
}

Jak moge zaimplementować to poprawnie?