Page 1 of 1

Different matrices in native and managed builds

Posted: 14 Feb 2024, 17:39
by n3b
I've been playing with managed build and found out DrawBatch produces matrix with Z axis being transformed into -1..1 range, while native keeps it in 0..1. Is it a recent change or am I missing some configuration option?
Would it make more sense to have viewport matrix exposed rather than doing hidden transformations internally?

Re: Different matrices in native and managed builds

Posted: 15 Feb 2024, 19:07
by jsantos
Although the projection matrix should be always the same for all renderers, a renderer can activate certain flags to change this:
struct DeviceCaps
{
    // Offset in pixel units from top-left corner to center of pixel
    float centerPixelOffset = 0.0f;

    // When this flag is enabled the device works in 'Linear' mode. All internal textures and
    // offscreens are created in 'sRGB' format. In this mode, the device also expects colors
    // (like the ones in the vertex buffer) in 'sRGB' format. It also indicates that the device
    // writes to the render target in linear space
    bool linearRendering = false;

    // This flag is enabled to indicate that the device supports LCD subpixel rendering. Extra
    // shaders and dual source blending are needed for this feature
    bool subpixelRendering = false;

    // Indicates whether the device clip space depth values range from 0 to 1
    bool depthRangeZeroToOne = true;

    // Whether the device clip space Y values are inverted (increase from top (-1) to bottom (1))
    bool clipSpaceYInverted = false;
};
Vulkan renderer is for example enabling clipSpaceYInverted and OpenGL is disabling depthRangeZeroToOne.

This could explain what you are observing. Are you using the same renderer in both native and managed?

Re: Different matrices in native and managed builds

Posted: 16 Feb 2024, 04:09
by n3b
Oh, thank you, I see the difference now. The .Net DeviceCaps does not have these two:
bool depthRangeZeroToOne = true;
bool clipSpaceYInverted = false;
Sorry I forgot to mention that I use my own custom Vulkan renderer in both cases.

And another question about those matrices - does Noesis keep the same viewport matrix in between BeginOnscreenRender() and BeginOnscreenRender() for all batches?
i.e. can I always expect it to be a transformation from View dimensions to NDC or can it be something else too?

Re: Different matrices in native and managed builds

Posted: 16 Feb 2024, 10:50
by jsantos
Oh, thank you, I see the difference now. The .Net DeviceCaps does not have these two:
Yes, sorry about that. Fixed here. Please add the following at the end of the C# struct
        /// <summary>Indicates whether the device clip space depth values range from 0 to 1</summary>
        [MarshalAs(UnmanagedType.U1)]
        public bool DepthRangeZeroToOne;

        /// <summary>Whether the device clip space Y values are inverted (increase from top (-1) to bottom (1))</summary>
        [MarshalAs(UnmanagedType.U1)]
        public bool ClipSpaceYInverted;
And another question about those matrices - does Noesis keep the same viewport matrix in between BeginOnscreenRender() and BeginOnscreenRender() for all batches?
i.e. can I always expect it to be a transformation from View dimensions to NDC or can it be something else too?
The projection matrix takes your from view space to NDC. The viewport matrix (I assume here what you set in vkCmdSetViewport) must bet set accordingly:
  • In the offscreen phase, this must be set in RenderDevice.SetRenderTarget to the dimensions of the surface.
  • In the onscreen phase, this must be set by you and must be same dimensions as you set in the view (with SetSize)
I hope this makes everything clearer.

Re: Different matrices in native and managed builds

Posted: 24 Feb 2024, 00:58
by n3b
Thank you.
What I meant is that Noesis internally multiplies the matrix I provide with another one which is not exposed. So I have to pass in it's inverse and MVP to cull in world space, and then to reapply it in DrawBatch in order to make it back to NDC to render to PBR textures.
I'm wondering if that internal matrix is persistent as long as view size is.

Re: Different matrices in native and managed builds

Posted: 26 Feb 2024, 13:46
by jsantos
Yes, the internal matrix only changes with the dimensions of the view.
void VGLContext::SetViewport(const Rect& rect, bool flipY)
{
    mViewport = rect;
    mViewportFlipY = flipY;

    if (IsZero(rect.width) || IsZero(rect.height))
    {
        mBias = Matrix4::Identity();
    }
    else
    {
        float sx = 2.0f / rect.width;
        float sy = 2.0f / rect.height;

        float dx = -sx * rect.x - 1.0f;
        float dy = -sy * rect.y - 1.0f;
        float s = !mClipSpaceYInverted ? (flipY ? -1.0f : 1.0f) : (flipY ? 1.0f : -1.0f);

        float sz = mDepthRangeZeroToOne ? 1.0f : 2.0f;
        float dz = mDepthRangeZeroToOne ? 0.0f : -1.0f;

        mBias = Matrix4
        (
              sx,    0.0f, 0.0f, 0.0f,
            0.0f,  sy * s, 0.0f, 0.0f,
            0.0f,    0.0f,   sz, 0.0f,
              dx,  dy * s,   dz, 1.0f
        );
    }

    mVisibleBox = AABBox2(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height);
    mBiasHash = GetHashCode(mBias);

    if (mHasProjection)
    {
        mProjectionMatrixBias = mProjectionMatrix * mBias;
        mProjectionHash = GetHashCode(mProjectionMatrixBias);
    }
    else
    {
        mProjectionMatrixBias = mBias;
        mProjectionHash = mBiasHash;
    }
}