Problemas de mapeamento de sombra pela primeira vez


9

Eu implementei o mapeamento básico de sombra pela primeira vez no OpenGL usando shaders e estou enfrentando alguns problemas. Abaixo você pode ver um exemplo da minha cena renderizada:

insira a descrição da imagem aqui

O processo do mapeamento de sombra que estou seguindo é que eu renderizo a cena para o framebuffer usando uma View Matrix do ponto de vista da luz e as matrizes de projeção e modelo usadas para renderização normal.

Na segunda passagem, envio a matriz MVP acima do ponto de vista da luz para o shader de vértice que transforma a posição no espaço da luz. O sombreador de fragmento divide a perspectiva e altera a posição para coordenar as texturas.

Aqui está o meu vertex shader,


#version 150 core

uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform mat4 MVPMatrix;
uniform mat4 lightMVP;

uniform float scale;

in vec3 in_Position;
in vec3 in_Normal;
in vec2 in_TexCoord;

smooth out vec3 pass_Normal;
smooth out vec3 pass_Position;
smooth out vec2 TexCoord;
smooth out vec4 lightspace_Position;

void main(void){
    pass_Normal = NormalMatrix * in_Normal; 
    pass_Position = (ModelViewMatrix * vec4(scale * in_Position, 1.0)).xyz;
    lightspace_Position = lightMVP * vec4(scale * in_Position, 1.0);
    TexCoord = in_TexCoord;

    gl_Position = MVPMatrix * vec4(scale * in_Position, 1.0);
}

E meu fragmento shader,


#version 150 core

struct Light{
    vec3 direction;
};

uniform Light light;
uniform sampler2D inSampler;
uniform sampler2D inShadowMap;

smooth in vec3 pass_Normal;
smooth in vec3 pass_Position;
smooth in vec2 TexCoord;
smooth in vec4 lightspace_Position;

out vec4 out_Color;


float CalcShadowFactor(vec4 lightspace_Position){

    vec3 ProjectionCoords = lightspace_Position.xyz / lightspace_Position.w;

    vec2 UVCoords;
    UVCoords.x = 0.5 * ProjectionCoords.x + 0.5;
    UVCoords.y = 0.5 * ProjectionCoords.y + 0.5;

    float Depth = texture(inShadowMap, UVCoords).x;
    if(Depth < (ProjectionCoords.z + 0.001)) return 0.5;
    else return 1.0;
}

void main(void){

    vec3 Normal = normalize(pass_Normal);
    vec3 light_Direction = -normalize(light.direction);
    vec3 camera_Direction = normalize(-pass_Position);
    vec3 half_vector = normalize(camera_Direction + light_Direction);

    float diffuse = max(0.2, dot(Normal, light_Direction));
    vec3 temp_Color = diffuse * vec3(1.0);

    float specular = max( 0.0, dot( Normal, half_vector) );

    float shadowFactor = CalcShadowFactor(lightspace_Position);

    if(diffuse != 0 && shadowFactor > 0.5){
        float fspecular = pow(specular, 128.0);
        temp_Color += fspecular;
    }

    out_Color = vec4(shadowFactor * texture(inSampler, TexCoord).xyz * temp_Color, 1.0);
}

Um dos problemas é a auto-sombreamento, como você pode ver na figura, a caixa tem sua própria sombra projetada sobre si mesma. O que tentei é ativar o deslocamento de polígono (por exemplo, glEnable (POLYGON_OFFSET_FILL), glPolygonOffset (GLfloat, GLfloat)), mas não mudou muito. Como você vê no shader de fragmento, coloquei um valor de deslocamento estático de 0,001, mas preciso alterar o valor dependendo da distância da luz para obter efeitos mais desejáveis, o que não é muito útil. Também tentei usar o abate da face frontal quando renderizo ao buffer de moldura, isso também não mudou muito.

O outro problema é que os pixels fora do campo de visão do Light ficam sombreados. O único objeto que deve ser capaz de projetar sombras é o caixote. Acho que devo escolher uma projeção mais apropriada e visualizar matrizes, mas não tenho certeza de como fazer isso. Quais são algumas práticas comuns, devo escolher uma projeção ortográfica?

Ao pesquisar um pouco, entendo que esses problemas não são tão triviais. Alguém tem fácil de implementar soluções para esses problemas. Você poderia me dar algumas dicas adicionais?

Por favor, pergunte-me se você precisar de mais informações sobre o meu código.

Aqui está uma comparação com e sem o mapeamento de sombra de um close da caixa. A auto-sombra é mais visível.


Parece que você deve descrever seus problemas com mais detalhes. Você tem uma frase sobre seus problemas e a próxima frase sugere uma correção para uma. Diga-nos exatamente quais são os problemas e o que você já fez para tentar corrigi-los.
MichaelHouse

Eu editei meu post, espero que esteja melhor agora.
Grieverheart

Respostas:


6

Você não deve sombrear os polígonos voltados para trás da luz.

Alterei seu shader de fragmento para o código abaixo.

