Skip to content

Commit 4bd942f

Browse files
authored
Merge pull request #211 from Unity-Technologies/Branch_ExportSky
Added a way to export the current sky and to override the sky lighting with a cubemap.
2 parents 483c046 + bb0d2c2 commit 4bd942f

14 files changed

Lines changed: 298 additions & 70 deletions

Assets/ScriptableRenderPipeline/HDRenderPipeline/Editor/HDRenderPipelineMenuItems.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using UnityEditor;
33
using UnityEngine.SceneManagement;
44
using UnityEngine.Experimental.Rendering.HDPipeline;
5+
using System.IO;
56

67
namespace UnityEditor.Experimental.Rendering.HDPipeline
78
{
@@ -109,5 +110,34 @@ static void RemoveTessellationMaterials()
109110
}
110111
}
111112
}
113+
114+
[MenuItem("HDRenderPipeline/Export Sky to Image")]
115+
static void ExportSkyToImage()
116+
{
117+
HDRenderPipelineInstance renderpipelineInstance = UnityEngine.Experimental.Rendering.RenderPipelineManager.currentPipeline as HDRenderPipelineInstance;
118+
if(renderpipelineInstance == null)
119+
{
120+
Debug.LogError("HDRenderPipeline is not instantiated.");
121+
return;
122+
}
123+
124+
Texture2D result = renderpipelineInstance.ExportSkyToTexture();
125+
if(result == null)
126+
{
127+
return;
128+
}
129+
130+
// Encode texture into PNG
131+
byte[] bytes = null;
132+
bytes = result.EncodeToEXR(Texture2D.EXRFlags.CompressZIP);
133+
Object.DestroyImmediate(result);
134+
135+
string assetPath = EditorUtility.SaveFilePanel("Export Sky", "Assets", "SkyExport", "exr");
136+
if (!string.IsNullOrEmpty(assetPath))
137+
{
138+
File.WriteAllBytes(assetPath, bytes);
139+
AssetDatabase.Refresh();
140+
}
141+
}
112142
}
113143
}

Assets/ScriptableRenderPipeline/HDRenderPipeline/HDRenderPipeline.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,11 @@ void RenderSky(HDCamera hdCamera, ScriptableRenderContext renderContext)
839839
m_SkyManager.RenderSky(hdCamera, m_LightLoop == null ? null : m_LightLoop.GetCurrentSunLight(), m_CameraColorBufferRT, m_CameraDepthStencilBufferRT, renderContext);
840840
}
841841

842+
public Texture2D ExportSkyToTexture()
843+
{
844+
return m_SkyManager.ExportSkyToTexture();
845+
}
846+
842847
void RenderForward(CullResults cullResults, Camera camera, ScriptableRenderContext renderContext, bool renderOpaque)
843848
{
844849
// TODO: Currently we can't render opaque object forward when deferred is enabled

Assets/ScriptableRenderPipeline/HDRenderPipeline/Sky/HDRISky/Editor/HDRISkyEditor.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ private class Styles
1919
public readonly GUIContent skyMultiplier = new GUIContent("Multiplier", "Intensity multiplier for the sky.");
2020
public readonly GUIContent environmentUpdateMode = new GUIContent("Environment Update Mode", "Specify how the environment lighting should be updated.");
2121
public readonly GUIContent environmentUpdatePeriod = new GUIContent("Environment Update Period", "If environment update is set to realtime, period in seconds at which it is updated (0.0 means every frame).");
22+
public readonly GUIContent lightingOverride = new GUIContent("Lighting Override", "If a lighting override cubemap is provided, this cubemap will be used to compute lighting instead of the result from the visible sky.");
2223
}
2324

2425
private static Styles s_Styles = null;
@@ -39,6 +40,7 @@ private static Styles styles
3940
private SerializedProperty m_SkyRotation;
4041
private SerializedProperty m_EnvUpdateMode;
4142
private SerializedProperty m_EnvUpdatePeriod;
43+
private SerializedProperty m_LightingOverride;
4244

4345
void OnEnable()
4446
{
@@ -49,6 +51,7 @@ void OnEnable()
4951
m_SkyRotation = serializedObject.FindProperty("rotation");
5052
m_EnvUpdateMode = serializedObject.FindProperty("updateMode");
5153
m_EnvUpdatePeriod = serializedObject.FindProperty("updatePeriod");
54+
m_LightingOverride = serializedObject.FindProperty("lightingOverride");
5255
}
5356

5457
public override void OnInspectorGUI()
@@ -66,6 +69,7 @@ public override void OnInspectorGUI()
6669
{
6770
EditorGUILayout.PropertyField(m_EnvUpdatePeriod, styles.environmentUpdatePeriod);
6871
}
72+
EditorGUILayout.PropertyField(m_LightingOverride, styles.lightingOverride);
6973

7074
serializedObject.ApplyModifiedProperties();
7175
}

