Jump to content
  • Advertisement
M4A1

Forward+ - problem with calculating frustum for tile

Recommended Posts

Posted (edited)

Hello,

I have started implementing forward+ rendering to my own graphic engine and I've noticed a problem. Only half of the screen "see" my point lights. In my opinion a problem is: in calculating frustum for specific tile or in calculating depth from the depth buffer.

Here is my code for calculating frustum for specific tile: 

//Four planes of a view frustum (in view space).
//The planes are:
// * Left,
// * Right,
// * Top,
// * Bottom.
//The back and/or front planes can be computed from depth values in the 
//light culling compute shader.
struct Frustum
{
    vec4 planes[4];   //left, right, top, bottom frustum planes.
};

//Convert clip space coordinates to view space
vec4 clipToView(vec4 tmp_clip, mat4 tmp_invProjMatrix)
{
    //View space position.
    vec4 tmp_view = tmp_invProjMatrix * tmp_clip;
    //Perspective projection.
    tmp_view = tmp_view / tmp_view.w;

    return tmp_view;
}

//Convert screen space coordinates to view space.
vec4 screenToView(vec4 tmp_screen, vec2 tmp_screenSize, mat4 tmp_invProjMatrix)
{
    //Convert to normalized texture coordinates
    vec2 tmp_texCoord = tmp_screen.xy / tmp_screenSize;

    // Convert to clip space
    vec4 tmp_clip = vec4(vec2(tmp_texCoord.x, 1.0f - tmp_texCoord.y) * 2.0f - 1.0f, tmp_screen.z, tmp_screen.w);

    return clipToView(tmp_clip, tmp_invProjMatrix);
}

vec4 computePlane(vec3 tmp_p0, vec3 tmp_p1, vec3 tmp_p2)
{
    vec4 tmp_plane;

    vec3 tmp_v0 = tmp_p1 - tmp_p0;
    vec3 tmp_v2 = tmp_p2 - tmp_p0;

    tmp_plane.xyz = normalize(cross(tmp_v0, tmp_v2));

    //Compute the distance to the origin using p0.
    tmp_plane.w = dot(tmp_plane.xyz, tmp_p0);

    return tmp_plane;
}

Frustum calculateFrustumForGroup(int tmp_pixelPerTile, ivec2 tmp_dispatchThreadID, 
								vec2 tmp_screenSize, mat4 tmp_invProjMatrix)
{
	vec3 tmp_eyePos = vec3(0, 0, 0);
	
    //Compute the 4 corner points on the far clipping plane to use as the
    //frustum vertices.
    vec4 tmp_screenSpace[4];
    //Top left point
    tmp_screenSpace[0] = vec4(tmp_dispatchThreadID.xy * tmp_pixelPerTile, -1.0f, 1.0f);
    //Top right point
    tmp_screenSpace[1] = vec4(vec2(tmp_dispatchThreadID.x + 1, tmp_dispatchThreadID.y) * tmp_pixelPerTile, -1.0f, 1.0f);
    //Bottom left point
    tmp_screenSpace[2] = vec4(vec2(tmp_dispatchThreadID.x, tmp_dispatchThreadID.y + 1) * tmp_pixelPerTile, -1.0f, 1.0f);
    //Bottom right point
    tmp_screenSpace[3] = vec4(vec2(tmp_dispatchThreadID.x + 1, tmp_dispatchThreadID.y + 1) * tmp_pixelPerTile, -1.0f, 1.0f);

    vec3 tmp_viewSpace[4];
    //Now convert the screen space points to view space
    for(int i = 0; i < 4; i++) 
	{
        tmp_viewSpace[i] = screenToView(tmp_screenSpace[i], tmp_screenSize, tmp_invProjMatrix).xyz;
    }

    //Now build the frustum planes from the view space points
	Frustum tmp_frustum;
    //Left plane
    tmp_frustum.planes[0] = computePlane(tmp_eyePos, tmp_viewSpace[2], tmp_viewSpace[0]);
    //Right plane
    tmp_frustum.planes[1] = computePlane(tmp_eyePos, tmp_viewSpace[1], tmp_viewSpace[3]);
    //Top plane
    tmp_frustum.planes[2] = computePlane(tmp_eyePos, tmp_viewSpace[0], tmp_viewSpace[1]);
    //Bottom plane
    tmp_frustum.planes[3] = computePlane(tmp_eyePos, tmp_viewSpace[3], tmp_viewSpace[2]);
    
	return tmp_frustum;
}

Next here is my code to check if specific light is inside tile frustum:
 

struct Sphere
{
    vec3 c; //Center point.
    float r; //Radius.
};

struct Cone
{
    vec3 T; //Cone tip.
    float h; //Height of the cone.
    vec3 d; //Direction of the cone.
    float r; //bottom radius of the cone.
};

//Check to see if a sphere is fully behind (inside the negative halfspace of) a plane.
//Source: Real-time collision detection, Christer Ericson (2005)
bool SphereInsidePlane(Sphere sphere, vec4 plane)
{
    return dot(plane.xyz, sphere.c) - plane.w < -sphere.r;
}

//Check to see if a point is fully behind (inside the negative halfspace of) a plane.
bool PointInsidePlane(vec3 p, vec4 plane)
{
    return dot(plane.xyz, p) - plane.w < 0;
}

