改换矩阵

基础篇

第一个OpenGL ES项目
绘制三角形
什么是Shader?
制图更加多的图形
更换矩阵
透视投影和正交投影
摄像机
绘图一个正方体
骨干光照
基本纹理


进阶篇

晶莹剔透和交集
激光特效
VBO和VAO
顶点索引
制图圆柱体
绘图地形
加载OBJ文件
高级光照
法线贴图
渲染到纹理

如何可以很快的发生更类似实际的黑影一贯是摄像游戏的一个很有挑衅的工作,本文介绍近年来所为人明白的三种影子技术之一的ShadowMap(阴影图)技术。

高级篇

纹理投影效果
ShadowMap(一)
ShadowMap(二)
基于CubeMap的反射功能
教您造一面镜子
教您兑现SkyBox
教你制作迷雾
大体引擎
Billboards
粒子效果

ShadowMap

ShadowMap技术的定义应该说是最早选择在视频游戏中的阴影达成技能,有着卓殊火速和急迅的特性,在落实阴影的还要只须求绝对很小的计量负担。

ShadowMap技术很好了解:首要是分成两步。第一步从光源方向(DirectionLight)/光源点(Spot
Light)绘制场景,将气象渲染到一张纹理中(Shadow
Map)。第二步就从主相机渲染,在渲染进度少校每个像素的吃水(光照空间)和ShadowMap中采样得到的深浅开展相比较,固然低于则不处在阴影中,否则处于阴影中,然后在举行融合就好了。

图片 1

shadowmap

但那一个技术存在多少个难点:1.由于ShadowMap的分辨率有限,导致阴影有强烈的锯齿。
2.影子没有半影区,边缘很硬,不实事求是。于是我们接纳PCF(percentage close
filter)举行优化,使用PCF的由来是:对品质影响小,不难已毕。

PCF(percentage close filter)

PCF 之所以说percentage
就是因为PCF阴影的标志不再唯有0,1多少个值,出现了小数,那些小树就表示阴影的权重,我想可能现在您曾经想到了,没错,PCF就是对ShadowMap中一定半径内的纹素举办采样,分别和当下点的深度比较(光照空间),sampleDepth
< depth ? 0 : 1 ,然后求和除以采样数到手权重,那里运用的是泊松采样。

图片 2

PCF

上边看下代码:

ShadowMap中的vs:

VertexOut VS(VertexIn vin)
{
    VertexOut vout;
    // 转换到齐次剪裁空间
    vout.PosH = mul(float4(vin.PosL, 1.0f), World);
    vout.PosH = mul(vout.PosH, View);
    vout.PosH = mul(vout.PosH, Proj);
    vout.Depth = vout.PosH.zw;
    return vout;
}

ShadowMap中的PS:

float4 PS(VertexOut pin) : SV_Target
{
    float d = pin.Depth.x / pin.Depth.y;
    float4 color = float4(d, d, d, 1.0f);
    return color;
}

影子渲染的VS:

VertexOut VS(VertexIn vin)
{
    VertexOut vout;

    // 转换到齐次剪裁空间
    vout.PosH = mul(float4(vin.PosL, 1.0f), World);
    vout.PosW = vout.PosH.xyz;
    vout.PosH = mul(vout.PosH, View);
    vout.PosH = mul(vout.PosH, Proj);
    //将法线变换到世界坐标系
    vout.Normal = mul(float4(vin.Normal, 0.0f), InverseTransposeWorld).rgb;

    vout.Tangent = mul(float4(vin.Tangent, 0.0f), World).rgb;
    vout.Tex = vin.Tex;
    //顶点在摄像机视角齐次裁剪空间
    vout.PosLightH = mul(float4(vin.PosL, 1.0f), LightView);
    vout.PosLightH = mul(vout.PosLightH, LightProj);

    return vout;
}

黑影渲染的PS:

float2 poissonDisk[16] = {
    float2(-0.94201624, -0.39906216),
    float2(0.94558609, -0.76890725),
    float2(-0.094184101, -0.92938870),
    float2(0.34495938, 0.29387760),
    float2(-0.91588581, 0.45771432),
    float2(-0.81544232, -0.87912464),
    float2(-0.38277543, 0.27676845),
    float2(0.97484398, 0.75648379),
    float2(0.44323325, -0.97511554),
    float2(0.53742981, -0.47373420),
    float2(-0.26496911, -0.41893023),
    float2(0.79197514, 0.19090188),
    float2(-0.24188840, 0.99706507),
    float2(-0.81409955, 0.91437590),
    float2(0.19984126, 0.78641367),
    float2(0.14383161, -0.14100790)
};

float PCF_NUM_SAMPLES = 16.0f;
float SHADOW_EPSILON = 0.000004;//深度信息不是线性的,存在误差,手动调整
float2 SMAP_SIZE = { 800, 600 };

float PCF_Filter(float2 uv, float zReceiver, float filterRadiusUV)    //zReceiver为深度信息
{
    float sum = 0.0f;
    for (int i = 0; i < PCF_NUM_SAMPLES; ++i)
    {
        float2 offset = poissonDisk[i] * filterRadiusUV / SMAP_SIZE;  //SMAP_SIZE是Shadow Map的材质大小
        float Zdepth = depthTex.Sample(samplerClamp, uv + offset).x + SHADOW_EPSILON;
        if (Zdepth >= zReceiver)
            sum += 1.0f;
    }
    return sum / PCF_NUM_SAMPLES;
}

float4 PS(VertexOut pin) : SV_Target
{
     int lightCount = 1;
     float3 toEye = normalize(g_eyePos - pin.PosW);

     //将投影空间的坐标转化为纹理空间坐标
     float2 depthCoord = 0.5*(pin.PosLightH.xy / pin.PosLightH.w + float2(1.0f, 1.0f));
     depthCoord.y = 1.0f - depthCoord.y;

     float4 ambient = { 0.0f, 0.0f, 0.0f, 0.0f };
     float4 diffuse = { 0.0f, 0.0f, 0.0f, 0.0f };
     float4 specular = { 0.0f, 0.0f, 0.0f, 0.0f };

     //纹理的颜色
     float4 texColor = woodTex.Sample(samplerTex, pin.Tex);

     //法线贴图采样
     float3 texnormal = normalTex.Sample(samplerTex, pin.Tex).rgb;
     float3 normal = NormalSampleToWorldSpace(texnormal, pin.Normal, pin.Tangent);//这个函数是上一篇NormalMap中的函数,用来把采样来的法线转换到世界坐标系

     //下面这段是用来计算光照的
     for (int i = 0; i < lightCount; ++i)
     {
         float4 a, d, s;
         ComputeDirLight(g_material, g_lights[i], normal, toEye, a, d, s);//这是我自己的函数,没有贴在这里
         ambient += a;
         diffuse += d;
         specular += s;
     }

     float4 color;
     float LightAmount = PCF_Filter(depthCoord, pin.PosLightH.z / pin.PosLightH.w, 1.0f);

     //不在阴影中
     if (LightAmount == 1.0f)
     {
        color = texColor*saturate(ambient + diffuse);// +saturate(specular);
     }
     else//根据权重决定颜色(阴影中)
     {
        color = texColor*saturate(ambient + diffuse*LightAmount);
     }
     return color;
}

效果图:

图片 3

ShadowMap+PCF

相关文章