Assets/ScriptableRenderPipeline/HDRenderPipeline/Sky/SkyManager.cs

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public class SkyManager
5151
RenderTexture m_SkyboxConditionalCdfRT = null;
5252

5353
Material m_StandardSkyboxMaterial = null; // This is the Unity standard skybox material. Used to pass the correct cubemap to Enlighten.
54+
Material m_BlitCubemapMaterial = null;
5455

5556
IBLFilterGGX m_iblFilterGgx = null;
5657

@@ -275,7 +276,9 @@ public void Build()
275276
m_iblFilterGgx = new IBLFilterGGX();
276277

277278
// TODO: We need to have an API to send our sky information to Enlighten. For now use a workaround through skybox/cubemap material...
278-
m_StandardSkyboxMaterial = Utilities.CreateEngineMaterial("Skybox/Cubemap");
279+
m_StandardSkyboxMaterial = Utilities.CreateEngineMaterial("Skybox/Cubemap");
280+
281+
m_BlitCubemapMaterial = Utilities.CreateEngineMaterial("Hidden/BlitCubemap");
279282

280283
m_CurrentUpdateTime = 0.0f;
281284
}
@@ -312,6 +315,24 @@ private void RenderSkyToCubemap(BuiltinSkyParameters builtinParams, SkySettings
312315
}
313316
}
314317

318+
private void BlitCubemap(ScriptableRenderContext renderContext, Cubemap source, RenderTexture dest)
319+
{
320+
321+
MaterialPropertyBlock propertyBlock = new MaterialPropertyBlock();
322+
323+
for (int i = 0; i < 6; ++i)
324+
{
325+
Utilities.SetRenderTarget(renderContext, dest, ClearFlag.ClearNone, 0, (CubemapFace)i);
326+
var cmd = new CommandBuffer { name = "" };
327+
propertyBlock.SetTexture("_MainTex", source);
328+
propertyBlock.SetFloat("_faceIndex", (float)i);
329+
cmd.DrawProcedural(Matrix4x4.identity, m_BlitCubemapMaterial, 0, MeshTopology.Triangles, 3, 1, propertyBlock);
330+
renderContext.ExecuteCommandBuffer(cmd);
331+
cmd.Dispose();
332+
}
333+
334+
}
335+
315336
private void RenderCubemapGGXConvolution(ScriptableRenderContext renderContext, BuiltinSkyParameters builtinParams, SkySettings skyParams, Texture input, RenderTexture target)
316337
{
317338
using (new Utilities.ProfilingSample("Sky Pass: GGX Convolution", renderContext))
@@ -387,8 +408,12 @@ public void UpdateEnvironment(HDCamera camera, Light sunLight, ScriptableRenderC
387408
)
388409
{
389410
// Render sky into a cubemap - doesn't happen every frame, can be controlled
390-
RenderSkyToCubemap(m_BuiltinParameters, skySettings, m_SkyboxCubemapRT);
391411
// Note that m_SkyboxCubemapRT is created with auto-generate mipmap, it mean that here we have also our mipmap correctly box filtered for importance sampling.
412+
if(m_SkySettings.lightingOverride == null)
413+
RenderSkyToCubemap(m_BuiltinParameters, skySettings, m_SkyboxCubemapRT);
414+
// In case the user overrides the lighting, we already have a cubemap ready but we need to blit it anyway for potential resize and so that we can generate proper mipmaps for enlighten.
415+
else
416+
BlitCubemap(renderContext, m_SkySettings.lightingOverride, m_SkyboxCubemapRT);
392417

393418
// Convolve downsampled cubemap
394419
RenderCubemapGGXConvolution(renderContext, m_BuiltinParameters, skySettings, m_SkyboxCubemapRT, m_SkyboxGGXCubemapRT);
@@ -434,5 +459,60 @@ public void RenderSky(HDCamera camera, Light sunLight, RenderTargetIdentifier co
434459
}
435460
}
436461
}
462+
463+
public Texture2D ExportSkyToTexture()
464+
{
465+
if(m_Renderer == null)
466+
{
467+
Debug.LogError("Cannot export sky to a texture, no SkyRenderer is setup.");
468+
return null;
469+
}
470+
471+
if(m_SkySettings == null)
472+
{
473+
Debug.LogError("Cannot export sky to a texture, no Sky settings are setup.");
474+
return null;
475+
}
476+
477+
int resolution = (int)m_SkySettings.resolution;
478+
479+
RenderTexture tempRT = new RenderTexture(resolution * 6, resolution, 0, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Linear);
480+
tempRT.dimension = TextureDimension.Tex2D;
481+
tempRT.useMipMap = false;
482+
tempRT.autoGenerateMips = false;
483+
tempRT.filterMode = FilterMode.Trilinear;
484+
tempRT.Create();
485+
486+
Texture2D temp = new Texture2D(resolution * 6, resolution, TextureFormat.RGBAFloat, false);
487+
Texture2D result = new Texture2D(resolution * 6, resolution, TextureFormat.RGBAFloat, false);
488+
489+
// Note: We need to invert in Y the cubemap faces because the current sky cubemap is inverted (because it's a RT)
490+
// So to invert it again so that it's a proper cubemap image we need to do it in several steps because ReadPixels does not have scale parameters:
491+
// - Convert the cubemap into a 2D texture
492+
// - Blit and invert it to a temporary target.
493+
// - Read this target again into the result texture.
494+
int offset = 0;
495+
for (int i = 0; i < 6; ++i)
496+
{
497+
Graphics.SetRenderTarget(m_SkyboxCubemapRT, 0, (CubemapFace)i);
498+
temp.ReadPixels(new Rect(0, 0, resolution, resolution), offset, 0);
499+
temp.Apply();
500+
offset += resolution;
501+
}
502+
503+
// Flip texture.
504+
// Temporarily disabled until proper API reaches trunk
505+
//Graphics.Blit(temp, tempRT, new Vector2(1.0f, -1.0f), new Vector2(0.0f, 0.0f));
506+
Graphics.Blit(temp, tempRT);
507+
508+
result.ReadPixels(new Rect(0, 0, resolution * 6, resolution), 0, 0);
509+
result.Apply();
510+
511+
Graphics.SetRenderTarget(null);
512+
Object.DestroyImmediate(temp);
513+
Object.DestroyImmediate(tempRT);
514+
515+
return result;
516+
}
437517
}
438518
}

