프로그래밍 가능한 머티리얼, 이펙트, 지오메트리 및 텍스처 데이터
Qt Quick 3D, DefaultMaterial 및 PrincipledMaterial 의 내장 머티리얼은 속성을 통해 광범위한 사용자 지정이 가능하지만, 버텍스 및 프래그먼트 셰이더 수준에서의 프로그래밍 기능은 제공하지 않습니다. 이를 가능하게 하기 위해 ` CustomMaterial ` 타입이 제공됩니다.
| 다음과 같은 모델의 경우 PrincipledMaterial | CustomMaterial 가 정점을 변환하는 |
|---|---|
|
|
View3D 가 정점을 변환하는 모델포스트 프로세싱 효과는 의 출력이 Qt Quick 로 전달되기 전에, 필요에 따라 심도 버퍼를 고려하여 색상 버퍼에 대해 하나 이상의 처리 단계를 수행하는 것으로, 다음 두 가지 유형으로 나뉩니다:
- ExtendedSceneEnvironment 을 통해 구성할 수 있는 내장 후처리 단계(예: 글로우/블룸, 피사계 심도, 비네팅, 렌즈 플레어)와,
custom응용 프로그램에서 프래그먼트 셰이더 코드와 Effect 객체 내의 처리 단계 사양을 통해 구현되는 효과.
실제로는 세 번째 범주의 후처리 효과가 존재합니다. 바로 Qt Quick 를 통해 구현되는 2D 효과로, 3D 렌더러의 개입 없이 View3D 항목의 출력값을 대상으로 작동합니다. 예를 들어, ` View3D ` 항목에 블러 효과를 적용하려면, ` MultiEffect`와 같은 ` Qt Quick`의 기존 기능을 사용하는 것이 가장 간단한 방법입니다. 3D 후처리 시스템은 뎁스 버퍼나 스크린 텍스처와 같은 3D 씬 개념을 포함하거나, HDR 톤 매핑을 처리해야 하거나, 중간 버퍼를 사용하는 다중 패스가 필요한 등 복잡한 효과에 유용합니다. 3D 씬이나 렌더러에 대한 이해가 필요 없는 간단한 2D 효과는 언제나 ShaderEffect 또는 MultiEffect 을 사용하여 구현할 수 있습니다.
| 효과가 적용되지 않은 씬 | 사용자 정의 후처리 효과가 적용된 동일한 장면 |
|---|---|
|
|
프로그래밍 가능한 머티리얼과 포스트 프로세싱 외에도, 일반적으로 파일 형태로 제공되는 두 가지 유형의 데이터가 있습니다(.mesh 파일 또는 .png 과 같은 이미지 ):
- 렌더링될 메시의 지오메트리를 비롯해 텍스처 좌표, 법선, 색상 및 기타 데이터를 포함한 버텍스 데이터,
- 렌더링된 오브젝트의 텍스처 맵으로 사용되거나, 스카이박스 또는 이미지 기반 조명과 함께 사용되는 텍스처 콘텐츠.
원하는 경우, 애플리케이션은 C++에서 QByteArray 형태로 이러한 데이터를 제공할 수 있습니다. 또한 이러한 데이터는 시간이 지남에 따라 변경될 수 있으므로, Model 또는 Texture 에 대한 데이터를 절차적으로 생성하고 나중에 수정할 수 있습니다.
| C++에서 정점 데이터를 동적으로 지정하여 렌더링된 그리드 | C++에서 생성된 이미지 데이터로 텍스처가 적용된 큐브 |
|---|---|
|
|
머티리얼, 효과, 지오메트리 및 텍스처를 사용자 정의하고 동적으로 만드는 이 네 가지 접근 방식은 셰이딩의 프로그래밍 가능성과 셰이더가 입력으로 받는 데이터의 절차적 생성을 가능하게 합니다. 다음 섹션에서는 이러한 기능에 대한 개요를 제공합니다. 전체 참조 내용은 각 유형에 대한 문서 페이지에서 확인할 수 있습니다:
참고: 다중 렌더 패스 및 사용자 정의 렌더 타깃을 포함하여 전체 렌더링 파이프라인을 완벽하게제어하려면 Qt Quick 3D 의 ‘사용자 정의 렌더 패스( User-Defined Render Passes) ’를 참조하십시오. 사용자 정의 렌더 패스는 전체 렌더링 파이프라인 아키텍처를 정의할 수 있게 함으로써 이 페이지에서 설명된 기능을 보완하는 반면, ‘ CustomMaterial ’ 및 ‘Effect’는 파이프라인 내의 개별 머티리얼과 후처리 단계를 사용자 정의하는 데 중점을 둡니다.
| 기능 | 참조 문서 | 관련 예제 |
|---|---|---|
| 사용자 정의 머티리얼 | CustomMaterial | Qt Quick 3D - 사용자 정의 셰이더 예제, Qt Quick 3D - 사용자 정의 머티리얼 예제 |
| 사용자 정의 후처리 효과 | Effect | Qt Quick 3D - 사용자 정의 효과 예제 |
| 사용자 정의 지오메트리 | QQuick3DGeometry, Model::geometry | Qt Quick 3D - 사용자 정의 지오메트리 예제 |
| 사용자 정의 텍스처 데이터 | QQuick3DTextureData, Texture::textureData | Qt Quick 3D - 절차적 텍스처 예시 |
머티리얼 프로그래밍 기능
큐브가 있는 씬을 만들고, 기본 PrincipledMaterial 와 CustomMaterial 로 시작해 봅시다:
| PrincipledMaterial | CustomMaterial |
|---|---|
| |
버텍스 셰이더나 프래그먼트 셰이더 코드가 추가되지 않은 경우, CustomMaterial 는 사실상 PrincipledMaterial 와 동일하므로 이 두 가지 모두 정확히 동일한 결과를 나타냅니다.

