Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some trouble with LineGL3D shader when camera is close #634

Open
bz-next opened this issue Feb 10, 2024 · 3 comments
Open

Some trouble with LineGL3D shader when camera is close #634

bz-next opened this issue Feb 10, 2024 · 3 comments
Milestone

Comments

@bz-next
Copy link

bz-next commented Feb 10, 2024

I toyed around with the LineGL3D shader to render a simple debug grid:

image

The trouble is, when I zoom in close to the grid, the window gets filled with some artifacts:

image

image

I am just using the default LineGL3D shader with no special config.

Below is the code that generates the line objects and attaches the drawables:

{
        debugLine = new GL::Mesh{
            MeshTools::compileLines(
                MeshTools::generateLines(
                    WorldPrimitiveGenerator::debugLine(
                        {-1.0f, 0.0f, 0.0f},
                        {1.0f, 0.0f, 0.0f})))};
        Object3D *debugLines = new Object3D{};
        debugLines->setParent(worldParent);
        debugLines->scale(Vector3{100.0, 100.0, 100.0});
        // Create debug grid
        int numLines = 20;
        float start = -1.0f;
        float step = 2.0f/numLines;

        // xy grid
        for (int i = 0; i <= numLines; ++i) {
            Object3D *xline = new Object3D{};
            xline->setParent(debugLines);
            xline->translate({0.0f, start+i*step, 0.0f});
            new DebugLineDrawable{*xline, _lineShader, 0.5*Color3{1.0f, 0.0f, 0.0f}, *debugLine, *worldDebugDrawables};
            Object3D *yline = new Object3D{};
            yline->setParent(debugLines);
            yline->rotateZ(Deg(90.0f));
            yline->translate({start+i*step, 0.0f, 0.0f});
            new DebugLineDrawable{*yline, _lineShader, 0.5*Color3{0.0f, 1.0f, 0.0f}, *debugLine, *worldDebugDrawables};
        }
        Object3D *zline = new Object3D{};
        zline->setParent(debugLines);
        zline->rotateY(Deg(90.0));
        new DebugLineDrawable{*zline, _lineShader, 0.5*Color3{0.0f, 0.0f, 1.0f}, *debugLine, *worldDebugDrawables};
    }

And the drawable itself:

void DebugLineDrawable::draw(const Matrix4& transformationMatrix, SceneGraph::Camera3D& camera) {
    _shader
        .setColor(_color)
        .setViewportSize(Vector2{GL::defaultFramebuffer.viewport().size()})
        .setTransformationProjectionMatrix(camera.projectionMatrix()*transformationMatrix)
        .setWidth(2.0f)
        .draw(_mesh);
}

Generator for the debug line mesh:

Trade::MeshData WorldPrimitiveGenerator::debugLine(Magnum::Math::Vector3<float> a, Magnum::Math::Vector3<float> b) {
    constexpr Trade::MeshAttributeData AttributeData3DWireframe[]{
        Trade::MeshAttributeData{Trade::MeshAttribute::Position, VertexFormat::Vector3,
            0, 0, sizeof(Vector3)}
    };
    constexpr UnsignedInt points = 11;

    Containers::Array<char> vertexData{points*sizeof(Vector3)};
    auto positions = Containers::arrayCast<Vector3>(vertexData);
    Vector3 delta = b-a;
    delta /= float(points)-1;

    Vector3 pos = a;
    for (int i = 0; i < points; ++i) {
        positions[i] = pos+delta*i;
    }

    return MeshTools::copy(Trade::MeshData{MeshPrimitive::LineStrip, Utility::move(vertexData),
        Trade::meshAttributeDataNonOwningArray(AttributeData3DWireframe), UnsignedInt(positions.size())});
}

In terms of scale: The lines themselves are 2 units long (-1.0 to 1.0 on their respective axes). They are parented to an Object3D that has a scale factor of 100 in all dimensions. That Object3D is parented to another object that has a scale factor of 0.05. The overall hierarchy is:

