Volume阴影我认为是做cs游戏的最难的一个点,但了解实现原理后其实不难。
目前完成的实现效果
shadow volume原理
不过让我不理解的是,计算shadow volume三角面的扩展顶点使得cpu计算时间增加了10倍,就图中两个模型加载一共耗时将近1分钟。应该有更优化的方法。而且加了Volume阴影我的天空盒无法显示,这个问题有待解决。
关键点一:扩展三角面顶点,一般三角面是3个顶点,但为了实现Volume阴影,用用到6个顶点。
void ModelComponent::DetermineAdjacency(vector<unsigned int>& el)
{
// Elements with adjacency info
vector<unsigned int> elAdj;
// Copy and make room for adjacency info
for (GLuint i = 0; i < el.size(); i += 3)
{
elAdj.push_back(el[i]);
elAdj.push_back(-1);
elAdj.push_back(el[i + 1]);
elAdj.push_back(-1);
elAdj.push_back(el[i + 2]);
elAdj.push_back(-1);
}
// Find matching edges
for (GLuint i = 0; i < elAdj.size(); i += 6)
{
// A triangle
int a1 = elAdj[i];
int b1 = elAdj[i + 2];
int c1 = elAdj[i + 4];
// Scan subsequent triangles
for (GLuint j = i + 6; j < elAdj.size(); j += 6)
{
int a2 = elAdj[j];
int b2 = elAdj[j + 2];
int c2 = elAdj[j + 4];
// Edge 1 == Edge 1
if ((a1 == a2 && b1 == b2) || (a1 == b2 && b1 == a2))
{
elAdj[i + 1] = c2;
elAdj[j + 1] = c1;
}
// Edge 1 == Edge 2
if ((a1 == b2 && b1 == c2) || (a1 == c2 && b1 == b2))
{
elAdj[i + 1] = a2;
elAdj[j + 3] = c1;
}
// Edge 1 == Edge 3
if ((a1 == c2 && b1 == a2) || (a1 == a2 && b1 == c2))
{
elAdj[i + 1] = b2;
elAdj[j + 5] = c1;
}
// Edge 2 == Edge 1
if ((b1 == a2 && c1 == b2) || (b1 == b2 && c1 == a2))
{
elAdj[i + 3] = c2;
elAdj[j + 1] = a1;
}
// Edge 2 == Edge 2
if ((b1 == b2 && c1 == c2) || (b1 == c2 && c1 == b2))
{
elAdj[i + 3] = a2;
elAdj[j + 3] = a1;
}
// Edge 2 == Edge 3
if ((b1 == c2 && c1 == a2) || (b1 == a2 && c1 == c2))
{
elAdj[i + 3] = b2;
elAdj[j + 5] = a1;
}
// Edge 3 == Edge 1
if ((c1 == a2 && a1 == b2) || (c1 == b2 && a1 == a2))
{
elAdj[i + 5] = c2;
elAdj[j + 1] = b1;
}
// Edge 3 == Edge 2
if ((c1 == b2 && a1 == c2) || (c1 == c2 && a1 == b2))
{
elAdj[i + 5] = a2;
elAdj[j + 3] = b1;
}
// Edge 3 == Edge 3
if ((c1 == c2 && a1 == a2) || (c1 == a2 && a1 == c2))
{
elAdj[i + 5] = b2;
elAdj[j + 5] = b1;
}
}
}
// Look for any outside edges
for (GLuint i = 0; i < elAdj.size(); i += 6)
{
if (elAdj[i + 1] == -1) elAdj[i + 1] = elAdj[i + 4];
if (elAdj[i + 3] == -1) elAdj[i + 3] = elAdj[i];
if (elAdj[i + 5] == -1) elAdj[i + 5] = elAdj[i + 2];
}
// Copy all data back into el
el = elAdj;
}
Volume shader
--------------vs
#version 430
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
layout (location = 3) in vec3 Tangent;
layout (location = 4) in vec3 Bitangent;
layout (location = 5) in ivec4 BoneIDs;
layout (location = 6) in vec4 Weights;
const int MAX_BONES = 100; // Max number of bones
uniform mat4 gBones[MAX_BONES]; // Bone transformations
out vec3 VPosition;
layout(std140 , binding = 0) uniform PV
{
mat4 projection;
mat4 view;
};
uniform mat4 model;
void main()
{
mat4 BoneTransform = gBones[ BoneIDs[0] ] * Weights[0];
BoneTransform += gBones[ BoneIDs[1] ] * Weights[1];
BoneTransform += gBones[ BoneIDs[2] ] * Weights[2];
BoneTransform += gBones[ BoneIDs[3] ] * Weights[3];
vec4 world = BoneTransform * vec4(aPos, 1.0);
VPosition = (model * world).xyz;
}
------------------gs
#version 430
layout( triangles_adjacency ) in;
layout( triangle_strip, max_vertices = 18 ) out;
in vec3 VPosition[];
layout(std140 , binding = 0) uniform PV
{
mat4 projection;
mat4 view;
};
layout(std140 , binding = 1) uniform BaseLight
{
vec4 lightDir;
vec4 color;
vec4 ambient;
vec4 lightPos;
};
vec3 LightPosition = (view * lightPos).xyz;
float EPSILON = 0.01;
/*bool facesLight( vec3 a, vec3 b, vec3 c )
{
LightPosition = (view * lightPos).xyz;
vec3 n = cross( b - a, c - a );
vec3 da = LightPosition.xyz - a;
vec3 db = LightPosition.xyz - b;
vec3 dc = LightPosition.xyz - c;
return dot(n, da) > 0 || dot(n, db) > 0 || dot(n, dc) > 0;
}
void emitEdgeQuad( vec3 a, vec3 b ) {
LightPosition = (view * lightPos).xyz;
vec3 LightDir = normalize(a - LightPosition.xyz);
vec3 deviation = LightDir * EPSILON;
gl_Position = projection * vec4(a + deviation, 1);
EmitVertex();
gl_Position = projection * vec4(LightDir, 0);
EmitVertex();
LightDir = normalize(b - LightPosition.xyz);
deviation = LightDir * EPSILON;
gl_Position = projection * vec4(b + deviation, 1);
EmitVertex();
gl_Position = projection * vec4(LightDir, 0);
EmitVertex();
EndPrimitive();
}
void main()
{
LightPosition = (view * lightPos).xyz;
if( facesLight(VPosition[0], VPosition[2], VPosition[4]) ) {
if( ! facesLight(VPosition[0],VPosition[1],VPosition[2]) )
emitEdgeQuad(VPosition[0],VPosition[2]);
if( ! facesLight(VPosition[2],VPosition[3],VPosition[4]) )
emitEdgeQuad(VPosition[2],VPosition[4]);
if( ! facesLight(VPosition[4],VPosition[5],VPosition[0]) )
emitEdgeQuad(VPosition[4],VPosition[0]);
//FRONT CAP
vec3 LightDir = normalize(VPosition[0] - LightPosition.xyz);
vec3 deviation = LightDir * EPSILON;
gl_Position = projection * vec4(VPosition[0] + deviation, 1);
EmitVertex();
LightDir = normalize(VPosition[2] - LightPosition.xyz);
deviation = LightDir * EPSILON;
gl_Position = projection * vec4(VPosition[2] + deviation, 1);
EmitVertex();
LightDir = normalize(VPosition[4] - LightPosition.xyz);
deviation = LightDir * EPSILON;
gl_Position = projection * vec4(VPosition[4] + deviation, 1);
EmitVertex();
EndPrimitive();
//BACK CAP
LightDir = normalize(VPosition[0] - LightPosition.xyz);
gl_Position = projection * vec4(LightDir, 0);
EmitVertex();
LightDir = normalize(VPosition[4] - LightPosition.xyz);
gl_Position = projection * vec4(LightDir, 0);
EmitVertex();
LightDir = normalize(VPosition[2] - LightPosition.xyz);
gl_Position = projection * vec4(LightDir, 0);
EmitVertex();
EndPrimitive();
}
}*/
struct sVSOutput
{
vec3 WorldPos;
};
void EmitQuad(int StartIndex, sVSOutput StartVertex, int EndIndex, sVSOutput EndVertex)
{
vec3 LightDir = normalize(StartVertex.WorldPos - lightPos.xyz);
vec3 l = LightDir * EPSILON;
gl_Position = projection * view * vec4((StartVertex.WorldPos + l), 1.0);
EmitVertex();
gl_Position = projection * view * vec4(LightDir, 0.0);
EmitVertex();
LightDir = normalize(EndVertex.WorldPos - lightPos.xyz);
l = LightDir * EPSILON;
gl_Position = projection * view * vec4((EndVertex.WorldPos + l), 1.0);
EmitVertex();
gl_Position = projection * view * vec4(LightDir, 0.0);
EmitVertex();
EndPrimitive();
}
void main()
{
/*gl_Position = projection * view * vec4(VPosition[0] ,1.0);
EmitVertex();
gl_Position = projection * view * vec4(VPosition[1] ,1.0);
EmitVertex();
gl_Position = projection * view * vec4(VPosition[2] ,1.0);
EmitVertex();
EndPrimitive();*/
vec3 e1 = VPosition[2] - VPosition[0];
vec3 e2 = VPosition[4] - VPosition[0];
vec3 e3 = VPosition[1] - VPosition[0];
vec3 e4 = VPosition[3] - VPosition[2];
vec3 e5 = VPosition[4] - VPosition[2];
vec3 e6 = VPosition[5] - VPosition[0];
vec3 Normal = cross(e1,e2);
vec3 LightDir = lightPos.xyz - VPosition[0];
if (dot(Normal, LightDir) > 0.000001) {
struct sVSOutput StartVertex, EndVertex;
Normal = cross(e3,e1);
if (dot(Normal, LightDir) <= 0) {
StartVertex.WorldPos = VPosition[0];
EndVertex.WorldPos = VPosition[2];
EmitQuad(0, StartVertex, 2, EndVertex);
}
Normal = cross(e4,e5);
LightDir = lightPos.xyz - VPosition[2];
if (dot(Normal, LightDir) <= 0) {
StartVertex.WorldPos = VPosition[2];
EndVertex.WorldPos = VPosition[4];
EmitQuad(2, StartVertex, 4, EndVertex);
}
Normal = cross(e2,e6);
LightDir = lightPos.xyz - VPosition[4];
if (dot(Normal, LightDir) <= 0) {
StartVertex.WorldPos = VPosition[4];
EndVertex.WorldPos = VPosition[0];
EmitQuad(4, StartVertex, 0, EndVertex);
}
vec3 LightDir = (normalize(VPosition[0] - lightPos.xyz)) * EPSILON;
gl_Position = projection * view * vec4((VPosition[0] + LightDir), 1.0);
EmitVertex();
LightDir = (normalize(VPosition[2] - lightPos.xyz)) * EPSILON;
gl_Position = projection * view * vec4((VPosition[2] + LightDir), 1.0);
EmitVertex();
LightDir = (normalize(VPosition[4] - lightPos.xyz)) * EPSILON;
gl_Position = projection * view * vec4((VPosition[4] + LightDir), 1.0);
EmitVertex();
EndPrimitive();
}
}
-------------fs
#version 430
out vec4 color;
void main() {
color = vec4(1.0f, 1.0f, 1.0f, 1.0f);
}
关键点二 模板测试。
while (!glfwWindowShouldClose(Window::window_ptr))
{
currentFrame = glfwGetTime();
SetCurTime(currentFrame);
float deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
SetDeltaTime(deltaTime);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glClearColor(0.05f, 0.05f, 0.05f, 1.0f);
glBindFramebuffer(GL_FRAMEBUFFER, gBuffer);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
_input->Update();
//for each frame
Scene::Instace->Update();
Scene::Instace->LateUpdate();
Scene::Instace->Render();
//glBindFramebuffer(GL_FRAMEBUFFER, 0);
//RenderShadowVolIntoStencil();
//SpecularGBuffer();
glBindFramebuffer(GL_READ_FRAMEBUFFER, gBuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // write to default framebuffer
// blit to default framebuffer. Note that this may or may not work as the internal formats of both the FBO and default framebuffer have to match.
// the internal formats are implementation defined. This works on all of my systems, but if it doesn't on yours you'll likely have to write to the
// depth buffer in another shader stage (or somehow see to match the default framebuffer's internal format with the FBO's internal format).
glBlitFramebuffer(0, 0, Width, Height, 0, 0, Width, Height, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// 2. lighting pass: calculate lighting by iterating over a screen filled quad pixel-by-pixel using the gbuffer's content.
// -----------------------------------------------------------------------------------------------------------------------
glClear(GL_COLOR_BUFFER_BIT);
glDepthMask(GL_FALSE);
//glDrawBuffer(GL_BACK);
AmbientGBuffer();
glEnable(GL_STENCIL_TEST);
//glEnable(GL_DEPTH_CLAMP);
//glEnable(GL_DEPTH_TEST);
//glDepthMask(GL_TRUE);
RenderShadowVolIntoStencil();
glDrawBuffer(GL_BACK);
//glDisable(GL_DEPTH_TEST);
//glDisable(GL_DEPTH_CLAMP);
// prevent update to the stencil buffer
glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_KEEP);
glStencilFunc(GL_EQUAL, 0x0, 0xFF);
glDepthMask(GL_FALSE);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ONE, GL_ONE);
//glDrawBuffer(GL_BACK);
SpecularGBuffer();
glDisable(GL_STENCIL_TEST);
glDisable(GL_BLEND);
glBindFramebuffer(GL_READ_FRAMEBUFFER, gBuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, Width, Height, 0, 0, Width, Height, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDepthFunc(GL_LEQUAL);
RenderSkyBox();
glDepthFunc(GL_LESS);
glClear(GL_DEPTH_BUFFER_BIT);
glfwSwapBuffers(window_ptr);
glfwPollEvents();
}
版权声明:本文为qq_41041725原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。