使用GL_ANY_SAMPLES_PASSED的遮挡查询在片段被遮挡时返回true

时间:2018-04-06 20:31:36

标签: java opengl 3d libgdx lwjgl

我正在为我的引擎实现镜头发光效果。

但是,当有问题的片段完全被遮挡时,尝试使用遮挡查询仅返回true。

也许问题在于我手动写入每个顶点的z值,因为我使用的是对数深度缓冲区。但是,我不确定为什么这会影响遮挡测试。

以下是相关的代码段:

public class Query implements Disposable{
    private final int id;
    private final int type;

    private boolean inUse = false;

    public Query(int type){
        this.type = type;
        int[] arr = new int[1];
        Gdx.gl30.glGenQueries(1,arr,0);
        id = arr[0];
    }

    public void start(){
        Gdx.gl30.glBeginQuery(type, id);
        inUse = true;
    }

    public void end(){
        Gdx.gl30.glEndQuery(type);
    }

    public boolean isResultReady(){
        IntBuffer result = BufferUtils.newIntBuffer(1);
        Gdx.gl30.glGetQueryObjectuiv(id,Gdx.gl30.GL_QUERY_RESULT_AVAILABLE, result);
        return result.get(0) == Gdx.gl.GL_TRUE;
    }

    public int getResult(){
        inUse = false;
        IntBuffer result = BufferUtils.newIntBuffer(1);
        Gdx.gl30.glGetQueryObjectuiv(id, Gdx.gl30.GL_QUERY_RESULT, result);
        return result.get(0);
    }

    public boolean isInUse(){
        return inUse;
    }

    @Override
    public void dispose() {
        Gdx.gl30.glDeleteQueries(1, new int[]{id},0);
    }
}

以下是我进行实际测试的方法:

private void doOcclusionTest(Camera cam){
        if(query.isResultReady()){
            int visibleSamples = query.getResult();
            System.out.println(visibleSamples);
        }


        temp4.set(cam.getPosition());
        temp4.sub(position);
        temp4.normalize();
        temp4.mul(getSize()*10);
        temp4.add(position);
        occlusionTestPoint.setPosition(temp4.x,temp4.y,temp4.z);


        if(!query.isInUse()) {
            query.start();
            Gdx.gl.glEnable(Gdx.gl.GL_DEPTH_TEST);
            occlusionTestPoint.render(renderer.getPointShader(), cam);
            query.end();
        }
    }

我的一个点的顶点着色器,包括对数深度缓冲计算:

#version 330 core
layout (location = 0) in vec3 aPos;

uniform mat4 modelView;
uniform mat4 projection;
uniform float og_farPlaneDistance;
uniform float u_logarithmicDepthConstant;

vec4 modelToClipCoordinates(vec4 position, mat4 modelViewPerspectiveMatrix, float depthConstant, float farPlaneDistance){
    vec4 clip = modelViewPerspectiveMatrix * position;

    clip.z = ((2.0 * log(depthConstant * clip.z + 1.0) / log(depthConstant * farPlaneDistance + 1.0)) - 1.0) * clip.w;
    return clip;
}

void main()
{
    gl_Position = modelToClipCoordinates(vec4(aPos, 1.0), projection * modelView, u_logarithmicDepthConstant, og_farPlaneDistance);
}

点的着色器着色器:

#version 330 core

uniform vec4 color;

void main() {
    gl_FragColor = color;
}

由于我只测试单个点的遮挡,我知道替代方法是在渲染所有内容后简单地检查该像素的深度值。但是,我不确定如何计算CPU上一个点的对数z值。

1 个答案:

答案 0 :(得分:0)

我找到了解决问题的方法。这是一种解决方法,只适用于单点,而不适用于整个模型,但现在就是:

首先,您必须计算点的z值及其所在的像素坐标。计算z值应该是直接的,但在我的情况下,我使用的是对数深度缓冲区。出于这个原因,我不得不为z值做一些额外的计算。

这是获取规范化设备坐标中坐标的方法,包括z值(temp4f可以是任何Vector4f):

public Vector4f worldSpaceToDeviceCoords(Vector4f pos){
    temp4f.set(pos);
    Matrix4f projection = transformation.getProjectionMatrix(FOV, screenWidth,screenHeight,1f,MAXVIEWDISTANCE);
    Matrix4f view = transformation.getViewMatrix(camera);
    view.transform(temp4f); //Multiply the point vector by the view matrix
    projection.transform(temp4f); //Multiply the point vector by the projection matrix


    temp4f.x = ((temp4f.x / temp4f.w) + 1) / 2f; //Convert x coordinate to range between 0 to 1
    temp4f.y = ((temp4f.y / temp4f.w) + 1) / 2f; //Convert y coordinate to range between 0 to 1

    //Logarithmic depth buffer z-value calculation (Get rid of this if not using a logarithmic depth buffer)
    temp4f.z = ((2.0f * (float)Math.log(LOGDEPTHCONSTANT * temp4f.z + 1.0f) /
            (float)Math.log(LOGDEPTHCONSTANT * MAXVIEWDISTANCE + 1.0f)) - 1.0f) * temp4f.w;

    temp4f.z /= temp4f.w; //Perform perspective division on the z-value
    temp4f.z = (temp4f.z + 1)/2f; //Transform z coordinate into range 0 to 1

    return temp4f;
}

另外这个方法用于获取屏幕上像素的坐标(temp2是任何Vector2f):

    public Vector2f projectPoint(Vector3f position){
    temp4f.set(worldSpaceToDeviceCoords(temp4f.set(position.x,position.y,position.z, 1)));
    temp4f.x*=screenWidth;
    temp4f.y*=screenHeight;

    //If the point is not visible, return null
    if (temp4f.w < 0){
        return null;
    }


    return temp2f.set(temp4f.x,temp4f.y);

}

最后,一种在给定像素处获取存储深度值的方法(outBuff是任何直接的FloatBuffer):

public float getFramebufferDepthComponent(int x, int y){
    Gdx.gl.glReadPixels(x,y,1,1,Gdx.gl.GL_DEPTH_COMPONENT,Gdx.gl.GL_FLOAT,outBuff);
    return outBuff.get(0);
}

因此,使用这些方法,您需要做的是确定某个点是否被遮挡:

  1. 检查该点所在的像素(第二种方法)
  2. 检索该像素处的当前存储的z值(第三种方法)
  3. 获取点的计算z值(第一种方法)
  4. 如果计算的z值低于存储的z值,则该点可见
  5. 请注意,您应该在采样深度缓冲区之前绘制场景中的所有内容,否则提取的深度缓冲区值将不会反映所有渲染的内容。