Skip to main content
GameDev.net gamedev.net
🔒 Locked 🎯 Unreal

GLSL Simple shadow volume

Started by Equilibrix Nov 16, 2009 at 6:39 PM 7 replies 5.1k views
Original Post
Equilibrix
Equilibrix
Hello, first of all I want to apologize for I'm not a native speaker, but will do my best so you can understand me, thank you! I have following very simple scene: My simple scene What I'm trying to do is to detect the red edge (in this case v0-v2) in geometry shader and then eventually extrude it along the light rays to achieve a simple shadow polygon (but that's not my problem now). The input into the geometry shader is the whole triangle v0-v1-v2 and all I need to do is to detect somehow the red edge. I have tried to detect it by comparing angles between vectors from the light to the edge vertices in camera's clip space; on the picture the angle between light->v0 and light->v2 is bigger than between the other two combinations, so there should be the red edge. It sometimes seems to work for some camera positions but generally it fails and I really don't know where the mistake could be. Here is my vertex shader:

varying vec3 lightToVertexDirection;

void main()
{
	// get vertex in clip space
	vec4 vertexClip = ftransform();
	// get light in clip space
	vec4 lightClip = gl_ProjectionMatrix * gl_LightSource[0].position;
	
	// get direction
	lightToVertexDirection = normalize(vertexClip.xyz - lightClip.xyz);
	
	gl_Position = gl_ModelViewMatrix * gl_Vertex;
}

Here is my geometry shader:

#version 120
#extension GL_EXT_geometry_shader4 : enable
#extension GL_EXT_gpu_shader4 : enable

varying in vec3 lightToVertexDirection[];

void main()
{
	// get light position in camera's eye space 
	vec4 lightPos = gl_LightSource[0].position;

	// compute normal at each vertex
	vec3 n[3];
	n[0] = cross(normalize(gl_PositionIn[1].xyz - gl_PositionIn[0].xyz), normalize(gl_PositionIn[2].xyz - gl_PositionIn[0].xyz));
	n[1] = cross(normalize(gl_PositionIn[2].xyz - gl_PositionIn[1].xyz), normalize(gl_PositionIn[0].xyz - gl_PositionIn[1].xyz));
	n[2] = cross(normalize(gl_PositionIn[0].xyz - gl_PositionIn[2].xyz), normalize(gl_PositionIn[1].xyz - gl_PositionIn[2].xyz));
	
	// compute direction from vertex to light position
	vec3 dir[3];
	dir[0] = normalize(gl_PositionIn[0].xyz - lightPos.xyz);
	dir[1] = normalize(gl_PositionIn[1].xyz - lightPos.xyz);
	dir[2] = normalize(gl_PositionIn[2].xyz - lightPos.xyz);
	
	// continue only if this triangle is not facing the light
	if (!(dot(n[0], dir[0]) > 0 || dot(n[1], dir[1]) > 0 || dot(n[2], dir[2]) > 0))
	{  
	    return;
	}
	
	gl_FrontColor = vec4(1.0, 1.0, 0.0, 0.0);
	
	// draw lines from light to vertices (just for visualisation purposes)
	for (int i = 0; i < gl_VerticesIn; ++i)
	{
		gl_FrontColor = vec4(1.0, 1.0, 0.0, 0.0);
		gl_Position = gl_ProjectionMatrix * gl_PositionIn;
		EmitVertex();
		gl_Position = gl_ProjectionMatrix * lightPos;
		EmitVertex();
		EndPrimitive();
	} 

	// normalize the light->vertex direction in clip space
	vec3 dirx[3];
	dirx[0] = normalize(lightToVertexDirection[0]);
	dirx[1] = normalize(lightToVertexDirection[1]);
	dirx[2] = normalize(lightToVertexDirection[2]);

	// detect the edge of the shadow polygon
	for (int i = 0; i < gl_VerticesIn; ++i)
	{
		// compute indices for the other two vertices
		int i0 = i;
		int i1 = (i+1) % 3;
		int i2 = (i+2) % 3;
		
		// get the angles between light and both edge vertices
		float d0 = acos(dot(dirx[i0], dirx[i1]));
		float d1 = acos(dot(dirx[i0], dirx[i2]));
		float d2 = acos(dot(dirx[i1], dirx[i2]));
		
		// if currently processed edge has the largest angle, it is the edge!
		if (d0 >= d1 && d0 >= d2)
		{
			gl_FrontColor = vec4(0.0, 1.0, 0.0, 0.0);
			
			// draw the edge
			gl_Position = gl_ProjectionMatrix * gl_PositionIn[i0];
			EmitVertex();			
			gl_Position = gl_ProjectionMatrix * gl_PositionIn[i1];
			EmitVertex();
			
			EndPrimitive();
			
			return;
		}		
	}
}

Maybe my whole approach is wrong, so please tell me how can I detect the edge? If my approach is not completely wrong, where do I have the mistake? Thank you very much! Equilibrix
Erik Rufelt
Erik Rufelt
Use normalize outside of cross, not inside, like normalize(cross(...)).

And in addition, why are you detecting the red edge, is this for 2D or 3D?

Assuming that it's 3D, and you want normal shadow volumes:
You should only have one normal, the one for the whole triangle, and you can't calculate very meaningful vertex normals like that anyway. It is also usually much nicer to use triangle strips with adjacency information for this in the geometry shader.

If you explain the final effect you wish to achieve it is easier to provide help, or links to explanations.
Equilibrix
Equilibrix
Yes, it is 3D but I need it to work without adjacency information.

It will be part of an optimization of my algorithm which computes view-independent hard shadows and works sort of like a (GPU) ray-tracer. The point is that I need to do the shadow ray-tracing only where the shadow can possibly be, therefore I need the shadow volume. Oh, and it is for my school project.

So, can you help me, please? I believe it's rather easy to find the red edge but I don't know where the mistake could be..

Thank you!
Erik Rufelt
Erik Rufelt
I don't understand your shader.. nor what method you're using.. :(

Shadow volumes usually aren't very complicated to implement though. Can you precompute anything, or do you need a shadow volume from only feeding your shader a standard visible model triangle strip?
That kinda works.. but usually you only extract edges between polygons where one of them faces the light, and the other doesn't. To calculate this you either need to feed your shader adjacent normals, or adjacency indices.

Here's an article on shadow volumes: http://www.gamedev.net/reference/articles/article1873.asp. Near the bottom there is a discussion on using vertex shaders for the volumes, but it's using the pre-compute method (which will probably be a bit faster than geometry shaders).

I hope I haven't misunderstood your problem.
Erik Rufelt
Erik Rufelt
If by the red edge you mean you want to find the edge that is facing the camera, then you need to calculate it differently. Your normals at the edges don't point outward, they all point either in the direction of the triangle normal, or in the opposite direction.
The cross product returns a vector perpendicular to the two input vectors. So to get the edge normals you would do something like:
triangleNormal = cross(v1-v0, v2-v0);n[0] = normalize(cross(triangleNormal, v0-v1));n[1] = normalize(cross(triangleNormal, v1-v2));n[2] = normalize(cross(triangleNormal, v2-v0));
Equilibrix
Equilibrix
Thank you again for your kind replies!

But I think you misunderstood. To put it simple, just imagine I want a shadow volume for every single triangle in the scene, no matter how it is connected with other triangles. And in order to do that, I need to find the "red" edge which is the edge that will be extruded along the "yellow" lines in next step.

For example, in my picture, if the light was north from point v2, then the red edge would be v0->v1. If the light was southeast from v1, then the red edge would be v0->v2 again.

My shader only tries to compute that edge and draw it, that is all. No extrusion or any other stuff happens there.

Thank you!
Erik Rufelt
Erik Rufelt
Did you try the alternative normal calculation?
Shinkage
Shinkage
This doesn't make much sense to me either. Unless I'm REALLY missing something, I have to ask, are you sure you understand the concept behind shadow volumes? Or did you misspeak when you said this is in 3D space? I'm not trying to be rude, but honestly the following ONLY makes sense in 2D space:

Quote:
Original post by Equilibrix
But I think you misunderstood. To put it simple, just imagine I want a shadow volume for every single triangle in the scene, no matter how it is connected with other triangles. And in order to do that, I need to find the "red" edge which is the edge that will be extruded along the "yellow" lines in next step.


In 3D space, if you want "a shadow volume for every single triangle" you will need to extrude all 3 edges of every single triangle, with the exception of triangles precisely edge-on to the light, which you can skip entirely (giving you the *volume* behind the triangle; If you only extrude one edge, you're not calculating a volume at all).

In other words, there is no "magic edge" that you can somehow extrude to get the entire volume obscured by the triangle.

Think of it in terms of finding the silhouette, which is what most shadow volume algorithms do. If you have connectivity information, you can find only those edges that contribute to an object's light-silhouette, and as such only need to consider those edges for a shadow volume. For individual triangles, EVERY edge contributes to its silhouette, unless the triangle is precisely edge-on to the light, in which case it will have no shadow volume at all.
Equilibrix
Equilibrix
Again, thank you for your replies!

Well, I was hoping a simple 2D quad covering the area where the possible shadow can lie would suffice. I am NOT implementing shadow volumes, I just need a 2D polygon (possibly a quad) which would cover the area where the shadow "emitted" by the triangle can lie.

Anyway, I have resolved the original problem with the "red" edge detection. The problem was in the vertex shader where I was assigning not normalized clip space coordinates to the lightToVertexDirection variable. Simply dividing the vertexClip.xyz by vertexClip.w and lightClip.xyz by lightClip.w did the trick!

Now the problem is that I don't get the expected results, but that would be far beyond the scope of this thread.

Thank you all for your help, I really appreciate it!
Equilibrix

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.