关闭

简单理解ShadowMap阴影的实现

标签: OpenGL光照阴影ShadowMap
2030人阅读 评论(0) 收藏 举报

shadowmap阴影的原理:

判断顶点距离光源的位置,如果是最近的,那么顶点不会有阴影覆盖。否则,顶点被阴影覆盖。

所以,首先判断顶点到光源的位置。就要先获得离光源最近的片段的深度。

于是,深度图来了。深度图就渲染到纹理吧,这里就用到FBO了,这个我就不细说了。

透视投影变换可以得到3D到2D的图像,变换的最终结果是vec4 Final = Projection * View * Model * Vertex;

Vertex 顶点本来就是vec4(vec4(x,y,z,w))。而Direction 方向是vec3。

经过Perspective透视变换后,x/w,y/w,z/w。都会在[-1,1]这个范围内。


Perspective(fovy,ratio,znear,zfar)这个函数得到的深度值z/w的增长不是线性的。离znear越近深度值z/w越精确。

在视景锥空间内,当顶点到摄像机的距离刚好为znear,深度z/w为0。当顶点到摄像机的距离为zfar,深度z/w为1。

但由于不是线性的,深度值会越来越不准确。所以我们要实现一个线性的深度,写个shader。

主要的就是float depth =distance(CameraPostion.xyz,WorldPos.xyz);

CameraPostion为光源的位置,因为我们计算的是进过世界坐标变换后的顶点的位置到光源的位置。depth 就是两者之间的距离。

另外一点是,OpenGL的Texture采样的话 vec4 color = texture(sampler2D,texcoord);得到的vec4总是在0.0到1.0的颜色空间范围。

所以要表示大于1的深度要采取折衷的方式来描述深度的位置。

//深度图 Vertex Shader
#version 330
layout (location = 0) in vec3 Position;
layout (location = 1) in vec3 Normal;
layout (location = 2) in vec2 TexCoord;

uniform mat4 matModel;
uniform mat4 matView;
uniform mat4 matProjection;
uniform mat4 matMVP;
uniform vec4 CameraPosition;

out float depth;

void main()
{
	//基本变换
	//MVP * pos = matProjection * matView * matModel * vec4(Position, 1.0);
	vec4 WorldPos = matModel * vec4(Position,1.0);
    gl_Position = matMVP * vec4(Position, 1.0);
	//深度值
	depth = distance(CameraPosition.xyz,WorldPos.xyz);
}

//深度图 Fragment Shader
#version 330
in vec2 TexCoord0;
in vec3 Normal0; 
in vec4 FinalLighting;

in float depth;
out vec4 FragColor; 

void main() 
{
	FragColor = vec4(depth ,depth/255 ,depth/65535 ,1.0);//深度图,自定义深度描述
}


深度图出来后,接着要进行的就是真正的渲染。

在渲染场景时,计算场景中的顶点到摄像机的距离,跟场景中的顶点到对应到深度图的所在的纹理坐标取得的深度值对比。

很简单,如果深度比深度图对应的纹素的深度要大,那么就是被遮挡了,颜色就暗一点。否则就是被直接光照的。

难的是,场景中的顶点怎么跟深度图的纹理坐标对应呢,就是场景中的顶点跟深度图的哪个纹素做对比。

最初我的疑难就是在这里。最后发觉把顶点变换成纹理坐标就是就是再传一个矩阵matShadowMapViewProjection。

这个matShadowMapViewProjection就是当时进行渲染深度图的在光源处进行透视的MVP矩阵。

到此就可以知道顶点应该跟深度图的哪个纹理坐标对比了,或者说顶点变换后的x/w,y/w就是纹理坐标。

只不过x/w,y/w的范围在[-1,1],刚才说过。纹理坐标是在[0,1] 所以 vec2 st = (vec2(x/w,y/w)+vec2(1,1))*0.5;

接着就是对st进行纹理采样。对比深度值,然后判断是否有阴影。

//正常渲染Vertex Shader
#version 330
layout (location = 0) in vec3 Position;
layout (location = 1) in vec3 Normal;
layout (location = 2) in vec2 TexCoord;

struct LightInfo{
	vec4 lightPosition;
    vec3 lightDirection;
    vec3 lightIntensity;
    vec3 lightAmbient;
    vec3 lightAttenuation;
};

