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 の2つのプロパティをマテリアルに追加します。これらはフラグメントシェーダによって拾われます。

フラグメントシェーダのコードは短いです:

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

すべてのシェーダはMAIN 関数を実装する必要があります。この関数では、マテリアルで定義されたプロパティを使用して、Qt の標準シェーダコードで使用される値を設定します。これらのプロパティをユニフォームとして宣言する必要はないことに注意してください。マテリアルのプロパティが一致しないと、シェーダのコンパイルエラーが発生します。

特別な変数SPECULAR_AMOUNTBASE_COLOR は、PrincipledMaterialspecularAmountbaseColor に対応します。これらは、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 はこの上にライティングを追加します。球体の表面に反射が見えるのはこのためです。

シェーディングされていないマテリアル

完全なシェーダープログラムを使用するカスタムマテリアルを作成することも可能です(便利なキーワードを使用しながら)。customshadersの例では、カスタムマテリアルのもう1つのグループである、シェーディングされていないカスタムマテリアルを示しています。

サンプルプロジェクト @ 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.