//Check to see if a cone if fully behind (inside the negative halfspace of) a plane.
//Source: Real-time collision detection, Christer Ericson (2005)
bool ConeInsidePlane(Cone cone, vec4 plane)
{
    //Compute the farthest point on the end of the cone to the positive space of the plane.
    vec3 m = cross(cross(plane.xyz, cone.d), cone.d);
    vec3 Q = cone.T + cone.d * cone.h - m * cone.r;

    //The cone is in the negative halfspace of the plane if both
    //the tip of the cone and the farthest point on the end of the cone to the 
    //positive halfspace of the plane are both inside the negative halfspace 
    //of the plane.
    return PointInsidePlane(cone.T, plane) && PointInsidePlane(Q, plane);
}

//Check to see of a light is partially contained within the frustum.
bool SphereInsideFrustum(Sphere sphere, Frustum frustum, float zNear, float zFar)
{
    bool result = true;

    //First check depth
    //Note: Here, the view vector points in the -Z axis so the 
    //far depth value will be approaching -infinity.
    if(sphere.c.z - sphere.r > zNear || sphere.c.z + sphere.r < zFar)
    {
        result = false;
    }

    //Then check frustum planes
    for(int i = 0; i < 4 && result; i++)
    {
        if(SphereInsidePlane( sphere, frustum.planes[i]))
        {
            result = false;
        }
    }

    return result;
}

bool ConeInsideFrustum(Cone cone, Frustum frustum, float zNear, float zFar)
{
    bool result = true;

	vec4 nearPlane = vec4(0.0, 0.0, -1.0, -zNear);
	vec4 farPlane = vec4(0.0, 0.0, 1.0, zFar);

    // First check the near and far clipping planes.
    if(ConeInsidePlane(cone, nearPlane) || ConeInsidePlane(cone, farPlane))
    {
        result = false;
    }

    // Then check frustum planes
    for(int i = 0; i < 4 && result; i++)
    {
        if(ConeInsidePlane(cone, frustum.planes[i]))
        {
            result = false;
        }
    }

    return result;
}

And finally code to read depth from the depth buffer and check all lights:
 

vec2 tmp_tex = vec2(dispatchThreadID) / u_screenSize;
	float tmp_depth = texture(u_depthMap, tmp_tex).r;
	
	uint tmp_uDepth = floatBitsToUint(tmp_depth);

	atomicMin(uMinDepth, tmp_uDepth);
	atomicMax(uMaxDepth, tmp_uDepth);
	
	barrier();
	
	float fMinDepth = uintBitsToFloat(uMinDepth);
    float fMaxDepth = uintBitsToFloat(uMaxDepth);
	
	//Convert depth values to view space.
    float minDepthVS = screenToView(vec4(0, 0, fMinDepth, 1), u_screenSize, inv_ProjMatrix).z;
    float maxDepthVS = screenToView(vec4(0, 0, fMaxDepth, 1), u_screenSize, inv_ProjMatrix).z;
    float nearClipVS = screenToView(vec4(0, 0, 0, 1), u_screenSize, inv_ProjMatrix).z;
	
	//Clipping plane for minimum depth value 
    //(used for testing lights within the bounds of opaque geometry).
    vec4 minPlane = vec4(0, 0, -1, -minDepthVS);
	
	//Cull lights
    //Each thread in a group will cull 1 light until all lights have been culled.
    for(uint i = groupIndex; i < u_lightCount; i += TILE_SIZE * TILE_SIZE)
    {
		Light currentLight = lightBuffer.data[i];
		uint lightType = uint(currentLight.type);
		switch(lightType)
        {
			case DIRECTIONAL_LIGHT_TYPE:
            {
                //Directional lights always get added to our light list.
                //(Hopefully there are not too many directional lights!)
                t_appendLight(i);
                o_appendLight(i);
            }
            break;
			case POINT_LIGHT_TYPE:
            {
				vec3 lightPos = vec3(currentLight.position[0], currentLight.position[1], currentLight.position[2]);
                Sphere sphere = {lightPos, currentLight.radius};
                if(SphereInsideFrustum(sphere, GroupFrustum, nearClipVS, maxDepthVS))
                {
                    //Add light to light list for transparent geometry.
                    t_appendLight(i);

                    if(!SphereInsidePlane(sphere, minPlane))
                    {
                        //Add light to light list for opaque geometry.
                        o_appendLight(i);
                    }
                }
            }
            break;
            case SPOT_LIGHT_TYPE:
            {
				vec3 lightPos = vec3(currentLight.position[0], currentLight.position[1], currentLight.position[2]);
				vec3 lightDir = vec3(currentLight.direction[0], currentLight.direction[1], currentLight.direction[2]);
                float coneRadius = tan(radians(currentLight.outerAngle)) * currentLight.radius;
                Cone cone = {lightPos, currentLight.radius, lightDir, coneRadius};
				
                if(ConeInsideFrustum(cone, GroupFrustum, nearClipVS, maxDepthVS))
                {
                    //Add light to light list for transparent geometry.
                    t_appendLight(i);

                    if(!ConeInsidePlane(cone, minPlane))
                    {
                        // Add light to light list for opaque geometry.
                        o_appendLight(i);
                    }
                }
            }
            break;
		}
	}

My code is based on: https://www.3dgep.com/forward-plus/ and I have tried to convert directx code to OpenGL. Any help will be appreciate (sorry for my bad english) :)

Edited by M4A1

Share this post


Link to post
Share on other sites
Advertisement

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!