참고: baseColor, metalness, baseColorMap 등과 같은속성들은 CustomMaterial QML 유형에는 이에 상응하는 속성이 없습니다. 이는 의도된 설계입니다. 머티리얼을 사용자 정의하는 것은 단순히 몇 가지 고정된 값을 제공하는 것이 아니라 셰이더 코드를 통해 이루어집니다.
첫 번째 버텍스 셰이더
사용자 정의 버텍스 셰이더 스니펫을 추가해 봅시다. 이는 vertexShader 속성에 파일을 참조하여 수행됩니다. 프래그먼트 셰이더의 경우에도 접근 방식은 동일합니다. 이러한 참조는 Image.source 또는 ShaderEffect.vertexShader 과 마찬가지로 작동합니다. 즉, 로컬 또는 qrc URL이며, 상대 경로는 .qml 파일의 위치를 기준으로 처리됩니다. 따라서 일반적인 방법은 .vert 및 .frag 파일을 Qt 리소스 시스템(CMake 사용 시qt_add_resources )에 배치하고 상대 경로를 사용하여 참조하는 것입니다.
Qt 6.0에서는 Qt Quick 나 Qt Quick 3D 모두에서 인라인 셰이더 문자열이 더 이상 지원되지 않습니다. (이러한 속성은 문자열이 아닌 URL이라는 점에 유의하십시오.) 그러나 본질적으로 동적인 특성 때문에, Qt Quick 3D 의 사용자 정의 머티리얼 및 후처리 효과는 여전히 참조된 파일 내에 소스 형태의 셰이더 스니펫을 제공합니다. 이는 ShaderEffect 과는 다른 점입니다. 의 경우 셰이더가 그 자체로 완성되어 있으며, 엔진에 의한 추가 수정이 필요하지 않으므로, 미리 준비된 .qsb 셰이더 팩 형태로 제공되어야 합니다.
참고: Qt Quick 3D 에서는 URL이 로컬 리소스만을 참조할 수 있습니다. 원격 콘텐츠에 대한 스키마는 지원되지 않습니다.
참고: 사용되는 셰이딩언어는 Vulkan 호환 GLSL입니다. .vert 및 .frag 파일은 그 자체로 완전한 셰이더가 아니므로, 흔히 snippets 라고 불립니다. 그렇기 때문에 이 스니펫들은 유니폼 블록, 입력 및 출력 변수, 샘플러 유니폼을 직접 제공하지 않습니다. 대신 Qt Quick 3D 엔진이 적절하게 수정합니다.
| main.qml 및 material.vert의 변경 사항 | 결과 |
|---|---|
|
|
사용자 정의 버텍스 또는 프래그먼트 셰이더 스니펫은 MAIN, DIRECTIONAL_LIGHT, POINT_LIGHT, SPOT_LIGHT, AMBIENT_LIGHT, SPECULAR_LIGHT 와 같이 미리 정의된 이름을 가진 하나 이상의 함수를 제공해야 합니다. 지금은 MAIN 에 집중해 보겠습니다.
여기서 볼 수 있듯이, MAIN()을 비워둔 최종 결과는 이전과 정확히 동일합니다.
더 흥미로운 내용을 다루기 전에, 사용자 정의 버텍스 셰이더 스니펫에서 가장 흔히 사용되는 특수 키워드들을 간략히 살펴보겠습니다. 이 목록은 전체를 다룬 것은 아닙니다. 전체 참조 내용은 CustomMaterial 페이지를 확인하시기 바랍니다.
| 키워드 | 유형 | 설명 |
|---|---|---|
| MAIN | void MAIN()은 진입점입니다. 이 함수는 사용자 정의 버텍스 셰이더 스니펫에 반드시 포함되어야 하며, 그렇지 않으면 스니펫을 작성할 의미가 없습니다. | |
| VERTEX | vec3 | 셰이더가 입력으로 받는 정점 위치입니다. 사용자 정의 머티리얼에서 정점 셰이더를 사용하는 일반적인 용례는, 벡터 전체나 일부 성분에 값을 할당하여 이 벡터의 x, y, z 값을 변경(변위)하는 것입니다. |
| NORMAL | vec3 | 입력 메쉬 데이터에서 가져온 버텍스 노멀입니다. 노멀이 제공되지 않은 경우에는 모든 성분이 0으로 설정됩니다. VERTEX와 마찬가지로, 셰이더는 필요에 따라 이 값을 자유롭게 변경할 수 있습니다. 변경된 값은 프래그먼트 스테이지의 라이팅 계산을 포함하여 파이프라인의 나머지 단계에서 사용됩니다. |
| UV0 | vec2 | 입력 메쉬 데이터의 첫 번째 텍스처 좌표 세트이며, UV 값이 제공되지 않은 경우 모두 0으로 설정됩니다. VERTEX 및 NORMAL과 마찬가지로 이 값도 변경될 수 있습니다. |
| MODELVIEWPROJECTION_MATRIX | mat4 | 모델-뷰-투영 행렬입니다. 어떤 그래픽 API를 사용하여 렌더링하든 동작을 통일하기 위해, 이 단계에서 모든 정점 데이터와 변환 행렬은 OpenGL 규약을 따릅니다. (Y축이 위를 가리키는, OpenGL 호환 투영 행렬) 읽기 전용입니다. |
| MODEL_MATRIX | mat4 | 모델(월드) 행렬입니다. 읽기 전용입니다. |
| NORMAL_MATRIX | mat3 | 모델 행렬의 왼쪽 상단 3x3 영역의 전치 역행렬입니다. 읽기 전용입니다. |
| CAMERA_POSITION | vec3 | 월드 공간에서의 카메라 위치입니다. 이 페이지의 예제에서는 (0, 0, 600) 입니다. 읽기 전용입니다. |
| CAMERA_DIRECTION | vec3 | 카메라 방향 벡터입니다. 이 페이지의 예제에서는 (0, 0, -1) 입니다. 읽기 전용입니다. |
| CAMERA_PROPERTIES | vec2 | 카메라의 근점(near) 및 원점(far) 클립 값입니다. 이 페이지의 예제에서는 (10, 10000) 입니다. 읽기 전용입니다. |
| POINT_SIZE | float | 이 값은 점(point) 토폴로지로 렌더링할 때만 관련이 있습니다. 예를 들어, ` custom geometry `가 메시(mesh)에 대해 이러한 지오메트리를 제공하는 경우입니다. 이 값에 쓰기 작업을 수행하는 것은 ` pointSize on a PrincipledMaterial`을 설정하는 것과 동일합니다. |
| POSITION | vec4 | gl_Position 와 동일합니다. 이 값이 없을 경우, MODELVIEWPROJECTION_MATRIX 및 VERTEX 를 사용하여 기본 할당 문이 자동으로 생성됩니다. 이것이 바로 빈 MAIN()이 작동하는 이유이며, 대부분의 경우 여기에 사용자 정의 값을 할당할 필요가 없습니다. |
특정 패턴에 따라 정점의 위치를 변위시키는 사용자 정의 머티리얼을 만들어 봅시다. 더 흥미롭게 만들기 위해, 애니메이션이 적용된 QML 속성들을 추가하고, 이 속성들의 값이 셰이더 코드에서 유니폼으로 노출되도록 합시다. (정확히 말하면, 대부분의 속성은 런타임에 유니폼 버퍼로 뒷받침되는 유니폼 블록의 멤버에 매핑되지만, Qt Quick 3D 덕분에 사용자 정의 머티리얼 작성자에게는 이러한 세부 사항이 편리하게 투명하게 처리됩니다.)
| main.qml의 material.vert 변경 | 결과 |
|---|---|
|
QML 속성에서 파생된 유니폼
CustomMaterial 객체의 사용자 정의 속성은 유니폼으로 매핑됩니다. 위 예제에서는 uAmplitude 및 uTime 이 이에 해당합니다. 값이 변경될 때마다 업데이트된 값이 셰이더에서 반영됩니다. 이 개념은 ShaderEffect 에서 이미 익숙할 수 있습니다.
QML 속성 이름과 GLSL 변수 이름은 일치해야 합니다. 개별 유니폼에 대해 셰이더 코드에서 별도로 선언할 필요는 없습니다. 대신 QML 속성 이름을 그대로 사용할 수 있습니다. 이것이 바로 위의 예제에서 버텍스 셰이더 스니펫 내에서 uTime 및 uAmplitude 을 별도의 사전 선언 없이 바로 참조할 수 있는 이유입니다.
다음 표는 유형 간의 매핑 관계를 보여줍니다:
| QML 유형 | 셰이더 유형 | 비고 |
|---|---|---|
| real, int, bool | float, int, bool | |
| color | vec4 | sRGB에서 선형으로의 변환은 암시적으로 수행됩니다 |
| vector2d | vec2 | |
| vector3d | vec3 | |
| vector4d | vec4 | |
| matrix4x4 | mat4 | |
| 쿼터니언 | vec4 | 스칼라 값은 w |
| rect | vec4 | |
| 점, 크기 | vec2 | |
| TextureInput | sampler2D |
예제 개선하기
더 진행하기 전에 예제를 좀 더 보기 좋게 만들어 봅시다. 회전된 직사각형 메쉬를 추가하고 DirectionalLight 이 그림자를 드리우도록 설정하면, 큐브의 정점 변경 사항이 섀도우 맵을 포함한 모든 렌더링 패스에 올바르게 반영되는지 확인할 수 있습니다. 눈에 띄는 그림자를 얻기 위해, 이제 조명을 Y축 상단으로 조금 더 높게 배치하고, 조명이 부분적으로 아래쪽을 향하도록 회전을 적용했습니다. (이 조명은 ‘ directional ’ 유형이므로 회전이 중요합니다)
| main.qml, material.vert | 결과 |
|---|---|
|
프래그먼트 셰이더 추가
많은 사용자 정의 머티리얼은 프래그먼트 셰이더도 필요로 합니다. 사실, 프래그먼트 셰이더만 필요한 경우도 많습니다. 버텍스 단계에서 프래그먼트 단계로 전달할 추가 데이터가 없고, 기본 버텍스 변환으로 충분하다면, CustomMaterial 에서 vertexShader 속성을 설정하는 과정을 생략할 수 있습니다.
| main.qml, material.frag 파일 수정 | 결과 |
|---|---|
|
|
첫 번째 프래그먼트 셰이더에는 빈 MAIN() 함수가 포함되어 있습니다. 이는 프래그먼트 셰이더 스니펫을 아예 지정하지 않은 것과 다를 바가 없습니다. 결과물은 기본 PrincipledMaterial 를 사용했을 때와 비슷하게 나타납니다.
이제 프래그먼트 셰이더에서 흔히 사용되는 키워드 몇 가지를 살펴보겠습니다. 이 목록은 전체가 아니므로, 완전한 참조를 보려면 CustomMaterial 문서를 참조하십시오. 이들 중 상당수는 읽기-쓰기(read-write) 유형으로, 기본값이 있지만 셰이더가 다른 값을 할당할 수 있으며, 실제로도 종종 그렇게 할 필요가 있습니다.
이름에서 알 수 있듯이, 이들 중 상당수는 메탈릭-러프니스(metallic-roughness) 머티리얼 모델을 따르며, 동일한 의미와 의미론을 가진 유사한 이름의 PrincipledMaterial 속성과 매핑됩니다. 이러한 값을 어떻게 계산할지는 사용자 정의 머티리얼 구현에 달려 있습니다. 예를 들어, ‘ BASE_COLOR ’의 값은 셰이더에 하드코딩될 수도 있고, 텍스처 샘플링을 기반으로 할 수도 있으며, 유니폼으로 노출된 QML 속성이나 버텍스 셰이더에서 전달된 보간 데이터를 기반으로 계산될 수도 있습니다.
| 키워드 | 유형 | 설명 |
|---|---|---|
| BASE_COLOR | vec4 | 기본 색상 및 알파 값입니다. PrincipledMaterial::baseColor 에 해당합니다. 프래그먼트의 최종 알파 값은 모델 불투명도에 기본 색상 알파를 곱한 값입니다. 기본값은 (1.0, 1.0, 1.0, 1.0) 입니다. |
| EMISSIVE_COLOR | vec3 | 자체 발광 색상입니다. PrincipledMaterial::emissiveFactor 에 해당합니다. 기본값은 (0.0, 0.0, 0.0) 입니다. |
| METALNESS | float | Metalness 0~1 범위의 값입니다. 기본값은 0이며, 이는 재질이 유전체(비금속)임을 의미합니다. |
| ROUGHNESS | float | Roughness 0~1 범위의 값입니다. 기본값은 0입니다. 값이 클수록 스페큘러 하이라이트가 부드러워지고 반사가 흐려집니다. |
| SPECULAR_AMOUNT | float | The strength of specularity 범위는 0~1입니다. 기본값은 0.5 입니다. ‘ metalness ’가 ‘ 1 ’로 설정된 금속 물체의 경우, 이 값은 아무런 영향을 미치지 않습니다. ‘ SPECULAR_AMOUNT ’와 ‘ METALNESS ’의 값이 모두 0보다 크지만 1보다 작을 때, 두 재료 모델이 혼합된 결과가 나타납니다. |
| NORMAL | vec3 | 월드 공간에서의 보간된 법선 벡터로, 면 켈링이 비활성화된 경우 양면성을 고려하여 조정됩니다. 읽기 전용입니다. |
| UV0 | vec2 | 보간된 텍스처 좌표입니다. 읽기 전용입니다. |
| VAR_WORLD_POSITION | vec3 | 월드 공간에서의 보간된 버텍스 위치. 읽기 전용. |
큐브의 기본 색상을 빨간색으로 설정해 봅시다:
| main.qml 파일의 material.frag 부분을 수정합니다. | 결과 |
|---|---|
|
|
이제 자체 발광 강도를 조금 높여 봅시다:
| main.qml의 material.frag에서 변경 | 결과 |
|---|---|
|
|
셰이더에 값을 하드코딩하는 대신, 유니폼으로 노출된 QML 속성을 사용할 수도 있으며, 애니메이션이 적용된 속성도 사용할 수 있습니다:
| main.qml의 material.frag 변경 사항 | 결과 |
|---|---|
|
PrincipledMaterial 와 그 표준 내장 속성으로는 구현할 수 없는, 좀 더 복잡한 작업을 해 봅시다. 다음 머티리얼은 큐브 메쉬의 텍스처 UV 좌표를 시각화합니다. U는 0부터 1까지(검정색에서 빨간색까지)이며, V 역시 0부터 1까지(검정색에서 녹색까지)입니다.
| main.qml의 material.frag 변경 사항 | 결과 |
|---|---|
|
|
이왕 하는 김에, 이번에는 구를 대상으로 노멀도 시각화해 봅시다. UV와 마찬가지로, 사용자 정의 버텍스 셰이더 스니펫이 NORMAL의 값을 변경하면, 프래그먼트 셰이더에서 프래그먼트별로 보간된 값(이 역시 NORMAL이라는 이름으로 노출됨)이 해당 조정 사항을 반영하게 됩니다.
| main.qml의 material.frag 변경 사항 | 결과 |
|---|---|
|
|
색상
잠시 찻주전자 모델로 전환하여, 재질을 금속성과 유전성 재질의 혼합으로 설정하고, 기본 색상을 녹색으로 지정해 봅시다. ‘ green ’ QColor 값은 ‘ (0, 128, 0) ’에 매핑되므로, 이를 바탕으로 첫 번째 시도는 다음과 같을 수 있습니다:
| main.qml, material.frag |
|---|
|