struct MaterialInfo{
    vec4 mtlEmssive;
    vec4 mtlReflectionAmbient;
    vec4 mtlReflectionDiffuse;
    vec4 mtlReflectionSpecular;
	float mtlSpecularShiness;
	vec4 mtlOpacity;
};
uniform LightInfo light0;
uniform MaterialInfo mtl0;
//vec3 is for direciton,vec4 is for position;
//in variable should use vec3 than vec4,shorter first.so data input Postion is using vec3.

uniform mat4 matModel;
uniform mat4 matView;
uniform mat4 matProjection;
uniform mat4 matMVP;
uniform vec4 CameraPosition;
uniform mat4 matShadowMapViewProjection;

out vec2 TexCoord0;
out vec3 Normal0;
out vec4 FinalLighting;

out vec2 depthTexCoord;
out float lengthToLight0;

void main()
{
	//基本变换
	//MVP * pos = matProjection * matView * matModel * vec4(Position, 1.0);
	vec4 WorldPos = matModel * vec4(Position,1.0);
    gl_Position = matMVP * vec4(Position, 1.0);
    TexCoord0 = TexCoord;
    Normal0 = (matModel * vec4(Normal,0.0)).xyz;
	vec3 l0dir = (matModel * vec4(light0.lightDirection,0.0)).xyz;
	//要用的变量,避免重复计算
	float angle =dot(Normal0, normalize(-l0dir)); 
	vec3 lightReflect = normalize(reflect(l0dir, Normal0));
	float lengthToEye = distance(CameraPosition,WorldPos);
	//阴影计算
	vec4 light0pos = (matShadowMapViewProjection * WorldPos);// x/w,y/w,z/w (-1,1)
	depthTexCoord = (light0pos.xy / light0pos.w + vec2(1.0,1.0))*0.5;//世界变换后顶点对应的深度纹理坐标
	lengthToLight0 = distance(light0.lightPosition,WorldPos) ;// 世界变换后顶点到光源的位置
	//环境光照
	vec3 directionToEye = normalize((CameraPosition - WorldPos).xyz);
	vec4 la = vec4(light0.lightAmbient,1.0) * mtl0.mtlReflectionAmbient;
	//漫射光照
	vec4 ld = vec4(0.0,0.0,0.0,0.0);
	if(angle > 0)
		ld = vec4(light0.lightIntensity,1.0) * mtl0.mtlReflectionDiffuse * angle;
	//镜面光照
	vec4 ls = vec4(0.0,0.0,0.0,0.0);
	float specularFactor = dot(directionToEye,lightReflect);
	if(specularFactor > 0 && angle > 0)
		ls = vec4(light0.lightIntensity,1.0) * mtl0.mtlReflectionSpecular * pow(specularFactor,mtl0.mtlSpecularShiness);
	//最终光照
	float attenuationFactor = 1.0 / (light0.lightAttenuation.x + light0.lightAttenuation.y * lengthToEye + pow(light0.lightAttenuation.z * lengthToEye,2));
	FinalLighting = la + mtl0.mtlEmssive + attenuationFactor * (ld + ls);
}

//正常渲染 Fragment Shader
#version 330
in vec2 TexCoord0;
in vec3 Normal0; 
in vec4 FinalLighting;

in vec2 depthTexCoord;
in float lengthToLight0;

uniform int TextureCount;
uniform sampler2D Sampler0;
uniform sampler2D SamplerDepthMap;
 
out vec4 FragColor; 

void main() 
{
	float factorShadow = 1.0;
	vec4 vd = texture(SamplerDepthMap,depthTexCoord);//纹理采样的范围最终为[0,1] value<0 的一律为0,value>1 的一律为1 
	float depth = vd.x + vd.y * 255 + vd.z * 65535;//自定义的深度描述
	if (lengthToLight0 > depth)//阴影判断
		factorShadow = 0.5;
	if(TextureCount == 0)
	{
		FragColor = FinalLighting * factorShadow;
	}
	else if(TextureCount == 1)
	{
		FragColor = texture(Sampler0, TexCoord0.xy) * FinalLighting * factorShadow;
	}
	//gl_FragDepth = Depth;
}


0
1

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:14138次
    • 积分:396
    • 等级:
    • 排名:千里之外
    • 原创:16篇
    • 转载:1篇
    • 译文:0篇
    • 评论:4条
    最新评论