Advertisement

Forward+ - problem with calculating frustum for tile

Started by April 02, 2019 02:10 PM
0 comments, last by M4A1 5 years, 10 months ago

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) :)

This topic is closed to new replies.

Advertisement