Assets/ScriptableRenderPipeline/HDRenderPipeline/Sky/SkySettings.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ protected class Unhashed : System.Attribute {}
1414
public SkyResolution resolution = SkyResolution.SkyResolution256;
1515
public EnvironementUpdateMode updateMode = EnvironementUpdateMode.OnChanged;
1616
public float updatePeriod = 0.0f;
17+
public Cubemap lightingOverride = null;
1718

1819
private FieldInfo[] m_Properties;
1920

Assets/ScriptableRenderPipeline/HDRenderPipeline/Utilities.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ public static HDRenderPipeline GetHDRenderPipeline()
332332
HDRenderPipeline renderContext = GraphicsSettings.renderPipelineAsset as HDRenderPipeline;
333333
if (renderContext == null)
334334
{
335-
Debug.LogWarning("SkyParameters component can only be used with HDRenderPipeline custom RenderPipeline.");
335+
Debug.LogWarning("HDRenderPipeline is not instantiated.");
336336
return null;
337337
}
338338

Assets/ScriptableRenderPipeline/common/Resources.meta

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Note: This shader is supposed to be removed at some point when Graphics.ConvertTexture can take a RenderTexture as a destination (it's only used by sky manager for now).
2+
Shader "Hidden/BlitCubemap" {
3+
SubShader {
4+
// Cubemap blit. Takes a face index.
5+
Pass {
6+
ZTest Always Cull Off ZWrite Off
7+
8+
HLSLPROGRAM
9+
#pragma vertex vert
10+
#pragma fragment frag
11+
#pragma target 4.5
12+
13+
#include "../../ShaderLibrary/Common.hlsl"
14+
15+
TEXTURECUBE(_MainTex);
16+
SAMPLERCUBE(sampler_MainTex);
17+
float _faceIndex;
18+
19+
struct appdata_t {
20+
uint vertexID : SV_VertexID;
21+
};
22+
23+
struct v2f {
24+
float4 vertex : SV_POSITION;
25+
float3 texcoord : TEXCOORD0;
26+
};
27+
28+
static const float3 faceU[6] = { float3(0, 0, -1), float3(0, 0, 1), float3(1, 0, 0), float3(1, 0, 0), float3(1, 0, 0), float3(-1, 0, 0) };
29+
static const float3 faceV[6] = { float3(0, -1, 0), float3(0, -1, 0), float3(0, 0, 1), float3(0, 0, -1), float3(0, -1, 0), float3(0, -1, 0) };
30+
31+
v2f vert (appdata_t v)
32+
{
33+
v2f o;
34+
35+
o.vertex = GetFullScreenTriangleVertexPosition(v.vertexID);
36+
float2 uv = GetFullScreenTriangleTexcoord(v.vertexID) * 2.0 - 1.0;
37+
38+
int idx = (int)_faceIndex;
39+
float3 transformU = faceU[idx];
40+
float3 transformV = faceV[idx];
41+
42+
float3 n = cross(transformV, transformU);
43+
o.texcoord = n + uv.x * transformU + uv.y * transformV;
44+
return o;
45+
}
46+
47+
float4 frag (v2f i) : SV_Target
48+
{
49+
return SAMPLE_TEXTURECUBE(_MainTex, sampler_MainTex, i.texcoord);
50+
}
51+
ENDHLSL
52+
53+
}
54+
}
55+
Fallback Off
56+
}

Assets/ScriptableRenderPipeline/common/Resources/BlitCubemap.shader.meta

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)