디퍼 드 라이팅 셋업에서 듀얼 포물면 포인트 라이트 섀도우


10

지연된 조명 설정 유형 인 light-pre-pass의 간단한 구현을 보여주는 이 자습서 / 샘플 코드 를 가지고 놀았습니다 .

이중 포물선 그림자 맵을 사용하여 포인트 라이트 그림자를 구현하는 중입니다. 다음 DPM에 대한 설명을 따르고 있습니다. http://gamedevelop.eu/en/tutorials/dual-paraboloid-shadow-mapping.htm

섀도 맵을 만들 수 있는데보기에 좋아 보입니다.

현재 가지고있는 문제는 픽셀 셰이더에 있다고 생각하여 포인트 라이트를 렌더링 할 때 그림자 맵에서 깊이 값을 조회합니다.

내 포인트 라이트 셰이더 코드는 다음과 같습니다. http://olhovsky.com/shadow_mapping/PointLight.fx

관심있는 픽셀 셰이더 기능은입니다 PointLightMeshShadowPS.

그 기능에 눈부신 오류가 있습니까?

잘하면 누군가 가이 문제를 전에 해결했습니다 :)

여기에 이미지 설명을 입력하십시오

여기에 이미지 설명을 입력하십시오

위의 이미지에서 볼 수 있듯이 게시물의 그림자가 게시물의 위치와 일치하지 않으므로 일부 변형이 어딘가에 잘못되었습니다 ...

포인트 라이트가지면에 매우 가까이있을 때 (거의지면에 닿는 경우)의 모습입니다.

여기에 이미지 설명을 입력하십시오

포인트 라이트가지면에 가까워지면 그림자가 모여 두 그림자 맵이 만나는 선을 따라 (즉, 두 개의 그림자 맵을 캡처하기 위해 라이트 카메라가 뒤집힌 평면을 따라) 만집니다.


편집하다:

추가 정보 :

여기에 이미지 설명을 입력하십시오

포인트 라이트를 원점에서 멀어지게하면 그림자를 자르는 라이트 카메라의 "오른쪽"벡터와 평행 한 선이 있습니다. 위의 이미지는 포인트 라이트를 왼쪽으로 움직 인 결과를 보여줍니다. 포인트 라이트를 오른쪽으로 움직이면 오른쪽에 동등한 클리핑 선이 있습니다. 그래서 이것은 내가 생각했던 것처럼 픽셀 쉐이더에서 잘못 변환하고 있음을 나타냅니다.


편집 :이 질문을보다 명확하게하기 위해 몇 가지 코드가 있습니다.

다음은 현재 섀도우 스폿 라이트 를 그리는 데 사용하는 코드입니다 . 이것은 작동하며 예상대로 그림자 매핑을 사용합니다.

VertexShaderOutputMeshBased SpotLightMeshVS(VertexShaderInput input)
{
    VertexShaderOutputMeshBased output = (VertexShaderOutputMeshBased)0;    
    output.Position = mul(input.Position, WorldViewProjection);

    //we will compute our texture coords based on pixel position further
    output.TexCoordScreenSpace = output.Position;
    return output;
}

//////////////////////////////////////////////////////
// Pixel shader to compute spot lights with shadows
//////////////////////////////////////////////////////
float4 SpotLightMeshShadowPS(VertexShaderOutputMeshBased input) : COLOR0
{
    //as we are using a sphere mesh, we need to recompute each pixel position into texture space coords
    float2 screenPos = PostProjectionSpaceToScreenSpace(input.TexCoordScreenSpace) + GBufferPixelSize;
    //read the depth value
    float depthValue = tex2D(depthSampler, screenPos).r;

    //if depth value == 1, we can assume its a background value, so skip it
    //we need this only if we are using back-face culling on our light volumes. Otherwise, our z-buffer
    //will reject this pixel anyway

    //if depth value == 1, we can assume its a background value, so skip it
    clip(-depthValue + 0.9999f);

    // Reconstruct position from the depth value, the FOV, aspect and pixel position
    depthValue*=FarClip;

    //convert screenPos to [-1..1] range
    float3 pos = float3(TanAspect*(screenPos*2 - 1)*depthValue, -depthValue);

    //light direction from current pixel to current light
    float3 lDir = LightPosition - pos;

    //compute attenuation, 1 - saturate(d2/r2)
    float atten = ComputeAttenuation(lDir);

    // Convert normal back with the decoding function
    float4 normalMap = tex2D(normalSampler, screenPos);
    float3 normal = DecodeNormal(normalMap);

    lDir = normalize(lDir);

    // N dot L lighting term, attenuated
    float nl = saturate(dot(normal, lDir))*atten;

    //spot light cone
    half spotAtten = min(1,max(0,dot(lDir,LightDir) - SpotAngle)*SpotExponent);
    nl *= spotAtten;

    //reject pixels outside our radius or that are not facing the light
    clip(nl -0.00001f);

    //compute shadow attenuation

    float4 lightPosition = mul(mul(float4(pos,1),CameraTransform), MatLightViewProjSpot);

    // Find the position in the shadow map for this pixel
    float2 shadowTexCoord = 0.5 * lightPosition.xy / 
                            lightPosition.w + float2( 0.5, 0.5 );
    shadowTexCoord.y = 1.0f - shadowTexCoord.y;
    //offset by the texel size
    shadowTexCoord += ShadowMapPixelSize;

    // Calculate the current pixel depth
    // The bias is used to prevent floating point errors 
    float ourdepth = (lightPosition.z / lightPosition.w) - DepthBias;

    nl = ComputeShadowPCF7Linear(nl, shadowTexCoord, ourdepth);

    float4 finalColor;

    //As our position is relative to camera position, we dont need to use (ViewPosition - pos) here
    float3 camDir = normalize(pos);

    // Calculate specular term
    float3 h = normalize(reflect(lDir, normal));
    float spec = nl*pow(saturate(dot(camDir, h)), normalMap.b*50);
    finalColor = float4(LightColor * nl, spec); 

    //output light
    return finalColor * LightBufferScale;
}

