Frustum Culling Idieas

Hi, I have sketched out a few frustum options for the grid, maybe you have some other ideas?

    ...
    const squadMatrix = [
        2, 2,
        1, 2,
        0, 2,
        0, 1,
        0, 0,
        1, 0,
        2, 0,
        2, 1,
        1, 1
    ];

    export const squadMatrixIndexes = [
        [4, 3, 2],
        [5, 8, 1],
        [6, 7, 0],
    ];
    ...
    public update(dt: number): void {

        const cameraPos = this._cameraEntity.getPosition();
        const cemeraDir = this._cameraEntity.forward;

        this._frustumV1(cameraDir);
        this._frustumV2(cameraPos);
    }
    ...
        private _frustumV2(cameraPos: pcx.Vec3) {

        if (!this._meshesInstLod1) return;

        const checkRadius = this.radius / 2;
        const frustumPlanes = this._camera.frustum.planes;
        const checkIsVisible = (min: pcx.Vec3, max: pcx.Vec3) => {
            let inside = 1;
            for (let p = 0; p < 6; p++) {
                const frustumPlane = frustumPlanes[p] as unknown as number[];
                const d = Math.max(min.x * frustumPlane[0], max.x * frustumPlane[0])
                        + Math.max(min.y * frustumPlane[1], max.y * frustumPlane[1])
                        + Math.max(min.z * frustumPlane[2], max.z * frustumPlane[2])
                        + frustumPlane[3];
                inside = inside & (d <= -checkRadius / 2 ? 0 : 1);
            }
            return inside;
        }

        for (let i = 0; i < 8; i++) {

            const mi = this._meshesInstLod1[i];
            const centerX = cameraPos.x + this.radius * (squadMatrix[i * 2 + 0] - 1);
            const centerZ = cameraPos.z + this.radius * (squadMatrix[i * 2 + 1] - 1);
            const min = new pc.Vec3(
                centerX - checkRadius,
                this._terrainEditor.terrain.minHeight,
                centerZ - checkRadius
            );

            const max = new pc.Vec3(
                centerX + checkRadius,
                this._terrainEditor.terrain.maxHeight,
                centerZ + checkRadius
            );

            const visible = !!checkIsVisible(min, max);

            mi.visible = visible;
            mi.visibleThisFrame = visible;
        }
    }
    ...
    private _frustumV1(cemeraDir: pcx.Vec3) {

        if (this._meshesInstLod1) {

            for (const mi of this._meshesInstLod1) {
                mi.visible = false;
                mi.visibleThisFrame = false;
            }

            let x = cemeraDir.x;
            let z = cemeraDir.z;
            const lengthSq = x * x + z * z;
            if (lengthSq > 0) {
                const invLength = 1 / Math.sqrt(lengthSq);
                x *= invLength;
                z *= invLength;
            }

            const squadX = Math.round(x) + 1;
            const squadZ = Math.round(z) + 1;

            const visibleRadius = 2;
            const visibleIndex = squadMatrixIndexes[squadX][squadZ];
            const minVisibleIndex = visibleIndex - visibleRadius;
            const maxVisibleIndex = visibleIndex + visibleRadius + 1;

            for (let i = minVisibleIndex; i < maxVisibleIndex; i++) {
                const normalizeIndex = i < 0 ? i + 8 : i % 8;
                const visibleMI = this._meshesInstLod1[normalizeIndex];
                visibleMI.visible = true;
                visibleMI.visibleThisFrame = true;
            }
        }
    }

@mvaligursky @LeXXik ?

I’m not sure I understand the question.

The camera follows a square in which grass is drawn, the square is divided into 9 squares, in the center of which is lod 0, and at the edges lod 1

That by itself makes sense.
But additionally you should do some distance based shader fading out. So when the camera moves away from the terrain, the grass fades out instead of being a visible circle.

On the ground it would allow you to smoothly go from full grass near by to less and less dense grass, to avoid a sharp boundary. Typically you would not do alpha blending, but some other way - in your case maybe make the blades shorter - scale them along Y by distance or similar.

We are not talking about beauty and smoothness now, I am more interested in the idea of ​​whether there is a better way to check whether part of the grass is visible.



Visualization via aabb (_frustumV2 method)

    private _minMaxStore: Array<[pcx.Vec3, pcx.Vec3, boolean]> = [];
    private _frustumV2(cameraPos: pcx.Vec3, frustum: pcx.Frustum, freeze: boolean) {

        if (!this._meshesInstLod1) return;

        const checkRadius = this.radius / 2;
        const frustumPlanes = frustum.planes;
        const checkIsVisible = (min: pcx.Vec3, max: pcx.Vec3) => {
            for (let p = 0; p < 6; p++) {
                const frustumPlane = frustumPlanes[p] as unknown as number[];
                const d = Math.max(min.x * frustumPlane[0], max.x * frustumPlane[0])
                        + Math.max(min.y * frustumPlane[1], max.y * frustumPlane[1])
                        + Math.max(min.z * frustumPlane[2], max.z * frustumPlane[2])
                        + frustumPlane[3];

                if (d <= -checkRadius / 4) {
                    return false;
                }
            }
            return true;
        }

        for (let i = 0; i < 8; i++) {

            if (!this._minMaxStore[i]) {
                this._minMaxStore[i] = [new pc.Vec3(), new pc.Vec3(), false];
            }

            if (!freeze) {

                const centerX = cameraPos.x + this.radius * (squadMatrix[i * 2 + 0] - 1);
                const centerZ = cameraPos.z + this.radius * (squadMatrix[i * 2 + 1] - 1);

                this._minMaxStore[i][0].set(
                    centerX - checkRadius,
                    this._terrainEditor.terrain.minHeight,
                    centerZ - checkRadius
                );

                this._minMaxStore[i][1].set(
                    centerX + checkRadius,
                    this._terrainEditor.terrain.maxHeight,
                    centerZ + checkRadius
                );

                this._minMaxStore[i][2] = !!checkIsVisible(this._minMaxStore[i][0], this._minMaxStore[i][1]);
            }

            const min = this._minMaxStore[i][0];
            const max = this._minMaxStore[i][1];
            const visible = this._minMaxStore[i][2];

            drawBox({ min, max, color: visible ? pc.Color.GREEN : pc.Color.RED });

            const mi = this._meshesInstLod1[i];

            mi.visible = freeze ? mi.visible : visible;
            mi.visibleThisFrame = freeze ? mi.visible : visible;
        }
    }

your idea is implemented, it remains to find the optimal algorithm for hiding invisible of grass by frustum.

Will it help speed up the shader or performance?

Vertex shader:

void main() {
            bool visible = check();
            if (!visible) {
                gl_Position = vec4(1.0, 1.0, 1.0, 0.0); // discard
                return;
            }
            ...
}

There are ideas on how to check AABB (min, max) in Frustom with high accuracy ?

The issue has been resolved, a more elegant solution has been found.

2 Likes