이 결과는 완전히 올바르게 보이지 않습니다. 두 번째 접근 방식과 비교해 보세요:
| main.qml, material.frag의 변경 사항 | 결과 |
|---|---|
|
|
PrincipledMaterial 로 전환하여, PrincipledMaterial::baseColor 을 "green"으로 설정하고 메탈니스(metalness) 및 기타 속성을 적용하면 결과가 두 번째 접근 방식과 동일함을 확인할 수 있습니다:
| main.qml의 변경 사항 | 결과 |
|---|---|
|
|
uColor 속성의 유형을 vector4d 로 변경하거나, color 이외의 다른 유형으로 변경하면 결과가 갑자기 바뀌어 첫 번째 접근 방식과 동일해집니다.
왜 그럴까요?
그 이유는 DefaultMaterial의 color 속성인 PrincipledMaterial 과, CustomMaterial 내에서 color 유형을 가진 사용자 정의 속성에 대해 암시적으로 수행되는 sRGB에서 선형(linear)으로의 변환에 있습니다. 다른 값에 대해서는 이러한 변환이 수행되지 않으므로, 셰이더가 색상 값을 하드코딩하거나 color 와 다른 유형의 QML 속성을 기반으로 하는 경우, 소스 값이 sRGB 색상 공간에 있는 경우 선형화를 수행하는 것은 셰이더의 몫입니다. Qt Quick 3D 는 프래그먼트 셰이딩의 결과에 대해 tonemapping 를 수행하며, 이 과정은 sRGB 공간의 값을 입력으로 가정하기 때문에 선형 변환은 중요합니다.
"green" 와 같은 내장된 QColor 상수들은 모두 sRGB 공간으로 주어집니다. 따라서 sRGB 공간에서 RGB 값 (0, 128, 0) 과 일치하는 결과를 얻고자 한다면, 처음 시도에서 단순히 vec4(0.0, 0.5, 0.0, 1.0) 를 BASE_COLOR 에 할당하는 것만으로는 불충분합니다. 이러한 색상 값을 선형화하는 공식은 CustomMaterial 의 ‘ BASE_COLOR ’ 문서를 참조하십시오. 텍스처 샘플링을 통해 가져온 색상 값에도 동일한 원리가 적용됩니다. 소스 이미지 데이터가 sRGB 색상 공간이 아닌 경우, 변환이 필요합니다( tonemapping 가 비활성화되어 있지 않은 한).
블렌딩
알파 블렌딩을 얻으려면 단순히 1.0 보다 작은 값을 BASE_COLOR.a 에 할당하는 것만으로는 충분하지 않습니다. 이러한 머티리얼은 원하는 결과를 얻기 위해 sourceBlend 및 destinationBlend 속성의 값을 변경하는 경우가 매우 많습니다.
또한 결합된 알파 값은 Node opacity 에 머티리얼 알파를 곱한 값이라는 점을 명심하십시오.
이를 시각화하기 위해, 0.5 의 알파 값을 가진 빨간색을 BASE_COLOR 에 할당하는 셰이더를 사용해 보겠습니다:
| main.qml, material.frag | 결과 |
|---|---|
|
|
첫 번째 큐브는 색상의 알파 값에 0.5를 할당하지만, 알파 블렌딩이 활성화되어 있지 않아 눈에 띄는 결과가 나타나지 않습니다. 두 번째 큐브는 CustomMaterial 속성을 통해 간단한 알파 블렌딩을 활성화합니다. 세 번째 큐브는 Model에 0.5의 불투명도를 할당하므로, 실제 불투명도는 0.25가 됩니다.
버텍스 셰이더와 프래그먼트 셰이더 간 데이터 전달
버텍스당 값을 계산한 후(예를 들어, 단일 삼각형의 경우 삼각형의 3개 꼭짓점에 대해), 이를 프래그먼트 단계로 전달합니다. 프래그먼트 단계에서는 각 프래그먼트(예를 들어, 래스터화된 삼각형에 포함된 모든 프래그먼트)에 대해 보간된 값을 사용할 수 있게 됩니다. 사용자 정의 머티리얼 셰이더 스니펫에서는 ` VARYING ` 키워드를 통해 이를 구현할 수 있습니다. 이 키워드는 GLSL 120 및 GLSL ES 100과 유사한 구문을 제공하지만, 실행 시 사용되는 그래픽 API와 관계없이 작동합니다. 엔진이 상황에 맞게 `varying` 선언을 재작성해 줍니다.
UV 좌표를 사용한 고전적인 텍스처 샘플링이 어떻게 보이는지 살펴보겠습니다. 텍스처에 대해서는 다음 섹션에서 다룰 예정이며, 지금은 셰이더 내의 ` texture() ` 함수에 전달할 수 있는 UV 좌표를 어떻게 구하는지에 집중해 봅시다.
| main.qml, material.vert, material.frag |
|---|
|
| qt_logo_rect.png | 결과 |
|---|---|
|
|
VARYING 선언에 유의하십시오. 이름과 유형이 일치해야 하며, 프래그먼트 셰이더 내의 uv 는 현재 프래그먼트에 대한 보간된 UV 좌표를 노출합니다.
다른 유형의 데이터도 유사한 방식으로 프래그먼트 단계로 전달할 수 있습니다. 일반적인 요구 사항의 상당 부분을 처리해 주는 내장 변수가 제공되므로, 대부분의 경우 머티리얼 고유의 바리에이션을 설정할 필요가 없다는 점을 유의해야 합니다. 여기에는 (보간된) 노멀, UV, 월드 위치(VAR_WORLD_POSITION), 또는 카메라를 향하는 벡터(VIEW_VECTOR)를 생성하는 것이 포함됩니다.
UV0 가 프래그먼트 단계에서도 자동으로 사용 가능하기 때문에, 위의 예제는 사실 다음과 같이 단순화할 수 있습니다:
| main.qml, material.frag 수정 | 결과 |
|---|---|
|
|
변수에 대한 보간을 비활성화하려면 버텍스 셰이더와 프래그먼트 셰이더 스니펫 모두에서 flat 키워드를 사용하십시오. 예를 들어:
VARYING flat vec2 v;텍스처
CustomMaterial 에는 내장된 텍스처 맵이 없으므로, 예를 들어 ` PrincipledMaterial::baseColorMap`과 같은 기능은 없습니다. 이는 동일한 기능을 구현하는 것이 대개 간단할 뿐만 아니라, DefaultMaterial 및 PrincipledMaterial 에 내장된 기능보다 훨씬 더 큰 유연성을 제공하기 때문입니다. 단순히 텍스처를 샘플링하는 것 외에도, 사용자 정의 프래그먼트 셰이더 스니펫은 BASE_COLOR, EMISSIVE_COLOR, ROUGHNESS 등에 할당할 값을 계산할 때 다양한 소스의 데이터를 자유롭게 결합하고 블렌딩할 수 있습니다. 이러한 계산은 QML 속성을 통해 제공된 데이터, 버텍스 단계에서 전달된 보간된 데이터, 텍스처 샘플링을 통해 가져온 값, 그리고 하드코딩된 값을 기반으로 할 수 있습니다.
앞서 본 예시에서 알 수 있듯이, 텍스처를 버텍스 셰이더, 프래그먼트 셰이더 또는 두 셰이더 모두에 노출하는 것은 스칼라 및 벡터 유니폼 값과 매우 유사합니다. 즉, TextureInput 타입의 QML 속성은 셰이더 코드 내의 sampler2D 와 자동으로 연결됩니다. 언제나 그렇듯이, 셰이더 코드에서 이 샘플러를 별도로 선언할 필요는 없습니다.
TextureInput 는 Texture 를 참조하며, 여기에 enabled 속성이 추가되어 있습니다. Texture 는 다음 세 가지 방법으로 데이터를 가져올 수 있습니다: from an image file, from a texture with live Qt Quick content, 또는 QQuick3DTextureData 를 통해 can be provided from C++ 를 사용할 수 있습니다.
참고: Texture 속성의경우 , 소스, 타일링 및 필터링과 관련된 속성만이 사용자 정의 머티리얼에서 암시적으로 고려되며, 나머지(예: UV 변환)는 사용자 정의 셰이더가 적절하다고 판단하는 대로 구현하면 됩니다.
라이브 Qt Quick 콘텐츠를 사용하여 모델(이 경우 구체)에 텍스처를 적용하는 예제를 살펴보겠습니다:
| main.qml, material.frag |
|---|
|
여기서는 이 미니 씬이 변경될 때마다 2D 서브트리(두 개의 자식 요소, 즉 또 다른 Rectangle과 Text를 가진 Rectangle)가 512x512 크기의 2D 텍스처로 렌더링됩니다. 그런 다음 이 텍스처는 ‘ someTextureMap ’라는 이름으로 커스텀 머티리얼에 노출됩니다.
셰이더에서 V 좌표가 반전되는 점에 유의하십시오. 앞서 언급한 바와 같이, 셰이더 수준에서 완전한 프로그래밍이 가능한 사용자 정의 머티리얼은 Texture 및 PrincipledMaterial 의 "고정된" 기능을 제공하지 않습니다. 즉, UV 좌표에 대한 모든 변환은 셰이더에서 적용해야 합니다. 여기서는 텍스처가 Texture::sourceItem 를 통해 생성된다는 것을 알고 있으므로, 사용 중인 메시의 UV 세트와 일치하도록 V를 반전시켜야 합니다.
이 예제는 PrincipledMaterial 를 사용해도 동일한 결과를 얻을 수 있음을 보여줍니다. 여기에 간단한 엠보스 효과를 추가하여 좀 더 흥미롭게 만들어 보겠습니다:
| material.frag | 결과 |
|---|---|
|
지금까지 다룬 기능들을 활용하면, 메쉬에 시각적으로 인상적인 음영을 주는 머티리얼을 만드는 데 있어 폭넓은 가능성이 열립니다. 기본 설명을 마무리하며, 평면 메쉬에 높이 맵과 법선 맵을 적용하는 예제를 살펴보겠습니다. (내장된 #Rectangle 는 세분화 수준이 충분하지 않으므로 여기서는 전용 .mesh 파일을 사용합니다) 더 나은 조명 결과를 얻기 위해 360도 HDR 이미지를 사용한 이미지 기반 조명을 적용하겠습니다. 또한 무슨 일이 일어나고 있는지 더 명확하게 보여주기 위해 이 이미지를 스카이박스로도 설정했습니다.
먼저 빈 CustomMaterial 부터 시작해 보겠습니다:
| main.qml | 결과 |
|---|---|
|
|
이제 메시에 높이 맵과 법선 맵을 적용하는 셰이더를 몇 개 만들어 봅시다:
| 하이츠 맵 | 노멀 맵 |
|---|---|
|
|
| material.vert, material.frag |
|---|
|
| main.qml의 변경 사항 | 결과 |
|---|---|
|
|
참고: ` WasdController `객체는 키보드와 마우스를 사용하여 익숙한 방식으로 씬 내를 탐색하고 주변을 둘러볼 수 있게 해주므로, 개발 및 문제 해결 과정에서 매우 유용합니다. ` WasdController `로 카메라를 제어하는 방법은 다음과 같이 간단합니다:
import QtQuick3D.Helpers
View3D {
PerspectiveCamera {
id: camera
}
// ...
}
WasdController {
controlledObject: camera
}깊이 및 화면 텍스처
사용자 정의 셰이더 스니펫에서 ` DEPTH_TEXTURE ` 또는 ` SCREEN_TEXTURE ` 키워드를 사용하면, 별도의 렌더 패스에서 해당 텍스처를 생성하도록 설정됩니다. 이는 반드시 효율적인 작업은 아니지만, 유리 같은 소재의 굴절 효과와 같은 다양한 기법을 구현할 수 있게 해줍니다.
DEPTH_TEXTURE sampler2D 는 장면 내의 모든 객체가 렌더링된 상태에서 깊이 버퍼의 내용을 바탕으로 텍스처를 샘플링할 수 있게 해줍니다. 마찬가지로, 는 로, 투명한 재질이나 SCREEN_TEXTURE를 사용하는 재질을 제외한 장면의 내용을 담은 텍스처를 샘플링할 수 있게 해줍니다. 이 텍스처는 렌더링되는 프레임버퍼의 내용을 필요로 하는 재질에 사용할 수 있습니다. SCREEN_TEXTURE 텍스처는 와 동일한 클리어 모드를 사용합니다. 이 텍스처들의 크기는 픽셀 단위로 의 크기와 일치합니다. opaque SCREEN_TEXTURE sampler2D View3D View3D
DEPTH_TEXTURE 를 통해 깊이 버퍼의 내용을 시각화하여 간단한 데모를 살펴보겠습니다. 여기서는 범위를 좁혀 시각화된 깊이 값의 차이를 더 명확하게 보여주기 위해 카메라의 far clip value 를 기본값인 10000에서 2000으로 줄였습니다. 그 결과, 장면의 깊이 버퍼가 표면 위에 시각화된 사각형이 나타납니다.
| main.qml, material.frag | 결과 |
|---|---|
|
원통은 반투명성을 사용하기 때문에 DEPTH_TEXTURE 에 나타나지 않는다는 점에 유의하십시오. 이는 모두 불투명한 다른 객체들과는 다른 범주에 속하기 때문입니다. 이러한 객체들은 불투명한 객체가 기록한 깊이 값에 대해서는 테스트를 수행하지만, 깊이 버퍼에는 기록하지 않으며, 뒤에서 앞으로의 순서로 렌더링되는 것에 의존합니다. 따라서 이 객체들은 DEPTH_TEXTURE 에도 나타나지 않습니다.
대신 셰이더를 SCREEN_TEXTURE 를 샘플링하도록 변경하면 어떻게 될까요?
| material.frag | 결과 |
|---|---|
|
|
여기서는 직사각형에 SCREEN_TEXTURE 를 텍스처로 적용하고, 투명한 픽셀을 보라색으로 대체했습니다.
라이트 프로세서 함수
CustomMaterial 의 고급 기능 중 하나는 프래그먼트 색상을 계산하는 데 사용되는 조명 방정식을 재구현하는 함수를 프래그먼트 셰이더에서 정의할 수 있는 기능입니다. 라이트 프로세서 함수가 존재할 경우, 장면 내의 각 라이트에 대해, 각 프래그먼트마다 한 번씩 호출됩니다. 각기 다른 조명 유형과 앰비언트 및 스페큘러 기여도에 대해 전용 함수가 제공됩니다. 해당 조명 프로세서 함수가 존재하지 않을 경우, PrincipledMaterial 에서 수행하는 것과 마찬가지로 표준 계산이 사용됩니다. 조명 프로세서가 존재하지만 함수 본문이 비어 있는 경우, 이는 장면 내 해당 유형의 조명이 기여하지 않음을 의미합니다.
DIRECTIONAL_LIGHT, POINT_LIGHT, SPOT_LIGHT, AMBIENT_LIGHT, SPECULAR_LIGHT 와 같은 함수에 대한 자세한 내용은 CustomMaterial 문서를 참조하십시오.
음영이 없는 사용자 정의 재질
CustomMaterial 에는 또 다른 유형인 unshaded 사용자 정의 머티리얼이 있습니다. 지금까지의 모든 예제에서는 shaded 사용자 정의 머티리얼을 사용했으며, shadingMode 속성은 기본값인 CustomMaterial.Shaded로 설정되어 있었습니다.
이 속성을 CustomMaterial.Unshaded로 변경하면 어떻게 될까요?
우선, BASE_COLOR, EMISSIVE_COLOR, METALNESS 등과 같은 키워드는 더 이상 원하는 효과를 내지 않습니다. 이는 이름에서 알 수 있듯이, 언셰이디드(unshaded) 머티리얼은 표준 셰이딩 코드의 상당 부분이 자동으로 적용되지 않으므로, 씬 내의 조명, 이미지 기반 조명, 그림자 및 앰비언트 오클루전을 무시하기 때문입니다. 대신, 언셰이디드 머티리얼은 ` FRAGCOLOR ` 키워드를 통해 셰이더에 완전한 제어권을 부여합니다. 이는 `gl_FragColor`와 유사합니다. 즉, ` FRAGCOLOR `에 할당된 색상은 ` Qt Quick 3D`에 의한 추가 조정 없이 프래그먼트의 결과이자 최종 색상이 됩니다.
| main.qml, material.frag, material2.frag | 결과 |
|---|---|
|
|
오른쪽 원기둥이 씬의 DirectionalLight 를 무시하는 것을 확인할 수 있습니다. 이 원기둥의 셰이딩은 씬 조명에 대해 전혀 고려하지 않으므로, 최종 프래그먼트 색상은 완전히 흰색입니다.
셰이딩이 적용되지 않은 머티리얼의 버텍스 셰이더는 여전히 VERTEX, NORMAL, MODELVIEWPROJECTION_MATRIX 등과 같은 일반적인 입력 변수를 사용할 수 있으며, POSITION 에 값을 쓸 수 있습니다. 그러나 프래그먼트 셰이더는 더 이상 이와 같은 편리한 기능을 사용할 수 없습니다. 즉, NORMAL, UV0, VAR_WORLD_POSITION 는 셰이딩이 적용되지 않은 머티리얼의 프래그먼트 셰이더에서는 사용할 수 없습니다. 대신, 이제 셰이더 코드에서 VARYING 를 사용하여 최종 프래그먼트 색상을 결정하는 데 필요한 모든 값을 직접 계산하고 전달해야 합니다.
버텍스 셰이더와 프래그먼트 셰이더가 모두 포함된 예제를 살펴보겠습니다. 변경된 버텍스 위치는 프래그먼트 셰이더로 전달되며, 모든 프래그먼트에 보간된 값이 제공됩니다.
| main.qml, material.vert, material.frag |
|---|
|
쉐이딩되지 않은 머티리얼은 씬 조명과의 상호작용이 필요하지 않거나 원하지 않는 경우, 그리고 머티리얼이 최종 프래그먼트 색상을 완전히 제어해야 할 때 유용합니다. 위의 예제에서 DirectionalLight 이나 다른 조명이 전혀 없음에도 불구하고, 사용자 정의 머티리얼이 적용된 구체가 예상대로 표시되는 것을 확인할 수 있습니다.
참고: 버텍스 셰이더 스니펫만 있고 fragmentShader 속성을 지정하지 않은 언셰이딩머티리얼은 여전히 작동하지만, 결과는 shadingMode가 Shaded로 설정된 것과 동일합니다. 따라서 버텍스 셰이더만 있는 머티리얼의 경우 shadingMode를 변경하는 것은 별 의미가 없습니다.
이펙트를 위한 프로그래밍 기능
포스트 프로세싱 효과는 View3D 의 결과물에 하나 이상의 프래그먼트 셰이더를 적용합니다. 그런 다음 원래 렌더링 결과 대신 이러한 프래그먼트 셰이더의 출력이 표시됩니다. 이는 개념적으로 Qt Quick 의 ShaderEffect 및 ShaderEffectSource 와 매우 유사합니다.
참고: 후처리 효과는 View3D 의 renderMode 가 View3D.Offscreen으로 설정된 경우에만 사용할 수 있습니다.
효과에 대해 사용자 정의 버텍스 셰이더 스니펫을 지정할 수도 있지만, 그 유용성이 제한적이므로 상대적으로 드물게 사용될 것으로 예상됩니다. 후처리 효과의 버텍스 입력은 쿼드(두 개의 삼각형 또는 삼각형 스트립)이며, 이에 대한 버텍스를 변환하거나 변위시키는 것은 대개 도움이 되지 않습니다. 그러나 VARYING 키워드를 사용하여 데이터를 계산하고 프래그먼트 셰이더로 전달하기 위해 버텍스 셰이더를 사용하는 것은 의미가 있을 수 있습니다. 평소와 마찬가지로, 프래그먼트 셰이더는 현재 프래그먼트 좌표를 기반으로 보간된 값을 수신하게 됩니다.
Effect 와 관련된 셰이더 스니펫의 구문은 셰이딩되지 않은 CustomMaterial 의 셰이더와 동일합니다. 내장된 특수 키워드의 경우, VARYING, MAIN, FRAGCOLOR (프래그먼트 셰이더 전용), POSITION (버텍스 셰이더 전용), VERTEX (버텍스 셰이더 전용), MODELVIEWPROJECTION_MATRIX 는 CustomMaterial 와 동일하게 작동합니다.
Effect 프래그먼트 셰이더에서 가장 중요한 특수 키워드는 다음과 같습니다:
| 이름 | 유형 | 설명 |
|---|---|---|
| INPUT | sampler2D 또는 sampler2DArray | 입력 텍스처에 대한 샘플러입니다. 효과는 일반적으로 ` INPUT_UV`를 사용하여 이 텍스처를 샘플링합니다. |
| INPUT_UV | vec2 | INPUT 의 샘플링을 위한 UV 좌표. |
| INPUT_SIZE | vec2 | INPUT 텍스처의 크기(픽셀 단위). 이는 textureSize()를 호출하는 대신 사용할 수 있는 편리한 방법입니다. |
| OUTPUT_SIZE | vec2 | 출력 텍스처의 크기(픽셀 단위). 대부분의 경우 INPUT_SIZE 과 동일하지만, 멀티패스 이펙트의 경우 크기가 다른 중간 텍스처로 출력되는 패스가 있을 수 있습니다. |
| DEPTH_TEXTURE | sampler2D | 씬 내 불투명한 오브젝트가 포함된 뎁스 버퍼 내용을 담은 뎁스 텍스처입니다. ` CustomMaterial`와 마찬가지로, 셰이더에 이 키워드가 존재하면 뎁스 텍스처가 자동으로 생성됩니다. |
참고: 멀티뷰 렌더링이 활성화된경우 , 입력 텍스처는 2D 텍스처 배열입니다. 이 경우 texture() 및 textureSize()와 같은 GLSL 함수는 각각 vec3/ivec3를 받아 반환합니다. 레이어에는 ` VIEW_INDEX `를 사용하십시오. 멀티뷰 렌더링이 활성화되었을 때와 비활성화되었을 때 모두 작동해야 하는 VR/AR 애플리케이션의 경우, 다음과 같이 셰이더 코드를 작성하는 것이 호환성을 높이는 방법입니다:
#if QSHADER_VIEW_COUNT >= 2
vec4 c = texture(INPUT, vec3(INPUT_UV, VIEW_INDEX));
#else
vec4 c = texture(INPUT, INPUT_UV);
#endif후처리 효과
이번에는 체크보드 텍스처를 기본 컬러 맵으로 사용하는 텍스처가 적용된 사각형을 포함하여, 오브젝트를 몇 개 더 추가한 간단한 장면부터 시작해 보겠습니다.
| main.qml | 결과 |
|---|---|
|
|
이제 전체 씬에 효과를 적용해 봅시다. 더 정확히 말하면, ` View3D`에 적용하는 것입니다. 씬에 여러 개의 ` View3D ` 항목이 있을 경우, 각 항목은 고유한 ` SceneEnvironment `를 가지므로 각각 고유의 후처리 효과 체인을 갖게 됩니다. 이 예제에서는 전체 창을 덮는 단일 ` View3D `가 하나 있습니다.
| main.qml의 변경 사항 | effect.frag |
|---|---|
| |
이 간단한 효과는 빨간색 채널의 값을 변경합니다. QML 속성을 유니폼(uniform)으로 노출하는 방식은 효과에서도 사용자 정의 머티리얼과 동일한 방식으로 작동합니다. 셰이더는 효과용 프래그먼트 셰이더를 작성할 때 매우 흔히 사용되는 한 줄로 시작합니다. 즉, UV 좌표 INPUT_UV 에서 INPUT 을 샘플링하는 것입니다. 그런 다음 원하는 계산을 수행하고, 최종 프래그먼트 색상을 FRAGCOLOR 에 할당합니다.
이 예제에서 설정된 많은 속성(효과, 패스, 셰이더)은 복수형으로 되어 있습니다. 요소가 하나뿐일 때는 [ ] 구문을 생략할 수 있지만, 이 모든 속성은 리스트이며 하나 이상의 요소를 포함할 수 있습니다. 그 이유는 무엇일까요?
- effects View3D 는 여러 이펙트를 연쇄적으로 연결할 수 있도록 허용하기 때문에 리스트 형태입니다. 이펙트는 리스트에 추가된 순서대로 적용됩니다. 이를 통해 에 두 개 이상의 이펙트를 함께 쉽게 적용할 수 있으며, 이는 에서 항목을 중첩하여 달성할 수 있는 방식과 유사합니다. 다음 효과의 ` ` 텍스처는 항상 이전 효과의 출력을 포함하는 텍스처입니다. ` `의 최종 출력으로 사용되는 것은 마지막 효과의 출력입니다. View3D Qt Quick ShaderEffect
INPUTView3D - passes 는 목록(list) 형태입니다. 이는 ` ShaderEffect`와 달리 `Effect`가 다중 패스(multiple passes)를 기본적으로 지원하기 때문입니다. 다중 패스 효과는 ` effects`에서 여러 개의 독립적인 효과를 연쇄적으로 연결하는 것보다 더 강력합니다. 패스는 효과의 원래 입력 텍스처 외에도 임시 중간 텍스처로 출력할 수 있으며, 이 텍스처는 후속 패스의 입력으로 사용될 수 있습니다. 이를 통해 최종 프래그먼트 색상을 얻기 위해 여러 텍스처를 계산, 렌더링 및 블렌딩하는 복잡한 이펙트를 만들 수 있습니다. 이 고급 사용 사례는 여기서는 다루지 않을 것입니다. 자세한 내용은 Effect 문서 페이지를 참조하십시오.
- shaders 는 리스트입니다. 이는 하나의 이펙트에 버텍스 셰이더와 프래그먼트 셰이더가 모두 연결될 수 있기 때문입니다.
여러 효과 연결
이전 예제의 이펙트에 내장된 ‘ DistortionSpiral ’ 이펙트와 유사한 또 다른 이펙트를 추가하여 보완하는 예제를 살펴보겠습니다.
| main.qml의 변경 사항 | effect2.frag |
|---|---|
| |
이제 다소 의아할 수 있는 질문이 있습니다. 왜 이것이 나쁜 예시일까요?
더 정확하게 말하자면, 이 예제가 나쁘다는 것이 아니라, 피하는 것이 종종 유익할 수 있는 패턴을 보여주기 때문입니다.
이러한 방식으로 이펙트를 연쇄적으로 연결하는 것은 유용할 수 있지만, 성능에 미치는 영향을 염두에 두는 것이 중요합니다. 두 번의 렌더링 패스(한 번은 조정된 빨간색 채널이 포함된 텍스처를 생성하기 위해, 다른 한 번은 왜곡을 계산하기 위해)를 수행하는 것은 한 번이면 충분할 때 상당히 비효율적입니다. 프래그먼트 셰이더 스니펫을 결합했다면, 단 하나의 이펙트로도 동일한 결과를 얻을 수 있었을 것입니다.
C++에서 메쉬 및 텍스처 데이터 정의하기
메시와 텍스처 이미지 데이터를 절차적으로 생성하는 과정은 모두 유사한 단계를 따릅니다:
- QQuick3DGeometry 을 상속받거나 QQuick3DTextureData
- 생성 시 기본 클래스의 보호된 멤버 함수를 호출하여 원하는 버텍스 또는 이미지 데이터를 설정
- 나중에 동적인 변경이 필요한 경우, 새로운 데이터를 설정하고 update()를 호출합니다.
- 구현이 완료되면, 해당 클래스가 QML에서 인식되도록 등록해야 합니다
- Model 이제 QML 내의 ` Texture ` 객체는 ` Model::geometry ` 또는 ` Texture::textureData ` 속성을 설정하여 사용자 정의 정점 또는 이미지 데이터 제공자를 사용할 수 있습니다
사용자 정의 버텍스 데이터
버텍스 데이터는 메쉬를 구성하는 (일반적으로 float) 값들의 시퀀스를 의미합니다. .mesh 파일을 불러오는 대신, 사용자 정의 지오메트리 제공자가 동일한 데이터를 제공하는 역할을 합니다. 버텍스 데이터는 위치, 텍스처(UV) 좌표, 법선과 같은 attributes 로 구성됩니다. 속성 사양은 어떤 종류의 속성이 존재하는지, 컴포넌트 유형(예: x, y, z 값으로 구성된 3성분 부동소수점 벡터)와 제공된 데이터 내에서 어느 오프셋에서 시작하는지, 그리고 스트라이드(동일한 속성의 다음 요소를 가리키기 위해 오프셋에 더해야 하는 증분)가 얼마인지 등을 설명합니다.
OpenGL이나 Vulkan과 같은 그래픽 API를 직접 다뤄본 적이 있다면 이 내용이 익숙하게 느껴질 수 있습니다. 이러한 API에서 정점 입력을 지정하는 방식은 .mesh 파일이나 QQuick3DGeometry 인스턴스가 정의하는 내용과 대략적으로 일치하기 때문입니다.
또한, 메시 토폴로지(프리미티브 유형)도 지정해야 합니다. 인덱스 기반 렌더링의 경우, 인덱스 버퍼에 대한 데이터도 함께 제공되어야 합니다.
내장된 사용자 정의 지오메트리 구현이 하나 있습니다. QtQuick3D 의 .Helpers 모듈에는 GridGeometry 유형이 포함되어 있습니다. 이를 통해 사용자 정의 QQuick3DGeometry 하위 클래스를 구현할 필요 없이, 라인 프리미티브를 사용하여 씬에 격자를 렌더링할 수 있습니다.
또 다른 일반적인 사용 사례는 점을 렌더링하는 것입니다. 속성 사양이 매우 간단하기 때문에 이를 구현하는 것은 상당히 쉽습니다. 각 정점에 대해 세 개의 부동 소수점 수치(x, y, z)만 제공하면 되며, 그 외에는 아무것도 필요하지 않습니다. QQuick3DGeometry 의 서브클래스는 다음과 같이 2,000개의 점으로 구성된 지오메트리를 구현할 수 있습니다:
clear();
const int N = 2000;
const int stride = 3 * sizeof(float);
QByteArray v;
v.resize(N * stride);
float *p = reinterpret_cast<float *>(v.data());
QRandomGenerator *rg = QRandomGenerator::global();
for (int i = 0; i < N; ++i) {
const float x = float(rg->bounded(200.0f) - 100.0f) / 20.0f;
const float y = float(rg->bounded(200.0f) - 100.0f) / 20.0f;
*p++ = x;
*p++ = y;
*p++ = 0.0f;
}
setVertexData(v);
setStride(stride);
setPrimitiveType(QQuick3DGeometry::PrimitiveType::Points);
addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0, QQuick3DGeometry::Attribute::F32Type);다음과 같은 머티리얼과 결합하면
DefaultMaterial {
lighting: DefaultMaterial.NoLighting
cullMode: DefaultMaterial.NoCulling
diffuseColor: "yellow"
pointSize: 4
}결과는 다음과 비슷합니다(여기서는 WasdController 의 도움을 받아 변경된 카메라 각도에서 본 모습입니다):

