版权声明:本文为 松阳 (blog.csdn.net/fansongy) 原创文章,转载必须注明出处: https://blog.csdn.net/fansongy/article/details/79303712
阴影 可以使角色与地面的关系更加明确。本文主要介绍如何通过 ShadowMap 在OpenGL中实现 阴影 。
核心原理
Shadow Map的基本思想:通过LightView
画一张DepthMap
,然后Camera View
渲场景的时候,把Pixel坐标变换到Light Space,比较Depth即可(Pixel的Depth大于Shadow Map的Depth即在阴影区)。也可以直接是ScreenSpace
对DepthMap
做Post-processing
。
阴影 生成需要三个步骤:
- 在角色正上方照出一张深度图。
- 视线方向的模型绘制点,在上方摄像机视口的Z值与深度图相同位置的Z值进行比较,如果比深度图中的值小则为亮区,反之需要绘制阴影。
- 将颜色物体颜色与阴影绘制在一起。
实现
深度图其实就是一张灰度图片,可以简单的将摄像机放到物体正上方,绘制出常规效果之后,只提取R通道。另外通过pow可以加深depthValue。
//fs
varying vec2 V_Texcoord;
uniform sampler2D U_MainTexture;
void main()
{
float depthValue=texture2D(U_MainTexture,V_Texcoord).r;
gl_FragColor=vec4(vec3(pow(depthValue,2.0)),1.0);
}
有了shadowmap,接下来需要在比较阴影区域的深度值,可以通过
//vs
attribute vec3 pos;
attribute vec2 texcoord;
attribute vec3 normal;
uniform mat4 M;
uniform mat4 P;
uniform mat4 V;
uniform mat4 U_ProjectorMatrix;
varying vec2 V_Texcoord;
varying vec3 V_WorldPos;
varying vec4 V_ProjectCoord;
void main()
{
V_Texcoord=texcoord;
vec4 worldPos=M*vec4(pos,1.0);
V_WorldPos=worldPos.xyz;
V_ProjectCoord=U_ProjectorMatrix*worldPos;
gl_Position=P*V*worldPos;
}
//fs
float CalculateShadow()
{
vec3 fragPos=V_ProjectorSpaceFragPos.xyz/V_ProjectorSpaceFragPos.w;
fragPos=fragPos*0.5+vec3(0.5); //to 0-1
float depthInShadowMap=texture2D(U_ShadowMap,fragPos.xy).r;
float currentDepth=fragPos.z;
float shadow=(currentDepth-0.001)>depthInShadowMap?1.0:0.0;
return shadow;
}
void main()
{
//...
vec4 color=ambientColor+(diffuseColor+specularColor)*vec4(vec3(1.0-CalculateShadow()),1.0);
}
总结
经过上述步骤可以将 阴影 绘制出来。如果希望阴影能降低锯齿,可以将深度图进行高斯模糊,或使用PCF(Percentage Closer Filtering)进行柔化。