0

So i made a vulkan renderer which uses instanced triangles to render the scene. As you can see in the image below, there are random artifacts all over the screen:

enter image description here

This would be the vertex shader:

#version 460

#extension GL_EXT_scalar_block_layout : enable

precision highp float;

struct Transform {
    vec2 translation;
    vec2 origin;
    vec2 scale;
    float rotation;
    int _align;
};

struct Triangle {
    Transform transform;
    Transform canvasTransform;
    vec4 colors[3];
    vec2 points[3];
    vec2 texCoords[3];
    float z;
    float texId;
    float blending;
    int _align;
};

layout(location = 0) out vec2 outTexCoord;
layout(location = 1) out vec4 outColor;
layout(location = 2) out float outTexId;
layout(location = 3) out float outBlending;

layout(set = 0, binding = 0) uniform Matrices {
    mat4 view;
    mat4 proj;
    vec2 screenSize;
} mat;

layout(set = 1, binding = 0, scalar) readonly buffer ObjectUbo {
    Triangle triangles[];
} triangles;

void main() {
    Triangle tri = triangles.triangles[gl_InstanceIndex];
    vec2 pos = tri.points[gl_VertexIndex];
    Transform transform = tri.transform;
    Transform canvasTransform = tri.canvasTransform;

    mat2 rot;
    rot[0] = vec2(cos(transform.rotation), 0 - sin(transform.rotation));
    rot[1] = vec2(sin(transform.rotation), cos(transform.rotation));
    pos -= transform.origin;
    pos = rot * (pos * transform.scale);
    pos += transform.origin;

    mat2 canvasRot;
    canvasRot[0] = vec2(cos(canvasTransform.rotation), 0 - sin(canvasTransform.rotation));
    canvasRot[1] = vec2(sin(canvasTransform.rotation), cos(canvasTransform.rotation));
    pos -= canvasTransform.origin;
    pos = canvasRot * (pos * canvasTransform.scale);
    pos += canvasTransform.origin;

    pos = pos + canvasTransform.translation + transform.translation;

    gl_Position = mat.proj * mat.view * vec4(pos, tri.z, 1.0);

    outTexCoord = tri.texCoords[gl_VertexIndex];
    outColor = tri.colors[gl_VertexIndex];
    outTexId = tri.texId;
    outBlending = tri.blending;
}

and fragment shader:

#version 420

#extension GL_EXT_nonuniform_qualifier : enable

layout(location = 0) out vec4 outColor;

layout(location = 0) in vec2 inTexCoords;
layout(location = 1) in vec4 inColor;
layout(location = 2) in float texId;
layout(location = 3) in float blending;

layout(set = 2, binding = 0) uniform sampler2D textures[];
layout(set = 2, binding = 1) uniform sampler2D fonts[];

float screenPxRange(sampler2D texture) {
    const float pxRange = 2.0f;

    vec2 unitRange = vec2(pxRange) / vec2(textureSize(texture, 0));
    vec2 screenTexSize = vec2(1.0) / fwidth(inTexCoords);
    return max(0.5 * dot(unitRange, screenTexSize), 1.0);
}

float median(float r, float g, float b) {
    return max(min(r, g), min(max(r, g), b));
}

void main() {
    int iTexId = int(texId);
    if (blending < 0.0f) {
        // font
        vec3 msd = texture(fonts[iTexId], inTexCoords).rgb;
        float sd = median(msd.r, msd.g, msd.b);
        float screenPxDistance = screenPxRange(fonts[iTexId]) * (sd - 0.5);
        float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);

        if (opacity <= 0) discard;

        outColor = vec4(vec3(1.0f), opacity);
    }
    else {
        // no font
        outColor = iTexId == -1 ? inColor : mix(texture(textures[iTexId], inTexCoords), inColor, blending);
    }
}

By the way, the fragments change across the whole screen, when something is rendered differently (i.e. moving the camera around).

1 Answer 1

4

Your texture access is non-uniform. To avoid the artifacts you see, you need to let the implementation know that you do non-uniform access by enabling the shaderSampledImageArrayNonUniformIndexing feature from the VK_EXT_descriptor_indexing extension in your application, the GL_EXT_nonuniform_qualifier extension in your shader (which you already do) and then (which you don't yet do) surround your texture indexing with the non-uniform qualifier.

So

texture(textures[iTexId]

becomes

texture(textures[nonuniformEXT(iTexId)]
Sign up to request clarification or add additional context in comments.

4 Comments

So currently there is a chance for each fragment to get a wrong texture from the array?
What you do is undefined behavior, that's why the artifacts occur. The way you access your textures, you MUST tell the GPU that your access is non uniform.
So i just got to test this and it actually solved the issue! Thank you so much man, ive been looking for a fix for a while.
Note that this extension has been promoted to Vulkan Core in 1.2, and can be enabled through VkPhysicalDeviceVulkan12Features::descriptorIndexing.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.