Graphics Tools for Unreal v0.2.0 Release (#5)

Graphics Tools for Unreal v0.2.0 Release
This commit is contained in:
Cameron 2021-04-16 12:14:00 -07:00 коммит произвёл GitHub
Родитель 8c7056b976
Коммит 7f638b02a8
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
165 изменённых файлов: 2848 добавлений и 554 удалений

9
Docs/Authors.md Normal file
Просмотреть файл

@ -0,0 +1,9 @@
# Authors
Graphics Tools is a collaborative project containing contributions from individuals around the world. Our sincere thanks to all who have, and continue to contribute.
- Alban Bergeret (AlbanBERGERET-Epic)
- Cameron Micka (Cameron-Micka)
- Daryl Obert
- Luis Valverde (luis-valverde-ms)
- Summer Wu (sw5813)

204
Docs/ClippingPrimitives.md Normal file
Просмотреть файл

@ -0,0 +1,204 @@
---
title: Clipping Primitives
description: Guide to graphic resources and techniques in Graphics Tools.
author: Cameron-Micka
ms.author: thmicka
ms.date: 02/02/2021
ms.localizationpriority: high
keywords: Unreal, Unreal Engine, UE4, HoloLens, HoloLens 2, Mixed Reality, development, MRTK, GT, Graphics Tools, graphics, rendering, materials
---
# Clipping Primitives
Clipping primitives can be used to dynamically slice away a mesh and peer inside the geometry. This is useful whenever you need to inspect the inner workings of a complex model.
A clipping primitive represents an analytic shape that passes its state and transformation data into a [material parameter collection](https://docs.unrealengine.com/en-US/RenderingAndGraphics/Materials/ParameterCollections/index.html). [Material functions](https://docs.unrealengine.com/en-US/RenderingAndGraphics/Materials/Functions/index.html) can then take this primitive data and perform calculations, such as returning the signed distance from the shape's surface.
![ClippingPrimitives](Images/FeatureCards/ClippingPrimitives.png)
> [!NOTE]
> The clipping algorithm is optimized for mixed reality but requires the use of [masked or translucent](https://docs.unrealengine.com/en-US/RenderingAndGraphics/Materials/MaterialProperties/BlendModes/index.html) materials. Masked and translucent materials have additional overhead over opaque ones and should be used sparingly.
Graphics Tools contains a few clipping primitive types for developers to pick from. All clipping primitive components derive from the `UGTClippingPrimitiveComponent`.
> [!NOTE]
> By default, Graphics Tools only transfers the data of one clipping primitive per type per level to materials (i.e., only one `MF_GTClippingSphere`, `MF_GTClippingBox`, etc. has an effect within a level.) To utilize more than one clipping primitive per type per level please see the [advanced usage](#Advanced-usage) section.
### Clipping Plane
The `UGTClippingPlane` component (or actor) represents an infinite plane. The plane's position is controlled by the component's transform location and plane normal is determined by transform's x axis direction. Use the `MF_GTClippingPlane` material function to calculate a point's signed distance from the primitive.
![ClippingPrimitivesPlane](Images/ClippingPrimitives/ClippingPrimitivesPlane.png)
### Clipping Sphere
The `UGTClippingSphere` component (or actor) represents a sphere (or [ellipsoid](https://en.wikipedia.org/wiki/Ellipsoid)). The sphere's location, rotation, and scale is controlled by the component's transform. Non-uniform scales will turn the sphere into a ellipsoid. Use the `MF_GTClippingSphere` material function to calculate a point's signed distance from the primitive.
![ClippingPrimitivesSphere](Images/ClippingPrimitives/ClippingPrimitivesSphere.png)
### Clipping Box
The `UGTClippingBox` component (or actor) represents a box (i.e., cube). The box's location, rotation, and scale are controlled by the component's transform. Use the `MF_GTClippingBox` material function to calculate a point's signed distance from the primitive.
![ClippingPrimitivesCube](Images/ClippingPrimitives/ClippingPrimitivesCube.png)
### Clipping Cone
The `UGTClippingCone` component (or actor) represents a cone or more exactly a resizable capped cylinder. The cone's location and rotation are controlled by the component's transform. The component transform's scale controls the cone's height, bottom cap radius, and top cap radius with the x, y, and z values respectively. Use the `MF_GTClippingCone` material function to calculate a point's signed distance from the primitive.
![ClippingPrimitivesCone](Images/ClippingPrimitives/ClippingPrimitivesCone.png)
![ClippingPrimitivesConeCylinder](Images/ClippingPrimitives/ClippingPrimitivesConeCylinder.png)
## Example level
There are demonstrations of clipping primitive techniques within the `\GraphicsToolsProject\Plugins\GraphicsToolsExamples\Content\ClippingPrimitives\ClippingPrimitives.umap` level.
## Example usage
In the following steps we will create a new material that when applied to a mesh will perform per pixel clipping based on clipping primitives within a level.
1. First we will create a material.
* Right click within the "Content Browser" and select "Material" under the "Create Basic Asset" menu listings.
* Let's name our material `M_ClippingPrimitives`.
* Double click on `M_ClippingPrimitives` to open the material editor.
2. It's good practice to keep your materials simple (in other words keep the number of shader instructions low) when authoring materials for Mixed Reality.
* To ensure this, mark `M_ClippingPrimitives` as "Unlit" (1) in the material's "Shading Model" property.
* Next, mark the material as "Masked" (2). This tell's Unreal's renderer that this material will discard pixels and exposes the "Opacity Mask" input.
* Masked materials have another important parameter called "Opacity Mask Clip Value" for our use case set this value to zero. (3) This value specifies a threshold at which pixels with opacity masks under this value will be discarded.
* (Optional) Mark the material as "Two Sided" (4). Two sided materials disable [back face culling](https://en.wikipedia.org/wiki/Back-face_culling), which can decrease rendering performance, but is sometimes desireable when peering inside a model.
* (Optional) If you would like your material to still look as though it is lit, then right click on the material graph and add the `MF_GTDefaultLit` material function. Connect the result of the `MF_GTDefaultLit` to the material's "Emissive Color." (5)
![Material Setup](Images/ClippingPrimitives/ClippingPrimitivesMaterialSetup.png)
3. Next let's give our material a base color. Very few materials exist in the real world that are completely black (and completely black materials render transparently on additive displays like the one found on HoloLens 2).
* Right click on the material graph and add a `ConstantVector3` node.
* Set the node's RGB channels to 0.2, 0.2, and 0.5 respectively, a muted purple color.
* Connect this node to the `BaseColor` input of the `MF_GTDefaultLit` material function. (1) Or "Emissive Color" if you opted not to use the `MF_GTDefaultLit` function in step 2.
![Material Color](Images/ClippingPrimitives/ClippingPrimitivesMaterialColor.png)
4. Our material now needs to add clipping primitive calculations. Let's start with a clipping sphere.
* To do this right click on the material graph and add the `MF_GTClippingSphere` material function (1).
* Connect the result of `MF_GTClippingSphere` to the material's "Opacity Mask."
* Note, you don't need to connect any inputs to the `MF_GTClippingSphere` by default. For an explanation as to why read on to the [advanced usage](#Advanced-usage) section below.
![Clipping Sphere](Images/ClippingPrimitives/ClippingPrimitivesMaterialClippingSphere.png)
5. It's time to preview our material interacting with a clipping sphere.
* First let's create a new level (File > New Level) and select "Empty Level."
* From the "Place Actors" panel drop a `Sphere` actor into the level.
* With the `Sphere` actor selected change the material to our newly created material, `M_ClippingPrimitives`.
* Scale the sphere down to about 25% so that it's not so large.
![Actor Setup](Images/ClippingPrimitives/ClippingPrimitivesActorSetup.png)
6. You may notice now that our `Sphere` actor is very dark. To fix this let's add a directional light to our scene. Because we are using the `MF_GTDefaultLit` material function we must use a `GTDirectionalLight` actor (or component).
* From the "Place Actors" panel drop a `GTDirectionalLight` actor into the level.
* Set the `GTDirectionalLight`'s "Intensity" to 2. The default value represents a "sunny day" and may wash out our material. More information about the `GTDirectionalLight` component can be found in the [lighting](Lighting.md) documentation.
7. It's now time to add a clipping sphere to the level.
* From the "Place Actors" panel drop a `GTClippingSphere` actor into the level.
* Move the `GTClippingSphere` actor around until it intersects the surface of the the `Sphere` actor. You will notice the sphere discards any pixels that intersect with it. (1)
* Try experimenting with different clipping primitive settings by toggling the "Clipping Side" from "Inside" to "Outside" to see how pixels get discarded. Or try changing the size of clipping sphere via scale. If you non-uniformly scale the clipping sphere it will clip pixels within an ellipsoid shape.
* If you want to disable the clipping sphere without removing the actor you can uncheck the "Visible" flag under the "Rendering" properties.
![Clipping Sphere Complete](Images/ClippingPrimitives/ClippingPrimitivesClippingSphereSetup.png)
7. It can be a bit difficult to discern the border between clipped and non-clipped pixels. To remedy this let's add a border highlight.
* Double click on the `M_ClippingPrimitives` material to reopen the material editor.
* Right click on the material graph and add the `MF_GTClippingBorder` material function (1).
* Add the result of `MF_GTClippingBorder` to the current "Emissive Color" input. (2)
![Clipping Sphere Complete](Images/ClippingPrimitives/ClippingPrimitivesBorderSetup.png)
* Save the material and jump back to the level editor. You should now see an orange highlight that outlines the clipping border. (1)
* (Optional) Open the `M_ClippingPrimitives` material and try adjusting the `MF_GTClippingBorder`'s "Width" and "Border Color" to see how the border changes.
![Clipping Sphere Complete](Images/ClippingPrimitives/ClippingPrimitivesBorderComplete.png)
8. Some scenarios require more than one clipping primitive to be used at once. Let's modify our material so that it can also be clipped by a clipping box.
* Open the `M_ClippingPrimitives` material in the material editor.
* Right click on the material graph and add the `MF_GTClippingBox` material function (1).
* Right click on the material graph and add the `Min` material function (2).
* Connect the output of `MF_GTClippingSphere` to the "A" input of `Min` and the output of `MF_GTClippingBox` to the "B" input of `Min`.
* Connect the output of `Min` to the material's "Opacity Mask" and to the "Distance" input of `MF_GTClippingBorder`.
* Note, any combination of clipping primitives can be "summed" using the min of their outputs.
![Clipping Box](Images/ClippingPrimitives/ClippingPrimitivesMaterialClippingBox.png)
7. Now let's add a clipping box to the level.
* Jump back to the level editor and from the "Place Actors" panel drop a `GTClippingBox` actor into the level.
* Move the `GTClippingBox` actor around until it intersects the surface of the the `Sphere` actor. You will notice the sphere and box discard any pixels that intersect with them. (1)
![Clipping Sphere Complete](Images/ClippingPrimitives/ClippingPrimitivesClippingBoxSetup.png)
## Advanced usage
In the above example you may have noticed that the `MF_GTClippingSphere`, `MF_GTClippingBox`, etc. material functions had inputs that we never connected to anything. This is because Graphics Tools automatically manages the transfer of data from a clipping primitive to a material for one instance of a clipping primitive (`GTClippingSphere`, `GTClippingBox`, etc.). If you were to add a second primitive to a level it would be ignored by the clipping functions by default. Some apps require more than one type of clipping primitive in use at a time. We will demonstrate how to achieve this below.
> [!NOTE]
> Having many clipping primitives affect a material will increase pixel shader instructions and will impact performance. Please profile these changes within your project.
*How to add a second clipping sphere to a level. Note, the same technique can be used on any primitive and for any permutation of primitives.*
1. First we will create a material.
* Right click within the "Content Browser" and select "Material" under the "Create Basic Asset" menu listings.
* Let's name our material `M_TwoClippingSpheres`.
* Double click on `M_TwoClippingSpheres` to open the material editor.
2. Next configure the material for primitive clipping.
* Mark the material as "Masked" (1). This tell's Unreal's renderer that this material will discard pixels and exposes the "Opacity Mask" input.
* Masked materials have another important parameter called "Opacity Mask Clip Value" for our use case set this value to zero. (2) This value specifies a threshold at which pixels with opacity masks under this value will be discarded.
* Next, it is recommended to mark materials as "Unlit" and to use the `MF_GTDefaultLit` function. But, to simplify this demonstration we will use Unreal's default lighting model.
* Finally give the material a base color. Right click on the material graph and add a `ConstantVector3` node. (3) Set the node's RGB channels to 0.2, 0.8, and 0.2 respectively, a slime green color. And connect the color to the "Base Color" input.
![Clipping Advanced Material Setup](Images/ClippingPrimitives/ClippingPrimitivesAdvancedMaterialSetup.png)
3. Our material now needs to add clipping primitive calculations.
* Right click on the material graph and add a `MF_GTClippingSphere` material function (1).
* Right click on the material graph again and add a second `MF_GTClippingSphere` material function (2).
* Right click on the material graph one more time and add the `Min` function (3).
* Connect the output of the first `MF_GTClippingSphere` to the "A" input of `Min` and the output of the second `MF_GTClippingSphere` to the "B" input of `Min`.
* Connect the output of `Min` to the material's "Opacity Mask."
![Clipping Advanced Two Spheres](Images/ClippingPrimitives/ClippingPrimitivesAdvancedTwoSpheres.png)
4. It's now time to add two clipping spheres to the level.
* From the "Place Actors" panel drop a `GTClippingSphere` actor into the level.
* Move the `GTClippingSphere` actor around until it intersects the surface of the the `Sphere` actor. You will notice the sphere discards any pixels that intersect with it. (1)
* From the "Place Actors" panel drop a second `GTClippingSphere` actor into the level. Move the second `GTClippingSphere` actor around until it intersects the surface of the the `Sphere` actor. You will notice the sphere does **not** discard any pixels that intersect with it. (2)
* Let's make some changes below to fix this.
![Clipping Advanced Two Spheres Level](Images/ClippingPrimitives/ClippingPrimitivesAdvancedTwoSpheresLevel.png)
5. Our material needs a mechanism to know about the second clipping sphere. We will use a material parameter collection to do this.
* Right click within the "Content Browser" and select "Material Parameter Collection" under the "Create Advanced Asset/Materials and Textures" menu listings.
* Let's name our material parameter collection `MPC_SecondClippingSphere`.
* Double click on `MPC_SecondClippingSphere` to open the material parameter collection editor.
* Add five vector parameters (1) that have a default value of black (0, 0, 0, 0).
* Name the parameters to match the parameter names found under the advanced tab of the clipping sphere. (2) You are free to change these names to whatever you want if they are unique and match between the clipping primitive and material parameter collection.
* Finally, assign `MPC_SecondClippingSphere` to the "Parameter Collection Override" property of the second clipping sphere. (3)
![Clipping Advanced Parameter Collection](Images/ClippingPrimitives/ClippingPrimitivesAdvancedParameterCollection.png)
6. Now let's assign the outputs of `MPC_SecondClippingSphere` to the inputs of our second `MF_GTClippingSphere` material function.
* Double click on the `M_TwoClippingSpheres` material to reopen the material editor.
* Drag and drop the `MPC_SecondClippingSphere` from the content browser to the material editor.
* Duplicate the `Collection Param` so that there are a total of five. (1)
* Select a "Parameter Name" for each `Collection Param` and assign it to the correct `MF_GTClippingSphere` input on the second clipping sphere function. For example, "ClippingSphereSettings" should connect to "Settings."
![Clipping Advance Parameter Collection Setup](Images/ClippingPrimitives/ClippingPrimitivesAdvancedParameterCollectionSetup.png)
7. Lastly, let's jump back to the level editor to verify our changes have worked.
* In the level editor move the second `GTClippingSphere` around until it intersects the surface of the the `Sphere` actor.
* You will now notice the sphere discards any pixels that intersect with it, along with the first sphere.
![Clipping Advanced Complete](Images/ClippingPrimitives/ClippingPrimitivesAdvancedComplete.png)
For additional insight into advanced use cases please see the `\GraphicsToolsProject\Plugins\GraphicsToolsExamples\Content\ClippingPrimitives\ClippingPrimitives.umap` level.
## See also
- [Lighting](Lighting.md)
- [Effects](Effects.md)

Просмотреть файл

@ -54,6 +54,51 @@ To create a rim lit material use Unreal's built in `Fresnel` material node (1) a
![Rim Lit Material](Images/Effects/EffectsRimLitMaterial.png)
# Procedural Normal
Surface normals are used extensively in computer graphics to determine which way a surface is facing and how light bounces off a surface. Normally surface normals are calculated for a mesh at creation or import time, but with procedural meshes this calculation may not occur. Fortunately, there is a way to automatically generate surface normals in a pixel shader.
> [!NOTE]
> Procedural normals do not account for [smoothing groups](https://en.wikipedia.org/wiki/Smoothing_group) and may make a model appear faceted.
### Example material
Example usage of the `MF_GTProceduralNormal` material function can be found within the `GraphicsToolsProject\Plugins\GraphicsToolsExamples\Content\SpatialPerception\Materials\M_SpatialNormals.uasset` material.
### Implementation details
To use procedural normals on any material use the `MF_GTProceduralNormal` material function. The `MF_GTProceduralNormal` material function takes a position that is interpolated across a triangle face, defaulting to a pixel's world space position, and returns a surface normal. The surface normal is calculated by taking the cross product of the the [partial derivatives](https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-ddx) of the pixel position with respect to the screen-space horizontal and vertical coordinates. In other words, the cross product between two vectors representing the horizontal and vertical delta position between neighboring pixels.
An example material graph of procedural normal calculation turned into a color can be seen below:
![Procedural Normal](Images/Effects/EffectsProceduralNormal.png)
# Biplanar Mapping
Biplanar mapping is a technique that projects a texture map onto a 3D surface based on vertex positions and normals. Textures are normally mapped onto a 3D surface using texture coordinates (or UVs) but occasionally a mesh does not contain texture coordinates or it is desirable to procedurally generate texture coordinates based on a the position of a mesh. This effect is commonly used for terrain or a [spatial mesh](SpatialPerception.md).
Biplanar mapping is a procedural texturing technique, like [triplanar mapping](https://bgolus.medium.com/normal-mapping-for-a-triplanar-shader-10bf39dca05a), but uses only two texture samples rather than three for improved performance on Mixed Reality devices. Please see [Inigo Quilez's article](https://www.iquilezles.org/www/articles/biplanar/biplanar.htm) to learn more.
### Example material
Example usage of the `MF_GTBiplanarMapping` material function can be found within the `GraphicsToolsProject\Plugins\GraphicsToolsExamples\Content\MaterialGallery\Materials\M_ShaderBallBiplanarMapping.uasset` material.
### Implementation details
To use biplanar mapping on any material use the `MF_GTBiplanarMapping` material function. The `MF_GTBiplanarMapping` material function takes a handful of inputs and returns a pixel color sampled from the input texture:
* `Position` a position that is interpolated across a triangle face. Normally a world or local space position. When using world space positions a texture will tile seamlessly across actors. Defaults to a pixel's world space position.
* `Normal` a surface normal that is interpolated across a triangle face. Normally a world or local space normal. Defaults to a pixel's world space normal.
* `Tiling` how often to tile a texture per Unreal unit. For example 10 will tile a texture 10 times per unit.
* `Sharpness` controls the blend sharpness where two projection planes meet.
* `Texture` The texture to sample during biplanar mapping. Textures which tile normally look best.
* `Explicit Gradients` when set to false the bilinear mapper will produce one-pixel wide line artifacts, setting this to true fixes the issue at the expense of extra instructions. It is recommended to normally set this to true, and false in performance critical situations.
An example material graph of local space biplanar mapping can be seen below:
![Biplanar Mapping](Images/Effects/EffectsBiplanarMapping.png)
## See also
- [Lighting](Lighting.md)
- [Spatial Perception](SpatialPerception.md)

Двоичные данные
Docs/Images/ClippingPrimitives/ClippingPrimitivesActorSetup.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 215 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 96 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 281 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 157 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 186 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 116 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 232 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 275 KiB

Двоичные данные
Docs/Images/ClippingPrimitives/ClippingPrimitivesBorderSetup.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 153 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 311 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 270 KiB

Двоичные данные
Docs/Images/ClippingPrimitives/ClippingPrimitivesCone.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 102 KiB

Двоичные данные
Docs/Images/ClippingPrimitives/ClippingPrimitivesConeCylinder.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 96 KiB

Двоичные данные
Docs/Images/ClippingPrimitives/ClippingPrimitivesCube.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 107 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 173 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 123 KiB

Двоичные данные
Docs/Images/ClippingPrimitives/ClippingPrimitivesMaterialColor.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 91 KiB

Двоичные данные
Docs/Images/ClippingPrimitives/ClippingPrimitivesMaterialSetup.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 258 KiB

Двоичные данные
Docs/Images/ClippingPrimitives/ClippingPrimitivesPlane.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 97 KiB

Двоичные данные
Docs/Images/ClippingPrimitives/ClippingPrimitivesSphere.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 98 KiB

Двоичные данные
Docs/Images/Effects/EffectsBiplanarMapping.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 567 KiB

Двоичные данные
Docs/Images/Effects/EffectsProceduralNormal.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 399 KiB

Двоичные данные
Docs/Images/FeatureCards/ClippingPrimitives.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 231 KiB

Двоичные данные
Docs/Images/FeatureCards/Profiling.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 133 KiB

Двоичные данные
Docs/Images/FeatureCards/SpatialPerception.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 428 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 390 KiB

После

Ширина:  |  Высота:  |  Размер: 136 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 116 KiB

После

Ширина:  |  Высота:  |  Размер: 147 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 113 KiB

После

Ширина:  |  Высота:  |  Размер: 141 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 234 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 354 KiB

Двоичные данные
Docs/Images/Readme/UnrealAndHL_Webinar.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 206 KiB

Двоичные данные
Docs/Images/SpatialPerception/SpatialPerceptionNormals.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 769 KiB

Двоичные данные
Docs/Images/SpatialPerception/SpatialPerceptionTextured.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.0 MiB

Двоичные данные
Docs/Images/SpatialPerception/SpatialPerceptionWireframe.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 735 KiB

Просмотреть файл

@ -17,7 +17,7 @@ Before getting started with Graphics Tools, make sure that you have [installed t
## Getting the prebuilt plugin
If you just want to add Graphics Tools to your game project, the quickest way is through the packaged plugin provided in the release page:
1. Download the packaged plugin zip from the [latest release](https://github.com/microsoft/MixedReality-GraphicsTools-Unreal/releases) page (e.g. _GTTools.0.1.0.zip_).
1. Download the packaged plugin zip from the [latest release](https://github.com/microsoft/MixedReality-GraphicsTools-Unreal/releases) page (e.g. _MixedReality-GraphicsTools-Unreal-0.2.0.zip_).
1. Unzip the file directly into your project's _Plugins_ folder. The _Plugins_ folder should be located at the root of your project, where the _.uproject_ file is. Create it if it doesn't exist.
1. Make sure your game project is a code one, as opposed to blueprint-only, if you are planning to package it for HoloLens. Otherwise UE will fail to package it because it can't build the plugin sources.
1. Open your project and enable the _GT Tools_ plugin in the plugins menu.

Просмотреть файл

@ -10,7 +10,7 @@ keywords: Unreal, Unreal Engine, UE4, HoloLens, HoloLens 2, Mixed Reality, devel
# Lighting
By default Unreal uses the [mobile lighting](https://docs.unrealengine.com/en-US/SharingAndReleasing/Mobile/Lighting/index.html) rendering path for Mixed Reality (specifically HoloLens 2). This lighting path is well suited for mobile phones, handhelds, etc. but may be too costly for devices like HoloLens 2, which need to render to a stereo display at [60 frames per second](https://docs.microsoft.com/en-us/windows/mixed-reality/develop/platform-capabilities-and-apis/understanding-performance-for-mixed-reality). To ensure developers have access to a lighting path that is performant on HoloLens 2, Graphics Tools incudes a simplified physically based lighting system accessible via the the `MF_GTDefaultLit` [material function](https://docs.unrealengine.com/en-US/RenderingAndGraphics/Materials/Functions/index.html).
By default, Unreal uses the [mobile lighting](https://docs.unrealengine.com/en-US/SharingAndReleasing/Mobile/Lighting/index.html) rendering path for Mixed Reality (specifically HoloLens 2). This lighting path is well suited for mobile phones, handhelds, etc. but may be too costly for devices like HoloLens 2, which need to render to a stereo display at [60 frames per second](https://docs.microsoft.com/en-us/windows/mixed-reality/develop/platform-capabilities-and-apis/understanding-performance-for-mixed-reality). To ensure developers have access to a lighting path that is performant on HoloLens 2, Graphics Tools incudes a simplified physically based lighting system accessible via the `MF_GTDefaultLit` [material function](https://docs.unrealengine.com/en-US/RenderingAndGraphics/Materials/Functions/index.html).
![Lighting](Images/FeatureCards/Lighting.png)

80
Docs/Profiling.md Normal file
Просмотреть файл

@ -0,0 +1,80 @@
---
title: Profiling
description: Guide to graphic resources and techniques in Graphics Tools.
author: Cameron-Micka
ms.author: thmicka
ms.date: 12/12/2020
ms.localizationpriority: high
keywords: Unreal, Unreal Engine, UE4, HoloLens, HoloLens 2, Mixed Reality, development, MRTK, GT, Graphics Tools, graphics, rendering, materials
---
# Profiling
The easiest way to rationalize performance is via framerate or how many times your application can render an image per second. It is important to meet the target framerate, as outlined by the platform being targeted (i.e [Windows Mixed Reality](https://docs.microsoft.com/windows/mixed-reality/understanding-performance-for-mixed-reality), [Oculus](https://developer.oculus.com/documentation/pcsdk/latest/concepts/dg-performance-guidelines/), etc). For example, on HoloLens, the target framerate is 60 FPS (or 16.66 milliseconds). Low framerate applications can result in deteriorated user experiences such as worsened [hologram stabilization](../hologram-Stabilization.md), world tracking, hand tracking, and more. To help developers track and achieve quality framerate, Graphics Tools provides profiling tools and best practices.
![Profiling](Images/FeatureCards/Profiling.png)
## Visual profiler
To continuously track performance over the lifetime of development, it is highly recommended to always show a framerate visual while running & debugging an application. Graphics Tool's provides the `GTVisualProfiler` actor which gives real-time information about the current frame times represented in milliseconds, draw call count, and visible polygon count in a stereo friendly application view.
> [!NOTE]
> To utilize the visual profiler simply drop an instance of the `GTVisualProfiler` actor in a level. To disable the profiler you can mark it as hidden in game (or not visible).
The visual profiler provides four metrics around frame performance and two metrics around level complexity as outlined below.
| Name | Description |
|------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Frame | Frame time represents the total amount of time spent generating one frame of the app. Since both the Game and Draw threads sync up before finishing a frame, frame time is often close to the time being shown in one of these threads. |
| Game | If Frame time is close to Game time, the app's performance is likely being bottlenecked (negatively impacted) by the game thread. The game thread is resposible for most app logic, especially Blueprint logic. |
| Draw | If Frame time is close to Draw time, the game's performance is likely being bottlenecked by the rendering thread. The rendering thread is responsible for deciding what to render and submitting work to the GPU. |
| GPU | GPU time measures how long the video card took to render the scene. Since GPU time is synced to the frame, it will likely be similar to Frame time. |
| Draw Calls | Draw calls can be thought of as the number on times a graphics API (such as [DirectX](https://en.wikipedia.org/wiki/DirectX)) is told to render an object. |
| Polys | Represents the number of polygons which are currently being submitted to the graphics API for rendering. This number may vary slightly to actual number being rendered due to frustum clipping or geometry generation on the GPU. |
> [!NOTE]
> It is particularly important to utilize the visual profiler to track frame time when running on the device as opposed to running in editor or an emulator. The most accurate performance results will be depicted when running on the device with "Shipping" build configuration.
## Example level
To better understand the `GTVisualProfiler` look at the `\GraphicsToolsProject\Plugins\GraphicsToolsExamples\Content\Profiling\Profiling.umap` level.
## General recommendations
Performance can be an ambiguous and constantly changing challenge for mixed reality developers and the spectrum of knowledge to rationalize performance is vast. There are some general recommendations for understanding how to approach performance for an application though.
It is useful to simplify the execution of an application into the pieces that run on the *CPU* or the *GPU* and thus identify whether an app is bound by either component. There can be bottlenecks that span both processing units and some unique scenarios that have to be carefully investigated. However, for getting started, it is good to grasp where an application is executing for the most amount of time.
### GPU bound
If the longest bar on the profiler is the "GPU" time then your app is likely GPU bound.
> [!NOTE]
> The visual profiler screenshot at the top of this document would be considered GPU-bound.
Since most platforms for mixed reality applications are utilizing [stereoscopic rendering](https://en.wikipedia.org/wiki/Stereoscopy), it is very common to be GPU-bound due to the nature of rendering a "double-wide" screen. Futhermore, mobile mixed reality platforms such as HoloLens or Oculus Quest will be limited by mobile-class CPU & GPU processing power.
When an app is GPU bound try moving towards holograms until they fill your view. If the GPU time increases as holograms fill your view you are likely [fill rate](https://en.wikipedia.org/wiki/Fillrate) bound and should focus on reducing material complexity and ensuring no full screen effects (such as post processing or MSAA) are enabled. If your GPU time decreases as holograms fill your view and increases as your whole level comes into view you are likely vertex processing bound and need to simplify your meshes.
### CPU bound
If the longest bar on the profiler is the "Game" or "Draw" time, then your app is likely CPU bound.
Mixed reality applications are normally bound by the time it takes to render a level, the "Draw" time when CPU bound. The easiest way to reduce rendering time is to remove "Draw Calls" via instancing (Unreal Engine supports [auto-instancing on mobile](https://docs.unrealengine.com/en-US/SharingAndReleasing/Mobile/Rendering/HowTo/AutoInstancingOnMobile/index.html)) or merging draw calls together in your asset creation tools or within the editor using the [Merge Actor Tool](https://docs.unrealengine.com/en-US/Basics/Actors/Merging/index.html#:~:text=The%20Merge%20Actors%20tool%20enables,by%20the%20newly%20merged%20asset).
If the "Game" thread is the longest bar on the profiler then you should look at optimizing logic in your Blueprint or C++ app code. For additional guidance please click on the links from the [see also](#See-also) section below.
## See also
- [Lighting](Lighting.md)
### Windows Mixed Reality
- [Performance recommendations for Unreal](https://docs.microsoft.com/en-us/windows/mixed-reality/develop/unreal/performance-recommendations-for-unreal)
- [Material recommendations in Unreal](https://docs.microsoft.com/en-us/windows/mixed-reality/develop/unreal/unreal-materials)
- [Profiling with Unreal Insights](https://docs.microsoft.com/en-us/windows/mixed-reality/develop/unreal/unreal-insights)
- [Understanding Performance for Mixed Reality](https://docs.microsoft.com/windows/mixed-reality/understanding-performance-for-mixed-reality)
### Unreal
- [Performance guidelines for mobile devices](https://docs.unrealengine.com/en-US/SharingAndReleasing/Mobile/Performance/index.html)
- [VR performance testing](https://docs.unrealengine.com/en-US/SharingAndReleasing/XRDevelopment/VR/DevelopVR/Profiling/Overview/index.html)
- [Virtual reality best practices](https://docs.unrealengine.com/en-US/SharingAndReleasing/XRDevelopment/VR/DevelopVR/ContentSetup/index.html)

Просмотреть файл

@ -15,7 +15,7 @@ A proximity light is a [Fluent Design System](https://www.microsoft.com/design/f
![ProximityLights](Images/FeatureCards/ProximityLights.png)
> [!NOTE]
> Up to three proximity lights can effect a material at once. Additional proximity lights will not be included in light calculations.
> Up to three proximity lights can effect a material at once. Additional proximity lights will not be included in light calculations by default. To add additional proximity lights please see the [advanced usage](#Advanced-usage) section.
Another unique feature of proximity lights is that they can pulse to draw attention. To pulse a proximity light call the `Pulse` member function via Blueprint or C++.
@ -34,21 +34,20 @@ In the following steps we will create a new material that is illuminated by a pr
2. It's good practice to keep your materials simple (in other words keep the number of shader instructions low) when authoring materials for Mixed Reality.
* To ensure this, mark `M_ProximityLit` as "Unlit" (1) in the material's "Shading Model" property.
* (Optional) If you would like your material to still look as though it is lit, then right click on the material graph and add the `MF_GTDefaultLit` material function.
* Connect the result of the `MF_GTDefaultLit` to the material's "Emissive Color." (2)
* (Optional) If you would like your material to still look as though it is lit, then right click on the material graph and add the `MF_GTDefaultLit` material function. Connect the result of the `MF_GTDefaultLit` to the material's "Emissive Color." (2)
![Material Setup](Images/ProximityLight/ProximityLightMaterialSetup.png)
3. Our material now needs to add proximity light calculations.
* To do this right click on the material graph and add the `MF_GTProximityLights` material function (1).
* Connect the result of `MF_GTProximityLights` to the material's "Emissive Color." If your material is also using the `MF_GTDefaultLit` material function (from step 3) then add the result from both functions before connecting the result to the material's "Emissive Color." (2)
* Connect the result of `MF_GTProximityLights` to the material's "Emissive Color." If your material is also using the `MF_GTDefaultLit` material function (from step 2) then add the result from both functions before connecting the result to the material's "Emissive Color." (2) Don't worry about the inputs to `MF_GTProximityLights` because they are auto-populated. To learn more see the [advanced usage](#Advanced-usage) section.
![Material Light](Images/ProximityLight/ProximityLightMaterialLight.png)
4. Next let's give our material a base color. Very few materials exist in the real world are completely black (and completely black materials render transparently on additive displays like the one found on HoloLens 2).
* Right click on the material graph and add a `ConstantVector3` node.
* Set the node's RGB channels to 0.5, a neutral gray color.
* Connect this node to the `BaseColor` input of the `MF_GTDefaultLit` material function. (1)
* Connect this node to the `BaseColor` input of the `MF_GTDefaultLit` material function. (1) Or, add to the `MF_GTProximityLights` output if you opted not to use the `MF_GTDefaultLit` function in step 2.
![Material Light](Images/ProximityLight/ProximityLightMaterialColor.png)
@ -74,42 +73,38 @@ In the following steps we will create a new material that is illuminated by a pr
## Advanced usage
By default only three proximity lights can illuminate a material at a time. If your project requires an additional proximity light to influence a material the sample code below demonstrates how to achieve this.
By default only three proximity lights can illuminate a material at a time. If your project requires additional proximity lights to influence a material the steps below demonstrates how to achieve this.
> [!NOTE]
> Having many proximity lights illuminate a material will increase pixel shader instructions and will impact performance. Please profile these changes within your project.
*How to increase the number of available proximity lights from three to four.*
*How to increase the number of proximity lights which effect a surface from three to six.*
```C++
// 1) Within GraphicsToolsProject\Plugins\GraphicsTools\Source\GraphicsTools\Private\GTProximityLightComponent.cpp change:
By default Graphics Tools will automatically send the proximity lighting state to materials via a material parameter collection up to the three light limit. As a developer you can pipe this data into materials yourself with a material parameter collection override.
#define GT_MAX_PROXIMITY_LIGHTS 3
1) Create a material that accepts proximity lighting as outlined in the [advanced usage](#Example-usage) section above.
* Place six proximity lights in a level that are near a surface that uses the material you just created.
* Note that only three lights illuminate the surface.
// to:
2) Our material needs a mechanism to know about the 3 extra proximity lights. We will use a material parameter collection to do this.
* Right click within the "Content Browser" and select "Material Parameter Collection" under the "Create Advanced Asset/Materials and Textures" menu listings.
* Let's name our material parameter collection `MPC_ExtraProximityLights`.
* Double click on `MPC_ExtraProximityLights` to open the material parameter collection editor.
* Add 18 vector parameters (1), six for each proximity light, that have a default value of black (0, 0, 0, 0).
* Name the parameters to match the parameter names found under the advanced tab of the proximity light. (2) You are free to change these names to whatever you want if they are unique and match between the proximity light and material parameter collection.
* Finally, assign `MPC_ExtraProximityLights` to the "Parameter Collection Override" property of the three additional proximity lights. (3)
#define GT_MAX_PROXIMITY_LIGHTS 4
![Material Parameter Collection](Images/ProximityLight/ProximityLightMaterialParameterCollection.png)
// 2) Open GraphicsToolsProject\Plugins\GraphicsTools\Content\Materials\MF_GTProximityLights.uasset within the Unreal Editor.
// With the `Contribution proximity lights` custom expression selected change the "Additional Defines" property from:
3) Next we will augment our material to calculate the three new proximity lights.
* To do this right click on the material graph and add a second `MF_GTProximityLights` material function (1).
* This time we will manually specify the inputs from from the `MPC_ExtraProximityLights` material parameter collection. Connect each input of the `MF_GTProximityLights`function with the corresponding `MPC_ExtraProximityLights` parameter. (2) Do this for all 18 inputs.
* Lastly, add the result of the two `MF_GTProximityLights` functions before adding to the `MF_GTDefaultLit` function. (3)
GT_MAX_PROXIMITY_LIGHTS 3
// to:
GT_MAX_PROXIMITY_LIGHTS 4
```
To add support for more than 4 lights the above changes must be made as well as the below changes.
1) Within the UpdateParameterCollection method in GTProximityLightComponent.cpp change each instance of ParameterNames (6 in total) to include a new entry.
2) In the `MPC_GTSettings` [material parameter collection](https://docs.unrealengine.com/en-US/RenderingAndGraphics/Materials/ParameterCollections/index.html) add 6 new entries for all parameter names which were added above.
3) In `GraphicsToolsProject\Plugins\GraphicsTools\Shaders\GTProximityLightingUnreal.ush` add additional function parameters to the `GTContributionProximityLights` function.
4) Open `GraphicsToolsProject\Plugins\GraphicsTools\Content\Materials\MF_GTProximityLights.uasset` within the Unreal Editor and include the additional inputs in the `Contribution Proximity Lights` [custom expression](https://docs.unrealengine.com/en-US/RenderingAndGraphics/Materials/ExpressionReference/Custom/index.html). Finally, connect those inputs to the corresponding `MPC_GTSettings` parameters outputs.
![Material Parameter Collection Connect](Images/ProximityLight/ProximityLightMaterialParameterCollectionConnect.png)
Out material will now perform lighting calculations for the three default proximity lights as well as our three extra proximity lights.
## See also
- [Lighting](Lighting.md)

Просмотреть файл

@ -8,28 +8,66 @@ ms.localizationpriority: high
keywords: Unreal, Unreal Engine, UE4, HoloLens, HoloLens 2, Mixed Reality, development, MRTK, GT, Graphics Tools, release notes
---
# Graphics Tools 0.1.0 Release Notes
# Graphics Tools 0.2.0 release notes
- [What's New](#whats-new)
- [Breaking Changes](#breaking-changes)
- [Known Issues](#known-issues)
The first release of the Graphics Tools supports only HoloLens 2. Support for other MR platforms remains a goal for us but is not the current focus.
The second release of the Graphics Tools supports only HoloLens 2, but many material/shader techniques should work on other platforms. Support for other MR platforms remains a goal for us but is not the current focus. Please see the [README](../README.md#graphics-building-blocks) page for a list of all features and building blocks.
Unreal 4.26.x required.
## What's new
Everything is new because this is the first release of Graphics Tools. Please see the [README](../README.md#graphics-building-blocks) page for a list of features and building blocks.
### OpenXR Support
Graphics Tools is now compatible with the OpenXR and Windows MR plugins! See the [OpenXR plugin](https://github.com/microsoft/Microsoft-OpenXR-Unreal) to learn more.
### Clipping Primitives
Tools to dynamically slice away a mesh and peer inside the geometry
[![ClippingPrimitives](Images/FeatureCards/ClippingPrimitives.png)](ClippingPrimitives.md)
### Profiling
Explore profiling techniques and tools useful for Mixed Reality development
[![Profiling](Images/FeatureCards/Profiling.png)](Profiling.md)
### Spatial Perception
Techniques that create visually compelling materials for real world geometry
[![SpatialPerception](Images/FeatureCards/SpatialPerception.png)](SpatialPerception.md)
### Support for arbitrary proximity light counts
Added documentation and examples to go above the three proximity light limit. See the [proximity lights](ProximityLights.md#Advanced-usage) and `GTSceneComponent` for additional information.
### Direct and indirect light scalar
Added two new scalar inputs to the `MF_GTDefaultLit` material function named `DirectLightIntensity` and `IndirectLightIntensity` to provide finer grained control over material lighting.
## Breaking changes
None, since this is the first release.
### Fully rough materials may appear a bit darker
This can be adjusted by altering the value of the `IndirectLightIntensity` to a value of ~0.9.
### The `GTLightComponent` now derives from the `GTSceneComponent` rather than the `SceneComponent`
This change allows all lights to have a material property collection override.
## Known issues
### M_ShaderBallRimLit Reprojection
### Crash when exiting the Spatial Perception example level on HoloLens 2
The `M_ShaderBallRimLit` material located with the examples plugin may "wobble or smear" on HoloLens 2 because it is a translucent material that does not write depth. Depth is required for all materials to reproject corectly because HoloLens 2 uses [depth based late stage reprojection](https://docs.microsoft.com/en-us/windows/mixed-reality/develop/platform-capabilities-and-apis/hologram-stability#reprojection).
This is a know engine issue in 4.26.1. A fix is slated for the next release of the engine.
### Translucent material reprojection
Translucent materials located with the examples plugin may "wobble or smear" on HoloLens 2 because translucent materials do not write depth. Depth is required for all materials to reproject correctly because HoloLens 2 uses [depth based late stage reprojection](https://docs.microsoft.com/en-us/windows/mixed-reality/develop/platform-capabilities-and-apis/hologram-stability#reprojection).
If you run into issues caused by Graphics Tools or have questions about how to do something, please [file an issue](https://github.com/microsoft/MixedReality-GraphicsTools-Unreal/issues/new) on the GitHub repo.

45
Docs/SpatialPerception.md Normal file
Просмотреть файл

@ -0,0 +1,45 @@
---
title: Spatial Perception
description: Guide to graphic resources and techniques in Graphics Tools.
author: Cameron-Micka
ms.author: thmicka
ms.date: 3/11/2021
ms.localizationpriority: high
keywords: Unreal, Unreal Engine, UE4, HoloLens, HoloLens 2, Mixed Reality, development, MRTK, GT, Graphics Tools, graphics, rendering, materials
---
# Spatial Perception
Mixed Reality devices can generate meshes of the real world via a unique blend of hardware and software. Unlike traditional geometry these meshes only contain vertex positions and not other vertex attributes like normals and texture coordinates. Without vertex normals and texture coordinates it can be difficult to create visually compelling materials. Below we demonstrate a few techniques to help solve this issue.
![Effects](Images/FeatureCards/SpatialPerception.png)
## Example level
To try all of the materials outlined below run the `\GraphicsToolsProject\Plugins\GraphicsToolsExamples\Content\SpatialPerception\SpatialPerception.umap` level.
## Implementation details and restrictions
The `ARTrackableNotify` actor component is used to subscribe to any events which add, update, and remove spatial meshes. Spatial meshes are geometric surfaces which come from surfaces such as a wall, table, or even your hands. Because these meshes get regenerated in real time, they normally dont contain vertex normals or texture coordinates which are needed for even the most basic effects. To solve this issue, we can try a few different techniques.
### Wireframe Spatial Mesh
The `GraphicsToolsProject\Plugins\GraphicsToolsExamples\Content\Common\Materials\M_Wireframe.uasset` material demonstrates a simple wireframe reconstruction of a mesh. The wireframe material uses a special material checkbox that is available in all Unreal materials. Note, only the wire color can be controlled. If you app needs to render a color for the triangle faces it will need to render the mesh a second time with a different material.
![Wireframe](Images/SpatialPerception/SpatialPerceptionWireframe.png)
### Surface Normal Spatial Mesh
The `GraphicsToolsProject\Plugins\GraphicsToolsExamples\Content\SpatialPerception\Materials\M_SpatialNormals.uasset` material shows how to procedurally generate a surface normal and translate the normal vector into a color. Please see [procedural normals](Effects.md#Procedural-Normal) for more information about normal generation.
![Normals](Images/SpatialPerception/SpatialPerceptionNormals.png)
### Textured Spatial Mesh
The `GraphicsToolsProject\Plugins\GraphicsToolsExamples\Content\SpatialPerception\Materials\M_SpatialCheckerLit.uasset` material demonstrates how to procedurally generate a surface normal and texture coordinate to sample a texture. With normals and texture coordinates generated it's possible to texture and light a spatial mesh. Please see [biplanar mapping](Effects.md#Biplanar-Mapping) for more information about texture coordinate generation.
![Textured](Images/SpatialPerception/SpatialPerceptionTextured.png)
## See also
- [Effects](Effects.md)

Просмотреть файл

@ -19,7 +19,7 @@ Graphics Tools is the second MRTK-Unreal component to be released and is current
If you're new to MRTK or Mixed Reality development in Unreal, we recommend starting at the beginning of our [Unreal development journey](https://docs.microsoft.com/windows/mixed-reality/unreal-development-overview), which was specifically created to walk you through installation, core concepts, and usage.
> [!CAUTION]
> The Unreal development journey currently uses **Graphics Tools 0.1.x** and **Unreal 4.26.0 or later**. If you're working with other configurations it's still recommended that you start there, but you can also refer to the **[installation instructions](Installation.md)**.
> The Unreal development journey currently uses **Graphics Tools 0.2.x** and **Unreal 4.26.0 or later**. If you're working with other configurations it's still recommended that you start there, but you can also refer to the **[installation instructions](Installation.md)**.
## Documentation versioning

Просмотреть файл

@ -11,5 +11,11 @@
href: ProximityLights.md
- name: Effects
href: Effects.md
- name: Clipping Primitives
href: ClippingPrimitives.md
- name: Profiling
href: Profiling.md
- name: Spatial Perception
href: SpatialPerception.md
- name: Contributing
href: CONTRIBUTING.md

Просмотреть файл

@ -118,6 +118,9 @@ r.SupportDepthOnlyIndexBuffers=True
r.SupportReversedIndexBuffers=True
r.SupportMaterialLayers=False
r.LightPropagationVolume=False
; Comment in to enable auto instancing: https://docs.unrealengine.com/en-US/SharingAndReleasing/Mobile/Rendering/HowTo/AutoInstancingOnMobile/index.html
;r.Mobile.SupportGPUScene=1
;r.Mobile.UseGPUSceneTexture=1
; Comment in to enable HLSL debugging in RenderDoc.
;r.Shaders.Optimize=0
;r.Shaders.KeepDebugInfo=1

Просмотреть файл

@ -79,4 +79,7 @@ bSkipMovies=False
+MapsToCook=(FilePath="/GraphicsToolsExamples/MaterialMatrix/MaterialMatrix")
+MapsToCook=(FilePath="/GraphicsToolsExamples/ProximityLights/ProximityLights")
+MapsToCook=(FilePath="/GraphicsToolsExamples/MaterialGallery/MaterialGallery")
+MapsToCook=(FilePath="/GraphicsToolsExamples/ClippingPrimitives/ClippingPrimitives")
+MapsToCook=(FilePath="/GraphicsToolsExamples/Profiling/Profiling")
+MapsToCook=(FilePath="/GraphicsToolsExamples/SpatialPerception/SpatialPerception")

Просмотреть файл

@ -25,6 +25,10 @@
"Win64",
"HoloLens"
]
},
{
"Name": "RenderDocPlugin",
"Enabled": true
}
]
}

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -1,7 +1,7 @@
{
"FileVersion": 3,
"Version": 1,
"VersionName": "0.1.0",
"VersionName": "0.2.0",
"FriendlyName": "Mixed Reality Graphics Tools",
"Description": "Graphics tools and components for developing Mixed Reality applications.",
"Category": "Other",
@ -36,15 +36,5 @@
"Win64"
]
}
],
"Plugins": [
{
"Name": "WindowsMixedReality",
"Enabled": true,
"WhitelistPlatforms": [
"Win64",
"HoloLens"
]
}
]
}

Просмотреть файл

@ -0,0 +1,57 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#ifndef GT_CLIPPING
#define GT_CLIPPING
#include "GTCommon.ush"
// Derived from distance functions provided from: https://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm
float GTPointVsPlane(float3 worldPosition, float4 plane, float clippingSide)
{
float3 planePosition = plane.xyz * plane.w;
return dot(worldPosition - planePosition, plane.xyz) * clippingSide;
}
float GTPointVsSphere(float3 worldPosition, float4x4 sphereInverseTransform, float clippingSide)
{
return (length(mul(sphereInverseTransform, float4(worldPosition, 1.0)).xyz) - 0.5) * clippingSide;
}
float GTPointVsBox(float3 worldPosition, float4x4 boxInverseTransform, float clippingSide)
{
float3 distance = abs(mul(boxInverseTransform, float4(worldPosition, 1.0))) - 0.5;
return (length(max(distance, 0.0)) + min(max(distance.x, max(distance.y, distance.z)), 0.0)) * clippingSide;
}
float GTPointVsCone(float3 worldPosition, float4 coneStart, float4 coneEnd, float clippingSide)
{
float3 p = worldPosition;
float3 a = coneStart.xyz;
float3 b = coneEnd.xyz;
float ra = coneStart.w;
float rb = coneEnd.w;
float rba = rb - ra;
float baba = dot(b - a, b - a);
float papa = dot(p - a, p - a);
float paba = dot(p - a, b - a) / baba;
float x = sqrt(papa - paba * paba * baba);
float cax = max(0.0, x - ((paba < 0.5) ? ra : rb));
float cay = abs(paba - 0.5) - 0.5;
float k = rba * rba + baba;
float f = clamp((rba * (x - ra) + paba * baba) / k, 0.0, 1.0);
float cbx = x - ra - f * rba;
float cby = paba - f;
float s = (cbx < 0.0 && cay < 0.0) ? -1.0 : 1.0;
return s * sqrt(min(cax * cax + cay * cay * baba, cbx * cbx + cby * cby * baba)) * clippingSide;
}
#endif // GT_CLIPPING

Просмотреть файл

@ -11,11 +11,21 @@
#define Half3x3 min16float3x3
#define Half3x4 min16float3x4
#define GT_PI 3.14159265359
#define GT_ONE_OVER_PI 1.0 / 3.14159265359
#define GT_MEDIUMP_FLT_MAX 65504.0
#define GT_PI Half(3.14159265359)
#define GT_ONE_OVER_PI Half(1.0) / GT_PI
#define GT_ONE_OVER_TWO_PI Half(1.0) / (Half(2.0) * GT_PI)
#define GT_FLOAT_MAX 3.402823466e+38
#define GT_MEDIUMP_FLOAT_MAX 65504.0
#define GT_MIN_N_DOT_V 1e-4
float4x4 CreateMatrixFromColumns(float4 c0, float4 c1, float4 c2, float4 c3)
{
return float4x4(c0.x, c1.x, c2.x, c3.x,
c0.y, c1.y, c2.y, c3.y,
c0.z, c1.z, c2.z, c3.z,
c0.w, c1.w, c2.w, c3.w);
}
Half GTLuminance(Half3 linearColor)
{
return dot(linearColor, Half3(0.3, 0.59, 0.11));

Просмотреть файл

@ -6,6 +6,68 @@
#include "GTCommon.ush"
float3 GTProceduralNormal(float3 position)
{
float3 normal = cross(ddx(position), ddy(position));
return normalize(normal);
}
// Biplanar mapping derived from: https://iquilezles.org/www/articles/biplanar/biplanar.htm
float4 GTBiplanarMapping(float3 position,
float3 normal,
float tiling,
float sharpness,
Texture2D baseColor,
SamplerState baseColorSampler)
{
normal = abs(normal);
// Major axis (in x; yz are following axis)
int3 major = (normal.x > normal.y && normal.x > normal.z) ? int3(0, 1, 2) : (normal.y > normal.z) ? int3(1, 2, 0) : int3(2, 0, 1);
// Minor axis (in x; yz are following axis)
int3 minor = (normal.x < normal.y && normal.x < normal.z) ? int3(0, 1, 2) : (normal.y < normal.z) ? int3(1, 2, 0) : int3(2, 0, 1);
// Median axis (in x; yz are following axis)
int3 median = int3(3, 3, 3) - minor - major;
// Project and fetch
position *= tiling;
#if GT_EXPLICIT_GRADIENTS
float3 dpdx = ddx(position);
float3 dpdy = ddy(position);
float4 x = baseColor.SampleGrad(baseColorSampler,
float2(position[major.y], position[major.z]),
float2(dpdx[major.y], dpdx[major.z]),
float2(dpdy[major.y], dpdy[major.z]));
float4 y = baseColor.SampleGrad(baseColorSampler,
float2(position[median.y], position[median.z]),
float2(dpdx[median.y], dpdx[median.z]),
float2(dpdy[median.y], dpdy[median.z]));
#else
float4 x = baseColor.Sample(baseColorSampler,
float2(position[major.y], position[major.z]));
float4 y = baseColor.Sample(baseColorSampler,
float2(position[median.y], position[median.z]));
#endif // GT_EXPLICIT_GRADIENTS
// Blend factors
float2 m = float2(normal[major.x], normal[median.x]);
// Add local support (prevents discontinuty)
m = saturate((m - 0.5773) / (1 - 0.5773));
// Transition control
float k = sharpness / 8.0;
m = pow(m, float2(k, k));
// Blending
return (x * m.x + y * m.y) / (m.x + m.y);
}
float3 GTIridescence(float ToI,
float threshold,
float angle,

Просмотреть файл

@ -34,7 +34,7 @@ Half GTDistribution(Half roughness,
Half k = roughness / (oneMinusNoHSquared + a * a);
Half d = k * k * GT_ONE_OVER_PI;
return min(d, GT_MEDIUMP_FLT_MAX);
return min(d, GT_MEDIUMP_FLOAT_MAX);
}
// Calculated at full precision to avoid artifacts.
@ -71,12 +71,11 @@ Half3 GTSpecularLobe(Half roughness,
Half3 GTDiffuseLobe(Half3 baseColor)
{
return baseColor * GT_ONE_OVER_PI;
return baseColor * GT_ONE_OVER_TWO_PI;
}
Half3 GTContributionDirectionalLight(Half3 baseColor,
Half metallic,
Half roughness,
Half roughnessSq,
Half specular,
Half3 worldNormal,
@ -100,11 +99,10 @@ Half3 GTContributionDirectionalLight(Half3 baseColor,
Half3 fresnel = Half(0.16) * specular * specular * dielectric + baseColor * metallic;
Half3 specularLobe = GTSpecularLobe(roughnessSq, NoV, NoL, NoH, LoH, NxH, fresnel);
Half3 diffuseLobe = GTDiffuseLobe(baseColor) * max(dielectric, roughness);
Half3 diffuseLobe = GTDiffuseLobe(baseColor) * dielectric;
#endif // GT_FULLY_ROUGH
Half energyCompensation = Half(1) + (Half(1) - (metallic * Half(1.5)));
return ((diffuseLobe + specularLobe) * lightColorIntensity.rgb) * lightColorIntensity.a * NoL * energyCompensation;
return ((diffuseLobe + specularLobe) * lightColorIntensity.rgb) * lightColorIntensity.a * NoL;
}
Half3 GTContributionSH(Half3 baseColor,
@ -119,13 +117,14 @@ Half3 GTContributionSH(Half3 baseColor,
#define GT_REFLECTION_CUBE_MAX_MIP 10
Half3 GTContributionReflection(Half3 baseColor,
Half metallic,
Half roughnessSq,
TextureCube reflectionCube,
SamplerState reflectionCubeSampler,
Half3 reflectionVector)
{
Half lod = (GT_REFLECTION_CUBE_MAX_MIP - Half(1)) - (Half(1) - log2(roughnessSq));
return reflectionCube.SampleLevel(reflectionCubeSampler, reflectionVector, lod).rgb * baseColor;
return reflectionCube.SampleLevel(reflectionCubeSampler, reflectionVector, lod).rgb * baseColor * max(metallic, Half(0.5));
}
Half3 GTProximityLightColor(Half4 centerColor,

Просмотреть файл

@ -0,0 +1,52 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#ifndef GT_CLIPPING_UNREAL
#define GT_CLIPPING_UNREAL
#include "Common/GTClipping.ush"
float GTClippingPlane(FMaterialPixelParameters Parameters,
float4 Settings,
float4 Plane)
{
float3 WorldLocation = GetWorldPosition(Parameters);
return (Settings.x > 0) ? GTPointVsPlane(WorldLocation, Plane, Settings.y) : GT_FLOAT_MAX;
}
float GTClippingSphere(FMaterialPixelParameters Parameters,
float4 Settings,
float4 TransformColumn0,
float4 TransformColumn1,
float4 TransformColumn2,
float4 TransformColumn3)
{
float3 WorldLocation = GetWorldPosition(Parameters);
return (Settings.x > 0) ? GTPointVsSphere(WorldLocation, CreateMatrixFromColumns(TransformColumn0, TransformColumn1, TransformColumn2, TransformColumn3), Settings.y) : GT_FLOAT_MAX;
}
float GTClippingBox(FMaterialPixelParameters Parameters,
float4 Settings,
float4 TransformColumn0,
float4 TransformColumn1,
float4 TransformColumn2,
float4 TransformColumn3)
{
float3 WorldLocation = GetWorldPosition(Parameters);
return (Settings.x > 0) ? GTPointVsBox(WorldLocation, CreateMatrixFromColumns(TransformColumn0, TransformColumn1, TransformColumn2, TransformColumn3), Settings.y) : GT_FLOAT_MAX;
}
float GTClippingCone(FMaterialPixelParameters Parameters,
float4 Settings,
float4 ConeStart,
float4 ConeEnd)
{
float3 WorldLocation = GetWorldPosition(Parameters);
return (Settings.x > 0) ? GTPointVsCone(WorldLocation, ConeStart, ConeEnd, Settings.y) : GT_FLOAT_MAX;
}
#endif // GT_CLIPPING_UNREAL

Просмотреть файл

@ -12,12 +12,12 @@ Half3 GTGetSkySHDiffuse(Half3 Normal)
Half4 NormalVector = Half4(Normal, Half(1));
Half3 Result;
Result.x = dot(View.SkyIrradianceEnvironmentMap[0], NormalVector);
Result.y = dot(View.SkyIrradianceEnvironmentMap[1], NormalVector);
Result.z = dot(View.SkyIrradianceEnvironmentMap[2], NormalVector);
Result.x = dot(View.SkyIrradianceEnvironmentMap[0], NormalVector);
Result.y = dot(View.SkyIrradianceEnvironmentMap[1], NormalVector);
Result.z = dot(View.SkyIrradianceEnvironmentMap[2], NormalVector);
// Max to avoid negative colors.
return max(Half3(0, 0, 0), Result);
return max(Half3(0, 0, 0), Result);
}
Half3 GTContributionDefaultLit(FMaterialPixelParameters Parameters,
@ -31,7 +31,8 @@ Half3 GTContributionDefaultLit(FMaterialPixelParameters Parameters,
SamplerState ReflectionCubeSampler,
float DirectLightIntensity,
float4 DirectionalLightDirectionEnabled,
float4 DirectionalLightColorIntensity)
float4 DirectionalLightColorIntensity,
float IndirectLightIntensity)
{
Half RoughnessSq = clamp(Roughness * Roughness, GT_MIN_N_DOT_V, Half(1));
@ -45,34 +46,37 @@ Half3 GTContributionDefaultLit(FMaterialPixelParameters Parameters,
Metallic,
Roughness,
SkySHDiffuse,
ResolvedView.SkyLightColor.rgb);
ResolvedView.SkyLightColor.rgb) *
IndirectLightIntensity;
#endif // GT_ENABLE_SH
// Indirect (reflection cube).
Result += GTContributionReflection(BaseColor,
Metallic,
RoughnessSq,
ReflectionCube,
ReflectionCubeSampler,
Parameters.ReflectionVector * Parameters.TwoSidedSign);
Parameters.ReflectionVector * Parameters.TwoSidedSign) *
IndirectLightIntensity;
#else
// Ensure fully rough materials aren't fully black.
Result += BaseColor * Half(0.025);
// Allows artist control so that fully rough materials can boost amibient.
Result += BaseColor * Half(1 - IndirectLightIntensity);
#endif // GT_FULLY_ROUGH
// Direct (directional light).
Result += GTContributionDirectionalLight(BaseColor,
Metallic,
Roughness,
RoughnessSq,
Specular,
Normal,
Parameters.CameraVector,
DirectionalLightDirectionEnabled.xyz,
DirectionalLightColorIntensity) *
DirectLightIntensity *
DirectionalLightDirectionEnabled.w;
DirectLightIntensity *
DirectionalLightDirectionEnabled.w;
return Result * AmbientOcclusion;
Half EnergyCompensation = Half(1) + (Half(1) - (Metallic * Half(1.5 * Roughness)));
return Result * EnergyCompensation * AmbientOcclusion;
}
#endif // GT_LIGHTING_UNREAL

Просмотреть файл

@ -27,32 +27,24 @@ Half3 GTContributionProximityLights(FMaterialPixelParameters Parameters,
Half4 ProximityLightCenterColor2,
Half4 ProximityLightMiddleColor2,
Half4 ProximityLightOuterColor2,
float4 ProximityLightLocation3,
float4 ProximityLightSettings3,
float4 ProximityLightPulseSettings3,
Half4 ProximityLightCenterColor3,
Half4 ProximityLightMiddleColor3,
Half4 ProximityLightOuterColor3)
float3 Normal)
{
// Custom material expressions do not support array input, so we need to pack the array in the shader.
float4 ProximityLightLocations[] = {ProximityLightLocation0, ProximityLightLocation1, ProximityLightLocation2, ProximityLightLocation3};
float4 ProximityLightSettings[] = {ProximityLightSettings0, ProximityLightSettings1, ProximityLightSettings2, ProximityLightSettings3};
float4 ProximityLightPulseSettings[] = {ProximityLightPulseSettings0, ProximityLightPulseSettings1, ProximityLightPulseSettings2, ProximityLightPulseSettings3};
Half4 ProximityLightCenterColors[] = {ProximityLightCenterColor0, ProximityLightCenterColor1, ProximityLightCenterColor2, ProximityLightCenterColor3};
Half4 ProximityLightMiddleColors[] = {ProximityLightMiddleColor0, ProximityLightMiddleColor1, ProximityLightMiddleColor2, ProximityLightMiddleColor3};
Half4 ProximityLightOuterColors[] = {ProximityLightOuterColor0, ProximityLightOuterColor1, ProximityLightOuterColor2, ProximityLightOuterColor3};
float4 ProximityLightLocations[] = {ProximityLightLocation0, ProximityLightLocation1, ProximityLightLocation2};
float4 ProximityLightSettings[] = {ProximityLightSettings0, ProximityLightSettings1, ProximityLightSettings2};
float4 ProximityLightPulseSettings[] = {ProximityLightPulseSettings0, ProximityLightPulseSettings1, ProximityLightPulseSettings2};
Half4 ProximityLightCenterColors[] = {ProximityLightCenterColor0, ProximityLightCenterColor1, ProximityLightCenterColor2};
Half4 ProximityLightMiddleColors[] = {ProximityLightMiddleColor0, ProximityLightMiddleColor1, ProximityLightMiddleColor2};
Half4 ProximityLightOuterColors[] = {ProximityLightOuterColor0, ProximityLightOuterColor1, ProximityLightOuterColor2};
float3 WorldLocation = GetWorldPosition(Parameters);
float3 WorldNormal = Parameters.WorldNormal;
Half3 Result = 0;
[unroll]
for (int i = 0; i < GT_MAX_PROXIMITY_LIGHTS; ++i)
[unroll] for (int i = 0; i < GT_MAX_PROXIMITY_LIGHTS; ++i)
{
Result += GTContributionProximityLight(WorldLocation,
WorldNormal,
Normal,
ProximityLightLocations[i],
ProximityLightSettings[i],
ProximityLightPulseSettings[i],

Просмотреть файл

@ -40,7 +40,8 @@ public class GraphicsTools : ModuleRules
"Slate",
"SlateCore",
"Projects",
"RenderCore"
"RenderCore",
"RHI"
// ... add private dependencies that you statically link with here ...
}
);

Просмотреть файл

@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "GTClippingBoxActor.h"
#include "GTClippingBoxComponent.h"
AGTClippingBoxActor::AGTClippingBoxActor()
{
// Make the ClippingBox component the root component.
ClippingPrimitiveComponent = CreateDefaultSubobject<UGTClippingBoxComponent>(TEXT("ClippingBoxComponent"));
RootComponent = ClippingPrimitiveComponent;
// Give the ClippingBox a sane initial size.
ClippingPrimitiveComponent->SetRelativeScale3D(FVector(5.0f, 5.0f, 5.0f));
}

Просмотреть файл

@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "GTClippingBoxComponent.h"
#include "GTWorldSubsystem.h"
UGTClippingBoxComponent::UGTClippingBoxComponent()
{
{
static const FName ParameterName("ClippingBoxSettings");
SetSettingsParameterName(ParameterName);
}
{
static const FName ParameterNames[4] = {
"ClippingBoxTransformColumn0", "ClippingBoxTransformColumn1", "ClippingBoxTransformColumn2", "ClippingBoxTransformColumn3"};
TArray<FName> Names;
Names.Append(ParameterNames, UE_ARRAY_COUNT(ParameterNames));
SetTransformColumnParameterNames(Names);
}
}
TArray<UGTSceneComponent*>& UGTClippingBoxComponent::GetWorldComponents()
{
return GetWorld()->GetSubsystem<UGTWorldSubsystem>()->ClippingBoxes;
}

Просмотреть файл

@ -0,0 +1,17 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "GTClippingConeActor.h"
#include "GTClippingConeComponent.h"
AGTClippingConeActor::AGTClippingConeActor()
{
// Make the ClippingCone component the root component.
ClippingPrimitiveComponent = CreateDefaultSubobject<UGTClippingConeComponent>(TEXT("ClippingConeComponent"));
RootComponent = ClippingPrimitiveComponent;
// Give the ClippingCone a sane initial rotation & size.
ClippingPrimitiveComponent->SetRelativeRotation(FRotator(0.0f, 0.0f, 90.0f));
ClippingPrimitiveComponent->SetRelativeScale3D(FVector(10.0f, 10.0f, 10.0f));
}

Просмотреть файл

@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "GTClippingConeComponent.h"
#include "GTWorldSubsystem.h"
UGTClippingConeComponent::UGTClippingConeComponent()
{
{
static const FName ParameterName("ClippingConeSettings");
SetSettingsParameterName(ParameterName);
}
{
static const FName ParameterNames[2] = {"ClippingConeStart", "ClippingConeEnd"};
TArray<FName> Names;
Names.Append(ParameterNames, UE_ARRAY_COUNT(ParameterNames));
SetTransformColumnParameterNames(Names);
}
}
TArray<UGTSceneComponent*>& UGTClippingConeComponent::GetWorldComponents()
{
return GetWorld()->GetSubsystem<UGTWorldSubsystem>()->ClippingCones;
}
void UGTClippingConeComponent::UpdateParameterCollectionTransform()
{
const FTransform& Transform = GetComponentTransform();
FVector HalfHeight = Transform.GetScaledAxis(EAxis::X) * 0.5f;
FVector Top = Transform.GetLocation() + HalfHeight;
FVector Bottom = Transform.GetLocation() - HalfHeight;
FVector ScaleBottomTop = Transform.GetScale3D() * 0.5f;
SetVectorParameterValue(GetTransformColumnParameterNames()[0], FLinearColor(Top.X, Top.Y, Top.Z, ScaleBottomTop.Z));
SetVectorParameterValue(GetTransformColumnParameterNames()[1], FLinearColor(Bottom.X, Bottom.Y, Bottom.Z, ScaleBottomTop.Y));
}

Просмотреть файл

@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "GTClippingPlaneActor.h"
#include "GTClippingPlaneComponent.h"
AGTClippingPlaneActor::AGTClippingPlaneActor()
{
// Make the ClippingPlane component the root component.
ClippingPrimitiveComponent = CreateDefaultSubobject<UGTClippingPlaneComponent>(TEXT("ClippingPlaneComponent"));
RootComponent = ClippingPrimitiveComponent;
// Give the ClippingPlane a sane initial size.
ClippingPrimitiveComponent->SetRelativeScale3D(FVector(10.0f, 10.0f, 10.0f));
}

Просмотреть файл

@ -0,0 +1,35 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "GTClippingPlaneComponent.h"
#include "GTWorldSubsystem.h"
UGTClippingPlaneComponent::UGTClippingPlaneComponent()
{
{
static const FName ParameterName("ClippingPlaneSettings");
SetSettingsParameterName(ParameterName);
}
{
static const FName ParameterNames[1] = {"ClippingPlane"};
TArray<FName> Names;
Names.Append(ParameterNames, UE_ARRAY_COUNT(ParameterNames));
SetTransformColumnParameterNames(Names);
}
}
TArray<UGTSceneComponent*>& UGTClippingPlaneComponent::GetWorldComponents()
{
return GetWorld()->GetSubsystem<UGTWorldSubsystem>()->ClippingPlanes;
}
void UGTClippingPlaneComponent::UpdateParameterCollectionTransform()
{
const FTransform& Tranform = GetComponentTransform();
FVector Normal = Tranform.GetUnitAxis(EAxis::X);
SetVectorParameterValue(
GetTransformColumnParameterNames()[0],
FLinearColor(Normal.X, Normal.Y, Normal.Z, FVector::DotProduct(Normal, Tranform.GetLocation())));
}

Просмотреть файл

@ -0,0 +1,9 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "GTClippingPrimitiveActor.h"
AGTClippingPrimitiveActor::AGTClippingPrimitiveActor()
{
PrimaryActorTick.bCanEverTick = false;
}

Просмотреть файл

@ -0,0 +1,124 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "GTClippingPrimitiveComponent.h"
#include "GraphicsTools.h"
#include "UObject/ConstructorHelpers.h"
UGTClippingPrimitiveComponent::UGTClippingPrimitiveComponent()
{
#if WITH_EDITORONLY_DATA
EditorTexture = nullptr;
#endif // WITH_EDITORONLY_DATA
{
static const FName ParameterName("ClippingPrimitiveSettings");
SettingsParameterName = ParameterName;
}
{
static const FName ParameterNames[4] = {
"ClippingPrimitiveTransformColumn0", "ClippingPrimitiveTransformColumn1", "ClippingPrimitiveTransformColumn2",
"ClippingPrimitiveTransformColumn3"};
TransformColumnParameterNames.Append(ParameterNames, UE_ARRAY_COUNT(ParameterNames));
}
}
void UGTClippingPrimitiveComponent::SetClippingSide(EGTClippingSide Side)
{
if (ClippingSide != Side)
{
ClippingSide = Side;
UpdateParameterCollection();
}
}
void UGTClippingPrimitiveComponent::SetSettingsParameterName(const FName& Name)
{
if (SettingsParameterName != Name)
{
SettingsParameterName = Name;
UpdateParameterCollection();
}
}
void UGTClippingPrimitiveComponent::SetTransformColumnParameterNames(const TArray<FName>& Names)
{
if (Names.Num() >= GetTransformColumnCount())
{
TransformColumnParameterNames = Names;
UpdateParameterCollection();
}
else
{
UE_LOG(
GraphicsTools, Warning,
TEXT("Unable to SetTransformColumnParameterNames because the input does not contain at least %i column names."),
GetTransformColumnCount());
}
}
#if WITH_EDITOR
bool UGTClippingPrimitiveComponent::CanEditChange(const FProperty* Property) const
{
bool IsEditable = Super::CanEditChange(Property);
if (IsEditable && Property != nullptr)
{
if (Property->GetFName() == GET_MEMBER_NAME_CHECKED(UGTClippingPrimitiveComponent, SettingsParameterName) ||
Property->GetFName() == GET_MEMBER_NAME_CHECKED(UGTClippingPrimitiveComponent, TransformColumnParameterNames))
{
IsEditable = HasParameterCollectionOverride();
}
}
return IsEditable;
}
void UGTClippingPrimitiveComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
if (PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(UGTClippingPrimitiveComponent, TransformColumnParameterNames))
{
// Ensure we always have GetTransformColumnCount names.
while (TransformColumnParameterNames.Num() < GetTransformColumnCount())
{
TransformColumnParameterNames.Add(FName());
}
}
Super::PostEditChangeProperty(PropertyChangedEvent);
}
#endif // WITH_EDITOR
void UGTClippingPrimitiveComponent::UpdateParameterCollection(bool IsDisabled)
{
if (IsValid())
{
const TArray<UGTSceneComponent*>& Components = GetWorldComponents();
const int32 ComponentIndex = Components.Find(this);
// Only the first clipping primitive of this type will be considered, or a disabled clipping primitive of this type in the case of
// removing the last clipping primitive of this type, or any components with an MPC override.
if (ComponentIndex == 0 || IsDisabled || HasParameterCollectionOverride())
{
const float Side = GetClippingSide() == EGTClippingSide::Inside ? 1 : -1;
SetVectorParameterValue(GetSettingsParameterName(), FLinearColor(!IsDisabled, Side, 0));
UpdateParameterCollectionTransform();
}
}
}
void UGTClippingPrimitiveComponent::UpdateParameterCollectionTransform()
{
FTransform Tranform = GetComponentTransform();
Tranform.SetScale3D(Tranform.GetScale3D() * 2); // Double the scale to ensure sizing is consistent with other Unreal primitives.
FMatrix InverseMatrixTranspose = Tranform.ToInverseMatrixWithScale().GetTransposed();
const TArray<FName>& ParameterNames = GetTransformColumnParameterNames();
for (int32 ColumnIndex = 0; ColumnIndex < GetTransformColumnCount(); ++ColumnIndex)
{
SetVectorParameterValue(ParameterNames[ColumnIndex], InverseMatrixTranspose.GetColumn(ColumnIndex));
}
}

Просмотреть файл

@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "GTClippingSphereActor.h"
#include "GTClippingSphereComponent.h"
AGTClippingSphereActor::AGTClippingSphereActor()
{
// Make the ClippingSphere component the root component.
ClippingPrimitiveComponent = CreateDefaultSubobject<UGTClippingSphereComponent>(TEXT("ClippingSphereComponent"));
RootComponent = ClippingPrimitiveComponent;
// Give the ClippingSphere a sane initial size.
ClippingPrimitiveComponent->SetRelativeScale3D(FVector(5.0f, 5.0f, 5.0f));
}

Просмотреть файл

@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "GTClippingSphereComponent.h"
#include "GTWorldSubsystem.h"
UGTClippingSphereComponent::UGTClippingSphereComponent()
{
{
static const FName ParameterName("ClippingSphereSettings");
SetSettingsParameterName(ParameterName);
}
{
static const FName ParameterNames[4] = {
"ClippingSphereTransformColumn0", "ClippingSphereTransformColumn1", "ClippingSphereTransformColumn2",
"ClippingSphereTransformColumn3"};
TArray<FName> Names;
Names.Append(ParameterNames, UE_ARRAY_COUNT(ParameterNames));
SetTransformColumnParameterNames(Names);
}
}
TArray<UGTSceneComponent*>& UGTClippingSphereComponent::GetWorldComponents()
{
return GetWorld()->GetSubsystem<UGTWorldSubsystem>()->ClippingSpheres;
}

Просмотреть файл

@ -7,7 +7,7 @@
AGTDirectionalLightActor::AGTDirectionalLightActor()
{
// Make the light component the root component.
// Make the DirectionalLight component the root component.
LightComponent = CreateDefaultSubobject<UGTDirectionalLightComponent>(TEXT("DirectionalLightComponent"));
RootComponent = LightComponent;

Просмотреть файл

@ -4,111 +4,13 @@
#include "GTDirectionalLightComponent.h"
#include "GTWorldSubsystem.h"
#include "GraphicsTools.h"
#include "Components/ArrowComponent.h"
#include "Materials/MaterialParameterCollection.h"
#include "Materials/MaterialParameterCollectionInstance.h"
namespace DirectionalLights
{
typedef TArray<UGTDirectionalLightComponent*> LightList;
LightList& GetLightList(const UWorld* World) { return World->GetSubsystem<UGTWorldSubsystem>()->DirectionalLights; }
void SetVectorParameterValue(
UMaterialParameterCollectionInstance* ParameterCollectionInstance, FName ParameterName, const FLinearColor& ParameterValue)
{
const bool bFoundParameter = ParameterCollectionInstance->SetVectorParameterValue(ParameterName, ParameterValue);
if (!bFoundParameter)
{
UE_LOG(
GraphicsTools, Warning, TEXT("Unable to find %s parameter in material parameter collection %s."), *ParameterName.ToString(),
*ParameterCollectionInstance->GetCollection()->GetPathName());
}
}
void UpdateParameterCollection(UGTDirectionalLightComponent* Light, bool LightEnabled = true)
{
if (!Light->IsValid())
{
return;
}
const LightList& Lights = GetLightList(Light->GetWorld());
const int32 LightIndex = Lights.Find(Light);
// Only the first directional light will be considered (or a disabled light in the case of removing the last light).
if (LightEnabled && (LightIndex != 0))
{
return;
}
UMaterialParameterCollectionInstance* ParameterCollectionInstance =
Light->GetWorld()->GetParameterCollectionInstance(Light->GetParameterCollection());
{
static FName DirectionEnabledParameterName("DirectionalLightDirectionEnabled");
FLinearColor DirectionEnabled(-Light->GetForwardVector());
DirectionEnabled.A = LightEnabled ? 1.0f : 0.0f;
SetVectorParameterValue(ParameterCollectionInstance, DirectionEnabledParameterName, DirectionEnabled);
}
{
static FName ColorIntensityParameterName("DirectionalLightColorIntensity");
FLinearColor ColorIntensity(Light->GetLightColor());
ColorIntensity.A = Light->GetLightIntensity();
SetVectorParameterValue(ParameterCollectionInstance, ColorIntensityParameterName, ColorIntensity);
}
}
void AddLight(UGTDirectionalLightComponent* Light)
{
if (!Light->IsValid())
{
return;
}
LightList& Lights = GetLightList(Light->GetWorld());
if (Lights.Find(Light) == INDEX_NONE)
{
if (Lights.Add(Light) == 0)
{
UpdateParameterCollection(Light);
}
}
}
void RemoveLight(UGTDirectionalLightComponent* Light)
{
if (!Light->IsValid())
{
return;
}
LightList& Lights = GetLightList(Light->GetWorld());
const int32 Index = Lights.Find(Light);
if (Index != INDEX_NONE)
{
Lights.RemoveAt(Index);
if (Index == 0)
{
bool bLightEnabled = Lights.Num() != 0;
UpdateParameterCollection(bLightEnabled ? Lights[0] : Light, bLightEnabled);
}
}
}
} // DirectionalLights
UGTDirectionalLightComponent::UGTDirectionalLightComponent()
{
PrimaryComponentTick.bCanEverTick = false;
bWantsOnUpdateTransform = true;
#if WITH_EDITORONLY_DATA
if (!IsRunningCommandlet())
{
@ -139,6 +41,15 @@ UGTDirectionalLightComponent::UGTDirectionalLightComponent()
ArrowComponent->bIsScreenSizeScaled = true;
}
#endif // WITH_EDITORONLY_DATA
{
static const FName ParameterName("DirectionalLightDirectionEnabled");
DirectionEnabledParameterName = ParameterName;
}
{
static const FName ParameterName("DirectionalLightColorIntensity");
ColorIntensityParameterName = ParameterName;
}
}
void UGTDirectionalLightComponent::SetLightIntensity(float Intensity)
@ -147,7 +58,7 @@ void UGTDirectionalLightComponent::SetLightIntensity(float Intensity)
{
LightIntensity = Intensity;
DirectionalLights::UpdateParameterCollection(this);
UpdateParameterCollection();
}
}
@ -157,19 +68,50 @@ void UGTDirectionalLightComponent::SetLightColor(FColor Color)
{
LightColor = Color;
DirectionalLights::UpdateParameterCollection(this);
UpdateParameterCollection();
}
}
void UGTDirectionalLightComponent::SetDirectionEnabledParameterName(const FName& Name)
{
if (DirectionEnabledParameterName != Name)
{
DirectionEnabledParameterName = Name;
UpdateParameterCollection();
}
}
void UGTDirectionalLightComponent::SetColorIntensityParameterName(const FName& Name)
{
if (ColorIntensityParameterName != Name)
{
ColorIntensityParameterName = Name;
UpdateParameterCollection();
}
}
#if WITH_EDITOR
bool UGTDirectionalLightComponent::CanEditChange(const FProperty* Property) const
{
bool IsEditable = Super::CanEditChange(Property);
if (IsEditable && Property != nullptr)
{
if (Property->GetFName() == GET_MEMBER_NAME_CHECKED(UGTDirectionalLightComponent, DirectionEnabledParameterName) ||
Property->GetFName() == GET_MEMBER_NAME_CHECKED(UGTDirectionalLightComponent, ColorIntensityParameterName))
{
IsEditable = HasParameterCollectionOverride();
}
}
return IsEditable;
}
#endif // WITH_EDITOR
void UGTDirectionalLightComponent::OnRegister()
{
Super::OnRegister();
if (IsVisible())
{
DirectionalLights::AddLight(this);
}
#if WITH_EDITORONLY_DATA
if (ArrowComponent != nullptr)
{
@ -178,39 +120,9 @@ void UGTDirectionalLightComponent::OnRegister()
#endif // WITH_EDITORONLY_DATA
}
void UGTDirectionalLightComponent::OnUnregister()
{
Super::OnUnregister();
DirectionalLights::RemoveLight(this);
}
void UGTDirectionalLightComponent::OnVisibilityChanged()
{
Super::OnVisibilityChanged();
if (IsVisible())
{
DirectionalLights::AddLight(this);
}
else
{
DirectionalLights::RemoveLight(this);
}
}
void UGTDirectionalLightComponent::OnUpdateTransform(EUpdateTransformFlags UpdateTransformFlags, ETeleportType Teleport)
{
Super::OnUpdateTransform(UpdateTransformFlags, Teleport);
DirectionalLights::UpdateParameterCollection(this);
}
#if WITH_EDITOR
void UGTDirectionalLightComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
DirectionalLights::UpdateParameterCollection(this);
if (ArrowComponent != nullptr)
{
ArrowComponent->ArrowColor = GetLightColor();
@ -219,3 +131,33 @@ void UGTDirectionalLightComponent::PostEditChangeProperty(FPropertyChangedEvent&
Super::PostEditChangeProperty(PropertyChangedEvent);
}
#endif // WITH_EDITOR
TArray<UGTSceneComponent*>& UGTDirectionalLightComponent::GetWorldComponents()
{
return GetWorld()->GetSubsystem<UGTWorldSubsystem>()->DirectionalLights;
}
void UGTDirectionalLightComponent::UpdateParameterCollection(bool IsDisabled)
{
if (IsValid())
{
const TArray<UGTSceneComponent*>& Components = GetWorldComponents();
const int32 ComponentIndex = Components.Find(this);
// Only the first directional light will be considered, or a disabled light in the case of removing the last light, or any
// components with an MPC override.
if (ComponentIndex == 0 || IsDisabled || HasParameterCollectionOverride())
{
{
FLinearColor DirectionEnabled(-GetForwardVector());
DirectionEnabled.A = !IsDisabled;
SetVectorParameterValue(GetDirectionEnabledParameterName(), DirectionEnabled);
}
{
FLinearColor ColorIntensity(GetLightColor());
ColorIntensity.A = GetLightIntensity();
SetVectorParameterValue(GetColorIntensityParameterName(), ColorIntensity);
}
}
}
}

Просмотреть файл

@ -3,63 +3,16 @@
#include "GTLightComponent.h"
#include "GTWorldSubsystem.h"
#include "Components/BillboardComponent.h"
#include "Materials/MaterialParameterCollection.h"
#include "UObject/ConstructorHelpers.h"
UGTLightComponent::UGTLightComponent()
{
PrimaryComponentTick.bCanEverTick = false;
static ConstructorHelpers::FObjectFinder<UMaterialParameterCollection> Finder(TEXT("/GraphicsTools/Materials/MPC_GTSettings"));
check(Finder.Object);
ParameterCollection = Finder.Object;
#if WITH_EDITORONLY_DATA
bVisualizeComponent = true;
if (!IsRunningCommandlet())
{
static ConstructorHelpers::FObjectFinder<UTexture2D> Texture(TEXT("/Engine/EditorResources/LightIcons/S_LightPoint"));
check(Texture.Object);
EditorTexture = Texture.Object;
EditorTextureScale = 0.5f;
}
#endif // WITH_EDITORONLY_DATA
}
UMaterialParameterCollection* UGTLightComponent::GetParameterCollection()
{
return const_cast<UMaterialParameterCollection*>(static_cast<const UGTLightComponent*>(this)->GetParameterCollection());
}
const UMaterialParameterCollection* UGTLightComponent::GetParameterCollection() const
{
// Avoid returning a collection which is being destroyed since any systems storing soft pointers may assert.
if (ParameterCollection != nullptr && ParameterCollection->HasAnyFlags(RF_BeginDestroyed))
{
return nullptr;
}
return ParameterCollection;
}
bool UGTLightComponent::IsValid() const
{
return (GetWorld() != nullptr && GetWorld()->GetSubsystem<UGTWorldSubsystem>() != nullptr && GetParameterCollection() != nullptr);
}
#if WITH_EDITOR
void UGTLightComponent::OnRegister()
{
Super::OnRegister();
if (SpriteComponent != nullptr)
{
SpriteComponent->SetSprite(EditorTexture);
SpriteComponent->SetRelativeScale3D(FVector(EditorTextureScale));
}
}
#endif // WITH_EDITOR

Просмотреть файл

@ -7,7 +7,7 @@
AGTProximityLightActor::AGTProximityLightActor()
{
// Make the light component the root component.
// Make the ProximityLight component the root component.
LightComponent = CreateDefaultSubobject<UGTProximityLightComponent>(TEXT("ProximityLightComponent"));
RootComponent = LightComponent;
}

Просмотреть файл

@ -6,154 +6,7 @@
#include "GTWorldSubsystem.h"
#include "GraphicsTools.h"
#include "Materials/MaterialParameterCollection.h"
#include "Materials/MaterialParameterCollectionInstance.h"
#define GT_MAX_PROXIMITY_LIGHTS 4
namespace ProximityLights
{
enum class EParameterCollectionFlags : uint8
{
NoneDirty = 0,
LocationDirty = 1 << 2,
SettingsDirty = 1 << 3,
PulseSettingsDirty = 1 << 4,
CenterColorDirty = 1 << 5,
MiddleColorDirty = 1 << 6,
OuterColorDirty = 1 << 7,
AllDirty = static_cast<uint8>(~0)
};
ENUM_CLASS_FLAGS(ProximityLights::EParameterCollectionFlags);
typedef TArray<UGTProximityLightComponent*> LightList;
LightList& GetLightList(const UWorld* World) { return World->GetSubsystem<UGTWorldSubsystem>()->ProximityLights; }
void SetVectorParameterValue(
UMaterialParameterCollectionInstance* ParameterCollectionInstance, FName ParameterName, const FLinearColor& ParameterValue)
{
const bool bFoundParameter = ParameterCollectionInstance->SetVectorParameterValue(ParameterName, ParameterValue);
if (!bFoundParameter)
{
UE_LOG(
GraphicsTools, Warning, TEXT("Unable to find %s parameter in material parameter collection %s."), *ParameterName.ToString(),
*ParameterCollectionInstance->GetCollection()->GetPathName());
}
}
void UpdateParameterCollection(UGTProximityLightComponent* Light, ProximityLights::EParameterCollectionFlags DirtyFlags)
{
if (!Light->IsValid())
{
return;
}
const LightList& Lights = GetLightList(Light->GetWorld());
const int32 LightIndex = Lights.Find(Light);
if (LightIndex != INDEX_NONE && LightIndex < GT_MAX_PROXIMITY_LIGHTS)
{
UMaterialParameterCollectionInstance* ParameterCollectionInstance =
Light->GetWorld()->GetParameterCollectionInstance(Light->GetParameterCollection());
if (EnumHasAnyFlags(DirtyFlags, ProximityLights::EParameterCollectionFlags::LocationDirty) ||
DirtyFlags == ProximityLights::EParameterCollectionFlags::NoneDirty)
{
static FName ParameterNames[GT_MAX_PROXIMITY_LIGHTS] = {
"ProximityLightLocation0", "ProximityLightLocation1", "ProximityLightLocation2", "ProximityLightLocation3"};
FLinearColor Location(Light->GetComponentLocation());
Location.A = DirtyFlags == ProximityLights::EParameterCollectionFlags::NoneDirty ? 0.0f : 1.0f;
SetVectorParameterValue(ParameterCollectionInstance, ParameterNames[LightIndex], Location);
}
if (EnumHasAnyFlags(DirtyFlags, ProximityLights::EParameterCollectionFlags::SettingsDirty))
{
static FName ParameterNames[GT_MAX_PROXIMITY_LIGHTS] = {
"ProximityLightSettings0", "ProximityLightSettings1", "ProximityLightSettings2", "ProximityLightSettings3"};
const float PulseScaler = 1.0f + Light->GetPulseTime();
SetVectorParameterValue(
ParameterCollectionInstance, ParameterNames[LightIndex],
FLinearColor(
Light->GetProjectedRadius() * PulseScaler, 1.0f / Light->GetAttenuationRadius() * PulseScaler,
1.0f / Light->GetShrinkDistance() * PulseScaler, Light->GetShrinkPercentage()));
}
if (EnumHasAnyFlags(DirtyFlags, ProximityLights::EParameterCollectionFlags::PulseSettingsDirty))
{
static FName ParameterNames[GT_MAX_PROXIMITY_LIGHTS] = {
"ProximityLightPulseSettings0", "ProximityLightPulseSettings1", "ProximityLightPulseSettings2",
"ProximityLightPulseSettings3"};
SetVectorParameterValue(
ParameterCollectionInstance, ParameterNames[LightIndex],
FLinearColor(Light->GetProjectedRadius() * Light->GetPulseTime(), 1.0f - Light->GetPulseFadeTime(), 0.0f, 0.0f));
}
if (EnumHasAnyFlags(DirtyFlags, ProximityLights::EParameterCollectionFlags::CenterColorDirty))
{
static FName ParameterNames[GT_MAX_PROXIMITY_LIGHTS] = {
"ProximityLightCenterColor0", "ProximityLightCenterColor1", "ProximityLightCenterColor2", "ProximityLightCenterColor3"};
SetVectorParameterValue(ParameterCollectionInstance, ParameterNames[LightIndex], Light->GetCenterColor());
}
if (EnumHasAnyFlags(DirtyFlags, ProximityLights::EParameterCollectionFlags::MiddleColorDirty))
{
static FName ParameterNames[GT_MAX_PROXIMITY_LIGHTS] = {
"ProximityLightMiddleColor0", "ProximityLightMiddleColor1", "ProximityLightMiddleColor2", "ProximityLightMiddleColor3"};
SetVectorParameterValue(ParameterCollectionInstance, ParameterNames[LightIndex], Light->GetMiddleColor());
}
if (EnumHasAnyFlags(DirtyFlags, ProximityLights::EParameterCollectionFlags::OuterColorDirty))
{
static FName ParameterNames[GT_MAX_PROXIMITY_LIGHTS] = {
"ProximityLightOuterColor0", "ProximityLightOuterColor1", "ProximityLightOuterColor2", "ProximityLightOuterColor3"};
SetVectorParameterValue(ParameterCollectionInstance, ParameterNames[LightIndex], Light->GetOuterColor());
}
}
}
} // namespace ProximityLights
void AddLight(UGTProximityLightComponent* Light)
{
if (!Light->IsValid())
{
return;
}
ProximityLights::LightList& Lights = ProximityLights::GetLightList(Light->GetWorld());
if (Lights.Find(Light) == INDEX_NONE)
{
Lights.Add(Light);
ProximityLights::UpdateParameterCollection(Light, ProximityLights::EParameterCollectionFlags::AllDirty);
}
}
void RemoveLight(UGTProximityLightComponent* Light)
{
if (!Light->IsValid())
{
return;
}
ProximityLights::LightList& Lights = ProximityLights::GetLightList(Light->GetWorld());
const int32 Index = Lights.Find(Light);
if (Index != INDEX_NONE)
{
// Disable the last active light.
ProximityLights::UpdateParameterCollection(Lights[Lights.Num() - 1], ProximityLights::EParameterCollectionFlags::NoneDirty);
Lights.RemoveAt(Index);
for (auto CurrentLight : Lights)
{
ProximityLights::UpdateParameterCollection(CurrentLight, ProximityLights::EParameterCollectionFlags::AllDirty);
}
}
}
#define GT_MAX_PROXIMITY_LIGHTS 3
UGTProximityLightComponent::UGTProximityLightComponent()
{
@ -161,7 +14,36 @@ UGTProximityLightComponent::UGTProximityLightComponent()
// Don't start ticking until the light needs to be animated.
PrimaryComponentTick.bStartWithTickEnabled = false;
bWantsOnUpdateTransform = true;
{
static const FName ParameterNames[GT_MAX_PROXIMITY_LIGHTS] = {
"ProximityLightLocation0", "ProximityLightLocation1", "ProximityLightLocation2"};
LocationParameterNames.Append(ParameterNames, UE_ARRAY_COUNT(ParameterNames));
}
{
static const FName ParameterNames[GT_MAX_PROXIMITY_LIGHTS] = {
"ProximityLightSettings0", "ProximityLightSettings1", "ProximityLightSettings2"};
SettingsParameterNames.Append(ParameterNames, UE_ARRAY_COUNT(ParameterNames));
}
{
static const FName ParameterNames[GT_MAX_PROXIMITY_LIGHTS] = {
"ProximityLightPulseSettings0", "ProximityLightPulseSettings1", "ProximityLightPulseSettings2"};
PulseSettingsParameterNames.Append(ParameterNames, UE_ARRAY_COUNT(ParameterNames));
}
{
static const FName ParameterNames[GT_MAX_PROXIMITY_LIGHTS] = {
"ProximityLightCenterColor0", "ProximityLightCenterColor1", "ProximityLightCenterColor2"};
CenterColorParameterNames.Append(ParameterNames, UE_ARRAY_COUNT(ParameterNames));
}
{
static const FName ParameterNames[GT_MAX_PROXIMITY_LIGHTS] = {
"ProximityLightMiddleColor0", "ProximityLightMiddleColor1", "ProximityLightMiddleColor2"};
MiddleColorParameterNames.Append(ParameterNames, UE_ARRAY_COUNT(ParameterNames));
}
{
static const FName ParameterNames[GT_MAX_PROXIMITY_LIGHTS] = {
"ProximityLightOuterColor0", "ProximityLightOuterColor1", "ProximityLightOuterColor2"};
OuterColorParameterNames.Append(ParameterNames, UE_ARRAY_COUNT(ParameterNames));
}
}
void UGTProximityLightComponent::SetProjectedRadius(float Radius)
@ -175,7 +57,7 @@ void UGTProximityLightComponent::SetProjectedRadius(float Radius)
AttenuationRadius = ProjectedRadius;
}
ProximityLights::UpdateParameterCollection(this, ProximityLights::EParameterCollectionFlags::SettingsDirty);
UpdateParameterCollection();
}
}
@ -190,7 +72,7 @@ void UGTProximityLightComponent::SetAttenuationRadius(float Radius)
ProjectedRadius = AttenuationRadius;
}
ProximityLights::UpdateParameterCollection(this, ProximityLights::EParameterCollectionFlags::SettingsDirty);
UpdateParameterCollection();
}
}
@ -200,7 +82,7 @@ void UGTProximityLightComponent::SetShrinkDistance(float Distance)
{
ShrinkDistance = FMath::Clamp(Distance, 1.0f, 500.0f);
ProximityLights::UpdateParameterCollection(this, ProximityLights::EParameterCollectionFlags::SettingsDirty);
UpdateParameterCollection();
}
}
@ -210,7 +92,7 @@ void UGTProximityLightComponent::SetShrinkPercentage(float Percentage)
{
ShrinkPercentage = FMath::Clamp(Percentage, 0.0f, 1.0f);
ProximityLights::UpdateParameterCollection(this, ProximityLights::EParameterCollectionFlags::SettingsDirty);
UpdateParameterCollection();
}
}
@ -220,7 +102,7 @@ void UGTProximityLightComponent::SetCenterColor(FColor Color)
{
CenterColor = Color;
ProximityLights::UpdateParameterCollection(this, ProximityLights::EParameterCollectionFlags::CenterColorDirty);
UpdateParameterCollection();
}
}
@ -230,7 +112,7 @@ void UGTProximityLightComponent::SetMiddleColor(FColor Color)
{
MiddleColor = Color;
ProximityLights::UpdateParameterCollection(this, ProximityLights::EParameterCollectionFlags::MiddleColorDirty);
UpdateParameterCollection();
}
}
@ -240,7 +122,97 @@ void UGTProximityLightComponent::SetOuterColor(FColor Color)
{
OuterColor = Color;
ProximityLights::UpdateParameterCollection(this, ProximityLights::EParameterCollectionFlags::OuterColorDirty);
UpdateParameterCollection();
}
}
void UGTProximityLightComponent::SetLocationParameterNames(const TArray<FName>& Names)
{
if (Names.Num() >= GT_MAX_PROXIMITY_LIGHTS)
{
LocationParameterNames = Names;
UpdateParameterCollection();
}
else
{
UE_LOG(
GraphicsTools, Warning, TEXT("Unable to SetLocationParameterNames because the input does not contain at least %i names."),
GT_MAX_PROXIMITY_LIGHTS);
}
}
void UGTProximityLightComponent::SetSettingsParameterNames(const TArray<FName>& Names)
{
if (Names.Num() >= GT_MAX_PROXIMITY_LIGHTS)
{
SettingsParameterNames = Names;
UpdateParameterCollection();
}
else
{
UE_LOG(
GraphicsTools, Warning, TEXT("Unable to SetSettingsParameterNames because the input does not contain at least %i names."),
GT_MAX_PROXIMITY_LIGHTS);
}
}
void UGTProximityLightComponent::SetPulseSettingsParameterNames(const TArray<FName>& Names)
{
if (Names.Num() >= GT_MAX_PROXIMITY_LIGHTS)
{
PulseSettingsParameterNames = Names;
UpdateParameterCollection();
}
else
{
UE_LOG(
GraphicsTools, Warning, TEXT("Unable to SetPulseSettingsParameterNames because the input does not contain at least %i names."),
GT_MAX_PROXIMITY_LIGHTS);
}
}
void UGTProximityLightComponent::SetCenterColorParameterNames(const TArray<FName>& Names)
{
if (Names.Num() >= GT_MAX_PROXIMITY_LIGHTS)
{
CenterColorParameterNames = Names;
UpdateParameterCollection();
}
else
{
UE_LOG(
GraphicsTools, Warning, TEXT("Unable to SetCenterColorParameterNames because the input does not contain at least %i names."),
GT_MAX_PROXIMITY_LIGHTS);
}
}
void UGTProximityLightComponent::SetMiddleColorParameterNames(const TArray<FName>& Names)
{
if (Names.Num() >= GT_MAX_PROXIMITY_LIGHTS)
{
MiddleColorParameterNames = Names;
UpdateParameterCollection();
}
else
{
UE_LOG(
GraphicsTools, Warning, TEXT("Unable to SetMiddleColorParameterNames because the input does not contain at least %i names."),
GT_MAX_PROXIMITY_LIGHTS);
}
}
void UGTProximityLightComponent::SetOuterColorParameterNames(const TArray<FName>& Names)
{
if (Names.Num() >= GT_MAX_PROXIMITY_LIGHTS)
{
OuterColorParameterNames = Names;
UpdateParameterCollection();
}
else
{
UE_LOG(
GraphicsTools, Warning, TEXT("Unable to SetOuterColorParameterNames because the input does not contain at least %i names."),
GT_MAX_PROXIMITY_LIGHTS);
}
}
@ -279,13 +251,34 @@ float UGTProximityLightComponent::GetPulseFadeTime() const
return 0;
}
#if WITH_EDITOR
bool UGTProximityLightComponent::CanEditChange(const FProperty* Property) const
{
bool IsEditable = Super::CanEditChange(Property);
if (IsEditable && Property != nullptr)
{
if (Property->GetFName() == GET_MEMBER_NAME_CHECKED(UGTProximityLightComponent, LocationParameterNames) ||
Property->GetFName() == GET_MEMBER_NAME_CHECKED(UGTProximityLightComponent, SettingsParameterNames) ||
Property->GetFName() == GET_MEMBER_NAME_CHECKED(UGTProximityLightComponent, PulseSettingsParameterNames) ||
Property->GetFName() == GET_MEMBER_NAME_CHECKED(UGTProximityLightComponent, CenterColorParameterNames) ||
Property->GetFName() == GET_MEMBER_NAME_CHECKED(UGTProximityLightComponent, MiddleColorParameterNames) ||
Property->GetFName() == GET_MEMBER_NAME_CHECKED(UGTProximityLightComponent, OuterColorParameterNames))
{
IsEditable = HasParameterCollectionOverride();
}
}
return IsEditable;
}
#endif // WITH_EDITOR
void UGTProximityLightComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
PulseState = PulseTick(DeltaTime);
ProximityLights::UpdateParameterCollection(
this, ProximityLights::EParameterCollectionFlags::SettingsDirty | ProximityLights::EParameterCollectionFlags::PulseSettingsDirty);
UpdateParameterCollection();
if (PulseState == EPulseState::Idle)
{
@ -293,44 +286,6 @@ void UGTProximityLightComponent::TickComponent(float DeltaTime, ELevelTick TickT
}
}
void UGTProximityLightComponent::OnRegister()
{
Super::OnRegister();
if (IsVisible())
{
AddLight(this);
}
}
void UGTProximityLightComponent::OnUnregister()
{
Super::OnUnregister();
RemoveLight(this);
}
void UGTProximityLightComponent::OnVisibilityChanged()
{
Super::OnVisibilityChanged();
if (IsVisible())
{
AddLight(this);
}
else
{
RemoveLight(this);
}
}
void UGTProximityLightComponent::OnUpdateTransform(EUpdateTransformFlags UpdateTransformFlags, ETeleportType Teleport)
{
Super::OnUpdateTransform(UpdateTransformFlags, Teleport);
ProximityLights::UpdateParameterCollection(this, ProximityLights::EParameterCollectionFlags::LocationDirty);
}
#if WITH_EDITOR
void UGTProximityLightComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
@ -348,13 +303,106 @@ void UGTProximityLightComponent::PostEditChangeProperty(FPropertyChangedEvent& P
ProjectedRadius = AttenuationRadius;
}
}
ProximityLights::UpdateParameterCollection(this, ProximityLights::EParameterCollectionFlags::AllDirty);
else if (PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(UGTProximityLightComponent, LocationParameterNames))
{
// Ensure we always have GT_MAX_PROXIMITY_LIGHTS names.
while (LocationParameterNames.Num() < GT_MAX_PROXIMITY_LIGHTS)
{
LocationParameterNames.Add(FName());
}
}
else if (PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(UGTProximityLightComponent, SettingsParameterNames))
{
// Ensure we always have GT_MAX_PROXIMITY_LIGHTS names.
while (SettingsParameterNames.Num() < GT_MAX_PROXIMITY_LIGHTS)
{
SettingsParameterNames.Add(FName());
}
}
else if (PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(UGTProximityLightComponent, PulseSettingsParameterNames))
{
// Ensure we always have GT_MAX_PROXIMITY_LIGHTS names.
while (PulseSettingsParameterNames.Num() < GT_MAX_PROXIMITY_LIGHTS)
{
PulseSettingsParameterNames.Add(FName());
}
}
else if (PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(UGTProximityLightComponent, CenterColorParameterNames))
{
// Ensure we always have GT_MAX_PROXIMITY_LIGHTS names.
while (CenterColorParameterNames.Num() < GT_MAX_PROXIMITY_LIGHTS)
{
CenterColorParameterNames.Add(FName());
}
}
else if (PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(UGTProximityLightComponent, MiddleColorParameterNames))
{
// Ensure we always have GT_MAX_PROXIMITY_LIGHTS names.
while (MiddleColorParameterNames.Num() < GT_MAX_PROXIMITY_LIGHTS)
{
MiddleColorParameterNames.Add(FName());
}
}
else if (PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(UGTProximityLightComponent, OuterColorParameterNames))
{
// Ensure we always have GT_MAX_PROXIMITY_LIGHTS names.
while (OuterColorParameterNames.Num() < GT_MAX_PROXIMITY_LIGHTS)
{
OuterColorParameterNames.Add(FName());
}
}
Super::PostEditChangeProperty(PropertyChangedEvent);
}
#endif // WITH_EDITOR
TArray<UGTSceneComponent*>& UGTProximityLightComponent::GetWorldComponents()
{
return GetWorld()->GetSubsystem<UGTWorldSubsystem>()->ProximityLights;
}
void UGTProximityLightComponent::UpdateParameterCollection(bool IsDisabled)
{
if (IsValid())
{
// When the proximity light has a material parameter collection override, assume the light is index zero.
int32 ComponentIndex = 0;
if (!HasParameterCollectionOverride())
{
const TArray<UGTSceneComponent*>& Components = GetWorldComponents();
ComponentIndex = Components.Find(this);
}
if (ComponentIndex != INDEX_NONE && ComponentIndex < GT_MAX_PROXIMITY_LIGHTS)
{
{
FLinearColor Location(GetComponentLocation());
Location.A = !IsDisabled;
SetVectorParameterValue(GetLocationParameterNames()[ComponentIndex], Location);
}
{
const float PulseScaler = 1.0f + GetPulseTime();
SetVectorParameterValue(
GetSettingsParameterNames()[ComponentIndex],
FLinearColor(
GetProjectedRadius() * PulseScaler, 1.0f / GetAttenuationRadius() * PulseScaler,
1.0f / GetShrinkDistance() * PulseScaler, GetShrinkPercentage()));
}
{
SetVectorParameterValue(
GetPulseSettingsParameterNames()[ComponentIndex],
FLinearColor(GetProjectedRadius() * GetPulseTime(), 1.0f - GetPulseFadeTime(), 0.0f, 0.0f));
}
{
SetVectorParameterValue(GetCenterColorParameterNames()[ComponentIndex], GetCenterColor());
SetVectorParameterValue(GetMiddleColorParameterNames()[ComponentIndex], GetMiddleColor());
SetVectorParameterValue(GetOuterColorParameterNames()[ComponentIndex], GetOuterColor());
}
}
}
}
EPulseState UGTProximityLightComponent::PulseTick(float DeltaTime)
{
switch (PulseState)

Просмотреть файл

@ -0,0 +1,191 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "GTSceneComponent.h"
#include "GTWorldSubsystem.h"
#include "GraphicsTools.h"
#include "Components/BillboardComponent.h"
#include "Materials/MaterialParameterCollection.h"
#include "Materials/MaterialParameterCollectionInstance.h"
#include "UObject/ConstructorHelpers.h"
TArray<UGTSceneComponent*> UGTSceneComponent::Empty;
UGTSceneComponent::UGTSceneComponent()
{
PrimaryComponentTick.bCanEverTick = false;
bWantsOnUpdateTransform = true;
static ConstructorHelpers::FObjectFinder<UMaterialParameterCollection> Finder(TEXT("/GraphicsTools/Materials/MPC_GTSettings"));
check(Finder.Object);
WorldParameterCollection = Finder.Object;
#if WITH_EDITORONLY_DATA
bVisualizeComponent = true;
if (!IsRunningCommandlet())
{
static ConstructorHelpers::FObjectFinder<UTexture2D> Texture(TEXT("/Engine/EditorResources/EmptyActor.EmptyActor"));
check(Texture.Object);
EditorTexture = Texture.Object;
EditorTextureScale = 0.5f;
}
#endif // WITH_EDITORONLY_DATA
}
void UGTSceneComponent::SetParameterCollectionOverride(UMaterialParameterCollection* Override)
{
RemoveFromWorldParameterCollection();
ParameterCollectionOverride = (Override == WorldParameterCollection) ? nullptr : Override;
AddToWorldParameterCollection();
}
void UGTSceneComponent::OnRegister()
{
Super::OnRegister();
if (IsVisible())
{
AddToWorldParameterCollection();
}
#if WITH_EDITOR
if (SpriteComponent != nullptr)
{
SpriteComponent->SetSprite(EditorTexture);
SpriteComponent->SetRelativeScale3D(FVector(EditorTextureScale));
}
#endif // WITH_EDITOR
}
void UGTSceneComponent::OnUnregister()
{
Super::OnUnregister();
RemoveFromWorldParameterCollection();
}
void UGTSceneComponent::OnVisibilityChanged()
{
Super::OnVisibilityChanged();
if (IsVisible())
{
AddToWorldParameterCollection();
}
else
{
RemoveFromWorldParameterCollection();
}
}
void UGTSceneComponent::OnUpdateTransform(EUpdateTransformFlags UpdateTransformFlags, ETeleportType Teleport)
{
Super::OnUpdateTransform(UpdateTransformFlags, Teleport);
UpdateParameterCollection();
}
#if WITH_EDITOR
void UGTSceneComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
UpdateParameterCollection();
Super::PostEditChangeProperty(PropertyChangedEvent);
}
#endif // WITH_EDITOR
bool UGTSceneComponent::IsValid() const
{
// Ensure the world isn't being destroyed since any systems storing soft pointers may assert.
return (
!IsEngineExitRequested() && GetWorld() != nullptr && GetWorld()->HasAnyFlags(RF_BeginDestroyed) == false &&
GetWorld()->GetSubsystem<UGTWorldSubsystem>() != nullptr && GetParameterCollection() != nullptr);
}
const UMaterialParameterCollection* UGTSceneComponent::GetParameterCollection() const
{
const UMaterialParameterCollection* CurrentCollection =
HasParameterCollectionOverride() ? ParameterCollectionOverride : WorldParameterCollection;
// Avoid returning a collection which is being destroyed since any systems storing soft pointers may assert.
if (CurrentCollection != nullptr && CurrentCollection->HasAnyFlags(RF_BeginDestroyed))
{
return nullptr;
}
return CurrentCollection;
}
bool UGTSceneComponent::SetVectorParameterValue(FName ParameterName, const FLinearColor& ParameterValue)
{
UMaterialParameterCollectionInstance* ParameterCollectionInstance =
GetWorld()->GetParameterCollectionInstance(GetParameterCollection());
if (ParameterCollectionInstance != nullptr)
{
if (ParameterCollectionInstance->SetVectorParameterValue(ParameterName, ParameterValue))
{
return true;
}
UE_LOG(
GraphicsTools, Warning, TEXT("Unable to find %s parameter in material parameter collection %s."), *ParameterName.ToString(),
*ParameterCollectionInstance->GetCollection()->GetPathName());
}
return false;
}
void UGTSceneComponent::AddToWorldParameterCollection()
{
if (IsValid())
{
if (HasParameterCollectionOverride())
{
UpdateParameterCollection();
}
else
{
TArray<UGTSceneComponent*>& Components = GetWorldComponents();
if (Components.Find(this) == INDEX_NONE)
{
Components.Add(this);
UpdateParameterCollection();
}
}
}
}
void UGTSceneComponent::RemoveFromWorldParameterCollection()
{
if (IsValid())
{
if (HasParameterCollectionOverride())
{
UpdateParameterCollection(true);
}
else
{
TArray<UGTSceneComponent*>& Components = GetWorldComponents();
const int32 ComponentIndex = Components.Find(this);
if (ComponentIndex != INDEX_NONE)
{
// Disable the last component.
Components[Components.Num() - 1]->UpdateParameterCollection(true);
Components.RemoveAt(ComponentIndex);
for (auto Component : Components)
{
Component->UpdateParameterCollection();
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,304 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "GTVisualProfiler.h"
#include "GraphicsTools.h"
#include "Components/StaticMeshComponent.h"
#include "Components/TextRenderComponent.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "UObject/ConstructorHelpers.h"
const float DefaultThresholdFrameTime = (1.0f / 60.0f) * 1000; // Default to 16.6ms.
const int32 SortPriorityLow = 100;
const int32 SortPriorityMed = 101;
const int32 SortPriorityHigh = 102;
AGTVisualProfiler::AGTVisualProfiler()
: bSnapped(false)
, ThresholdFrameTime(DefaultThresholdFrameTime)
, FrameTime(0.0f)
, RenderThreadTime(0.0f)
, GameThreadTime(0.0f)
, GPUFrameTime(0.0f)
, PrevFrameTime(0)
, PrevRenderThreadTime(0)
, PrevGameThreadTime(0)
, PrevGPUFrameTime(0)
, PrevNumDrawCalls(0)
, PrevNumPrimitives(0)
{
PrimaryActorTick.bCanEverTick = true;
ThresholdFrameTime = QueryThresholdFrameTime();
// Acquire default meshes and materials.
static ConstructorHelpers::FObjectFinder<UStaticMesh> DefaultQuadMeshFinder(TEXT("/Engine/BasicShapes/Plane"));
check(DefaultQuadMeshFinder.Object);
DefaultQuadMesh = DefaultQuadMeshFinder.Object;
// TODO, as of Unreal 4.26 translucent materials cannot write depth required for depth based late stage re-projection. Once depth write
// is supported it would be best to swap these materials with translucent materials that do not read depth so that the profiler renders
// on top of all other objects. Translucent sort priorities are already configured to do this.
static ConstructorHelpers::FObjectFinder<UMaterial> DefaultMaterialFinder(
TEXT("/Engine/EngineDebugMaterials/LevelColorationUnlitMaterial"));
check(DefaultMaterialFinder.Object);
DefaultMaterial = DefaultMaterialFinder.Object;
static ConstructorHelpers::FObjectFinder<UMaterial> DefaultTextMaterialFinder(TEXT("/Engine/EngineMaterials/UnlitText"));
check(DefaultTextMaterialFinder.Object);
DefaultTextMaterial = DefaultTextMaterialFinder.Object;
// UI constants.
static const float PrefixYOffset = -3.9f;
static const float LabelYOffset = -2.4f;
static const float PivotYOffset = 0;
static const float HeightZOffset = 0.45f;
static const FVector BarPivot(0, 1, 0);
static const FVector BarSize(0.004f, 0.02f, 1);
static const FRotator QuadRotation(90, 0, 0);
static const FString ZeroMs(TEXT("0.00 ms"));
static const FLinearColor BackPlateColor(FColor(80, 80, 80)); // Dark Gray
static const FLinearColor FrameTimeColor(FColor(0, 164, 239)); // Vivid Cerulean
static const FLinearColor GameThreadColor(FColor(255, 185, 0)); // Selective Yellow
static const FLinearColor RenderThreadColor(FColor(242, 80, 34)); // Orioles Orange
static const FLinearColor GPUTimeColor(FColor(127, 186, 0)); // Apple Green
// Build the profiler by creating child components.
RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("VisualProfiler"));
RootComponent->SetAutoActivate(true);
FVector Location(-0.02f, 0, 0.9f);
CreateQuad(TEXT("BackPlate"), RootComponent, FVector::ZeroVector, FVector(0.025f, 0.08f, 1), SortPriorityLow, BackPlateColor);
Location.Y = PrefixYOffset;
CreateText(TEXT("FrameTimeLabelPrefix"), RootComponent, Location, TEXT("Frame: "), SortPriorityMed);
Location.Y = LabelYOffset;
FrameTimeLabel = CreateText(TEXT("FrameTimeLabel"), RootComponent, Location, ZeroMs, SortPriorityMed);
Location.Y = PivotYOffset;
FrameTimePivot = CreateScene<USceneComponent>(TEXT("FrameTimePivot"), RootComponent, Location, QuadRotation);
CreateQuad(TEXT("FrameTimePivotQuad"), FrameTimePivot, BarPivot, BarSize, SortPriorityMed, FrameTimeColor, FRotator::ZeroRotator);
Location.Z = Location.Z - HeightZOffset;
Location.Y = PrefixYOffset;
CreateText(TEXT("GameThreadTimeLabelPrefix"), RootComponent, Location, TEXT("Game: "), SortPriorityMed);
Location.Y = LabelYOffset;
GameThreadTimeLabel = CreateText(TEXT("GameThreadTimeLabel"), RootComponent, Location, ZeroMs, SortPriorityMed);
Location.Y = PivotYOffset;
GameThreadTimePivot = CreateScene<USceneComponent>(TEXT("GameThreadTimePivot"), RootComponent, Location, QuadRotation);
CreateQuad(TEXT("GameTimePivotQuad"), GameThreadTimePivot, BarPivot, BarSize, SortPriorityMed, GameThreadColor, FRotator::ZeroRotator);
Location.Z = Location.Z - (HeightZOffset * 0.5f);
CreateQuad(
TEXT("TargetLine"), RootComponent, FVector(Location.X * 2, BarPivot.Y * 2, Location.Z), FVector(0.018f, 0.0005f, 1),
SortPriorityHigh);
Location.Z = Location.Z - (HeightZOffset * 0.5f);
Location.Y = PrefixYOffset;
CreateText(TEXT("RenderThreadTimeLabelPrefix"), RootComponent, Location, TEXT("Draw: "), SortPriorityMed);
Location.Y = LabelYOffset;
RenderThreadTimeLabel = CreateText(TEXT("RenderThreadTimeLabel"), RootComponent, Location, ZeroMs, SortPriorityMed);
Location.Y = PivotYOffset;
RenderThreadTimePivot = CreateScene<USceneComponent>(TEXT("RenderThreadTimePivot"), RootComponent, Location, QuadRotation);
CreateQuad(
TEXT("RenderThreadTimePivotQuad"), RenderThreadTimePivot, BarPivot, BarSize, SortPriorityMed, RenderThreadColor,
FRotator::ZeroRotator);
Location.Z = Location.Z - HeightZOffset;
Location.Y = PrefixYOffset;
CreateText(TEXT("GPUTimeLabelPrefix"), RootComponent, Location, TEXT("GPU: "), SortPriorityMed);
Location.Y = LabelYOffset;
GPUTimeLabel = CreateText(TEXT("GPUTimeLabel"), RootComponent, Location, ZeroMs, SortPriorityMed);
Location.Y = PivotYOffset;
GPUTimePivot = CreateScene<USceneComponent>(TEXT("GPUTimePivot"), RootComponent, Location, QuadRotation);
CreateQuad(TEXT("GPUTimePivotQuad"), GPUTimePivot, BarPivot, BarSize, SortPriorityMed, GPUTimeColor, FRotator::ZeroRotator);
Location.Z = Location.Z - HeightZOffset;
Location.Y = PrefixYOffset;
DrawCallsLabel = CreateText(TEXT("DrawCallsLabel"), RootComponent, Location, TEXT("Draw Calls: 0"), SortPriorityMed);
Location.Y = 0;
PrimitivesLabel = CreateText(TEXT("PrimitivesLabel"), RootComponent, Location, TEXT("Polys: 0"), SortPriorityMed);
}
void AGTVisualProfiler::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (RootComponent->IsActive() && RootComponent->IsVisible())
{
// Calculate the current frame times. (Timing calculations mirrored from FStatUnitData.)
{
float DiffTime = FApp::GetCurrentTime() - FApp::GetLastTime();
float RawFrameTime = DiffTime * 1000.0f;
FrameTime = 0.9 * FrameTime + 0.1 * RawFrameTime;
// Number of milliseconds the game thread was used last frame.
float RawGameThreadTime = FPlatformTime::ToMilliseconds(GGameThreadTime);
GameThreadTime = 0.9 * GameThreadTime + 0.1 * RawGameThreadTime;
// Number of milliseconds the render thread was used last frame.
float RawRenderThreadTime = FPlatformTime::ToMilliseconds(GRenderThreadTime);
RenderThreadTime = 0.9 * RenderThreadTime + 0.1 * RawRenderThreadTime;
// Number of milliseconds the GPU was busy last frame.
const uint32 GPUCycles = RHIGetGPUFrameCycles(0); // We only track the first GPU.
float RawGPUFrameTime = FPlatformTime::ToMilliseconds(GPUCycles);
GPUFrameTime = 0.9 * GPUFrameTime + 0.1 * RawGPUFrameTime;
ApplyTiming(FrameTime, PrevFrameTime, FrameTimeLabel, FrameTimePivot);
ApplyTiming(GameThreadTime, PrevGameThreadTime, GameThreadTimeLabel, GameThreadTimePivot);
ApplyTiming(RenderThreadTime, PrevRenderThreadTime, RenderThreadTimeLabel, RenderThreadTimePivot);
ApplyTiming(GPUFrameTime, PrevGPUFrameTime, GPUTimeLabel, GPUTimePivot);
}
// Draw calls.
{
static const int32 ProfilerDrawCalls = 16; // Removed profiling induced draw calls.
const int32 NumDrawCalls = FMath::Max(GNumDrawCallsRHI - ProfilerDrawCalls, 0);
if (CheckCountDirty(NumDrawCalls, PrevNumDrawCalls))
{
DrawCallsLabel->SetText(FText::Format(FText::AsCultureInvariant("Draw Calls: {0}"), FText::AsNumber(NumDrawCalls)));
}
}
// Primitives.
{
static const int32 ProfilerPrimitives = 336; // Removed profiling induced primitives.
int32 NumPrimitives = FMath::Max(GNumPrimitivesDrawnRHI - ProfilerPrimitives, 0);
if (CheckCountDirty(NumPrimitives, PrevNumPrimitives))
{
if (NumPrimitives < 10000)
{
PrimitivesLabel->SetText(FText::Format(FText::AsCultureInvariant("Polys: {0}"), FText::AsNumber(NumPrimitives)));
}
else
{
float NumPrimitivesK = NumPrimitives / 1000.f;
PrimitivesLabel->SetText(FText::AsCultureInvariant(FString::Printf(TEXT("Polys: %.1fK"), NumPrimitivesK)));
}
}
}
SolveToCamera(DeltaTime);
}
}
void AGTVisualProfiler::SolveToCamera(float DeltaTime)
{
if (APlayerController* PlayerController = GetWorld()->GetFirstPlayerController())
{
if (APlayerCameraManager* CameraManager = PlayerController->PlayerCameraManager)
{
const FVector CameraLocation = CameraManager->GetCameraLocation();
const FRotator CameraRotation = CameraManager->GetCameraRotation();
FVector TargetLocation = CameraLocation + CameraRotation.RotateVector(FollowOffset);
FQuat TargetRotation = CameraRotation.Quaternion();
TargetRotation *= FQuat(FVector(0, 1, 0), FMath::DegreesToRadians(PitchOffset));
const float T = bSnapped ? FMath::Clamp(DeltaTime * FollowSpeed, 0.0f, 1.0f) : 1;
SetActorLocationAndRotation(
FMath::Lerp(GetActorLocation(), TargetLocation, T), FQuat::Slerp(GetActorQuat(), TargetRotation, T));
bSnapped = true;
}
}
}
UStaticMeshComponent* AGTVisualProfiler::CreateQuad(
const FName& Name, USceneComponent* Parent, FVector RelativeLocation, FVector RelativeScale, int32 SortPriority, FLinearColor Color,
FRotator RelativeRotation)
{
UStaticMeshComponent* Component = CreateScene<UStaticMeshComponent>(Name, Parent, RelativeLocation, RelativeRotation);
Component->SetRelativeScale3D(RelativeScale);
Component->SetStaticMesh(DefaultQuadMesh);
UMaterialInstanceDynamic* Material = Component->CreateDynamicMaterialInstance(0, DefaultMaterial);
Material->SetVectorParameterValue(TEXT("Color"), Color);
Component->SetMaterial(0, Material);
Component->SetCollisionEnabled(ECollisionEnabled::NoCollision);
Component->SetTranslucentSortPriority(SortPriority);
return Component;
}
UTextRenderComponent* AGTVisualProfiler::CreateText(
const FName& Name, USceneComponent* Parent, FVector RelativeLocation, const FString& Text, int32 SortPriority,
FRotator RelativeRotation, float Size, bool LeftAlign)
{
UTextRenderComponent* Component = CreateScene<UTextRenderComponent>(Name, Parent, RelativeLocation, RelativeRotation);
Component->SetMaterial(0, DefaultTextMaterial);
Component->SetWorldSize(Size);
Component->SetHorizontalAlignment(LeftAlign ? EHorizTextAligment::EHTA_Left : EHorizTextAligment::EHTA_Right);
Component->SetVerticalAlignment(EVerticalTextAligment::EVRTA_TextCenter);
Component->SetText(FText::AsCultureInvariant(Text));
Component->SetTranslucentSortPriority(SortPriority);
return Component;
}
void AGTVisualProfiler::ApplyTiming(float Time, int32& PrevTime, UTextRenderComponent* Label, USceneComponent* Pivot)
{
if (AGTVisualProfiler::CheckTimeDirty(Time, PrevTime))
{
Label->SetText(FText::AsCultureInvariant(FString::Printf(TEXT("%3.2f ms"), Time)));
Label->SetTextRenderColor(TimeToTextColor(Time));
Pivot->SetRelativeScale3D(FVector(1, TimeToScale(Time), 1));
}
}
float AGTVisualProfiler::TimeToScale(float Time) const
{
return FMath::Clamp(Time / ThresholdFrameTime, 0.0f, 2.0f);
}
FColor AGTVisualProfiler::TimeToTextColor(float Time) const
{
static const FColor MissedFrameColor(255, 20, 5); // Orange
return (static_cast<int32>(Time) > (static_cast<int32>(ThresholdFrameTime) + 1)) ? MissedFrameColor : FColor::White;
}
bool AGTVisualProfiler::CheckTimeDirty(float Time, int32& PrevTime)
{
int32 NewTime = static_cast<int32>(Time * 100);
const bool Dirty = NewTime != PrevTime;
PrevTime = NewTime;
return Dirty;
}
bool AGTVisualProfiler::CheckCountDirty(int32 Count, int32& PrevCount)
{
const bool Dirty = Count != PrevCount;
PrevCount = Count;
return Dirty;
}
float AGTVisualProfiler::QueryThresholdFrameTime()
{
float Result = -1;
FScreenResolutionArray Resolutions;
if (RHIGetAvailableResolutions(Resolutions, false))
{
for (int32 Index = 0; Index < Resolutions.Num(); ++Index)
{
if (Resolutions[Index].RefreshRate != 0)
{
Result = (1.0f / Resolutions[Index].RefreshRate) * 1000.0f;
break;
}
}
}
return (Result <= 0) ? DefaultThresholdFrameTime : Result;
}

Просмотреть файл

@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#pragma once
#include "CoreMinimal.h"
#include "GTClippingPrimitiveActor.h"
#include "GTClippingBoxActor.generated.h"
/**
* Utility actor which automatically adds a UGTClippingBoxComponent.
*/
UCLASS(ClassGroup = GraphicsTools)
class GRAPHICSTOOLS_API AGTClippingBoxActor : public AGTClippingPrimitiveActor
{
GENERATED_BODY()
public:
AGTClippingBoxActor();
};

Просмотреть файл

@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#pragma once
#include "CoreMinimal.h"
#include "GTClippingPrimitiveComponent.h"
#include "GTClippingBoxComponent.generated.h"
/**
* Sub class of a ClippingPrimitive that represents an analytic box used to pass state to materials for per pixel clipping. A box's
* transformation is described by 4x4 matrix that represents the box's location, rotation, and scale. A sphere can be non-uniformally
* scaled along the x, y, or z axis. The box's dimension along the x, y, or z axis is determined by the magnitude of the
* scale along each axis. A scale of one represents a unit box.
*/
UCLASS(ClassGroup = (GraphicsTools), meta = (BlueprintSpawnableComponent))
class GRAPHICSTOOLS_API UGTClippingBoxComponent : public UGTClippingPrimitiveComponent
{
GENERATED_BODY()
public:
UGTClippingBoxComponent();
protected:
//
// UGTSceneComponent interface
/** Accessor to all UGTClippingBoxComponent components within a world writing to the WorldParameterCollection. */
virtual TArray<UGTSceneComponent*>& GetWorldComponents() override;
};

Просмотреть файл

@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#pragma once
#include "CoreMinimal.h"
#include "GTClippingPrimitiveActor.h"
#include "GTClippingConeActor.generated.h"
/**
* Utility actor which automatically adds a UGTClippingConeComponent.
*/
UCLASS(ClassGroup = GraphicsTools)
class GRAPHICSTOOLS_API AGTClippingConeActor : public AGTClippingPrimitiveActor
{
GENERATED_BODY()
public:
AGTClippingConeActor();
};

Просмотреть файл

@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#pragma once
#include "CoreMinimal.h"
#include "GTClippingPrimitiveComponent.h"
#include "GTClippingConeComponent.generated.h"
/**
* Sub class of a ClippingPrimitive that represents an analytic cone used to pass state to materials for per pixel clipping. A cone's
* transformation is described by two points and two radii. The points represent the top and bottom faces of the cone and the two radii
* represent the radius of those two faces. The orientation and height of the cone is described by the vector between the top and bottom
* points direction and magnitude respectively. To change the height, top radius, or bottom radius of the cone adjust the scale of the
* component. The scale along the x-axis is the height, and scale along y-axis is the bottom radius, and the scale along the z-axis is the
* top radius. If the y and z axis scales are the same the cone becomes a capped cylinder.
*/
UCLASS(ClassGroup = (GraphicsTools), meta = (BlueprintSpawnableComponent))
class GRAPHICSTOOLS_API UGTClippingConeComponent : public UGTClippingPrimitiveComponent
{
GENERATED_BODY()
public:
UGTClippingConeComponent();
protected:
//
// UGTSceneComponent interface
/** Accessor to all UGTClippingConeComponent components within a world writing to the WorldParameterCollection. */
virtual TArray<UGTSceneComponent*>& GetWorldComponents() override;
//
// UGTClippingPrimitiveComponent interface
/** Sends the cone's end points and radii into the current parameter collection. */
virtual void UpdateParameterCollectionTransform() override;
/** Cone's only need two FVectors (two points and two radii) to specify their transform. */
virtual int32 GetTransformColumnCount() const override { return 2; }
};

Просмотреть файл

@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#pragma once
#include "CoreMinimal.h"
#include "GTClippingPrimitiveActor.h"
#include "GTClippingPlaneActor.generated.h"
/**
* Utility actor which automatically adds a UGTClippingPlaneComponent.
*/
UCLASS(ClassGroup = GraphicsTools)
class GRAPHICSTOOLS_API AGTClippingPlaneActor : public AGTClippingPrimitiveActor
{
GENERATED_BODY()
public:
AGTClippingPlaneActor();
};

Просмотреть файл

@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#pragma once
#include "CoreMinimal.h"
#include "GTClippingPrimitiveComponent.h"
#include "GTClippingPlaneComponent.generated.h"
/**
* Sub class of a ClippingPrimitive that represents an analytic plane used to pass state to materials for per pixel clipping. A plane's
* transformation is described by a normal and distance from the origin along that normal. The normal is the x-axis of the component. The
* distance is calculated based on the component's world location.
*/
UCLASS(ClassGroup = (GraphicsTools), meta = (BlueprintSpawnableComponent))
class GRAPHICSTOOLS_API UGTClippingPlaneComponent : public UGTClippingPrimitiveComponent
{
GENERATED_BODY()
public:
UGTClippingPlaneComponent();
protected:
//
// UGTSceneComponent interface
/** Accessor to all UGTClippingPlaneComponent components within a world writing to the WorldParameterCollection. */
virtual TArray<UGTSceneComponent*>& GetWorldComponents() override;
//
// UGTClippingPrimitiveComponent interface
/** Sends the plane's normal and distance from the origin into the current parameter collection. */
virtual void UpdateParameterCollectionTransform() override;
/** Plane's only need single FVector to specify their transform. */
virtual int32 GetTransformColumnCount() const override { return 1; }
};

Просмотреть файл

@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "GTClippingPrimitiveActor.generated.h"
class UGTClippingPrimitiveComponent;
/**
* Abstract base class for all Clipping Primitive actor types within Graphics Tools.
*/
UCLASS(Abstract, ClassGroup = GraphicsTools, hideCategories = (Input, Collision, Replication))
class GRAPHICSTOOLS_API AGTClippingPrimitiveActor : public AActor
{
GENERATED_BODY()
public:
AGTClippingPrimitiveActor();
protected:
/** Handle to the clipping primitive component derived Graphics Tools clipping primitive actors instantiate. */
UPROPERTY(Category = "GT Clipping Primitive", VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
class UGTClippingPrimitiveComponent* ClippingPrimitiveComponent;
};

Просмотреть файл

@ -0,0 +1,103 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#pragma once
#include "CoreMinimal.h"
#include "GTSceneComponent.h"
#include "GTClippingPrimitiveComponent.generated.h"
class UMaterialParameterCollection;
UENUM(BlueprintType)
enum class EGTClippingSide : uint8
{
/** Pixels on the interior of the clipping primitive are discarded. */
Inside,
/** Pixels on the exterior of the clipping primitive are discarded. */
Outside
};
/**
* Abstract base class for all ClippingPrimitive actor components within Graphics Tools. Represents an analytic shape used to pass state to
* materials for per pixel clipping. A sphere's transformation is described by 4x4 matrix that represents the sphere's location, rotation,
* and scale. A sphere can be non-uniformally scaled along the x, y, or z axis to form an ellipsoid. The sphere's radii along the x, y, or
* z axis is determined by the magnitude of the scale along each axis. A scale of one represents a unit sphere.
*/
UCLASS(Abstract, ClassGroup = (GraphicsTools), meta = (BlueprintSpawnableComponent))
class GRAPHICSTOOLS_API UGTClippingPrimitiveComponent : public UGTSceneComponent
{
GENERATED_BODY()
public:
UGTClippingPrimitiveComponent();
/** Gets if pixels will be clipped on the inside or outside of the primitive shape. */
UFUNCTION(BlueprintPure, Category = "Clipping Primitive")
EGTClippingSide GetClippingSide() const { return ClippingSide; }
/** Sets if pixels will be clipped on the inside or outside of the primitive shape. */
UFUNCTION(BlueprintSetter, Category = "Clipping Primitive")
void SetClippingSide(EGTClippingSide Side);
/** Gets the material parameter name used for general settings. */
UFUNCTION(BlueprintPure, Category = "Clipping Primitive")
const FName& GetSettingsParameterName() const { return SettingsParameterName; }
/** Sets the material parameter name used for general settings. */
UFUNCTION(BlueprintSetter, Category = "Clipping Primitive")
void SetSettingsParameterName(const FName& Name);
/** Gets the material parameter name array used to represent each column of the primitive's transformation matrix. */
UFUNCTION(BlueprintPure, Category = "Clipping Primitive")
const TArray<FName>& GetTransformColumnParameterNames() const { return TransformColumnParameterNames; }
/** Sets the material parameter name array used to represent each column of the primitive's transformation matrix. */
UFUNCTION(BlueprintSetter, Category = "Clipping Primitive")
void SetTransformColumnParameterNames(const TArray<FName>& Names);
protected:
//
// UObject interface
#if WITH_EDITOR
/** Disables the material parameter name properties when a ParameterCollectionOverride isn't present. */
virtual bool CanEditChange(const FProperty* Property) const override;
//
// USceneComponent interface
/** Ensures projected and attenuation radii remain less than or greater than each other. */
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif // WITH_EDITOR
//
// UGTSceneComponent interface
/** Updates the current parameter collection based on the current primitive. */
virtual void UpdateParameterCollection(bool IsDisabled = false) override;
/** Sends the primitive's transform into the current parameter collection. */
virtual void UpdateParameterCollectionTransform();
/** The number of column's this primitives transform expects. */
virtual int32 GetTransformColumnCount() const { return 4; }
private:
/** Specifies if the primitive discards pixels on the inside or outside of the primitive shape. */
UPROPERTY(EditAnywhere, Category = "Clipping Primitive", BlueprintGetter = "GetClippingside", BlueprintSetter = "SetClippingside")
EGTClippingSide ClippingSide = EGTClippingSide::Inside;
/** Material parameter name used for general primitive settings to pass to a material. */
UPROPERTY(
EditAnywhere, Category = "Clipping Primitive", BlueprintGetter = "GetSettingsParameterName",
BlueprintSetter = "SetSettingsParameterName", AdvancedDisplay)
FName SettingsParameterName;
/** Parameter name array used to represent each column of the primitive's transformation matrix to pass to a material. */
UPROPERTY(
EditAnywhere, Category = "Clipping Primitive", BlueprintGetter = "GetTransformColumnParameterNames",
BlueprintSetter = "SetTransformColumnParameterNames", AdvancedDisplay)
TArray<FName> TransformColumnParameterNames;
};

Просмотреть файл

@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#pragma once
#include "CoreMinimal.h"
#include "GTClippingPrimitiveActor.h"
#include "GTClippingSphereActor.generated.h"
/**
* Utility actor which automatically adds a UGTClippingSphereComponent.
*/
UCLASS(ClassGroup = GraphicsTools)
class GRAPHICSTOOLS_API AGTClippingSphereActor : public AGTClippingPrimitiveActor
{
GENERATED_BODY()
public:
AGTClippingSphereActor();
};

Просмотреть файл

@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#pragma once
#include "CoreMinimal.h"
#include "GTClippingPrimitiveComponent.h"
#include "GTClippingSphereComponent.generated.h"
/**
* Sub class of a ClippingPrimitive that represents an analytic sphere used to pass state to materials for per pixel clipping. A sphere's
* transformation is described by 4x4 matrix that represents the sphere's location, rotation, and scale. A sphere can be non-uniformally
* scaled along the x, y, or z axis to become an ellipsoid. The sphere's dimension along the x, y, or z axis is determined by the magnitude
* of the scale along each axis. A scale of one represents a unit sphere.
*/
UCLASS(ClassGroup = (GraphicsTools), meta = (BlueprintSpawnableComponent))
class GRAPHICSTOOLS_API UGTClippingSphereComponent : public UGTClippingPrimitiveComponent
{
GENERATED_BODY()
public:
UGTClippingSphereComponent();
protected:
//
// UGTSceneComponent interface
/** Accessor to all UGTClippingSphereComponent components within a world writing to the WorldParameterCollection. */
virtual TArray<UGTSceneComponent*>& GetWorldComponents() override;
};

Просмотреть файл

@ -38,42 +38,61 @@ public:
UFUNCTION(BlueprintSetter, Category = "Light")
void SetLightColor(FColor Color);
/** Gets the material parameter name used for the direction and enabled state. */
UFUNCTION(BlueprintPure, Category = "Light")
const FName& GetDirectionEnabledParameterName() const { return DirectionEnabledParameterName; }
/** Sets the material parameter name used for the direction and enabled state. */
UFUNCTION(BlueprintSetter, Category = "Light")
void SetDirectionEnabledParameterName(const FName& Name);
/** Gets the material parameter name used for the color and intensity state. */
UFUNCTION(BlueprintPure, Category = "Light")
const FName& GetColorIntensityParameterName() const { return ColorIntensityParameterName; }
/** Sets the material parameter name used for the color and intensity state. */
UFUNCTION(BlueprintSetter, Category = "Light")
void SetColorIntensityParameterName(const FName& Name);
protected:
//
// UObject interface
#if WITH_EDITOR
/** Disables the material parameter name properties when a ParameterCollectionOverride isn't present. */
virtual bool CanEditChange(const FProperty* Property) const override;
#endif // WITH_EDITOR
//
// UActorComponent interface
/** Adds the DirectionalLight to the global light list. */
virtual void OnRegister() override;
/** Removes the DirectionalLight from the global light list. */
virtual void OnUnregister() override;
/** Adds or removes the DirectionalLight to the global light list based on visibility. */
virtual void OnVisibilityChanged() override;
//
// USceneComponent interface
/** Notifies systems of the DirectionalLight's new direction. */
virtual void OnUpdateTransform(EUpdateTransformFlags UpdateTransformFlags, ETeleportType Teleport = ETeleportType::None) override;
#if WITH_EDITOR
/** Updates the material parameter collection. */
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif // WITH_EDITOR
#if WITH_EDITORONLY_DATA
public:
/** Returns ArrowComponent subobject **/
UArrowComponent* GetArrowComponent() const { return ArrowComponent; }
//
// UGTSceneComponent interface
/** Accessor to all UGTDirectionalLightComponent components within a world writing to the WorldParameterCollection. */
virtual TArray<UGTSceneComponent*>& GetWorldComponents() override;
/** Updates the current parameter collection based on the current UGTDirectionalLightComponent. */
virtual void UpdateParameterCollection(bool IsDisabled = false) override;
private:
#if WITH_EDITORONLY_DATA
// Reference to editor visualization arrow
UPROPERTY()
UArrowComponent* ArrowComponent = nullptr;
#endif // WITH_EDITORONLY_DATA
private:
/** Total energy that the DirectionalLight emits. */
UPROPERTY(
EditAnywhere, BlueprintGetter = "GetLightIntensity", BlueprintSetter = "SetLightIntensity", Category = "Light",
@ -83,4 +102,16 @@ private:
/** The color of the DirectionalLight. */
UPROPERTY(EditAnywhere, BlueprintGetter = "GetLightColor", BlueprintSetter = "SetLightColor", Category = "Light")
FColor LightColor = FColor(255, 255, 255, 255);
/** Material parameter name used for the lights direction and enabled state to pass to a material. */
UPROPERTY(
EditAnywhere, Category = "Light", BlueprintGetter = "GetDirectionEnabledParameterName",
BlueprintSetter = "SetDirectionEnabledParameterName", AdvancedDisplay)
FName DirectionEnabledParameterName;
/** Material parameter name used for the lights color and intensity to pass to a material. */
UPROPERTY(
EditAnywhere, Category = "Light", BlueprintGetter = "GetColorIntensityParameterName",
BlueprintSetter = "SetColorIntensityParameterName", AdvancedDisplay)
FName ColorIntensityParameterName;
};

Просмотреть файл

@ -4,8 +4,7 @@
#pragma once
#include "CoreMinimal.h"
#include "Components/SceneComponent.h"
#include "GTSceneComponent.h"
#include "GTLightComponent.generated.h"
@ -15,43 +14,10 @@ class UMaterialParameterCollection;
* Abstract base class for all light actor components within Graphics Tools.
*/
UCLASS(Abstract, ClassGroup = (GraphicsTools), meta = (BlueprintSpawnableComponent))
class GRAPHICSTOOLS_API UGTLightComponent : public USceneComponent
class GRAPHICSTOOLS_API UGTLightComponent : public UGTSceneComponent
{
GENERATED_BODY()
public:
UGTLightComponent();
/** Accessor to the global Graphics Tools material parameter collection. */
UMaterialParameterCollection* GetParameterCollection();
/** Const accessor to the global Graphics Tools material parameter collection. */
const UMaterialParameterCollection* GetParameterCollection() const;
/** Returns true if the light belongs to a world and has a material parameter collection. */
bool IsValid() const;
protected:
#if WITH_EDITOR
//
// UActorComponent interface
/** Applies editor sprite textures. */
virtual void OnRegister() override;
#endif // WITH_EDITOR
#if WITH_EDITORONLY_DATA
/** Sprite for the light in the editor. */
UPROPERTY(transient)
UTexture2D* EditorTexture;
/** Sprite scaling for the light in the editor. */
UPROPERTY(transient)
float EditorTextureScale;
#endif // WITH_EDITORONLY_DATA
private:
/** The MaterialParameterCollection this light will write to. */
UPROPERTY(Transient)
UMaterialParameterCollection* ParameterCollection = nullptr;
};

Просмотреть файл

@ -85,6 +85,54 @@ public:
UFUNCTION(BlueprintSetter, Category = "Light")
void SetOuterColor(FColor Color);
/** Gets the material parameter name array used to represent each location of a ProximityLight. */
UFUNCTION(BlueprintPure, Category = "Light")
const TArray<FName>& GetLocationParameterNames() const { return LocationParameterNames; }
/** Sets the material parameter name array used to represent each location of a ProximityLight. */
UFUNCTION(BlueprintSetter, Category = "Light")
void SetLocationParameterNames(const TArray<FName>& Names);
/** Gets the material parameter name array used to represent each setting of a ProximityLight. */
UFUNCTION(BlueprintPure, Category = "Light")
const TArray<FName>& GetSettingsParameterNames() const { return SettingsParameterNames; }
/** Sets the material parameter name array used to represent each setting of a ProximityLight. */
UFUNCTION(BlueprintSetter, Category = "Light")
void SetSettingsParameterNames(const TArray<FName>& Names);
/** Gets the material parameter name array used to represent each pulse setting of a ProximityLight. */
UFUNCTION(BlueprintPure, Category = "Light")
const TArray<FName>& GetPulseSettingsParameterNames() const { return PulseSettingsParameterNames; }
/** Sets the material parameter name array used to represent each pulse setting of a ProximityLight. */
UFUNCTION(BlueprintSetter, Category = "Light")
void SetPulseSettingsParameterNames(const TArray<FName>& Names);
/** Gets the material parameter name array used to represent each center color of a ProximityLight. */
UFUNCTION(BlueprintPure, Category = "Light")
const TArray<FName>& GetCenterColorParameterNames() const { return CenterColorParameterNames; }
/** Sets the material parameter name array used to represent each center color of a ProximityLight. */
UFUNCTION(BlueprintSetter, Category = "Light")
void SetCenterColorParameterNames(const TArray<FName>& Names);
/** Gets the material parameter name array used to represent each middle color of a ProximityLight. */
UFUNCTION(BlueprintPure, Category = "Light")
const TArray<FName>& GetMiddleColorParameterNames() const { return MiddleColorParameterNames; }
/** Sets the material parameter name array used to represent each middle color of a ProximityLight. */
UFUNCTION(BlueprintSetter, Category = "Light")
void SetMiddleColorParameterNames(const TArray<FName>& Names);
/** Gets the material parameter name array used to represent each outer color of a ProximityLight. */
UFUNCTION(BlueprintPure, Category = "Light")
const TArray<FName>& GetOuterColorParameterNames() const { return OuterColorParameterNames; }
/** Sets the material parameter name array used to represent each outer color of a ProximityLight. */
UFUNCTION(BlueprintSetter, Category = "Light")
void SetOuterColorParameterNames(const TArray<FName>& Names);
/** Initiates a pulse, if one is not already occurring, which simulates a user touching a surface. */
UFUNCTION(BlueprintCallable, Category = "Light")
void Pulse(float Duration = 0.2f, float FadeOffset = 0.1f, float FadeDuration = 0.1f);
@ -98,32 +146,37 @@ public:
float GetPulseFadeTime() const;
protected:
//
// UObject interface
#if WITH_EDITOR
/** Disables the material parameter name properties when a ParameterCollectionOverride isn't present. */
virtual bool CanEditChange(const FProperty* Property) const override;
#endif // WITH_EDITOR
//
// UActorComponent interface
/** Conditional tick method which occurs when a light needs to animate. */
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
/** Adds the ProximityLight to the global light list. */
virtual void OnRegister() override;
/** Removes the ProximityLight from the global light list. */
virtual void OnUnregister() override;
/** Adds or removes the ProximityLight to the global light list based on visibility. */
virtual void OnVisibilityChanged() override;
//
// USceneComponent interface
/** Notifies systems of the ProximityLight's new location. */
virtual void OnUpdateTransform(EUpdateTransformFlags UpdateTransformFlags, ETeleportType Teleport = ETeleportType::None) override;
#if WITH_EDITOR
/** Ensures projected and attenuation radii remain less than or greater than each other. */
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif // WITH_EDITOR
//
// UGTSceneComponent interface
/** Accessor to all UGTProximityLightComponent components within a world writing to the WorldParameterCollection. */
virtual TArray<UGTSceneComponent*>& GetWorldComponents() override;
/** Updates the current parameter collection based on the current UGTProximityLightComponent. */
virtual void UpdateParameterCollection(bool IsDisabled = false) override;
private:
EPulseState PulseTick(float DeltaTime);
@ -165,6 +218,42 @@ private:
UPROPERTY(EditAnywhere, BlueprintGetter = "GetOuterColor", BlueprintSetter = "SetOuterColor", Category = "Light")
FColor OuterColor = FColor(114, 55, 191, 255);
/** Parameter name array used to represent each location of a ProximityLight to pass to a material. */
UPROPERTY(
EditAnywhere, Category = "Light", BlueprintGetter = "GetLocationParameterNames", BlueprintSetter = "SetLocationParameterNames",
AdvancedDisplay)
TArray<FName> LocationParameterNames;
/** Parameter name array used to represent each setting of a ProximityLight to pass to a material. */
UPROPERTY(
EditAnywhere, Category = "Light", BlueprintGetter = "GetSettingsParameterNames", BlueprintSetter = "SetSettingsParameterNames",
AdvancedDisplay)
TArray<FName> SettingsParameterNames;
/** Parameter name array used to represent each pulse setting of a ProximityLight to pass to a material. */
UPROPERTY(
EditAnywhere, Category = "Light", BlueprintGetter = "GetPulseSettingsParameterNames",
BlueprintSetter = "SetPulseSettingsParameterNames", AdvancedDisplay)
TArray<FName> PulseSettingsParameterNames;
/** Parameter name array used to represent each center color of a ProximityLight to pass to a material. */
UPROPERTY(
EditAnywhere, Category = "Light", BlueprintGetter = "GetCenterColorParameterNames",
BlueprintSetter = "SetCenterColorParameterNames", AdvancedDisplay)
TArray<FName> CenterColorParameterNames;
/** Parameter name array used to represent each middle color of a ProximityLight to pass to a material. */
UPROPERTY(
EditAnywhere, Category = "Light", BlueprintGetter = "GetMiddleColorParameterNames",
BlueprintSetter = "SetMiddleColorParameterNames", AdvancedDisplay)
TArray<FName> MiddleColorParameterNames;
/** Parameter name array used to represent each outer color of a ProximityLight to pass to a material. */
UPROPERTY(
EditAnywhere, Category = "Light", BlueprintGetter = "GetOuterColorParameterNames", BlueprintSetter = "SetOuterColorParameterNames",
AdvancedDisplay)
TArray<FName> OuterColorParameterNames;
EPulseState PulseState = EPulseState::Idle;
float PulseTimer = 0;
float PulseFadeTimer = 0;

Просмотреть файл

@ -0,0 +1,101 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#pragma once
#include "CoreMinimal.h"
#include "Components/SceneComponent.h"
#include "GTSceneComponent.generated.h"
class UMaterialParameterCollection;
/**
* Abstract base class for all scene actor components within Graphics Tools.
*/
UCLASS(Abstract, ClassGroup = (GraphicsTools), meta = (BlueprintSpawnableComponent))
class GRAPHICSTOOLS_API UGTSceneComponent : public USceneComponent
{
GENERATED_BODY()
public:
UGTSceneComponent();
/** Specifies the current WorldParameterCollection override, if null is passed in the component will start writing to the
* WorldParameterCollection. */
UFUNCTION(BlueprintSetter, Category = "GT Scene Component")
void SetParameterCollectionOverride(UMaterialParameterCollection* Override);
/** Returns true if the WorldParameterCollection is currently being overridden. */
UFUNCTION(BlueprintPure, Category = "GT Scene Component")
bool HasParameterCollectionOverride() const { return ParameterCollectionOverride != nullptr; }
protected:
//
// UActorComponent interface
/** Adds the component to the world and applies editor sprite textures. */
virtual void OnRegister() override;
/** Removes the component from the world list. */
virtual void OnUnregister() override;
/** Adds or removes the component to the world list based on visibility. */
virtual void OnVisibilityChanged() override;
//
// USceneComponent interface
/** Updates the parameter collection when the transform changes. */
virtual void OnUpdateTransform(EUpdateTransformFlags UpdateTransformFlags, ETeleportType Teleport = ETeleportType::None) override;
#if WITH_EDITOR
/** Updates the material parameter collection. */
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif // WITH_EDITOR
/** Returns true if the scene belongs to a world and has a material parameter collection. */
virtual bool IsValid() const;
/** Const accessor to the current material parameter collection in use. */
const UMaterialParameterCollection* GetParameterCollection() const;
/** Sets a vector value on the current material parameter collection, returns true if successful. */
bool SetVectorParameterValue(FName ParameterName, const FLinearColor& ParameterValue);
/** Adds this component to a list which will be processed when writing to the WorldParameterCollection. */
void AddToWorldParameterCollection();
/** Removes this component from a list which will be processed when writing to the WorldParameterCollection. */
void RemoveFromWorldParameterCollection();
/** Pure virtual accessor to all components of a specific type within a world writing to the WorldParameterCollection. */
virtual TArray<UGTSceneComponent*>& GetWorldComponents() PURE_VIRTUAL(UGTSceneComponent::UpdateParameterCollection, return Empty;);
/** Pure virtual method that updates the parameter collection based on the current type. */
virtual void UpdateParameterCollection(bool IsDisabled = false) PURE_VIRTUAL(UGTSceneComponent::UpdateParameterCollection, );
#if WITH_EDITORONLY_DATA
/** Sprite for the scene in the editor. */
UPROPERTY(transient)
UTexture2D* EditorTexture;
/** Sprite scaling for the scene in the editor. */
UPROPERTY(transient)
float EditorTextureScale;
#endif // WITH_EDITORONLY_DATA
private:
/** An override that removes the component from the WorldParameterCollection and allows the user to control what
* MaterialParameterCollection gets written to. */
UPROPERTY(EditAnywhere, Category = "GT Scene Component", BlueprintSetter = "SetParameterCollectionOverride", AdvancedDisplay)
UMaterialParameterCollection* ParameterCollectionOverride = nullptr;
/** The default MaterialParameterCollection all components within the current world will write to. */
UPROPERTY(Transient)
UMaterialParameterCollection* WorldParameterCollection = nullptr;
/** Empty component list which is used by the pure virtual GetWorldComponents method. */
static TArray<UGTSceneComponent*> Empty;
};

Просмотреть файл

@ -0,0 +1,168 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "GTVisualProfiler.generated.h"
class UStaticMeshComponent;
class UTextRenderComponent;
/**
* The VisualProfiler provides a drop in, single actor class, solution for viewing your Windows Mixed Reality Unreal application's frame,
* game, render, and GPU time. Missed frames are displayed as red text and bar graphs to find problem areas. Draw calls and primitive counts
* (polygons/triangles) are reported as well.
*/
UCLASS(ClassGroup = GraphicsTools)
class GRAPHICSTOOLS_API AGTVisualProfiler : public AActor
{
GENERATED_BODY()
public:
AGTVisualProfiler();
/** Getter to the FollowSpeed. */
UFUNCTION(BlueprintPure, Category = "Visual Profiler")
float GetFollowSpeed() const { return FollowSpeed; }
/** Setter to the FollowSpeed. */
UFUNCTION(BlueprintSetter, Category = "Visual Profiler")
void SetFollowSpeed(float Speed) { FollowSpeed = Speed; }
/** Getter to the FollowOffset. */
UFUNCTION(BlueprintPure, Category = "Visual Profiler")
FVector GetFollowOffset() const { return FollowOffset; }
/** Setter to the FollowOffset. */
UFUNCTION(BlueprintSetter, Category = "Visual Profiler")
void SetFollowOffset(FVector Offset) { FollowOffset = Offset; }
/** Getter to the PitchOffset. */
UFUNCTION(BlueprintPure, Category = "Visual Profiler")
float GetPitchOffset() const { return PitchOffset; }
/** Setter to the PitchOffset. */
UFUNCTION(BlueprintSetter, Category = "Visual Profiler")
void SetPitchOffset(float Offset) { PitchOffset = Offset; }
private:
//
// AActor interface
/** Updates frame timings and solves the profiler towards the camera. */
virtual void Tick(float DeltaTime) override;
/** Moves the profiler actor towards the first player controller's camera. */
void SolveToCamera(float DeltaTime);
/** Utility method to allocate and add a scene component to the profiler. */
template <class T>
T* CreateScene(FName Name, USceneComponent* Parent, FVector RelativeLocation, FRotator RelativeRotation)
{
T* Component = CreateDefaultSubobject<T>(Name);
Component->SetupAttachment(Parent);
Component->SetRelativeLocation(RelativeLocation);
Component->SetRelativeRotation(RelativeRotation);
return Component;
}
/** Creates a child actor component static mesh of a quad (plane). */
UStaticMeshComponent* CreateQuad(
const FName& Name, USceneComponent* Parent, FVector RelativeLocation, FVector RelativeScale, int32 SortPriority,
FLinearColor Color = FLinearColor::White, FRotator RelativeRotation = FRotator(90, 0, 0));
/** Creates a child actor component text renderer. */
UTextRenderComponent* CreateText(
const FName& Name, USceneComponent* Parent, FVector RelativeLocation, const FString& Text, int32 SortPriority,
FRotator RelativeRotation = FRotator(0, 180, 0), float Size = 0.5f, bool LeftAlign = true);
/** Applies the current time to the text label and bar graph. */
void ApplyTiming(float Time, int32& PrevTime, UTextRenderComponent* Label, USceneComponent* Pivot);
/** Converts a frame time to a scale used by the frame time bars. */
float TimeToScale(float Time) const;
/** Converts a frame time to a color used by the frame time labels. */
FColor TimeToTextColor(float Time) const;
/** Returns true if the time value presented to the user will change. */
static bool CheckTimeDirty(float Time, int32& PrevTime);
/** Returns true if the count value presented to the user will change. */
static bool CheckCountDirty(int32 Count, int32& PrevCount);
/** Enumerates the RHI for available refresh rates and picks the first valid one. */
static float QueryThresholdFrameTime();
/** How quickly to interpolate the profiler towards its target location and rotation. */
UPROPERTY(
EditAnywhere, Category = "Visual Profiler", BlueprintGetter = "GetFollowSpeed", BlueprintSetter = "SetFollowSpeed",
meta = (ClampMin = "0.0", ClampMax = "100.0", UIMin = "0.0", UIMax = "100.0"))
float FollowSpeed = 5.0f;
/** The offset from the target location in camera local space. */
UPROPERTY(EditAnywhere, Category = "Visual Profiler", BlueprintGetter = "GetFollowOffset", BlueprintSetter = "SetFollowOffset")
FVector FollowOffset = FVector(30.0f, 0.0f, -3.0f);
/** Extra pitch applied on top of the target rotation (in degrees). */
UPROPERTY(
EditAnywhere, Category = "Visual Profiler", BlueprintGetter = "GetPitchOffset", BlueprintSetter = "SetPitchOffset",
meta = (ClampMin = "0.0", ClampMax = "360.0", UIMin = "0.0", UIMax = "360.0"))
float PitchOffset = 18.0f;
/** Assets used to construct the profiler. */
UPROPERTY(Transient)
UStaticMesh* DefaultQuadMesh = nullptr;
UPROPERTY(Transient)
UMaterial* DefaultMaterial = nullptr;
UPROPERTY(Transient)
UMaterial* DefaultTextMaterial = nullptr;
/** Profiler components. */
UPROPERTY(Transient)
UTextRenderComponent* FrameTimeLabel = nullptr;
UPROPERTY(Transient)
USceneComponent* FrameTimePivot = nullptr;
UPROPERTY(Transient)
UTextRenderComponent* GameThreadTimeLabel = nullptr;
UPROPERTY(Transient)
USceneComponent* GameThreadTimePivot = nullptr;
UPROPERTY(Transient)
UTextRenderComponent* RenderThreadTimeLabel = nullptr;
UPROPERTY(Transient)
USceneComponent* RenderThreadTimePivot = nullptr;
UPROPERTY(Transient)
UTextRenderComponent* GPUTimeLabel = nullptr;
UPROPERTY(Transient)
USceneComponent* GPUTimePivot = nullptr;
UPROPERTY(Transient)
UTextRenderComponent* DrawCallsLabel = nullptr;
UPROPERTY(Transient)
UTextRenderComponent* PrimitivesLabel = nullptr;
/** Has the solver snapped to the camera yet. */
bool bSnapped;
/** The time in milliseconds before a frame cannot be completed in time to match the vsync. */
float ThresholdFrameTime;
/** Unit frame times filtered with a simple running average. */
float FrameTime;
float RenderThreadTime;
float GameThreadTime;
float GPUFrameTime;
/** Cache of above frame timings after formatting. Used to avoid unnecessary updates. */
int32 PrevFrameTime;
int32 PrevRenderThreadTime;
int32 PrevGameThreadTime;
int32 PrevGPUFrameTime;
/** Cache of other stats. Used to avoid unnecessary updates. */
int32 PrevNumDrawCalls;
int32 PrevNumPrimitives;
};

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше