Qt Quick 3D - 커스텀 머티리얼 예제

음영 처리된 사용자 정의 머티리얼을 작성하는 방법을 보여줍니다.

이 예제는 음영 처리된 custom materials 을 작성하는 방법을 보여줍니다. 음영 머티리얼을 사용하면 완전한 셰이더 프로그램을 작성할 필요가 없습니다. 대신 Qt의 표준 셰이더를 수정하는 함수를 작성하면 됩니다. 이렇게 하면 결과 머티리얼이 기본적으로 조명, 그림자 매핑에 참여하고 조명 프로브와 호환됩니다. 특별한 동작을 원하는 경우에만 커스텀 로직을 작성하면 됩니다. 이는 버텍스 및 프래그먼트 셰이더의 특정 단계에서 호출되는 자체 커스텀 함수를 사용하여 PrincipledMaterial 에 대해 생성되는 셰이더 코드를 효과적으로 보강함으로써 달성할 수 있습니다.

음영 처리된 커스텀 머티리얼을 만들려면 shadingMode 속성을 CustomMaterial.Shaded 으로 설정합니다.

간단한 머티리얼

첫 번째 모델은 커스텀 로직을 추가하지 않는 단순한 머티리얼을 사용합니다. 다른 머티리얼과 마찬가지로 모델에 커스텀 머티리얼을 설정합니다:

Model {
    source: "weirdShape.mesh"
    scale: Qt.vector3d(100, 100, 100)
    rotation: Quaternion.fromEulerAngles(-90, 0, 0)
    x: v3d.radius

    materials: [
        CustomMaterial {
            shadingMode: CustomMaterial.Shaded
            fragmentShader: "material_simple.frag"
            property color uDiffuse: "fuchsia"
            property real uSpecular: 1.0
        }
    ]
}

shadingModefragmentShader 을 설정하는 것 외에도 uDiffuseSpecular 이라는 두 가지 프로퍼티를 머티리얼에 추가합니다. 이 프로퍼티는 조각 셰이더에 의해 선택됩니다.

조각 셰이더의 코드는 짧습니다:

void MAIN()
{
    SPECULAR_AMOUNT = uSpecular;
    BASE_COLOR = uDiffuse;
}

모든 셰이더는 MAIN 함수를 구현해야 합니다. 여기서는 머티리얼에 정의된 프로퍼티를 사용하여 Qt의 표준 셰이더 코드에서 사용할 값을 설정합니다. 이를 유니폼으로 선언할 필요는 없습니다. 이름이 일치하는지 확인하기만 하면 됩니다. 머티리얼에 일치하는 프로퍼티가 없으면 셰이더 컴파일 오류가 발생합니다.

특수 변수 SPECULAR_AMOUNTBASE_COLORPrincipledMaterialspecularAmountbaseColor 에 해당합니다. 그런 다음 표준 셰이더 코드에서 PrincipledMaterial 을 사용한 것처럼 조명 계산을 수행하는 데 사용됩니다.

조명 커스텀 처리

다음 오브젝트는 커스텀 조명을 구현하는 더 복잡한 머티리얼을 사용합니다. 이 머티리얼의 이름은 다르지만 그 외에는 동일한 방식으로 사용합니다:

materials: [
    CustomMaterial {
        shadingMode: CustomMaterial.Shaded
        fragmentShader: "material_customlights.frag"
        property color uDiffuse: "orange"
        property real uShininess: 150
    }
]

조각 셰이더는 모든 다른 유형의 조명에 대한 사용자 정의 로직을 구현합니다:

void MAIN()
{
    SPECULAR_AMOUNT = 1.0;
    ROUGHNESS = 0.5;
    BASE_COLOR = uDiffuse;
}

void AMBIENT_LIGHT()
{
    DIFFUSE += uDiffuse.rgb * TOTAL_AMBIENT_COLOR;
}

void DIRECTIONAL_LIGHT()
{
    DIFFUSE += uDiffuse.rgb * LIGHT_COLOR * SHADOW_CONTRIB * vec3(max(0.0, dot(normalize(NORMAL), TO_LIGHT_DIR)));
}

void POINT_LIGHT()
{
    DIFFUSE += uDiffuse.rgb * LIGHT_COLOR * LIGHT_ATTENUATION * SHADOW_CONTRIB * vec3(max(0.0, dot(normalize(NORMAL), TO_LIGHT_DIR)));
}

