LeeMcQueen / GameDemo

Demo
0 stars 0 forks source link

Shadow Mapping 阴影 #43

Open LeeMcQueen opened 3 years ago

LeeMcQueen commented 3 years ago

//创建帧缓冲对象 GLuint depthMapFBO; glGenFramebuffers(1, &depthMapFBO); //创建一个2D纹理,给帧缓冲的深度缓冲使用 const GLuint SHADOW_WIDTH = 1280, SHADOW_HEIGHT = 720;

GLuint depthMap; glGenTexture(1, &depthMap); glBindTexture(GL_TEXTURE_2D, depthMap); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

//把生成的深度纹理作为帧缓存的深度缓冲 glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0); glDrawBuffer(GL_NONE); glReadBuffer(GL_NONE); glBindFramebuffer(GL_FRAMEBUFFER, 0);

//深度值渲染到纹理缓冲后 就可以生成深度贴图 就是main里面的产生反射贴图一样的思路 glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT); glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO); glClear(GL_DEPTH_BUFFER_BIT); RenderScene(); glBindFramebuffer(GL_FRAMEBUFFER, 0); //这里需要研究一下如果我们是使用GUI的部分进行渲染,需要设定什么参数 glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glBindTexture(GL_TEXTURE_2D, depthMap); RenderScene();

继续使用老办法 进行VertexShader和FragmentShader的反推 vertexShader里面多了一个uniform lightSpaceMatrix

1.光线正交投影 就是一个简单的正交投影 这里为什么不是屏幕的大小有点疑惑 lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane); 2.视图矩阵来变换每个物体 简单的说就是创建一个从光源出发的相机来观察游戏场景 我的游戏这里的中心点是玩家,这一点要注意 lightView = glm::lookAt(lightPos, glm::vec3(0.0f), glm::vec3(0.0, 1.0, 0.0)); lightSpaceMatrix = lightProjection * lightView

FragPos就是世界坐标 老掉牙了肯定知道的 如果在骨骼里面就是再乘上boneTransformationMatrix FragPosLightSpace = lightSpaceMatrix * vec4(FragPos, 1.0);

我大概弄明白了,在我的游戏里面先加上深度贴图,从光线出发带移动的矩阵以后,阴影的计算是要放在地面和草地上面的 感觉头痛 解释说的非常好 在阴影中是1.0 在阴影外是0.0 例子里面是blinn-phone的传统三件套 所以只有diffuse specular ambient 因为漫反射的原因 在阴影中的部分我们只把前面两个值设置为0 环境光照ambient就不变

最重要的GLSL 阴影计算函数 ShadowCalculation(vec4 fragPosLightSpace) //写这个learnopengl的老b真的厉害 //当我们在顶点着色器输出一个裁切空间顶点位置到gl_Position时,OpenGL自动进行一个透视除法 //将裁切空间坐标的范围-w到w转换为-1到1,这要将x,y,z元素除以向量的w元素来实现 vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;

因为来自深度贴图的深度在0到1的范围,我们也打算使用projCoords从深度贴图中取采样,所以我们将在NDC (我们在水面的 waterFragmentShader 里面是用过NDC坐标)坐标变换为0到1的范围 projCoords = projCoords * 0.5 + 0.5

通过projCoords.xy在shadowMap阴影贴图里面找到位置 然后通过xy得到shadowMap图的r 因为shadowMap是深度贴图 float closestDepth = textrue(shadowMap, projCoords.xy).r;

光透视视角的片段深度 float currentDepth = projCoords.z;

实际的判断其实非常的简单 就是检查currentDepth是否会高于closetDepth float shadow = currentDepth > closestDepth ? 1.0 : 0.0;

float ShadowCalculation(vec4 fragPosLightSpace) { vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; projCoords = projCoords * 0.5 + 0.5; float closestDepth = texture(shadowMap, projCoords.xy).r; float currentDepth = projCoords.z; float shadow = currentDepth > closestDepth ? 1.0 : 0.0;

return shadow;

}

在实际的GLSL fragmentShader里面 shadow 就是1.0和0.0 在阴影里面就是1.0 然后diffuse和specular就为0 float shadow = ShadowCalculation(FragPosLightSpace) vec3 lighting = (ambient + (1.0 - shadow) (diffuse + specular)) color;

FragColor = vec4(lighting, 1.0f);

LeeMcQueen commented 3 years ago

现有的masterRenderer函数只有透视投影 但是阴影渲染需要正交投影,所以需要在masterRenderer的参数里面加上一个bool值,作为使用透视投影和正交投影的开关