다음은 내가 사용 하는 포인트 라이트 코드입니다. 그림자 맵을 사용할 때 라이트 공간으로 변환하는 데 일종의 버그가 있습니다.

VertexShaderOutputMeshBased PointLightMeshVS(VertexShaderInput input)
{
    VertexShaderOutputMeshBased output = (VertexShaderOutputMeshBased)0;    
    output.Position = mul(input.Position, WorldViewProjection);

    //we will compute our texture coords based on pixel position further
    output.TexCoordScreenSpace = output.Position;
    return output;
}

float4 PointLightMeshShadowPS(VertexShaderOutputMeshBased input) : COLOR0
{
    // as we are using a sphere mesh, we need to recompute each pixel position 
    // into texture space coords
    float2 screenPos = 
        PostProjectionSpaceToScreenSpace(input.TexCoordScreenSpace) + GBufferPixelSize;

    // read the depth value
    float depthValue = tex2D(depthSampler, screenPos).r;

    // if depth value == 1, we can assume its a background value, so skip it
    // we need this only if we are using back-face culling on our light volumes. 
    // Otherwise, our z-buffer will reject this pixel anyway
    clip(-depthValue + 0.9999f);

    // Reconstruct position from the depth value, the FOV, aspect and pixel position
    depthValue *= FarClip;

    // convert screenPos to [-1..1] range
    float3 pos = float3(TanAspect*(screenPos*2 - 1)*depthValue, -depthValue);

    // light direction from current pixel to current light
    float3 lDir = LightPosition - pos;

    // compute attenuation, 1 - saturate(d2/r2)
    float atten = ComputeAttenuation(lDir);

    // Convert normal back with the decoding function
    float4 normalMap = tex2D(normalSampler, screenPos);
    float3 normal = DecodeNormal(normalMap);

    lDir = normalize(lDir);

    // N dot L lighting term, attenuated
    float nl = saturate(dot(normal, lDir))*atten;

    /* shadow stuff */

    float4 lightPosition = mul(mul(float4(pos,1),CameraTransform), LightViewProj);

    //float4 lightPosition = mul(float4(pos,1), LightViewProj);
    float posLength = length(lightPosition);
    lightPosition /= posLength;

    float ourdepth = (posLength - NearClip) / (FarClip - NearClip) - DepthBias;
    //float ourdepth = (lightPosition.z / lightPosition.w) - DepthBias;

    if(lightPosition.z > 0.0f)
    {
        float2 vTexFront;
        vTexFront.x =  (lightPosition.x /  (1.0f + lightPosition.z)) * 0.5f + 0.5f; 
        vTexFront.y =  1.0f - ((lightPosition.y /  (1.0f + lightPosition.z)) * 0.5f + 0.5f);    

        nl = ComputeShadow(FrontShadowMapSampler, nl, vTexFront, ourdepth);
    }
    else
    {
        // for the back the z has to be inverted        
        float2 vTexBack;
        vTexBack.x =  (lightPosition.x /  (1.0f - lightPosition.z)) * 0.5f + 0.5f; 
        vTexBack.y =  1.0f - ((lightPosition.y /  (1.0f - lightPosition.z)) * 0.5f + 0.5f); 

        nl = ComputeShadow(BackShadowMapSampler, nl, vTexBack, ourdepth);
    }

    /* shadow stuff */

    // reject pixels outside our radius or that are not facing the light
    clip(nl - 0.00001f);

    float4 finalColor;
    //As our position is relative to camera position, we dont need to use (ViewPosition - pos) here
    float3 camDir = normalize(pos);

    // Calculate specular term
    float3 h = normalize(reflect(lDir, normal));
    float spec = nl*pow(saturate(dot(camDir, h)), normalMap.b*100);
    finalColor = float4(LightColor * nl, spec);

    return finalColor * LightBufferScale;
}

당신은 그림자 자체가 문제가 /가있는 매핑 말한다 (당신은 그림자가 정확한 지점을 어둡게거야가 textureMap하기 위해 매핑 구울 경우 내 말은 없다?)
Ali1S232

광원 위치에서 카메라 렌더링의 FOV가 올바른지 100 % 확신합니까?
Roy T.