void SPOT_LIGHT()
{
     DIFFUSE += uDiffuse.rgb * LIGHT_COLOR * LIGHT_ATTENUATION * SPOT_FACTOR * SHADOW_CONTRIB * vec3(max(0.0, dot(normalize(VAR_WORLD_NORMAL), TO_LIGHT_DIR)));
}

여기에서는 다양한 조명 유형의 속성을 나타내는 새로운 특수 키워드를 많이 사용합니다. 각 키워드에 대한 설명은 CustomMaterial 문서를 참조하세요. 각 조명 유형에는 고유한 함수가 있습니다. 구현되지 않은 함수는 PrincipledMaterial 처럼 동작하는 기본 구현을 사용합니다. 예를 들어 이 셰이더에서는 SPECULAR_LIGHT() 을 구현하지 않았으므로 내장된 스페큘러 리플렉션을 가져옵니다.

버텍스 셰이더 추가하기

커스텀 머티리얼은 버텍스 셰이더를 사용하여 모델의 지오메트리를 수정할 수도 있습니다. 여기서는 조각 및 버텍스 셰이더를 모두 지정하고 균일한 값으로 선택될 프로퍼티를 몇 가지 더 추가합니다:

materials: [
    CustomMaterial {
        id: material
        shadingMode: CustomMaterial.Shaded
        vertexShader: "material_distortion.vert"
        fragmentShader: "material_customlights.frag"
        property real uTime: 0.0
        property real uAmplitude: 0.3
        property color uDiffuse: "yellow"
        property real uShininess: 50
        NumberAnimation {
            target: material
            property: "uTime"
            from: 0.0
            to: 31.4
            duration: 10000
            loops: Animation.Infinite
            running: true
        }
    }
]

버텍스 셰이더는 매우 짧습니다:

// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

void MAIN()
{
    VERTEX.y += sin(uTime + VERTEX.x*10.0) * uAmplitude;
}

시간에 따라 변화하는 사인파에 따라 각 정점을 이동시켜 모델을 변형합니다.

투명한 재질

마지막으로 투명한 재질로 구를 추가합니다. 성능상의 이유로 Qt는 투명도를 완전히 사실적인 방식으로 구현하지 않습니다. 대신 Qt는 씬의 모든 불투명한 오브젝트를 텍스처로 렌더링한 다음 이 텍스처에서 투명 머티리얼을 읽습니다. 즉, 투명한 머티리얼은 다른 오브젝트 앞에 배치할 때 최상의 결과를 제공합니다:

Model {
    id: screenSphere
    source: "#Sphere"
    scale: Qt.vector3d(0.75, 0.75, 0.75)
    y: 60
    z: 750;
    materials: [
        CustomMaterial {
            shadingMode: CustomMaterial.Shaded
            fragmentShader: "material_transparent.frag"
        }
    ]

이 예제에서는 실제 물리적 굴절을 시도하지 않는 단순한 왜곡 함수를 구현했습니다:

void MAIN()
{
    vec2 size = vec2(textureSize(SCREEN_TEXTURE, 0));
    vec2 uv = FRAGCOORD.xy / size;

    vec3 view = normalize(VIEW_VECTOR);
    vec3 projection = view - view * normalize(NORMAL);
    vec3 refraction = projection * projection;
    uv += refraction.xy * 0.5;

    vec3 col = texture(SCREEN_TEXTURE, uv).rgb;
    col = col * 0.8 + vec3(0.2);

    BASE_COLOR = vec4(col, 1.0);
}

SCREEN_TEXTURE 는 씬의 모든 불투명한 오브젝트를 보여주는 텍스처를 나타냅니다. 먼저 현재 버텍스의 화면 위치와 일치하는 이 텍스처 내부의 UV 좌표를 계산합니다. 그런 다음 텍스처 룩업을 수행하기 전에 이 위치에 오프셋을 추가하여 굴절 효과를 시뮬레이션합니다.

마지막으로 20%의 흰색을 혼합하여 약간의 흐릿함을 얻습니다. 출력은 BASE_COLOR 에 할당되므로 Qt XML은 이 위에 조명을 추가합니다. 이것이 구의 표면에서 반사를 볼 수 있는 이유입니다.

음영 처리되지 않은 재질

편의 키워드를 계속 사용하면서 완전한 셰이더 프로그램을 사용하는 커스텀 머티리얼을 가질 수도 있습니다. 커스텀 셰이더 예제는 다른 커스텀 머티리얼 그룹인 음영 처리되지 않은 커스텀 머티리얼을 보여줍니다.

예제 프로젝트 @ code.qt.io

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.