masterRenderer.h glm::mat4 getProjectionMatrix(bool projectionType);

masterRenderer.cpp 投影函数 glm::mat4 getProjectionMatrix(bool projectionType){

if(!projectionType){

    return glm::perspective(glm::radians(FOV), (float)DisplayManager::WIDTH / (float)DisplayManager::HEIGHT, NEAR_PLANE, FAR_PLANE);
}else{
    return glm::ortho(-100.0f, 100.0f, -100.0f, 100.0f, NEAR_PLANE, FAR_PLANE);
}

} 析构函数 MasterRenderer::MasterRenderer(Loader &loader,WaterFrameBuffers &fbo, ShadowFrameBuffer &shadowFBO, bool projectionType): projectionMatrix(getProjectionMatrix(projectionType)), entityRenderer(EntityRenderer(staticshader, projectionMatrix)), terrainRenderer(TerrainRenderer(terrainShader, projectionMatrix)), waterRenderer(WaterRenderer(waterShader, projectionMatrix, fbo, shadowFBO)), skyboxRenderer(SkyboxRenderer(skyboxShader, loader, projectionMatrix_)){

//背面剔除
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
// 开启混合通道(Enable alpha blend)
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// 开启抗锯齿(Enable Antialiasing)
glEnable(GL_MULTISAMPLE);

}

然后在所有调用构造函数的地方更具实际情况启动透视投影或者正交投影

LeeMcQueen commented 3 years ago

lightViewMaitrx 直接使用auto shadowCamera = camera方法,给shadowCamera进行set值就可以了

LeeMcQueen commented 3 years ago

具体设置的时候在terrainRenderer里面还要加上一个接口用来接收 lightVPMatrix

LeeMcQueen commented 3 years ago

MasterRenderer masterRenderer(loader, fbos, shadowFrameBuffer, true); MasterRenderer masterRendererOrtho(loader, fbos, shadowFrameBuffer, false);

glm::value_ptr(masterRenderer.getProjectionMatrix(bool projectionType = false)) glm::value_ptr(masterRenderer.getProjectionMatrix(bool projectionType = true))

LeeMcQueen commented 3 years ago

■terrainShader.h void loadLightSpaceMatrix(glm::mat4 matrix);

GLuint Location_lightSpaceMatrix;

■terrainShader.cpp void TerrainShader::loadLightSpaceMatrix(glm::mat4 matrix){

loadMatrix4(Location_lightSpaceMatrix);

}

getAllUniformLoaction(){

Location_lightSpaceMatrix = getUniformLocation("lightSpaceMatrix")

}

■terrainVertexShader

vec4 worldPosition = transformationMatrix * vec4(position, 1.0); 具体如何计算世界坐标就看自己的选择 可以在既有的vec4上只取xyz三个值 或者直接重写

//mat4 lightSpaceMatrix = projectionMatrix * viewMatrix;

uniform mat4 lightSpaceMatrix; FragPosLightSpace = lightSpaceMatrix * vec4(worldPosition.xyz, 1.0);

■terrainFragmentShader

解决阴影是全黑的问题 例子里面是用冯氏光照 有环境光 镜面反射 漫反射 我的例子里只有环境光 最低好像是0.2,我把影子的值直接设置成0.2就应该可以了

vec3 result = (1.0 - shadow) diffuse; //z这样设定以后在阴影里面的环境光就是最低的 0.2diffuse vec3 result = ((1.0 - shadow) + 0.2) * diffuse;

out_Colour = vec4(result, 1.0) * totalColour + vec4(finalSpecular, 1.0);

■terrainRenderer.h 需要的关键就是一个光源相机的lookat 一个是光源相机Ortho

■terrainRenderer.cpp

LeeMcQueen commented 2 years ago

使用光线 VPMatirx 无法渲染阴影的问题

■解决方案1 对vertexShader的传出算法进行修改 vec3 FragPos = vec3(transformationMatrix vec4(position, 1.0)); FragPosLightSpace = terrainLightVPMatrix vec4(FragPos, 1.0);

直接把 vec4 worldPosition.w 去掉 看看这样行不行 不然只有使用邪教的方法了 直接使用原摄像机 然后把摄像机原点往下拉一点 这样也有影子的效果就是有点奇怪 FragPosLightSpace = terrainLightVPMatrix * vec4(worldPosition.xyz, 1.0);

LeeMcQueen commented 2 years ago

float shadow = currentDepth > closestDepth ? 1.0 : 0.0; float shadow = currentDepth = closestDepth ? 1.0 : 0.0;

LeeMcQueen commented 2 years ago

到一定是要渲染地面的