참고: 기본 그래픽 API에 따라, 1이 아닌 점의 크기와 선의 두께는 런타임에서 지원되지 않을 수있습니다 . 이는 Qt가 제어할 수 없는 사항입니다. 따라서 점 및 선 그리기에 의존하는 대신 대체 기법을 구현해야 할 수도 있습니다.
사용자 정의 텍스처 데이터
텍스처의 경우, 제공해야 하는 데이터의 구조는 훨씬 더 간단합니다. 텍스처 형식에 따라 픽셀당 바이트 수가 달라지는 원시 픽셀 데이터입니다. 예를 들어, RGBA 텍스처는 픽셀당 4바이트를 필요로 하는 반면, RGBA16F 는 픽셀당 4개의 하프 플로트(half-float)를 사용합니다. 이는 QImage 가 내부적으로 저장하는 방식과 유사합니다. 그러나 Qt Quick 3D 텍스처의 경우, 해당 데이터를 QImage 로 표현할 수 없는 형식을 가질 수 있습니다. 예를 들어, 부동 소수점 HDR 텍스처나 압축 텍스처가 이에 해당합니다. 따라서 QQuick3DTextureData 의 데이터는 항상 원시 바이트 시퀀스 형태로 제공됩니다. OpenGL이나 Vulkan과 같은 그래픽 API를 직접 다뤄본 적이 있다면 이 방식이 익숙하게 느껴질 수 있습니다.
자세한 내용은 QQuick3DGeometry 및 QQuick3DTextureData 문서 페이지를 참조하십시오.
또한 다음 문서를 참조하십시오: CustomMaterial, Effect, QQuick3DGeometry, QQuick3DTextureData, Qt Quick 3D - 사용자 정의 이펙트 예제, Qt Quick 3D - 사용자 정의 셰이더 예제, Qt Quick 3D - 사용자 정의 머티리얼 예제, Qt Quick 3D - 사용자 정의 지오메트리 예제, Qt Quick 3D - 절차적 텍스처 예제.
© 2026 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.