포물면 왜곡을 위해 투영이 수동으로 수행되므로 광원 위치에서 카메라 렌더링에는 투영 행렬이 없습니다. 그래도 코드를 확인하겠습니다. 좋은 아이디어 Roy T.
Olhovsky

Gajet : "섀도우 맵을 텍스쳐 맵에 구우면 정확한 점이 어두워 질까요?" 그림자 맵은 밝은 공간에 그림자를 저장합니다.지도를 보면 그림자가 화면 공간에서 보이기 때문에 정확한지 알 수있는 쉬운 방법이 없습니다. "텍스처 맵"이란 무엇입니까? 텍스처를 의미합니까? 그림자 맵 텍스처입니다.
Olhovsky 2016 년

Roy T .: 빛을 움직이면 그림자 맵이 잘려서 그림자를 만들 때가 아니라 그림자를 실제로 사용할 때 변형에 문제가 있습니다.
Olhovsky 2016 년

답변:


2

PIX를 사용하면 분리 된 픽셀을 디버깅 할 수 있습니다. 아마도 이런 식으로 오류를 찾을 수 있습니다. FOV 또는 프로젝션 오류는 힌트입니다. 아니면 세상의 변화를 잊었습니까?!


당신은 또한 엔비디아 - fxComposer와 debuging 시도 할 수 있습니다
Ali1S232

어셈블리 코드 값을 쳐다 보는 것이이 시점에서 크게 도움이 될 것이라고 생각하지 않습니다. 왜냐하면 변형이 처음에 수행되는 방법을 이해하는 데 어려움이 있기 때문입니다. 따라서 레지스터 10의 값 또는 그 밖의 값을 보는 것이 실제로 도움이되지는 않습니다.
Olhovsky 2016 년

"또는 세상의 변화를 잊었습니까?" 그림자 맵을 만들 때 실제로 월드 변환을 적용하는 것을 잊었습니다. 이것은 이제 모든 셰이더를 그대로 유지하면서 작동합니다.
Olhovsky

1

Olhovsky, 좋은 질문입니다. 나는 당신의 고통을 알고 있습니다. 저는 마지막 작업에서 Deferred Shading, Inferred Lighting 및 shadows를 구현했습니다. 정말 재미 있었지만 예상대로 작동하지 않을 때에도 많은 고통이있었습니다.

PIX의 조언은 실제로 좋은 생각이라고 생각합니다. 셰이더의 어셈블러 명령어를 망설 일 필요는 없지만 섀도 맵 및 기타 렌더 타겟을보고 픽셀을 선택하고 픽셀 셰이더를 호출 한 다음 해당 버텍스 셰이더를 단계별로 실행할 수 있습니다.

이러한 종류의 상황에 대한 일반적인 디버그 트릭에는 장면 단순화가 포함됩니다.

내 마음에 오는 것은 : 조명 패스에서와 같은 vy와 다른 속성을 가진 카메라를 광원과 같은 위치에 두는 것입니다. 이제 픽셀 쉐이더의 값을 쉽게 비교할 수 있습니다. 현재 객체에 대한 일반 렌더 패스의 pixel-xy는 동일한 해상도를 갖는 한 그림자 맵에서 조회를 위해 계산 된 pixel-xy와 같아야합니다.

다른 하나는 직교 투영법으로 전환하여 쉽고 예측 가능하며 확인할 수있는 것을 만듭니다. 각 계산 단계를 더 잘 확인할 수 있습니다.

그 외에도 현재 픽셀의 그림자 맵에서 위치, 즉 화면 공간에서 광 공간으로의 변환을 계산하는 행렬을 만드는 방법을 보여줄 수 있습니까?


그냥 그림자 맵을 : 신경 끄시 고,하지 않습니다 작업 그림자 맵에서 현재의 픽셀 위치와 위치를 비교 조명 위치에 카메라를 넣어 훨씬 더 힘들어 디버그 및 아이디어로 만드는 Parabloid,되어보고
Maik Semder을

관심이 있으시면 kris@olhovsky.com으로 이메일을 보내 주시면 프로젝트 사본을 보내 드리겠습니다. 그렇지 않으면 : CameraTransform매트릭스는 실제로 현재 장면을보고있는 카메라의 월드 매트릭스입니다. LightViewProj빛의 뷰 행렬은 단지 신원 행렬로 행렬은 실제로 빛의 단지 세계 행렬이다.
Olhovsky 2016 년

간단한 C ++ 프로젝트를 만들 수 있습니까? 파라 블 로이드 변환도 제대로 이루어져야합니까?
Maik Semder 2016 년

포물면 변환은 질문에 링크 된 픽셀 쉐이더에 있습니다. 내 C ++ 기술은 지연된 렌더링 파이프 라인 전체를 캡슐화하는 빠른 C ++ 프로젝트를 만들기에는 너무 제한되어 있습니다. 특히 대부분의 문제는 실제로 픽셀 셰이더에 있으며 아마도 수학에 전달 되었기 때문입니다.
Olhovsky 2016 년

g_mDPView 및 g_mDPWorldView를 참조했습니다. 계산 방법을 보여줄 수 있습니까?
Maik Semder 2016 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.