阴影映射';暗疮';使用OpenGL和GLSL的工件
我编写了一个简单的3D应用程序,使用著名的正面剔除技术实现硬阴影和PCF阴影映射算法。不幸的是,这种技术的问题是只有密封的网格才能产生投射阴影。例如,平面不能产生这样的效果,因为平面本身就是正面 因此,解决方案是使用函数“glPolygonOffset”,其目标是在“灯光视图”中可见的每个顶点的深度渲染路径期间稍微修改深度值。换言之,需要此功能来避免“阴影痤疮”瑕疵,此时保留所有网格正面 下面是使用硬阴影映射算法进行渲染的显示: 正如您所见,阴影渲染是完美的,没有任何瑕疵阴影映射';暗疮';使用OpenGL和GLSL的工件,opengl,glsl,opengl-3,opengl-4,shadow-mapping,Opengl,Glsl,Opengl 3,Opengl 4,Shadow Mapping,我编写了一个简单的3D应用程序,使用著名的正面剔除技术实现硬阴影和PCF阴影映射算法。不幸的是,这种技术的问题是只有密封的网格才能产生投射阴影。例如,平面不能产生这样的效果,因为平面本身就是正面 因此,解决方案是使用函数“glPolygonOffset”,其目标是在“灯光视图”中可见的每个顶点的深度渲染路径期间稍微修改深度值。换言之,需要此功能来避免“阴影痤疮”瑕疵,此时保留所有网格正面 下面是使用硬阴影映射算法进行渲染的显示: 正如您所见,阴影渲染是完美的,没有任何瑕疵 这里是定义深度纹理渲
这里是定义深度纹理渲染路径的C++客户端代码:
/*glEnable(GL_CULL_FACE); //OLD TECHNIQUE
glCullFace(GL_FRONT);*/
for (uint32_t idy = 0; idy < lightSceneNodeList.size(); idy++)
{
if (lightSceneNodeList[idy]->IsShadowEnabled())
{
type::ShadowCasterPtr pShadowCaster = ShadowManager::GetSingleton()
.FindShadowCasterByName(lightSceneNodeList[idy]->GetName());
{
pShadowCaster->Bind(TARGET_FBO);
{
glEnable(GL_POLYGON_OFFSET_FILL); //NEW TECHNIQUE
glPolygonOffset(1.1f, 4.0f);
glClear(GL_DEPTH_BUFFER_BIT);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
{
pShadowCaster->UpdateFrustrumPosition(
lightSceneNodeList[idy]->GetParentModelMatrix());
pShadowCaster->SetViewport();
{
for (uint32_t idx = 0; idx < pBatchList.size(); idx++)
pBatchList[idx]->Render(pShadowCaster);
}
}
glDisable(GL_POLYGON_OFFSET_FILL);
}
pShadowCaster->Unbind(TARGET_FBO);
}
}
}
//glDisable(GL_CULL_FACE);
/*
** \brief Recover the depth value from shadow map by projection
*/
float Tex2D_Proj(sampler2DShadow shadowSampler, vec4 LightToVertexDir_LS)
{
float ShadowFactor = 1.0f;
{
vec3 LightToVertexDir_CS = LightToVertexDir_LS.xyz/LightToVertexDir_LS.w;
ShadowFactor = texture(shadowSampler, LightToVertexDir_CS);
}
return (ShadowFactor);
}
/*
** \brief Returns biased hard shadow factor.
*/
float Get_2D_Hard_ShadowFactor(sampler2DShadow shadowSampler, int index)
{
float shadowFactor = 1.0f;
{
if (ShadowCoords[index].z <= MaxShadowDist[index])
{
if (ShadowCoords[index].w > 0.0f);
{
shadowFactor = Tex2D_Proj(shadowSampler, ShadowCoords[index]);
}
}
}
return (shadowFactor);
}
只有调用“TextureProjectOffset(shadowSampler,ShadowCoords[index],ivec2(0,0))”才能正常工作(当然它指的是第一种硬阴影映射技术)
如果我只使用以下调用(因此使用简单的硬阴影映射,但使用偏移):
我有以下渲染:
正如你所看到的,只有在立方体的右边才有“暗影痤疮”人工制品
为了解决我的问题,我尝试了几种代码组合,添加了一些偏差值来处理光空间中的顶点深度值,但没有成功。不知道这是否有帮助。也许我把你的问题复杂化了。这段代码来自一个Objective C应用程序,但相关的代码大多只是C。它运行并工作。您将在场景片段着色器中看到一条语句:if(depthInShadow>0.006)。这个阴影深度数字是我必须“调整”以去除粉刺的东西 这是一个编辑。重新阅读您的帖子后,我看到一条语句:if(ShadowCoords[index].w>0.0f)。这看起来非常类似于我的场景片段着色器中的depthInShadow语句,我必须“调整”到略大于0.0的值才能去除粉刺。试试看 我将在下面留下代码,以防新接触阴影贴图的其他人感兴趣 许多变量都是在.h文件中声明的,但您会明白这一点。这是相当标准的阴影映射代码,所以如果您之前已经看到了这一切,您可以停在这里,节省您的阅读时间 我设置了阴影缓冲区和着色器:
// ************************************* Save the Current Frame Buffer
glGetIntegerv(GL_FRAMEBUFFER_BINDING, renderBuffer);
// ************************************ Create the Shadow Map Texture
glGenTextures(1, shadowTexture);
glBindTexture(GL_TEXTURE_2D, shadowTexture[0]);
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_NEAREST );
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_NEAREST );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, SHADOWMAPRATIO * VIEWWIDTH, SHADOWMAPRATIO * VIEWHEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glActiveTexture(GL_TEXTURE5);
glBindTexture(GL_TEXTURE_2D, shadowTexture[0]);
// ************************************ Create the Shadow Map Frame Buffer
glGenFramebuffersEXT(1, shadowBuffer);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, shadowBuffer[0]);
// **************************************** No Color Attachment
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
// ************************************* Attach the Shadow Texture to It
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, shadowTexture[0], 0);
// ******************************* Check to see if Frame Buffer is Complete
GLenum frameBufferStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT);
if(frameBufferStatus != GL_FRAMEBUFFER_COMPLETE)
{
NSLog(@"There is a problem with the shadow frame buffer, %d", frameBufferStatus);
if(frameBufferStatus == GL_INVALID_ENUM) NSLog(@"Invalid Enum.");
if(frameBufferStatus == GL_INVALID_VALUE) NSLog(@"Invalid Value.");
if(frameBufferStatus == GL_INVALID_OPERATION) NSLog(@"Invalid Operation");
if(frameBufferStatus == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) NSLog(@"Incomplete Attachment");
if(frameBufferStatus == GL_FRAMEBUFFER_UNSUPPORTED) NSLog(@"Unsupported");
}
// *********************************** Reset the original Frame Buffer
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, renderBuffer[0]);
// ************************************** Compile and Link the Shadow Shaders
ShaderInfo shadowShaderInfo[] = {
{ GL_VERTEX_SHADER, "path/shadow120.vsh" },
{ GL_FRAGMENT_SHADER, "path/shadow120.fsh" },
{ GL_NONE, NULL }
};
shadowShaders = LoadShaders(shadowShaderInfo);
glUseProgram(shadowShaders);
shadowPositionLoc = glGetAttribLocation(shadowShaders, "ShadowPosition");
shadowViewMatrixLoc= glGetUniformLocation(shadowShaders, "ShadowViewMatrix");
if(shadowViewMatrixLoc == -1) NSLog(@"View Matrix not found in shadow shader");
shadowModelMatrixLoc= glGetUniformLocation(shadowShaders, "ShadowModelMatrix");
if(shadowModelMatrixLoc == -1) NSLog(@"Model Matrix not found in shadow shader");
shadowProjectionMatrixLoc= glGetUniformLocation(shadowShaders, "ShadowProjectionMatrix");
if(shadowProjectionMatrixLoc == -1) NSLog(@"Projection Matrix not found in shadow shader");
shadowColorLoc= glGetUniformLocation(shadowShaders, "FrontColor");
if(shadowColorLoc == -1) NSLog(@"Front Color not found in shadow shader");
当然,均匀阴影矩阵来自摄影机位置
用于渲染到阴影缓冲区的阴影着色器非常简单
阴影顶点着色器:
#version 120
attribute vec4 ShadowPosition;
uniform mat4 ShadowModelMatrix;
uniform mat4 ShadowViewMatrix;
uniform mat4 ShadowProjectionMatrix;
void main()
{
gl_Position = ShadowProjectionMatrix * ShadowViewMatrix * ShadowModelMatrix * ShadowPosition;
}
#version 120
attribute vec4 RingPosition;
attribute vec3 RingNormal;
uniform vec4 LightPosition;
uniform mat4 RingModelMatrix;
uniform mat4 RingViewMatrix;
uniform mat4 RingProjectionMatrix;
uniform mat3 RingNormalMatrix;
uniform mat4 ShadowBiasMatrix;
uniform mat4 ShadowModelMatrix;
uniform mat4 ShadowViewMatrix;
uniform mat4 ShadowProjectionMatrix;
varying float DiffuseIntensity;
varying float SpecularIntensity;
varying vec4 ShadowCoordinate;
const float specularContribution = 1.0;
const float diffuseContribution = 1.0;
void main()
{
mat4 ShadowMatrix = ShadowBiasMatrix * ShadowProjectionMatrix * ShadowViewMatrix * ShadowModelMatrix;
ShadowCoordinate = ShadowMatrix * RingPosition;
vec3 lightPosition= vec3(LightPosition);
float shininess = gl_FrontMaterial.shininess;
vec3 ecPosition = vec3(RingViewMatrix * RingModelMatrix * RingPosition);
vec3 tnorm = normalize(RingNormalMatrix * RingNormal);
vec3 lightVec = normalize(lightPosition - ecPosition);
vec3 reflectVec = reflect(-lightVec, tnorm);
vec3 viewVec = normalize(-ecPosition);
float spec = clamp(dot(reflectVec, viewVec), 0.0, 1.0);
SpecularIntensity = specularContribution * pow(spec, shininess / 5.0);
DiffuseIntensity = diffuseContribution * max(dot(lightVec, tnorm), 0.0);
gl_Position = RingProjectionMatrix * RingViewMatrix * RingModelMatrix * RingPosition;
}
阴影片段着色器:
float Get_2D_PCF_ShadowFactor(sampler2DShadow shadowSampler, int index)
{
float shadowFactor = 0.0f;
{
int kernel_base = int(PCFKernelType[index])/2;
float kernel_count = pow(int(PCFKernelType[index]), 2.0f);
if (ShadowCoords[index].z <= MaxShadowDist[index])
{
if (ShadowCoords[index].w > 0.0f)
{
shadowFactor += textureProjOffset(shadowSampler, ShadowCoords[index], ivec2(-1, 1));
shadowFactor += textureProjOffset(shadowSampler, ShadowCoords[index], ivec2(1, 1));
shadowFactor += textureProjOffset(shadowSampler, ShadowCoords[index], ivec2(1, -1));
shadowFactor += textureProjOffset(shadowSampler, ShadowCoords[index], ivec2(-1, -1));
shadowFactor *= 0.25f;
}
}
}
return (shadowFactor);
}
#version 120
uniform vec4 FrontColor;
void main()
{
gl_FragColor = FrontColor;
}
#version 120
uniform sampler2D ShadowMap;
varying float DiffuseIntensity;
varying float SpecularIntensity;
varying vec4 ShadowCoordinate;
void main()
{
vec3 emission = vec3(gl_FrontMaterial.emission);
vec3 ambient = vec3(gl_FrontMaterial.ambient);
vec3 diffuse = vec3(gl_FrontMaterial.diffuse);
vec3 specular = vec3(gl_FrontMaterial.specular);
// Normalize the Shadow Map coordinates
// shadowCoordinateWdivide.z = current fragment depth from light
vec3 shadowCoordinateWdivide = ShadowCoordinate.xyz / ShadowCoordinate.w ;
float distanceFromLight = texture2D(ShadowMap, shadowCoordinateWdivide.xy).r;
float depthInShadow = shadowCoordinateWdivide.z - distanceFromLight;
float specularIntensity = SpecularIntensity;
float diffuseIntensity = DiffuseIntensity;
if (depthInShadow > 0.006)
{
specularIntensity = SpecularIntensity * 0.0;
diffuseIntensity = DiffuseIntensity * 0.0;
}
vec3 lightColor = emission;
lightColor = lightColor + ambient;
lightColor = lightColor + (specularIntensity * specular);
lightColor = lightColor + (diffuseIntensity * diffuse);
lightColor = clamp(lightColor, 0.0, 1.0);
gl_FragColor = vec4(lightColor, 1.0);
}
我首先使用以下方法将场景渲染到阴影缓冲区:
glUseProgram(shadowShaders);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, shadowBuffer[0]);
glColorMask ( GL_FALSE , GL_FALSE , GL_FALSE , GL_FALSE );
glActiveTexture(GL_TEXTURE5);
glBindTexture(GL_TEXTURE_2D, shadowTexture[0]);
glClearDepth(1.0);
glClear(GL_DEPTH_BUFFER_BIT);
glUniformMatrix4fv(shadowModelMatrixLoc, 1, GL_FALSE, lightGLKModelMatrix.m);
glUniformMatrix4fv(shadowViewMatrixLoc, 1, GL_FALSE, lightGLKViewMatrix.m);
glUniformMatrix4fv(shadowProjectionMatrixLoc, 1, GL_FALSE, lightGLKProjectionMatrix.m);
glUniform4fv(shadowColorLoc, 1, worldAmbient);
.... rendering code ....
glDisable(GL_POLYGON_OFFSET_FILL);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, renderBuffer[0]);
glColorMask ( GL_TRUE , GL_TRUE , GL_TRUE , GL_TRUE );
然后,我使用以下着色器正常渲染场景:
场景顶点着色器:
#version 120
attribute vec4 ShadowPosition;
uniform mat4 ShadowModelMatrix;
uniform mat4 ShadowViewMatrix;
uniform mat4 ShadowProjectionMatrix;
void main()
{
gl_Position = ShadowProjectionMatrix * ShadowViewMatrix * ShadowModelMatrix * ShadowPosition;
}
#version 120
attribute vec4 RingPosition;
attribute vec3 RingNormal;
uniform vec4 LightPosition;
uniform mat4 RingModelMatrix;
uniform mat4 RingViewMatrix;
uniform mat4 RingProjectionMatrix;
uniform mat3 RingNormalMatrix;
uniform mat4 ShadowBiasMatrix;
uniform mat4 ShadowModelMatrix;
uniform mat4 ShadowViewMatrix;
uniform mat4 ShadowProjectionMatrix;
varying float DiffuseIntensity;
varying float SpecularIntensity;
varying vec4 ShadowCoordinate;
const float specularContribution = 1.0;
const float diffuseContribution = 1.0;
void main()
{
mat4 ShadowMatrix = ShadowBiasMatrix * ShadowProjectionMatrix * ShadowViewMatrix * ShadowModelMatrix;
ShadowCoordinate = ShadowMatrix * RingPosition;
vec3 lightPosition= vec3(LightPosition);
float shininess = gl_FrontMaterial.shininess;
vec3 ecPosition = vec3(RingViewMatrix * RingModelMatrix * RingPosition);
vec3 tnorm = normalize(RingNormalMatrix * RingNormal);
vec3 lightVec = normalize(lightPosition - ecPosition);
vec3 reflectVec = reflect(-lightVec, tnorm);
vec3 viewVec = normalize(-ecPosition);
float spec = clamp(dot(reflectVec, viewVec), 0.0, 1.0);
SpecularIntensity = specularContribution * pow(spec, shininess / 5.0);
DiffuseIntensity = diffuseContribution * max(dot(lightVec, tnorm), 0.0);
gl_Position = RingProjectionMatrix * RingViewMatrix * RingModelMatrix * RingPosition;
}
场景片段着色器:
float Get_2D_PCF_ShadowFactor(sampler2DShadow shadowSampler, int index)
{
float shadowFactor = 0.0f;
{
int kernel_base = int(PCFKernelType[index])/2;
float kernel_count = pow(int(PCFKernelType[index]), 2.0f);
if (ShadowCoords[index].z <= MaxShadowDist[index])
{
if (ShadowCoords[index].w > 0.0f)
{
shadowFactor += textureProjOffset(shadowSampler, ShadowCoords[index], ivec2(-1, 1));
shadowFactor += textureProjOffset(shadowSampler, ShadowCoords[index], ivec2(1, 1));
shadowFactor += textureProjOffset(shadowSampler, ShadowCoords[index], ivec2(1, -1));
shadowFactor += textureProjOffset(shadowSampler, ShadowCoords[index], ivec2(-1, -1));
shadowFactor *= 0.25f;
}
}
}
return (shadowFactor);
}
#version 120
uniform vec4 FrontColor;
void main()
{
gl_FragColor = FrontColor;
}
#version 120
uniform sampler2D ShadowMap;
varying float DiffuseIntensity;
varying float SpecularIntensity;
varying vec4 ShadowCoordinate;
void main()
{
vec3 emission = vec3(gl_FrontMaterial.emission);
vec3 ambient = vec3(gl_FrontMaterial.ambient);
vec3 diffuse = vec3(gl_FrontMaterial.diffuse);
vec3 specular = vec3(gl_FrontMaterial.specular);
// Normalize the Shadow Map coordinates
// shadowCoordinateWdivide.z = current fragment depth from light
vec3 shadowCoordinateWdivide = ShadowCoordinate.xyz / ShadowCoordinate.w ;
float distanceFromLight = texture2D(ShadowMap, shadowCoordinateWdivide.xy).r;
float depthInShadow = shadowCoordinateWdivide.z - distanceFromLight;
float specularIntensity = SpecularIntensity;
float diffuseIntensity = DiffuseIntensity;
if (depthInShadow > 0.006)
{
specularIntensity = SpecularIntensity * 0.0;
diffuseIntensity = DiffuseIntensity * 0.0;
}
vec3 lightColor = emission;
lightColor = lightColor + ambient;
lightColor = lightColor + (specularIntensity * specular);
lightColor = lightColor + (diffuseIntensity * diffuse);
lightColor = clamp(lightColor, 0.0, 1.0);
gl_FragColor = vec4(lightColor, 1.0);
}
阴影偏移矩阵为:
GLfloat shadowBiasMatrix[16] = {
0.5, 0.0, 0.0, 0.0,
0.0, 0.5, 0.0, 0.0,
0.0, 0.0, 0.5, 0.0,
0.5, 0.5, 0.5, 1.0};
它被称为shadow AcneglPolygonPoset,与前面/后面的开关结合使用——后面被渲染到位;前表面以与其坡度成比例的偏移进行渲染。无论是仅正面渲染,还是光空间中的恒定偏移,都不会以相同的方式有效。如果您需要一个平面来投射阴影,只需将其设置为双面平面即可。