阴影映射(Shadow Map)的研究(六)

75 篇文章 10 订阅

阴影映射(Shadow Map)的研究(六)

       成功地将阴影映射与Qt Quick 2整合之后,接下来可以将阴影映射的效果变得更漂亮一些。如果你成功地运行过我制作的演示程序,那么就会发现,阴影映射的效果并不是那么理想,可能有噪点(粉刺)的出现。这个是和阴影的产生相关,主要还是由于阴影映射这个算法它要求产生的阴影精度是有限的。很多改进的算法都是围绕着如何让阴影更加自然进行研究的。这里我也尝试模仿了一个稍微简单的算法:PCF算法。

       PCF算法的理念也比较简单,简言之就是让产生的阴影更加模糊。它主要在阴影映射的第二遍渲染中做文章。在第二遍渲染的着色器中,执行一个循环,对每一个阴影FBO纹理的从(x-1.5,y-1.5)到(x+1.5,y+1.5)范围内做4 *4=16的采样,采样出来的数据最后做一次平均,最终得到一个平均的阴影。这样,能够较好地减少shadow acne(阴影粉刺)的不和谐的效果。

为了方便以后查阅,将改进后的片断着色器贴出来,其中包含了PCF阴影:

// Common.frag

#ifdef GL_ES
precision highp float;
#endif
const float c_NoShadow      = 0.0;
const float c_SimpleShadow  = 1.0;
const float c_SimpleShadowDynamicBias = 2.0;
const float c_PCFShadow     = 3.0;

const float c_NoLight       = 0.0;
const float c_SimpleLight   = 1.0;

uniform sampler2D texture;
uniform sampler2D shadowTexture;

uniform vec3 lightPosition;
uniform mat4 viewMatrix;
uniform float lightType;
uniform float shadowType;

// PCF专用
uniform float pixelOffsetX;
uniform float pixelOffsetY;

varying vec4 viewSpacePosition;
varying vec4 lightProjectedPosition;
varying vec2 v_texCoord;
varying vec3 v_normal;

float unpack (vec4 colour)
{
    const vec4 bitShifts = vec4(1.0 / (256.0 * 256.0 * 256.0),
                                1.0 / (256.0 * 256.0),
                                1.0 / 256.0,
                                1);
    return dot(colour , bitShifts);
}

float simpleShadow( float bias )
{
    vec4 shadowMapPosition = lightProjectedPosition / lightProjectedPosition.w;
    shadowMapPosition = ( shadowMapPosition + 1.0 ) / 2.0;

    vec4 packedZValue = texture2DProj( shadowTexture, shadowMapPosition );

    float distanceFromLight = unpack( packedZValue );
    float shadowZ = unpack( packedZValue );

    return float( shadowZ > shadowMapPosition.z - bias );
}

float calcBias( )
{
    float bias;

    vec3 n = normalize( v_normal );
    // Direction of the light (from the fragment to the light)
    vec3 l = normalize( lightPosition );

    // Cosine of the angle between the normal and the light direction,
    // clamped above 0
    //  - light is at the vertical of the triangle -> 1
    //  - light is perpendiular to the triangle -> 0
    //  - light is behind the triangle -> 0
    float cosTheta = clamp( dot( n, l ), 0.0, 1.0 );

    bias = 0.0001 * tan( acos( cosTheta ) );
    bias = clamp( bias, 0.0, 0.01 );

    return bias;
}

float lookup( vec2 offSet, float bias )
{
    vec4 shadowMapPosition = lightProjectedPosition / lightProjectedPosition.w;
    shadowMapPosition = ( shadowMapPosition + 1.0 ) / 2.0;

    vec4 coordOffset = vec4( offSet.x * pixelOffsetX, offSet.y * pixelOffsetY, 0.05, 0.0 );
    vec4 packedZValue = texture2DProj( shadowTexture, shadowMapPosition + coordOffset );
    float distanceFromLight = unpack( packedZValue );
    float shadowZ = unpack( packedZValue );
    return float( shadowZ > shadowMapPosition.z - bias );
}

float PCFShadow( float bias )// PCF阴影
{
    float shadow = 0.0;

    for ( float y = -1.5; y <= 1.5; y = y + 1.0 )
    {
        for ( float x = -1.5; x <= 1.5; x = x + 1.0 )
        {
            shadow += lookup( vec2( x, y ), bias );
        }
    }

    shadow /= 16.0;
    return shadow;
}

void main( )
{
    // 计算纹理
    vec4 textureColor = texture2D( texture, v_texCoord );

    // 计算光照
    vec4 lightColor = vec4( 1.0 );
    if ( lightType <= c_NoLight )
    {
        // 没有光照,没有任何操作
    }
    else if ( lightType <= c_SimpleLight )
    {
        vec4 viewSpaceLightPosition = viewMatrix * vec4( lightPosition, 1.0 );
        vec4 lightVector = viewSpaceLightPosition - viewSpacePosition;
        lightVector = normalize( lightVector );
        float NdotL = dot( v_normal, vec3( lightVector ) );

        float diffuse = max( 0.0, NdotL );
        float ambient = 0.3;
        lightColor = vec4( ambient + diffuse );
    }

    // 计算阴影
    float shadow = 1.0;
    if ( shadowType <= c_NoShadow )
    {
        // 没有阴影
    }
    else
    {
        if ( lightProjectedPosition.w > 0.0 )
        {
            if ( shadowType <= c_SimpleShadow )
            {
                shadow = simpleShadow( 0.0005 );
            }
            else if ( shadowType <= c_SimpleShadowDynamicBias )
            {
                shadow = simpleShadow( calcBias( ) );
            }
            else if ( shadowType <= c_PCFShadow )
            {
                shadow = PCFShadow( calcBias( ) );
            }
            shadow = shadow * 0.8 + 0.2;
        }
    }
    gl_FragColor = textureColor * lightColor * shadow;
}

下面是PCF阴影以及普通阴影的比较:

       这次的程序,除了Windows等桌面平台,在Android以及Windows Phone均可以运行。

       有关PCF的详细原理,可以参考NVIDIA的资料:这里

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值