Search code examples
qtopenglqmlqt5qt3d

How to keep texture aspect ratio when the mesh dimensions change in Qt3D


I’m testing the current status of Qt3D. I’m very fond of the MetalRoughMaterial, but I can’t seem to handle this simple use case in Qt3D: I would like to use textures that repeat to fill the face they are on.

Desired result:

desired result desired result2

What I get is this (see code below):

longcube

In case you’re wondering, I made 2 cubes next to one another to make the 2nd image, but I don’t consider this a real solution…)

I have been experimenting with WrapMode, Qtexture, textureScale but can’t find a solution in Qt3D yet.

I noticed that when I change textureScale, this repeats the texture on all faces. But on the small faces we get the same amount of images as in the longer ones. Is there a way to change this for x, y and z differently?

code:

import Qt3D.Core 2.12
import Qt3D.Render 2.12
import Qt3D.Input 2.12
import Qt3D.Extras 2.12

Entity {
    id: sceneRoot

    components: [
        RenderSettings {
            activeFrameGraph: ForwardRenderer{ 
                camera: camera
            }
        },
        InputSettings { },

        DirectionalLight {
            worldDirection: Qt.vector3d(-1, -1, -1);
            color: "white"
            intensity: 2.0
        }
   ]

    Camera {
        id: camera
        projectionType: CameraLens.PerspectiveProjection
        fieldOfView: 45
        aspectRatio: 800/600
        nearPlane : 0.1
        farPlane : 1000.0
        position: Qt.vector3d( 2.0, 2.0, 2.0 )
        upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
        viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
    }

    OrbitCameraController { camera: camera }

    CuboidMesh {
        id: cuboidMesh
        xExtent:  2
        yExtent:  1
        zExtent:  1
    }

    Transform {
        id: cuboidTranslation
        translation: Qt.vector3d(0, 0, 0)
    }

    Entity {
        id: cuboidEntity
        components: [ cuboidMesh, testMaterial, cuboidTranslation ]
    }

    MetalRoughMaterial {
        id: testMaterial
        baseColor: TextureLoader {
            source: "qrc:/assets/textures/checkers.png"
        }

        metalness:0.0
        roughness: 1.0
        textureScale :1
    }
}

I know how to do it in pure opengl(without Qt3D) by altering texturecoordinates of vertex data. If that is the direction to go I suspect I will have to make my own custom Mesh. Can somebody confirm this is the way to go? Or are there other solutions for this use case?

Thanks for your time reading this. Every help, suggestion is welcome, even the pure opengl solutions.


Solution

  • Yes, you can change texture coordinates in geometry, but I think it's easier to write custom shader, which gets proper texture coordinates from computations. For example, you can start with SimpleMaterial shader, modify it to display grid like you want. I had similar code for fragment shader:

    #version 150 core
    
    uniform vec3 boxSize;//all model
    uniform vec2 wallSectionSize;//x y - size of cell inside this model
    varying highp vec3 pos;
    
    void main() {
        vec3 pt = pos.xyz;
        float x = pt.x;
        float y = pt.y;
        float z = pt.z;
        bool side = false;
        if(x==0 || x==boxSize.x) {
            x = z;
            side = true;
        } else if(y==0 || y==boxSize.y) {
            y = z;
        }
        //b == block number
        const int nbx = int(x/wallSectionSize.x);
        const int nby = int(y/wallSectionSize.y);//floor number from 0
        //coordinates in this block umber
        float bx = x - nbx * wallSectionSize.x;
        float by = y - nby * wallSectionSize.y;
        if(nbx % 2)
            //fragColor = texture2D(...);
            fragColor = vec4(1,1,0,0);
        else
            fragColor = vec4(1,0,1,0);
    }