[scene manipulator] <- [world object (0.05x)] <- [debug lines object (100x)] <- [the lines]

The camera projection matrix is defined by .setProjectionMatrix(Matrix4::perspectiveProjection(35.0_degf, 1.0f, 0.1f, 1000.0f))

I tried increasing my near plane from 0.1f to 1.0f since I read this helps with z-buffer artifacts, and it doesn't eliminate the issue, just changes it:

image

Is there something I'm doing wrong with using the shader? I've tried changing the number of points / line (ideally I'd just use 2, but I tried varying it in case it made a difference. Here I'm using 11 points.)

@mosra mosra added this to the 2023.0a milestone Feb 14, 2024
@mosra
Copy link
Owner

mosra commented Feb 14, 2024

Hello -- I already replied on Gitter, but replying here as well to not make it look like I'm ignoring this.

Artifacts with the near clip plane are a problem I'm aware of and on my list of things to fix. Enabling GL::Renderer::Feature::DepthClamp should help with that, at least in most cases. I looked around and discovered that it's now also available on WebGL, so with bb4064b you should be able to use it there as well.

I'll comment here once I get back to investigating this.

@frustaci
Copy link

frustaci commented Nov 25, 2024

I was experimenting with the engine for the first time, when i ran into this issue. Specially with the near frustum plane.

Fixed by clamping the line 410 in Line.vert as per comment -1 to 1

highp const vec2 screenspaceLineDirection = clamp(lineDirection*viewportSize/2.0,-1.0,1.0);

DepthClamp wont help as per note: "What is unclipped are objects between the projection near-plane and the camera" https://www.khronos.org/opengl/wiki/Vertex_Post-Processing#Depth_clamping

Still not perfect but 99% better.

Also I would like to contribute this code for drawing conic sections, based on polar formula, very much based on circle3DWireframe().
If u want to adapt it and add it to the engine, feel free to do so.

constexpr Trade::MeshAttributeData AttributeData3DWireframe[]{
    Trade::MeshAttributeData{Trade::MeshAttribute::Position, VertexFormat::Vector3,
        0, 0, sizeof(Vector3)}
};

Trade::MeshData ViewerExample::conic3DWireframe(const float semilattus, const float eccentricity, const UnsignedInt segments) {
    CORRADE_ASSERT(segments >= 3, "Primitives::conic3DWireframe(): segments must be >= 3",
        (Trade::MeshData{MeshPrimitive::LineLoop, 0}));

    Containers::Array<char> vertexData{segments*sizeof(Vector3)};
    auto positions = Containers::arrayCast<Vector3>(vertexData);

    Rad phi = Math::acos(-1.0f/eccentricity);
    Rad phi2 = 2.0f*phi;
    /* Points on conic */
    const Rad angleIncrement(eccentricity >= 1.0f? phi2/segments : Rad(Constants::tau()/segments));
    for(UnsignedInt i = 0; i != segments; ++i) {

        const Rad angle(eccentricity >= 1.0f? (Float(i)*angleIncrement-phi):Float(i)*angleIncrement);

        if(eccentricity >= 1.0f && angle>=phi)
            break;

        const Containers::Pair<Float, Float> sincos = Math::sincos(angle);
        float r = semilattus/(1+eccentricity*sincos.second());
        positions[i] = {r*sincos.second(), r*sincos.first(), 0.0f};
    }

    //cap the "inf ends"
    if(eccentricity >= 1.0f) {
        positions[0] = positions[1];
        positions[segments-1] = positions[segments-2];
    }

    return Trade::MeshData{MeshPrimitive::LineLoop, Utility::move(vertexData),
        Trade::meshAttributeDataNonOwningArray(AttributeData3DWireframe), UnsignedInt(positions.size())};
}

@mosra
Copy link
Owner

mosra commented Nov 25, 2024

Wow, thanks for both the fix suggestion and the code. I'll integrate both as soon as I get a chance, things piled up quite a bit over the weekend :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

3 participants