Linux OpenGL 實踐篇-11-shadow
OpenGL 陰影
在三維場景中,為了使場景看起來更加的真實,通常需要為其添加陰影,OpenGL可以使用很多種技術實現陰影,其中有一種非常經典的實現是使用一種叫陰影貼圖的實現,在本節中我們將使用陰影貼圖來實現一個簡單場景的陰影。
原理
使用陰影貼圖實現陰影,原理就是使用OpenGL渲染到貼圖的方式把當前場景通過深度測試的片元的深度值渲染到一張深度貼圖中,然後再次渲染物體時通過深度比較判斷片元是否在陰影中。
實現步驟
主要分為兩個步驟:
1.從光源的角度渲染場景,這一次的渲染我們不關心場景看起來像什麽,只是為了獲取片元的深度值,並把這個深度值存儲到一張深度貼圖中,這個深度表示的是光源的光線所能達到的最大深度;
2.從攝像機的角度再次渲染場景,在渲染片元時同時計算片元在光源坐標系下的深度值,使用這個深度值和深度貼圖中存儲的同一片元的深度值比較,如果小於或等於深度貼圖中的深度值,則表示不在陰影中,否則就是在陰影中。
代碼
創建深度貼圖,同時作為渲染附件附加到幀緩存中。
//創建幀緩存
glGenFramebuffers(1,&fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER,fbo);
//創建深度紋理 glGenTextures(1,&depthTex); glBindTexture(GL_TEXTURE_2D,depthTex); glTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT,width,height,0,GL_DEPTH_COMPONENT, GL_FLOAT, NULL); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC,GL_LEQUAL); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); //綁定 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_TEXTURE_2D,depthTex,0);
綁定幀緩存,把本次的渲染結果存儲到深度貼圖中。
//渲染陰影貼圖 glBindFramebuffer(GL_DRAW_FRAMEBUFFER,fbo); glViewport(0,0,width,height); glClearDepth(1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //打開多邊形偏移,以避免深度數據的zfighting問題 glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(2.0f,4.0f); //渲染 tShader->Use(); tShader->SetMatrix("model",boxModelMat.Get()); glBindVertexArray(boxVao); glDrawArrays(GL_TRIANGLES,0,36); tShader->SetMatrix("model",planeModelMat.Get()); glBindVertexArray(vao); glDrawArrays(GL_TRIANGLES,0,6); glDisable(GL_POLYGON_OFFSET_FILL);
渲染陰影貼圖所使用的著色器,只是最簡單的著色器,片元著色器甚至什麽都不幹。
shadow.vert
#version 330 core layout(location=0) in vec3 iPos; uniform mat4 model; uniform mat4 lightSpace; void main() { gl_Position = lightSpace * model * vec4(iPos,1.0); }
shadow.frag
#version 330 core void main() { //gl_FragDepth = gl_FragCoord.z; }
也可以把註釋的代碼放開表示顯示設置片元的深度,但註釋掉後更有效率,因為底層無論如何都會設置深度緩沖。
完成陰影貼圖渲染後,再次渲染場景,並使用陰影貼圖。
//回到攝像機視角 glBindFramebuffer(GL_FRAMEBUFFER,0); glViewport(0,0,width,height); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //繪制場景 planeShader->Use(); glBindTexture(GL_TEXTURE_2D,tt); planeShader->SetMatrix("model",boxModelMat.Get()); glBindVertexArray(boxVao); glDrawArrays(GL_TRIANGLES,0,36); planeShader->Use(); glBindTexture(GL_TEXTURE_2D,depthTex); modelShader->SetMatrix("model",planeModelMat.Get()); glBindVertexArray(vao); glDrawArrays(GL_TRIANGLES,0,6); glBindTexture(GL_TEXTURE_2D,0);
陰影貼圖著色器,註意在本次的實踐中,因為只渲染了最簡單的box,沒有使用紋理,所以使用了默認的glBindTexture來綁定陰影貼圖;但在復雜模型渲染時,一定要註意陰影貼圖的綁定(在著色器中使用多張紋理)。
plane.vert
#version 330 core layout(location=0) in vec3 iPos; layout(location=1) in vec2 iTexcoords; uniform mat4 model; uniform mat4 view; uniform mat4 proj; uniform mat4 lightView; out VS_OUT { vec3 fragPos; vec4 fragPosLightSpace; }vs_out; out vec2 texcoords; void main() { vs_out.fragPos = vec3(model * vec4(iPos,1.0)); vs_out.fragPosLightSpace = lightView * vec4(vs_out.fragPos,1.0); texcoords = iTexcoords; gl_Position = proj * view * model * vec4(iPos,1.0); }
plane.frag
#version 330 core in VS_OUT { vec3 fragPos; vec4 fragPosLightSpace; } fs_in; in vec2 texcoords; uniform sampler2D shadowMap; out vec4 color; 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; } void main() { vec4 fragPosLightSpace = fs_in.fragPosLightSpace; vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; projCoords = projCoords * 0.5 + 0.5; float shadow = ShadowCalculation(fs_in.fragPosLightSpace); vec3 red = vec3(1,0,0); vec3 lighting = vec3(0.1,0.1,0.1) + (1-shadow) * red; color = vec4(lighting,1.0); }
效果
完整代碼:
https://github.com/xin-lover/opengl-learn/tree/master/chapter-11-shadow
Linux OpenGL 實踐篇-11-shadow