从零开始的openGL--cs游戏(15) Volume阴影。

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版权协议,转载请附上原文出处链接和本声明。