Singularity/Library/PackageCache/com.unity.render-pipelines..../Documentation~/writing-shaders-urp-reconst...

257 lines
11 KiB
Markdown
Raw Permalink Normal View History

2024-05-06 14:45:45 -04:00
# Reconstruct the world space positions of pixels from the depth texture
The Unity shader in this example reconstructs the world space positions for pixels using a depth texture and screen space UV coordinates. The shader draws a checkerboard pattern on a mesh to visualize the positions.
The following illustration shows the end result:
![Checkerboard pattern visualizing the reconstructed world space positions.](Images/shader-examples/urp-shader-tutorial-reconstruct-world-positions-from-depth.png)
This page contains the following sections:
* [Create the sample scene](#create-the-sample-scene)
* [Edit the ShaderLab code](#edit-the-shaderlab-code)
* [The complete ShaderLab code](#the-complete-shaderlab-code)
## Create the sample scene
Create the sample scene to follow the steps in this section:
1. Install URP into an existing Unity project, or create a new project using the [__Universal Project Template__](creating-a-new-project-with-urp.md).
2. In the sample Scene, create a plane GameObject and place it so that it occludes some of the GameObjects.
![Create a plane](Images/shader-examples/urp-shader-tutorial-create-place-gameobj.png)
3. Create a new Material and assign it to the plane.
4. Create a new shader and assign it to the material. Copy and paste the Unity shader source code from the page [URP unlit basic shader](writing-shaders-urp-basic-unlit-structure.md).
5. Select the URP Asset. If you created the project using the Universal Render Pipeline template, the URP Asset path is `Assets/Settings/UniversalRP-HighQuality`.
6. In the URP Asset, in the General section, enable `Depth Texture`.
![In URP Asset, enable Depth Texture](Images/shader-examples/urp-asset-depth-texture.png)
7. Open the shader you created on step 4.
## Edit the ShaderLab code
This section assumes that you copied the source code from the page [URP unlit basic shader](writing-shaders-urp-basic-unlit-structure.md).
Make the following changes to the ShaderLab code:
1. In the `HLSLPROGRAM` block, add the include declaration for the depth texture shader header. For example, place it under the existing include declaration for `Core.hlsl`.
```c++
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
// The DeclareDepthTexture.hlsl file contains utilities for sampling the Camera
// depth texture.
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
```
The `DeclareDepthTexture.hlsl` file contains functions for sampling the Camera depth texture. This example uses the `SampleSceneDepth` function for sampling the Z coordinate for pixels.
2. In the fragment shader definition, add `Varyings IN` as input.
```c++
half4 frag(Varyings IN) : SV_Target
```
In this example, the fragment shader uses the `positionHCS` property from the `Varyings` struct to get locations of pixels.
3. In the fragment shader, to calculate the UV coordinates for sampling the depth buffer, divide the pixel location by the render target resolution `_ScaledScreenParams`. The property `_ScaledScreenParams.xy` takes into account any scaling of the render target, such as Dynamic Resolution.
```c++
float2 UV = IN.positionHCS.xy / _ScaledScreenParams.xy;
```
4. In the fragment shader, use the `SampleSceneDepth` functions to sample the depth buffer.
```c++
#if UNITY_REVERSED_Z
real depth = SampleSceneDepth(UV);
#else
// Adjust z to match NDC for OpenGL
real depth = lerp(UNITY_NEAR_CLIP_VALUE, 1, SampleSceneDepth(UV));
#endif
```
The `SampleSceneDepth` function comes from the `DeclareDepthTexture.hlsl` file. It returns the Z value in the range `[0, 1]`.
For the reconstruction function (`ComputeWorldSpacePosition`) to work, the depth value must be in the normalized device coordinate (NDC) space. In D3D, Z is in range `[0,1]`, in OpenGL, Z is in range `[-1, 1]`.
This example uses the `UNITY_REVERSED_Z` constant to determine the platform and adjust the Z value range. See step 6 in this example for more explanations.
The `UNITY_NEAR_CLIP_VALUE` variable is a platform independent near clipping plane value for the clip space.
For more information, see [Platform-specific rendering differences](https://docs.unity3d.com/Manual/SL-PlatformDifferences.html).
5. Reconstruct world space positions from the UV and Z coordinates of pixels.
```c++
float3 worldPos = ComputeWorldSpacePosition(UV, depth, UNITY_MATRIX_I_VP);
```
`ComputeWorldSpacePosition` is a utility function that calculates the world space position from the UV and the depth (Z) values. This function is defined in the `Common.hlsl` file of the SRP Core package.
`UNITY_MATRIX_I_VP` is an inverse view projection matrix which transforms points from the clip space to the world space.
6. To visualize the world space positions of pixels, create the checkboard effect.
```c++
uint scale = 10;
uint3 worldIntPos = uint3(abs(worldPos.xyz * scale));
bool white = (worldIntPos.x & 1) ^ (worldIntPos.y & 1) ^ (worldIntPos.z & 1);
half4 color = white ? half4(1,1,1,1) : half4(0,0,0,1);
```
The `scale` is the inverse scale of the checkboard pattern size.
The `abs` function mirrors the pattern to the negative coordinate side.
The `uint3` declaration for the `worldIntPos` variable snaps the coordinate positions to integers.
The `AND` operator in the expresion `<integer value> & 1` checks if the value is even (0) or odd (1). The expression lets the code divide the surface into squares.
The `XOR` operator in the expresion `<integer value> ^ <integer value>` flips the square color.
The depth buffer might not have valid values for areas where no geometry is rendered. The following code draws black color in such areas.
```c++
#if UNITY_REVERSED_Z
if(depth < 0.0001)
return half4(0,0,0,1);
#else
if(depth > 0.9999)
return half4(0,0,0,1);
#endif
```
Different platforms use different Z values for far clipping planes (0 == far, or 1 == far). The `UNITY_REVERSED_Z` constant lets the code handle all platforms correctly.
Save the shader code, the example is ready.
The following illustration shows the end result:
![3D Checkerboard](Images/shader-examples/urp-shader-tutorial-reconstruct-world-positions-from-depth.png)
## The complete ShaderLab code
Below is the complete ShaderLab code for this example.
```c++
// This Unity shader reconstructs the world space positions for pixels using a depth
// texture and screen space UV coordinates. The shader draws a checkerboard pattern
// on a mesh to visualize the positions.
Shader "Example/URPReconstructWorldPos"
{
Properties
{ }
// The SubShader block containing the Shader code.
SubShader
{
// SubShader Tags define when and under which conditions a SubShader block or
// a pass is executed.
Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
Pass
{
HLSLPROGRAM
// This line defines the name of the vertex shader.
#pragma vertex vert
// This line defines the name of the fragment shader.
#pragma fragment frag
// The Core.hlsl file contains definitions of frequently used HLSL
// macros and functions, and also contains #include references to other
// HLSL files (for example, Common.hlsl, SpaceTransforms.hlsl, etc.).
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
// The DeclareDepthTexture.hlsl file contains utilities for sampling the
// Camera depth texture.
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
// This example uses the Attributes structure as an input structure in
// the vertex shader.
struct Attributes
{
// The positionOS variable contains the vertex positions in object
// space.
float4 positionOS : POSITION;
};
struct Varyings
{
// The positions in this struct must have the SV_POSITION semantic.
float4 positionHCS : SV_POSITION;
};
// The vertex shader definition with properties defined in the Varyings
// structure. The type of the vert function must match the type (struct)
// that it returns.
Varyings vert(Attributes IN)
{
// Declaring the output object (OUT) with the Varyings struct.
Varyings OUT;
// The TransformObjectToHClip function transforms vertex positions
// from object space to homogenous clip space.
OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
// Returning the output.
return OUT;
}
// The fragment shader definition.
// The Varyings input structure contains interpolated values from the
// vertex shader. The fragment shader uses the `positionHCS` property
// from the `Varyings` struct to get locations of pixels.
half4 frag(Varyings IN) : SV_Target
{
// To calculate the UV coordinates for sampling the depth buffer,
// divide the pixel location by the render target resolution
// _ScaledScreenParams.
float2 UV = IN.positionHCS.xy / _ScaledScreenParams.xy;
// Sample the depth from the Camera depth texture.
#if UNITY_REVERSED_Z
real depth = SampleSceneDepth(UV);
#else
// Adjust Z to match NDC for OpenGL ([-1, 1])
real depth = lerp(UNITY_NEAR_CLIP_VALUE, 1, SampleSceneDepth(UV));
#endif
// Reconstruct the world space positions.
float3 worldPos = ComputeWorldSpacePosition(UV, depth, UNITY_MATRIX_I_VP);
// The following part creates the checkerboard effect.
// Scale is the inverse size of the squares.
uint scale = 10;
// Scale, mirror and snap the coordinates.
uint3 worldIntPos = uint3(abs(worldPos.xyz * scale));
// Divide the surface into squares. Calculate the color ID value.
bool white = ((worldIntPos.x) & 1) ^ (worldIntPos.y & 1) ^ (worldIntPos.z & 1);
// Color the square based on the ID value (black or white).
half4 color = white ? half4(1,1,1,1) : half4(0,0,0,1);
// Set the color to black in the proximity to the far clipping
// plane.
#if UNITY_REVERSED_Z
// Case for platforms with REVERSED_Z, such as D3D.
if(depth < 0.0001)
return half4(0,0,0,1);
#else
// Case for platforms without REVERSED_Z, such as OpenGL.
if(depth > 0.9999)
return half4(0,0,0,1);
#endif
return color;
}
ENDHLSL
}
}
}
```