float CalcShadowFactor(vec4 lightspace_Position)
{

    vec3 ProjectionCoords = lightspace_Position.xyz / lightspace_Position.w;

    vec2 UVCoords;
    UVCoords.x = 0.5 * ProjectionCoords.x + 0.5;
    UVCoords.y = 0.5 * ProjectionCoords.y + 0.5;

    float Depth = texture(inShadowMap, UVCoords).x;
    if(Depth < (ProjectionCoords.z + 0.001)) return 0.5;
    else return 1.0;
}

void main(void)
{

    vec3 Normal = normalize(pass_Normal);
    vec3 light_Direction = -normalize(light.direction);
    vec3 camera_Direction = normalize(-pass_Position);
    vec3 half_vector = normalize(camera_Direction + light_Direction);

    float fndotl = dot(Normal, light_Direction);
    float shadowFactor = CalcShadowFactor(lightspace_Position);
    float diffuse = max(0.0, fndotl) * shadowFactor + 0.2;
    vec3 temp_Color = vec3(diffuse);

    float specular = max( 0.0, dot( Normal, half_vector) );

    if(shadowFactor > 0.9){
        float fspecular = pow(specular, 128.0);
        temp_Color += fspecular;
    }

    out_Color = vec4(shadowFactor * texture(inSampler, TexCoord).xyz * temp_Color, 1.0);
}

8

Em relação à auto-sombra, não tenho certeza do que você está se referindo - não vejo "acne sombria" na caixa na sua captura de tela. Se você quer dizer que os rostos voltados para longe da luz são escuros, bem, é claro que são - deveriam ser! :) A face lateral parece um pouco estranha, dividida na diagonal em meio iluminada e meio sombreada. Se você estiver usando N ponto L para iluminação e tiver boas normais (ou seja, normais duras neste cubo, não normais suavizadas), isso não deve acontecer.

Usar o deslocamento de polígono e / ou desenhar as faces traseiras no mapa de sombras são as soluções padrão (bem ... soluções realmente) para a acne de sombras.

Quanto à matriz de projeção, você deve pensar na luz como uma câmera. Se for uma luz pontual, seria uma câmera em perspectiva e, se for uma luz direcional, seria como uma câmera ortográfica. Construa a matriz de projeção da mesma maneira que faria para uma câmera, usando o campo de visão, a proporção da luz, etc.

Para restringir o sombreador de pixel a desenhar apenas os pixels no campo de visão da luz, depois de calcular os UVs do mapa de sombras, verifique se eles estão entre 0 e 1 e discardse não (ou defina a cor da luz como zero). Outra maneira é definir a textura do mapa de sombra no modo "fixar na borda" e definir uma cor de borda igual a zero, o que garantirá que tudo fora do mapa de sombras fique totalmente sombreado.


Aqui está um close da caixa e comparação com e sem link de mapeamento de sombra . A auto-sombra é mais visível aqui. Você poderia explicar por que usaria uma câmera em perspectiva para luzes pontuais e ortográfica para direcional?
Grieverheart

Ok, nessa foto parece com acne sombria. Desenhar as faces de volta no mapa de sombras (e não as faces da frente) deve corrigir isso. Eu sei que você disse que tentou isso, mas talvez algo deu errado? É só glCullFace(GL_FRONT). Quanto ao ponto x direcional, é tudo sobre os raios. Os raios da câmera convergem para um ponto em uma câmera de perspectiva e os raios de luz convergem para um ponto em uma luz pontual; os raios da câmera são paralelos para uma câmera ortográfica e os raios de luz são paralelos para uma luz direcional.
Nathan Reed

Entendo, isso faz sentido. Aqui está outra comparação com e sem seleção de rosto. O da parte inferior tem o descarte de face ativado. Vou tentar usar a projeção ortográfica para minha matriz de exibição de luz, mas isso não deve afetar o problema de auto-sombreamento, certo? Talvez meu intervalo de clipes seja muito grande (ou seja, eu tenha de 0,1 a 100)?
Grievheart

Aqueles parecem exatamente o mesmo. Você está usando a seleção do rosto para o resto da cena, certo? Você fez glEnable(GL_CULL_FACE)?
Nathan Reed

Eles não são exatamente iguais, olhe atentamente para a caixa. Na figura acima, a linha de sombra é convexa, enquanto a linha de baixo é côncava.
Grievheart

3

Os pixels fora da visualização das luzes estão escuros porque estão sendo amostrados de fora da textura da profundidade das luzes.

Quando você transforma o vértice pela matriz de projeção, obtém as coordenadas do clipe [lightspace_Position] do fragmento. Você está convertendo isso em coordenadas de textura [UVCoords]. No entanto, as coordenadas de espaço de clipe de um fragmento ainda podem estar fora do intervalo de -1,0 a 1,0 (fora da tela e, portanto, cortadas) e, portanto, as coordenadas de textura convertidas podem estar fora do intervalo de 0,0 a 1,0 da sua textura.

Tente o seguinte:

if(UVCoords.x >= 0.0 && UVCoords.x <= 1.0 && UVCoords.y >= 0.0 && UVCoords.y <= 1.0 && (Depth < (ProjectionCoords.z + 0.001)))
    return 0.5;

Você também pode multiplicar uma matriz de viés no seu [lightMVP] e evitar fazer a conversão de coordenadas no seu shader de fragmento.


A matriz de viés geralmente é a maneira como as pessoas seguem. É muito mais rápido que ramificar código em um shader de fragmento.
Ian Young
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.