summaryrefslogtreecommitdiff
path: root/Runtime/Camera/RenderLoops/PrePassRenderLoop.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/Camera/RenderLoops/PrePassRenderLoop.cpp')
-rw-r--r--Runtime/Camera/RenderLoops/PrePassRenderLoop.cpp1958
1 files changed, 1958 insertions, 0 deletions
diff --git a/Runtime/Camera/RenderLoops/PrePassRenderLoop.cpp b/Runtime/Camera/RenderLoops/PrePassRenderLoop.cpp
new file mode 100644
index 0000000..8aa3ab6
--- /dev/null
+++ b/Runtime/Camera/RenderLoops/PrePassRenderLoop.cpp
@@ -0,0 +1,1958 @@
+#include "UnityPrefix.h"
+#include "Runtime/GfxDevice/GfxDeviceConfigure.h"
+
+#if GFX_SUPPORTS_RENDERLOOP_PREPASS
+#include "RenderLoopPrivate.h"
+#include "Runtime/Camera/Renderqueue.h"
+#include "Runtime/Camera/BaseRenderer.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Camera/Camera.h"
+#include "Runtime/Camera/ImageFilters.h"
+#include "Runtime/Geometry/Intersection.h"
+#include "External/shaderlab/Library/intshader.h"
+#include "External/shaderlab/Library/properties.h"
+#include "External/shaderlab/Library/shaderlab.h"
+#include "Runtime/Shaders/Shader.h"
+#include "Runtime/Shaders/ShaderNameRegistry.h"
+#include "Runtime/Camera/RenderSettings.h"
+#include "Runtime/Misc/ResourceManager.h"
+#include "Runtime/Shaders/Material.h"
+#include "Runtime/Camera/CameraUtil.h"
+#include "Runtime/Graphics/RenderBufferManager.h"
+#include "Runtime/Graphics/GraphicsHelper.h"
+#include "Runtime/GfxDevice/GfxDevice.h"
+#include "Runtime/Camera/Light.h"
+#include "Runtime/Camera/Shadows.h"
+#include "Runtime/Graphics/LightmapSettings.h"
+#include "External/shaderlab/Library/texenv.h"
+#include "Runtime/Misc/QualitySettings.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Graphics/GeneratedTextures.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/GfxDevice/BatchRendering.h"
+#include "Runtime/Profiler/ExternalGraphicsProfiler.h"
+#include "ReplacementRenderLoop.h"
+#if UNITY_EDITOR
+#include "Runtime/BaseClasses/Tags.h"
+#endif
+#include "BuiltinShaderParamUtility.h"
+#include "Runtime/Math/ColorSpaceConversion.h"
+#include "Runtime/Math/SphericalHarmonics.h"
+#include "Runtime/Camera/LightManager.h"
+#include "External/MurmurHash/MurmurHash2.h"
+#include "Runtime/Filters/Mesh/LodMesh.h"
+#include "Runtime/Graphics/DrawUtil.h"
+
+// Enable/disable hash based pre pass render loop sorting functionality.
+#define ENABLE_PRE_PASS_LOOP_HASH_SORTING 0
+
+#define SEPERATE_PREPASS_SPECULAR UNITY_XENON
+
+PROFILER_INFORMATION(gPrepassSort, "RenderPrePass.Sort", kProfilerRender)
+PROFILER_INFORMATION(gPrepassGeom, "RenderPrePass.GeometryPass", kProfilerRender)
+PROFILER_INFORMATION(gPrepassLighting, "RenderPrePass.Lighting", kProfilerRender)
+PROFILER_INFORMATION(gPrepassLight, "RenderPrePass.Light", kProfilerRender)
+PROFILER_INFORMATION(gPrepassFinal, "RenderPrePass.FinalPass", kProfilerRender)
+PROFILER_INFORMATION(gPrepassFwdDepth, "RenderPrePass.ForwardObjectsToDepth", kProfilerRender)
+PROFILER_INFORMATION(gPrepassCombineDepthNormals, "RenderPrePass.CombineDepthNormals", kProfilerRender)
+
+
+static SHADERPROP (LightPos);
+static SHADERPROP (LightDir);
+static SHADERPROP (LightColor);
+static SHADERPROP (LightTexture0);
+static SHADERPROP (LightBuffer);
+static SHADERPROP (LightAsQuad);
+
+// ShadowMapTexture must be in namespace or otherwise it conflicts with property in
+// ForwardShaderRenderLoop.cpp in batched Android build.
+namespace PrePassPrivate
+{
+static SHADERPROP (ShadowMapTexture);
+}
+
+#if SEPERATE_PREPASS_SPECULAR
+static SHADERPROP (LightSpecBuffer);
+#endif
+
+static Material* s_LightMaterial = NULL;
+static Material* s_CollectMaterial = NULL;
+
+static ShaderKeyword kKeywordHDRLightPrepassOn = keywords::Create ("HDR_LIGHT_PREPASS_ON");
+
+static PPtr<Mesh> s_Icosahedron = NULL;
+static PPtr<Mesh> s_Icosphere = NULL;
+static PPtr<Mesh> s_Pyramid = NULL;
+
+
+enum {
+ kLightingLayerCount = 4, // bits of stencil used for lighting layers
+
+ // 3 highest bits used for excluding lights for other reasons.
+ kStencilMaskSomething = (1<<7), // any object (i.e. not background)
+ kStencilMaskNonLightmapped = (1<<6), // non-lightmapped object
+ kStencilMaskBeyondShadowDistace = (1<<5), // beyond shadow distance
+ kStencilMaskLightBackface = (1<<4), // don't render light where it's backface passes z test
+
+ // Next 4 highest bits (3 down to 0) used for lighting layers.
+ kStencilBitLayerStart = 0, // start of lighting layer bits
+ kStencilMaskLayers = ((1<<kLightingLayerCount)-1) << kStencilBitLayerStart,
+
+ kStencilGeomWriteMask = kStencilMaskSomething | kStencilMaskNonLightmapped | kStencilMaskBeyondShadowDistace | kStencilMaskLayers,
+};
+
+
+
+// Lights can illuminate arbitrary layer masks. Say we have several lights:
+// La = XXXXXXXX
+// Lb = XXXXXXX-
+// Lc = XXXX-XXX
+// Ld = XXXX-X--
+// Layers used for excluding lights are then:
+// ----O-OO (3 in total)
+// In stencil buffer, we allocate 3 consecutive bits to handle this:
+// LaS = ---
+// LbS = --O
+// LcS = O--
+// LdS = OOO
+//
+// When rendering an object, set that bit if object belongs to one of light layers.
+//
+// When drawing a light, set stencil mask to light layer stencil mask, and stencil
+// test should be equal to zero in those bits.
+
+struct LightingLayers
+{
+ enum { kLayerCount = 32 };
+
+ LightingLayers (UInt32 lightMask)
+ : lightingLayerMask(lightMask)
+ {
+ for (int i = 0; i < kLayerCount; ++i)
+ layerToStencil[i] = -1;
+
+ int bit = kStencilBitLayerStart + kLightingLayerCount - 1;
+ lightLayerCount = 0;
+ UInt32 mask = 1;
+ for (int i = 0; i < kLayerCount; ++i, mask<<=1)
+ {
+ if (lightMask & mask)
+ {
+ if (lightLayerCount < kLightingLayerCount)
+ layerToStencil[i] = bit;
+ --bit;
+ ++lightLayerCount;
+ }
+ }
+ }
+
+ UInt32 lightingLayerMask;
+ int layerToStencil[kLayerCount];
+ int lightLayerCount;
+};
+
+struct PrePassRenderData {
+ int roIndex;
+#if ENABLE_PRE_PASS_LOOP_HASH_SORTING
+ UInt32 hash;
+#endif
+};
+typedef dynamic_array<PrePassRenderData> PreRenderPasses;
+
+
+struct PrePassRenderLoop
+{
+ const RenderLoopContext* m_Context;
+ RenderObjectDataContainer* m_Objects;
+
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+ BatchRenderer m_BatchRenderer;
+ #endif
+
+ PreRenderPasses m_PlainRenderPasses;
+
+ RenderTexture* RenderBasePass (RenderTexture* rtMain, const LightingLayers& lightingLayers, RenderObjectDataContainer& outRemainingObjects, MinMaxAABB& receiverBounds);
+
+ void RenderLighting (
+ ActiveLights& activeLights,
+ RenderTexture* rtMain,
+ TextureID depthTextureID,
+ RenderTexture* rtNormalsSpec,
+ RenderTexture*& rtLight,
+
+#if SEPERATE_PREPASS_SPECULAR
+ RenderTexture*& rtLightSpec,
+#endif
+ const Vector4f& lightFade,
+ const LightingLayers& lightingLayers,
+ MinMaxAABB& receiverBounds,
+ RenderTexture** outMainShadowMap);
+
+ void RenderFinalPass (RenderTexture* rtMain,
+ RenderTexture* rtLight,
+#if SEPERATE_PREPASS_SPECULAR
+ RenderTexture* rtLightSpec,
+#endif
+ bool hdr,
+ bool linearLighting);
+
+ struct RenderPrePassObjectSorterHash
+ {
+ bool operator()( const PrePassRenderData& ra, const PrePassRenderData& rb ) const;
+ const PrePassRenderLoop* queue;
+ };
+
+ void SortPreRenderPassData( PreRenderPasses& passes )
+ {
+ RenderPrePassObjectSorterHash sorter;
+ sorter.queue = this;
+ std::sort( passes.begin(), passes.end(), sorter );
+ }
+};
+
+
+struct RenderPrePassObjectSorter {
+ bool operator()( const RenderObjectData& ra, const RenderObjectData& rb ) const;
+};
+
+
+
+
+bool RenderPrePassObjectSorter::operator()( const RenderObjectData& ra, const RenderObjectData& rb ) const
+{
+ // Sort by layering depth.
+ bool globalLayeringResult;
+ if (CompareGlobalLayeringData(ra.globalLayeringData, rb.globalLayeringData, globalLayeringResult))
+ return globalLayeringResult;
+
+ // Sort by render queues first
+ if( ra.queueIndex != rb.queueIndex )
+ return ra.queueIndex < rb.queueIndex;
+
+ // sort by lightmap index
+ if( ra.lightmapIndex != rb.lightmapIndex )
+ return ra.lightmapIndex < rb.lightmapIndex;
+
+#if GFX_ENABLE_DRAW_CALL_BATCHING
+ // if part of predefined static batch, then sort by static batch index
+ if( ra.staticBatchIndex != rb.staticBatchIndex )
+ return ra.staticBatchIndex > rb.staticBatchIndex; // assuming that statically batched geometry occlude more - render it first
+#endif
+
+ // then sort by material (maybe better sort by shader?)
+ if( ra.material != rb.material )
+ return ra.material->GetInstanceID() < rb.material->GetInstanceID(); // just compare instance IDs
+
+ // Sort front to back
+ return ra.distance > rb.distance;
+}
+
+#if ENABLE_PRE_PASS_LOOP_HASH_SORTING
+bool PrePassRenderLoop::RenderPrePassObjectSorterHash::operator()( const PrePassRenderData& ra, const PrePassRenderData& rb ) const
+{
+ const RenderObjectData& dataa = (*queue->m_Objects)[ra.roIndex];
+ const RenderObjectData& datab = (*queue->m_Objects)[rb.roIndex];
+
+ // Sort by layering depth.
+ bool globalLayeringResult;
+ if (CompareGlobalLayeringData(dataa.globalLayeringData, datab.globalLayeringData, globalLayeringResult))
+ return globalLayeringResult;
+
+ // Sort by render queues first
+ if( dataa.queueIndex != datab.queueIndex )
+ return dataa.queueIndex < datab.queueIndex;
+
+ // sort by hash
+ if( ra.hash != rb.hash )
+ return ra.hash < rb.hash;
+
+ // Sort front to back
+ return dataa.distance > datab.distance;
+}
+#endif
+
+static Texture* defaultSpotCookie = NULL;
+
+static void AssignCookieToMaterial(const Light& light, Material* lightMaterial)
+{
+ //@TODO: when computing positions from screen space, mipmapping of cookie will really play against
+ // us, when some adjacent pixels will happen to have very similar UVs. It will sample high levels which
+ // will be mostly black!
+ // Proper fix would be manual derivatives based on something else in the shader, but that needs SM3.0 on D3D
+ // and GLSL in GL. So just use bad mip bias for now.
+
+ Texture* cookie = light.GetCookie();
+
+ if(cookie)
+ {
+ lightMaterial->SetTexture (kSLPropLightTexture0, cookie);
+ }
+ else if(light.GetType() == kLightSpot)
+ {
+ if(!defaultSpotCookie)
+ {
+ defaultSpotCookie = (Texture*)GetRenderSettings().GetDefaultSpotCookie();
+ }
+ lightMaterial->SetTexture (kSLPropLightTexture0, defaultSpotCookie);
+ }
+}
+
+
+// To properly collect & blur directional light's screen space shadow map,
+// we need to have shadow receivers that are forward-rendered in the depth buffer.
+// Also, if camera needs a depth texture, forward-rendered objects should be there
+// as well.
+static void RenderForwardObjectsIntoDepth (
+ const RenderLoopContext& ctx,
+ RenderTexture* rt,
+ RenderObjectDataContainer* forwardRenderedObjects,
+ RenderSurfaceHandle rtColorSurface,
+ RenderSurfaceHandle rtDepthSurface,
+ int width, int height,
+ bool cameraNeedsDepthTexture)
+{
+ Assert (rt);
+
+ if (!forwardRenderedObjects || forwardRenderedObjects->size() == 0)
+ return; // nothing to do
+
+ PROFILER_AUTO_GFX(gPrepassFwdDepth, ctx.m_Camera);
+ GPU_AUTO_SECTION(kGPUSectionOpaquePass);
+
+ Shader* depthShader = GetCameraDepthTextureShader ();
+ if (!depthShader)
+ return;
+
+ // If we do not need the depth texture, leave only the objects that will possibly receive shadows;
+ // no need to render all forward objects.
+ RenderObjectDataContainer forwardRenderedShadowReceivers;
+ if (!cameraNeedsDepthTexture)
+ {
+ size_t n = forwardRenderedObjects->size();
+ forwardRenderedShadowReceivers.reserve (n / 4);
+ for (size_t i = 0; i < n; ++i)
+ {
+ RenderObjectData& roData = (*forwardRenderedObjects)[i];
+ DebugAssert (roData.visibleNode);
+ BaseRenderer* renderer = roData.visibleNode->renderer;
+ DebugAssert (renderer);
+ if (!renderer->GetReceiveShadows())
+ continue; // does not receive shadows
+ Shader* shader = roData.shader;
+ int ss = shader->GetShaderLabShader()->GetDefaultSubshaderIndex (kRenderPathExtForward);
+ if (ss == -1)
+ continue; // is not forward rendered
+ forwardRenderedShadowReceivers.push_back (roData);
+ }
+
+ if (forwardRenderedShadowReceivers.size() == 0)
+ return; // nothing left to render
+ forwardRenderedObjects = &forwardRenderedShadowReceivers;
+ }
+
+ RenderTexture::SetActive (1, &rtColorSurface, rtDepthSurface, rt);
+ RenderSceneShaderReplacement (*forwardRenderedObjects, depthShader, "RenderType");
+}
+
+static RenderTexture* ComputeScreenSpaceShadowMap (
+ const RenderLoopContext& ctx,
+ RenderTexture* shadowMap,
+ float blurWidth,
+ float blurFade,
+ ShadowType shadowType)
+{
+ Assert (shadowMap);
+
+ GfxDevice& device = GetGfxDevice();
+
+ if (!s_CollectMaterial)
+ {
+ Shader* shader = GetScriptMapper().FindShader ("Hidden/Internal-PrePassCollectShadows");
+ s_CollectMaterial = Material::CreateMaterial (*shader, Object::kHideAndDontSave);
+ }
+
+ SetShadowsKeywords (kLightDirectional, shadowType, false, false);
+ RenderBufferManager& rbm = GetRenderBufferManager ();
+
+ RenderTexture* screenShadowMap = rbm.GetTempBuffer (RenderBufferManager::kFullSize, RenderBufferManager::kFullSize, kDepthFormatNone, kRTFormatARGB32, 0, kRTReadWriteLinear);
+ RenderTexture::SetActive (screenShadowMap);
+
+ // Clear so that tiled and multi-GPU systems don't do a RT unresolve
+ float clearColor[4] = {1,0,1,0};
+ device.Clear(kGfxClearColor, clearColor, 1.0f, 0);
+
+ LoadFullScreenOrthoMatrix ();
+ s_CollectMaterial->SetTexture (PrePassPrivate::kSLPropShadowMapTexture, shadowMap);
+ s_CollectMaterial->SetPass (0);
+
+ Vector3f ray;
+ device.ImmediateBegin (kPrimitiveQuads);
+
+ float x1 = 0.0f;
+ float x2 = 1.0f;
+ float y1 = 0.0f;
+ float y2 = 1.0f;
+ float f = ctx.m_Camera->GetProjectionFar();
+
+ const Transform& camtr = ctx.m_Camera->GetComponent(Transform);
+ Matrix4x4f cameraWorldToLocalNoScale = camtr.GetWorldToLocalMatrixNoScale();
+
+ device.ImmediateTexCoord (0, x1, y1, 0.0f);
+ ray = cameraWorldToLocalNoScale.MultiplyPoint3(ctx.m_Camera->ViewportToWorldPoint (Vector3f(x1, y1, f)));
+ device.ImmediateNormal (ray.x, ray.y, ray.z);
+ device.ImmediateVertex (x1, y1, 0.1f);
+
+ device.ImmediateTexCoord (0, x2, y1, 0.0f);
+ ray = cameraWorldToLocalNoScale.MultiplyPoint3(ctx.m_Camera->ViewportToWorldPoint (Vector3f(x2, y1, f)));
+ device.ImmediateNormal (ray.x, ray.y, ray.z);
+ device.ImmediateVertex (x2, y1, 0.1f);
+
+ device.ImmediateTexCoord (0, x2, y2, 0.0f);
+ ray = cameraWorldToLocalNoScale.MultiplyPoint3(ctx.m_Camera->ViewportToWorldPoint (Vector3f(x2, y2, f)));
+ device.ImmediateNormal (ray.x, ray.y, ray.z);
+ device.ImmediateVertex (x2, y2, 0.1f);
+
+ device.ImmediateTexCoord (0, x1, y2, 0.0f);
+ ray = cameraWorldToLocalNoScale.MultiplyPoint3(ctx.m_Camera->ViewportToWorldPoint (Vector3f(x1, y2, f)));
+ device.ImmediateNormal (ray.x, ray.y, ray.z);
+ device.ImmediateVertex (x1, y2, 0.1f);
+
+ device.ImmediateEnd ();
+ GPU_TIMESTAMP();
+
+ rbm.ReleaseTempBuffer (shadowMap);
+
+ // possibly blur into another screen-space render texture
+ SetShadowsKeywords (kLightDirectional, shadowType, true, true);
+ if (IsSoftShadow(shadowType) && GetSoftShadowsEnabled())
+ return BlurScreenShadowMap (screenShadowMap, shadowType, f, blurWidth, blurFade);
+
+ return screenShadowMap;
+}
+
+static void RenderLightGeom (const RenderLoopContext& ctx, const ActiveLight& light, const Vector3f& lightPos, const Matrix4x4f& lightMatrix, const bool renderAsQuad)
+{
+ // Spot and point lights: render as tight geometry. If it doesn't intersect near or far, stencil optimisation will be used
+ // (rendering the z tested back faces into stencil and then front faces will only pass for these pixels).
+ // If it intersects near, back faces with z test greater will be rendered (shouldn't use that when not intersecting near, because
+ // then there could be objects between the cam and the light, not touching the light).
+ // If it intersects far, render front faces without any gimmicks.
+ // If it intersects both near and far, render as a quad.
+
+ GfxDevice& device = GetGfxDevice();
+ Light& l = *light.light;
+ float r = l.GetRange();
+ float n = ctx.m_Camera->GetProjectionNear() * 1.001f;
+
+ if (l.GetType() == kLightPoint && !renderAsQuad)
+ {
+ #if GFX_USE_SPHERE_FOR_POINT_LIGHT
+ ChannelAssigns ch;
+ ch.Bind (kShaderChannelVertex, kVertexCompVertex);
+
+ // Older content might have included/overriden old Internal-PrePassLighting.shader,
+ // which relied on normals being zeros here. Light .fbx files have zero normals just for that.
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1))
+ ch.Bind (kShaderChannelNormal, kVertexCompNormal);
+
+ Matrix4x4f m;
+ m.SetTranslate (lightPos);
+ m.Get (0, 0) = r;
+ m.Get (1, 1) = r;
+ m.Get (2, 2) = r;
+ // Point lights bigger than 0.25 of the screen height can be rendered with high-poly, but tighter geometry.
+ DrawUtil::DrawMesh (ch, light.screenRect.height > 0.25f ? *s_Icosphere : *s_Icosahedron, m, -1);
+ #else
+ // PS3 is not the best at vertex processing, so stick to low-poly meshes
+ device.ImmediateShape(lightPos.x, lightPos.y, lightPos.z, r, GfxDevice::kShapeCube);
+ #endif
+ }
+ else if (l.GetType() == kLightSpot && !renderAsQuad)
+ {
+ Matrix4x4f m (lightMatrix);
+ ChannelAssigns ch;
+ ch.Bind (kShaderChannelVertex, kVertexCompVertex);
+ if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1))
+ ch.Bind (kShaderChannelNormal, kVertexCompNormal);
+ float sideLength = r / l.GetCotanHalfSpotAngle ();
+ m.Scale (Vector3f(sideLength, sideLength, r));
+ DrawUtil::DrawMesh (ch, *s_Pyramid, m, -1);
+ }
+ else // Directional light or spot/point that needs to be rendered as a quad
+ {
+ DeviceViewProjMatricesState preserveViewProj;
+
+ const Camera* camera = ctx.m_Camera;
+ float nearPlane = 0;
+
+ float x1 = light.screenRect.x;
+ float x2 = light.screenRect.x + light.screenRect.width;
+ float y1 = light.screenRect.y;
+ float y2 = light.screenRect.y + light.screenRect.height;
+
+ // Calculate rays pointing from the camera to the near plane's corners in camera space
+ Vector3f ray1 = camera->ViewportToCameraPoint (Vector3f(x1, y1, n));
+ Vector3f ray2 = camera->ViewportToCameraPoint (Vector3f(x1, y2, n));
+ Vector3f ray3 = camera->ViewportToCameraPoint (Vector3f(x2, y2, n));
+ Vector3f ray4 = camera->ViewportToCameraPoint (Vector3f(x2, y1, n));
+
+ // Set up orthographic projection not to have to deal with precision problems
+ // that show up when drawing a full screen quad in perspective projection in world space.
+ LoadFullScreenOrthoMatrix (nearPlane, camera->GetProjectionFar(), true);
+
+ // Draw the fullscreen quad on the near plane
+ device.ImmediateBegin (kPrimitiveQuads);
+
+ device.ImmediateNormal (ray1.x, ray1.y, ray1.z);
+ device.ImmediateVertex (x1, y1, nearPlane);
+
+ device.ImmediateNormal (ray2.x, ray2.y, ray2.z);
+ device.ImmediateVertex (x1, y2, nearPlane);
+
+ device.ImmediateNormal (ray3.x, ray3.y, ray3.z);
+ device.ImmediateVertex (x2, y2, nearPlane);
+
+ device.ImmediateNormal (ray4.x, ray4.y, ray4.z);
+ device.ImmediateVertex (x2, y1, nearPlane);
+
+ device.ImmediateEnd ();
+ GPU_TIMESTAMP();
+ }
+}
+
+static UInt32 LightMask (const Light& l, const LightingLayers& lightingLayers)
+{
+ UInt32 mask = 0U;
+ UInt32 lightExcludeLayers = ~l.GetCullingMask();
+ int bit = 0;
+ while (lightExcludeLayers)
+ {
+ if (lightExcludeLayers & 1)
+ {
+ int layerStencilBit = lightingLayers.layerToStencil[bit];
+ if (layerStencilBit != -1)
+ mask |= 1 << layerStencilBit;
+ }
+ lightExcludeLayers >>= 1;
+ ++bit;
+ }
+ return mask;
+}
+
+static RenderTexture* RenderLight (
+ const RenderLoopContext& ctx,
+ const ShadowCullData& shadowCullData,
+ QualitySettings::ShadowQuality shadowQuality,
+ const LightmapSettings::LightmapsMode lightmapsMode,
+ RenderTexture*& rtLight,
+ RenderTexture* rtMain,
+ int width, int height,
+ DeviceStencilState* devStDisabled,
+ const MinMaxAABB& receiverBounds,
+ const DeviceMVPMatricesState& mvpState,
+ const Vector4f& lightFade,
+ const LightingLayers& lightingLayers,
+ const ActiveLight& light,
+#if SEPERATE_PREPASS_SPECULAR
+ bool specularPass,
+#endif
+ bool returnShadowMap)
+{
+ Light& l = *light.light;
+
+ PROFILER_AUTO_GFX(gPrepassLight, &l);
+
+ const Light::Lightmapping lightmappingMode = l.GetLightmappingForRender();
+ const Transform& trans = l.GetComponent(Transform);
+ Matrix4x4f lightMatrix = trans.GetLocalToWorldMatrixNoScale();
+ Vector3f lightPos = lightMatrix.GetPosition();
+
+ Assert(light.isVisibleInPrepass);
+ Assert(!light.screenRect.IsEmpty());
+
+ ShadowType lightShadows = l.GetShadows();
+ // Shadows on local lights are Pro only
+ if (lightShadows != kShadowNone && l.GetType() != kLightDirectional &&
+ !GetBuildSettings().hasLocalLightShadows)
+ lightShadows = kShadowNone;
+
+ // Check if soft shadows are allowed by license, quality settings etc.
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a1) &&
+ lightShadows > kShadowHard && !GetSoftShadowsEnabled())
+ lightShadows = kShadowHard;
+
+ GfxDevice& device = GetGfxDevice();
+ BuiltinShaderParamValues& params = device.GetBuiltinParamValues();
+
+ RenderSurfaceHandle rtSurfaceColor;
+ RenderSurfaceHandle rtSurfaceDepth = rtMain->GetDepthSurfaceHandle(); // re-use depth from final target
+ RenderSurfaceHandle rtSurfaceMainColor = rtMain->GetColorSurfaceHandle(); // will allocate color later (if any lights will actually be present)
+
+ bool hdr = ctx.m_Camera->GetUsingHDR();
+ float white[] = {1,1,1,1};
+ float black[] = {0,0,0,0};
+ UInt32 rtFlags = RenderTexture::kFlagDontRestoreColor;
+
+ if (!rtLight)
+ {
+ rtLight = GetRenderBufferManager().GetTempBuffer (RenderBufferManager::kFullSize, RenderBufferManager::kFullSize, kDepthFormatNone, hdr ? GetGfxDevice().GetDefaultHDRRTFormat() : kRTFormatARGB32, 0, kRTReadWriteLinear);
+
+ if (!rtLight->IsCreated())
+ rtLight->Create();
+
+ rtLight->SetFilterMode (kTexFilterNearest);
+ rtSurfaceColor = rtLight->GetColorSurfaceHandle();
+
+
+ RenderTexture::SetActive (1, &rtSurfaceColor, rtSurfaceDepth, rtLight, 0, kCubeFaceUnknown, rtFlags);
+ GraphicsHelper::Clear(kGfxClearColor, hdr ? black : white, 1.0f, 0);
+ GPU_TIMESTAMP();
+ }
+
+ rtSurfaceColor = rtLight->GetColorSurfaceHandle();
+
+ l.SetLightKeyword();
+
+ Vector3f lightDir = lightMatrix.GetAxisZ();
+ ColorRGBAf lightCol = GammaToActiveColorSpace (l.GetColor()) * l.GetIntensity() * 2.0f;
+
+ Matrix4x4f temp1, temp2, temp3;
+ if (l.GetType() == kLightSpot)
+ {
+ Matrix4x4f worldToLight = l.GetWorldToLocalMatrix();
+ {
+ temp1.SetScale (Vector3f (-.5f, -.5f, 1.0f));
+ temp2.SetTranslate (Vector3f (.5f, .5f, 0.0f));
+ temp3.SetPerspectiveCotan( l.GetCotanHalfSpotAngle(), 0.0f, l.GetRange() );
+ // temp2 * temp3 * temp1 * worldToLight
+ Matrix4x4f temp4;
+ MultiplyMatrices4x4 (&temp2, &temp3, &temp4);
+ MultiplyMatrices4x4 (&temp4, &temp1, &temp2);
+ MultiplyMatrices4x4 (&temp2, &worldToLight, &params.GetWritableMatrixParam(kShaderMatLightMatrix));
+ }
+ }
+ else if (l.GetCookie())
+ {
+ if (l.GetType() == kLightPoint)
+ {
+ params.SetMatrixParam(kShaderMatLightMatrix, l.GetWorldToLocalMatrix());
+ }
+ else if (l.GetType() == kLightDirectional)
+ {
+ float scale = 1.0f / l.GetCookieSize();
+ temp1.SetScale (Vector3f (scale, scale, 0));
+ temp2.SetTranslate (Vector3f (.5f, .5f, 0));
+ // temp2 * temp1 * l.GetWorldToLocalMatrix()
+ MultiplyMatrices4x4 (&temp2, &temp1, &temp3);
+ MultiplyMatrices4x4 (&temp3, &l.GetWorldToLocalMatrix(), &params.GetWritableMatrixParam(kShaderMatLightMatrix));
+ }
+ }
+
+ AssignCookieToMaterial(l, s_LightMaterial);
+
+ const bool renderAsQuad = light.intersectsNear && light.intersectsFar || l.GetType() == kLightDirectional;
+ ShaderLab::g_GlobalProperties->SetFloat(kSLPropLightAsQuad, renderAsQuad ? 1.0f : 0.0f);
+ ShaderLab::g_GlobalProperties->SetVector (kSLPropLightPos, lightPos.x, lightPos.y, lightPos.z, 1.0f / (l.GetRange() * l.GetRange()));
+ ShaderLab::g_GlobalProperties->SetVector (kSLPropLightDir, lightDir.x, lightDir.y, lightDir.z, 0.0f);
+ ShaderLab::g_GlobalProperties->SetVector (kSLPropLightColor, lightCol.GetPtr());
+ ///@TODO: cleanup, remove this from Internal-PrePassLighting shader
+ s_LightMaterial->SetTexture (ShaderLab::Property("_LightTextureB0"), builtintex::GetAttenuationTexture());
+
+ RenderTexture* shadowMap = NULL;
+ ShadowCameraData camData(shadowCullData);
+
+
+ if (light.shadowedLight != NULL && receiverBounds.IsValid() && shadowQuality != QualitySettings::kShadowsDisable)
+ {
+ Assert(light.insideShadowRange);
+
+ ShadowType lightShadows = l.GetShadows();
+
+ if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a1))
+ {
+ if (shadowQuality == QualitySettings::kShadowsHardOnly && lightShadows != kShadowNone)
+ lightShadows = kShadowHard;
+ }
+
+ SetShadowsKeywords (l.GetType(), lightShadows, false, true);
+
+ Matrix4x4f shadowMatrices[kMaxShadowCascades];
+ device.SetViewMatrix (ctx.m_CurCameraMatrix.GetPtr());
+ device.SetStencilState (devStDisabled, 0);
+
+ // Rendering shadowmaps will switch away from the lighting buffer and then will switch back.
+ // Nothing we can do about it, so don't produce the warning.
+ device.IgnoreNextUnresolveOnCurrentRenderTarget();
+
+ shadowMap = RenderShadowMaps (camData, light, receiverBounds, false, shadowMatrices);
+
+ if (!shadowMap)
+ {
+ // If shadow map could not actually be created (no casters, out of VRAM, whatever),
+ // set the no shadows keywords and proceed. So there will be no shadows,
+ // but otherwise it will be ok.
+ SetNoShadowsKeywords();
+ }
+ else
+ {
+ Vector4f data;
+
+ // ambient & shadow fade out
+ data.x = 1.0f - l.GetShadowStrength(); // R = 1-strength
+ data.y = data.z = data.w = 0.0f;
+ params.SetVectorParam(kShaderVecLightShadowData, data);
+
+ if (l.GetType() == kLightDirectional)
+ {
+ params.SetMatrixParam(kShaderMatWorldToShadow, shadowMatrices[0]);
+ SetCascadedShadowShaderParams (shadowMatrices, camData.splitDistances, camData.splitSphereCentersAndSquaredRadii);
+
+ shadowMap = ComputeScreenSpaceShadowMap (
+ ctx,
+ shadowMap,
+ l.GetShadowSoftness(),
+ l.GetShadowSoftnessFade(),
+ lightShadows);
+ }
+ else if (l.GetType() == kLightSpot)
+ {
+ params.SetMatrixParam(kShaderMatWorldToShadow, shadowMatrices[0]);
+ }
+
+ // texel offsets for PCF
+ float offX = 0.5f / shadowMap->GetGLWidth();
+ float offY = 0.5f / shadowMap->GetGLHeight();
+ data.z = 0.0f; data.w = 0.0f;
+ data.x = -offX; data.y = -offY; params.SetVectorParam(kShaderVecShadowOffset0, data);
+ data.x = offX; data.y = -offY; params.SetVectorParam(kShaderVecShadowOffset1, data);
+ data.x = -offX; data.y = offY; params.SetVectorParam(kShaderVecShadowOffset2, data);
+ data.x = offX; data.y = offY; params.SetVectorParam(kShaderVecShadowOffset3, data);
+ s_LightMaterial->SetTexture (PrePassPrivate::kSLPropShadowMapTexture, shadowMap);
+
+ if (rtLight != NULL)
+ RenderTexture::SetActive (1, &rtSurfaceColor, rtSurfaceDepth, rtLight);
+ else
+ RenderTexture::SetActive (1, &rtSurfaceMainColor, rtSurfaceDepth, rtMain);
+ }
+ device.SetViewMatrix(mvpState.GetView().GetPtr());
+ device.SetProjectionMatrix(mvpState.GetProj());
+ SetClippingPlaneShaderProps();
+
+ // restore the cull mode, since it could be changed by a shadow caster with odd-negative scale
+ device.SetNormalizationBackface( kNormalizationDisabled, false );
+ }
+ else
+ {
+ SetNoShadowsKeywords ();
+ }
+
+ // Draw each light in two passes: illuminate non-lightmapped objects; illuminate lightmapped objects.
+ int lightPassCount = 2;
+ int lightPassAddBits[2] = { kStencilMaskNonLightmapped, 0 };
+ if (lightmappingMode == Light::kLightmappingRealtimeOnly)
+ {
+ // If light is realtime only, it's enough to draw one pass; that illuminates any object
+ // be it lightmapped or not.
+ lightPassCount = 1;
+ lightPassAddBits[0] = 0;
+ }
+ else if (lightmappingMode == Light::kLightmappingAuto && lightmapsMode != LightmapSettings::kDualLightmapsMode)
+ {
+ // If it's an auto light but we're in single lightmaps mode, draw in one pass only to illuminate
+ // non-lightmapped objects
+ // TODO: realtime shadows from auto lights won't be received by lightmapped objects. Do we want to fix it?
+ lightPassCount = 1;
+ lightPassAddBits[0] = kStencilMaskNonLightmapped;
+ }
+
+ //TODO: skip if smaller than certain size
+ const bool useStencilMask = !light.intersectsNear && !light.intersectsFar &&
+ (lightmappingMode == Light::kLightmappingRealtimeOnly) &&
+ (l.GetType() == kLightSpot || l.GetType() == kLightPoint );
+
+ const UInt32 lightmask = LightMask (l, lightingLayers);
+
+ // Render stencil mask, to discard all light pixels, at which the light is fully in front of scene geometry.
+ if (useStencilMask)
+ {
+ Material::GetDefault ()->SetPass (0);
+ #if UNITY_XENON
+ device.SetNullPixelShader ();
+ #endif
+
+ GfxBlendState blendstate;
+ blendstate.renderTargetWriteMask = 0U;
+ device.SetBlendState (device.CreateBlendState(blendstate), 0);
+
+ GfxRasterState rasterstate;
+ rasterstate.cullMode = kCullOff;
+ device.SetRasterState (device.CreateRasterState(rasterstate));
+
+ GfxDepthState depthstate;
+ depthstate.depthWrite = false;
+ depthstate.depthFunc = kFuncLEqual;
+ device.SetDepthState (device.CreateDepthState(depthstate));
+
+ GfxStencilState lightStencil;
+ lightStencil.stencilEnable = true;
+ lightStencil.readMask = 0xFFU;
+ lightStencil.writeMask = kStencilMaskLightBackface;
+ lightStencil.stencilZFailOpBack = kStencilOpInvert;
+ lightStencil.stencilZFailOpFront = kStencilOpInvert;
+ lightStencil.stencilPassOpBack = kStencilOpKeep;
+ lightStencil.stencilPassOpFront = kStencilOpKeep;
+ lightStencil.stencilFuncBack = (lightmask != 0 ) ? kFuncNotEqual : kFuncAlways;
+ lightStencil.stencilFuncFront = (lightmask != 0 ) ? kFuncNotEqual : kFuncAlways;
+ device.SetStencilState (device.CreateStencilState(lightStencil), lightmask|kStencilMaskSomething|kStencilMaskNonLightmapped);
+
+ #if UNITY_XENON
+ // Clear within light-geom, sets all HiS to cull.
+ // Set to cull where equal to background (to deal with lightmasks), unoptimal but works
+ if (useStencilMask)
+ device.SetHiStencilState (false, true, kStencilMaskSomething|kStencilMaskNonLightmapped, kFuncEqual);
+ #endif
+
+ RenderLightGeom (ctx, light, lightPos, lightMatrix, renderAsQuad);
+
+ blendstate.renderTargetWriteMask = KColorWriteAll;
+ device.SetBlendState (device.CreateBlendState(blendstate), 0);
+
+ #if UNITY_XENON
+ device.HiStencilFlush (kHiSflush_sync);
+ #endif
+ }
+
+ for (int pp = 0; pp < lightPassCount; ++pp)
+ {
+ Vector4f lightingFade = lightFade;
+ Vector4f shadowFade = lightFade;
+ shadowFade.x = 1.0f - l.GetShadowStrength();
+ if (pp == 0 || lightmappingMode == Light::kLightmappingRealtimeOnly)
+ lightingFade.z = lightingFade.w = 0.0f;
+ else
+ shadowFade.z = shadowFade.w = 0.0f;
+ params.SetVectorParam(kShaderVecLightmapFade, lightingFade);
+ params.SetVectorParam(kShaderVecLightShadowData, shadowFade);
+
+ // Disable mipmapping on light cookies
+ ShaderLab::TexEnv* cookieEnv = s_LightMaterial->GetProperties().GetTexEnv(kSLPropLightTexture0);
+ if (cookieEnv)
+ {
+ cookieEnv->TextureMipBiasChanged (-8);
+ }
+
+ #if SEPERATE_PREPASS_SPECULAR
+ if (s_LightMaterial->GetPassCount () > 2 && ctx.m_Camera->GetUsingHDR() && specularPass)
+ s_LightMaterial->SetPass (2);
+ else
+ #endif
+ if (s_LightMaterial->GetPassCount () > 1 && ctx.m_Camera->GetUsingHDR())
+ s_LightMaterial->SetPass (1);
+ else
+ s_LightMaterial->SetPass (0);
+
+ // Construct stencil read mask
+ GfxStencilState stencil;
+ stencil.stencilEnable = true;
+ stencil.stencilFuncFront = stencil.stencilFuncBack = kFuncEqual;
+ stencil.readMask = kStencilMaskSomething;
+ // Check lightmapped vs. non-lightmapped unless it's a realtime light that
+ // only cares about not illuminating non-something.
+ if (lightmappingMode != Light::kLightmappingRealtimeOnly)
+ stencil.readMask |= kStencilMaskNonLightmapped;
+
+ if (pp != 0 && lightmappingMode != Light::kLightmappingRealtimeOnly)
+ stencil.readMask |= kStencilMaskBeyondShadowDistace;
+
+ stencil.readMask |= lightmask;
+ int stencilRef = kStencilMaskSomething + lightPassAddBits[pp];
+
+ if (useStencilMask)
+ {
+ // Clear stencil while rendering
+ stencil.writeMask = kStencilMaskLightBackface;
+ stencil.stencilZFailOpBack = kStencilOpZero;
+ stencil.stencilZFailOpFront = kStencilOpZero;
+ stencil.stencilPassOpBack = kStencilOpZero;
+ stencil.stencilPassOpFront = kStencilOpZero;
+ // Clear the kStencilMaskLightBackface bit even if rejecting pixel due to stencil layer mask
+ stencil.stencilFailOpBack = kStencilOpZero;
+ stencil.stencilFailOpFront = kStencilOpZero;
+
+ stencil.readMask |= kStencilMaskLightBackface;
+ stencilRef |= kStencilMaskLightBackface;
+ }
+
+ DeviceStencilState* devStCheck = device.CreateStencilState (stencil);
+ device.SetStencilState (devStCheck, stencilRef);
+
+ #if UNITY_XENON
+ // Set to cull when all == background (to deal with lightmasks), unoptimal but works
+ if (useStencilMask)
+ device.SetHiStencilState (true, true, kStencilMaskSomething|kStencilMaskNonLightmapped, kFuncEqual);
+ #endif
+
+ // Draw light shape
+ GfxRasterState rasterstate;
+ GfxDepthState depthstate;
+ depthstate.depthWrite = false;
+ if (light.intersectsNear && !light.intersectsFar && (l.GetType() == kLightSpot || l.GetType() == kLightPoint))
+ {
+ // When near (but not far) plane intersects the light, render back faces (tighter than rendering a bounding quad).
+ // Can't use this when not intersecting, since it would waste processing for objects between
+ // the light and the cam, even when they don't touch the light.
+ rasterstate.cullMode = kCullFront;
+ depthstate.depthFunc = kFuncGreater;
+ }
+ else
+ {
+ depthstate.depthFunc = kFuncLEqual;
+ #if UNITY_XENON
+ device.SetHiZEnable (kHiZEnable);
+ #endif
+ }
+ device.SetRasterState (device.CreateRasterState (rasterstate));
+ device.SetDepthState (device.CreateDepthState (depthstate));
+
+ RenderLightGeom (ctx, light, lightPos, lightMatrix, renderAsQuad);
+
+ #if UNITY_XENON
+ device.SetHiZEnable (kHiZAuto);
+ if (useStencilMask)
+ device.HiStencilFlush (kHiSflush_async);
+ #endif
+ }
+
+ if (shadowMap && !returnShadowMap)
+ GetRenderBufferManager().ReleaseTempBuffer (shadowMap);
+
+ return returnShadowMap ? shadowMap : NULL;
+}
+
+
+void PrePassRenderLoop::RenderLighting (
+ ActiveLights& activeLights,
+ RenderTexture* rtMain,
+ TextureID depthTextureID,
+ RenderTexture* rtNormalsSpec,
+ RenderTexture*& rtLight,
+
+#if SEPERATE_PREPASS_SPECULAR
+ RenderTexture*& rtLightSpec,
+#endif
+ const Vector4f& lightFade,
+ const LightingLayers& lightingLayers,
+ MinMaxAABB& receiverBounds,
+ RenderTexture** outMainShadowMap)
+{
+ PROFILER_AUTO_GFX(gPrepassLighting, m_Context->m_Camera);
+ GPU_AUTO_SECTION(kGPUSectionDeferedLighting);
+ *outMainShadowMap = NULL;
+
+ Assert(rtLight == NULL);
+#if SEPERATE_PREPASS_SPECULAR
+ Assert(rtLightSpec == NULL);
+#endif
+ const QualitySettings::ShadowQuality shadowQuality = static_cast<QualitySettings::ShadowQuality>(GetQualitySettings().GetCurrent().shadows);
+ const LightmapSettings::LightmapsMode lightmapsMode = static_cast<LightmapSettings::LightmapsMode>(GetLightmapSettings().GetLightmapsMode());
+
+ ShadowCameraData camData(*m_Context->m_ShadowCullData);
+
+ // Prevent receiver bounds to be zero size in any dimension;
+ // causes trouble with calculating intersection of frustum and bounds.
+ receiverBounds.Expand( 0.01f );
+
+ const Rectf screenRect = m_Context->m_Camera->GetScreenViewportRect();
+
+ if (!s_LightMaterial) {
+ Shader* shader = GetScriptMapper().FindShader ("Hidden/Internal-PrePassLighting");
+ s_LightMaterial = Material::CreateMaterial (*shader, Object::kHideAndDontSave);
+ }
+
+ if (s_Icosahedron.IsNull ())
+ s_Icosahedron = GetBuiltinResource<Mesh> ("icosahedron.fbx");
+ if (s_Icosphere.IsNull ())
+ s_Icosphere = GetBuiltinResource<Mesh> ("icosphere.fbx");
+ if (s_Pyramid.IsNull ())
+ s_Pyramid = GetBuiltinResource<Mesh> ("pyramid.fbx");
+
+ static SHADERPROP (CameraDepthTexture);
+ static SHADERPROP (CameraNormalsTexture);
+ const int width = rtNormalsSpec->GetGLWidth();
+ const int height = rtNormalsSpec->GetGLHeight();
+ if (gGraphicsCaps.hasStencilInDepthTexture)
+ {
+ ShaderLab::g_GlobalProperties->SetRectTextureID (
+ kSLPropCameraDepthTexture,
+ depthTextureID,
+ width,
+ height,
+ rtMain->GetTexelSizeX(),
+ rtMain->GetTexelSizeY(),
+ rtMain->GetUVScaleX(),
+ rtMain->GetUVScaleY()
+ );
+ }
+
+ // set as _CameraNormalsTexture for external access
+ ShaderLab::g_GlobalProperties->SetTexture (kSLPropCameraNormalsTexture, rtNormalsSpec);
+
+ GfxDevice& device = GetGfxDevice();
+
+ SetAndRestoreWireframeMode setWireframeOff(false); // turn off wireframe; will restore old value in destructor
+ device.SetNormalizationBackface( kNormalizationDisabled, false );
+
+ DeviceStencilState* devStDisabled = device.CreateStencilState (GfxStencilState());
+
+
+ DeviceMVPMatricesState preserveMVP;
+
+ device.SetWorldMatrix (Matrix4x4f::identity.GetPtr());
+
+ RenderTexture** currentLightTex = &rtLight;
+#if SEPERATE_PREPASS_SPECULAR
+ //Do 2 passes for HDR prepass lighting on xenon
+ for (int lp = 0; lp < (m_Context->m_Camera->GetUsingHDR() ? 2 : 1); ++lp)
+ {
+ if (lp == 0)
+ currentLightTex = &rtLight;
+ else
+ currentLightTex = &rtLightSpec;
+#endif
+
+ const ActiveLight* mainActiveLight = GetMainActiveLight(activeLights);
+ ActiveLights::Array::iterator it, itEnd = activeLights.lights.end();
+ for (it = activeLights.lights.begin(); it != itEnd; ++it)
+ {
+ if (!it->isVisibleInPrepass)
+ continue;
+ if (&*it == mainActiveLight)
+ {
+ // skip main light now; will render it last
+ continue;
+ }
+ RenderLight (*m_Context, camData, shadowQuality, lightmapsMode,
+ *currentLightTex,
+ rtMain,
+ width, height, devStDisabled, receiverBounds,
+ preserveMVP, lightFade, lightingLayers, *it,
+#if SEPERATE_PREPASS_SPECULAR
+ lp == 1,
+#endif
+ false);
+ }
+
+ #if UNITY_XENON
+ device.SetStencilState (devStDisabled, 0);
+ device.SetHiStencilState (false, false, 0, kFuncEqual);
+ #endif
+
+ // render main light
+ if (mainActiveLight)
+ {
+ RenderTexture* shadowMap = RenderLight (
+ *m_Context, camData, shadowQuality, lightmapsMode,
+ *currentLightTex,
+ rtMain,
+ width, height, devStDisabled, receiverBounds,
+ preserveMVP, lightFade, lightingLayers, *mainActiveLight,
+#if SEPERATE_PREPASS_SPECULAR
+ lp == 1,
+#endif
+ true);
+ if (shadowMap)
+ {
+ AddRenderLoopTempBuffer (m_Context->m_RenderLoop, shadowMap);
+ *outMainShadowMap = shadowMap;
+ }
+ }
+#if SEPERATE_PREPASS_SPECULAR
+ }
+#endif
+ SetNoShadowsKeywords ();
+
+ Vector4f lightmapFade = lightFade;
+ // if we're not in dual lightmaps mode, always use the far lightmap, i.e. lightmapFade = 1
+ if (GetLightmapSettings().GetLightmapsMode() != LightmapSettings::kDualLightmapsMode)
+ lightmapFade.z = lightmapFade.w = 1.0f;
+
+ device.GetBuiltinParamValues().SetVectorParam(kShaderVecLightmapFade, lightmapFade);
+
+ device.SetStencilState (devStDisabled, 0);
+
+ #if !UNITY_XENON
+ // Ok, we didn't really have any lights worth rendering.
+ // Create a small render texture and clear it to white and pass it as the lighting buffer.
+ // Don't do that on 360; pointless and saves a resolve.
+ if (!rtLight)
+ {
+ rtLight = GetRenderBufferManager().GetTempBuffer (16, 16, kDepthFormatNone, kRTFormatARGB32, 0, kRTReadWriteLinear);
+ RenderTexture::SetActive (rtLight);
+ float white[] = {1,1,1,1};
+ float black[] = {0,0,0,0};
+ GraphicsHelper::Clear (kGfxClearColor, m_Context->m_Camera->GetUsingHDR() ? black : white, 1.0f, 0);
+ GPU_TIMESTAMP();
+
+ // We just switched away from a Z buffer (only in case when no lights were there!),
+ // and we'll switch back to it. So ignore the unresolve warning on it.
+ device.IgnoreNextUnresolveOnRS(rtMain->GetDepthSurfaceHandle());
+ }
+ #endif
+}
+
+
+static RenderTexture* CombineDepthNormalsTexture (const RenderLoopContext& ctx, RenderObjectDataContainer& remainingObjects)
+{
+ PROFILER_AUTO_GFX(gPrepassCombineDepthNormals, ctx.m_Camera);
+
+ static Material* s_CombineMaterial = NULL;
+ if (!s_CombineMaterial)
+ {
+ Shader* shader = GetScriptMapper ().FindShader ("Hidden/Internal-CombineDepthNormals");
+ if (shader)
+ s_CombineMaterial = Material::CreateMaterial (*shader, Object::kHideAndDontSave);
+ if (!s_CombineMaterial) {
+ AssertString ("Coult not find depth+normals combine shader");
+ return NULL;
+ }
+ }
+
+ RenderTexture* depthNormals = GetRenderBufferManager().GetTempBuffer (RenderBufferManager::kFullSize, RenderBufferManager::kFullSize, kDepthFormatNone, kRTFormatARGB32, 0, kRTReadWriteLinear);
+ RenderTexture::SetActive (depthNormals);
+ GraphicsHelper::Clear (kGfxClearColor, ColorRGBAf(0.5f,0.5f,1.0f,1.0f).GetPtr(), 1.0f, 0);
+ GPU_TIMESTAMP();
+
+ // Combine depth & normals into single texture
+ ImageFilters::Blit (NULL, depthNormals, s_CombineMaterial, 0, false);
+
+ AddRenderLoopTempBuffer (ctx.m_RenderLoop, depthNormals);
+
+ static SHADERPROP (CameraDepthNormalsTexture);
+ ShaderLab::g_GlobalProperties->SetTexture (kSLPropCameraDepthNormalsTexture, depthNormals);
+
+ return depthNormals;
+}
+
+
+
+// Separate pass to render depth into a separate target. Only used on Macs with Radeon HDs, since
+// only there doing it the regular way is broken.
+#if GFX_SUPPORTS_OPENGL
+static RenderTexture* RenderBasePassDepth (const RenderLoopContext& ctx, RenderObjectDataContainer& renderData, PreRenderPasses& plainRenderPasses)
+{
+ GPU_AUTO_SECTION(kGPUSectionDeferedPrePass);
+
+ GfxDevice& device = GetGfxDevice();
+
+ RenderTexture* rt = GetRenderBufferManager().GetTempBuffer (RenderBufferManager::kFullSize, RenderBufferManager::kFullSize, kDepthFormat24, kRTFormatDepth, 0, kRTReadWriteLinear);
+ rt->SetFilterMode (kTexFilterNearest);
+ if (!rt->IsCreated())
+ rt->Create();
+ RenderTexture::SetActive (rt);
+ AddRenderLoopTempBuffer (ctx.m_RenderLoop, rt);
+
+ float black[] = {0,0,0,0};
+ GraphicsHelper::Clear (kGfxClearAll, black, 1.0f, 0);
+ GPU_TIMESTAMP();
+
+ device.SetViewMatrix (ctx.m_CurCameraMatrix.GetPtr());
+
+ size_t ndata = renderData.size();
+
+ for( size_t i = 0; i < ndata; ++i )
+ {
+ const PrePassRenderData& rpData = plainRenderPasses[i];
+ const RenderObjectData& roData = renderData[rpData.roIndex];
+ Shader* shader = roData.shader;
+ int ss = shader->GetShaderLabShader()->GetDefaultSubshaderIndex(kRenderPathExtPrePass);
+ if (ss == -1)
+ continue;
+
+ const VisibleNode *node = roData.visibleNode;
+
+ SetObjectScale (device, node->lodFade, node->invScale);
+
+ //@TODO: if this returns true and we have any sort of batching, we'd have to break batches here
+ node->renderer->ApplyCustomProperties(*roData.material, shader, ss);
+
+ ShaderLab::SubShader& subshader = roData.shader->GetShaderLabShader()->GetSubShader (ss);
+ int shaderPassCount = subshader.GetValidPassCount();
+ for (int p = 0; p < shaderPassCount; ++p)
+ {
+ ShaderPassType passType;
+ UInt32 passRenderOptions;
+ subshader.GetPass(p)->GetPassOptions (passType, passRenderOptions);
+ if (passType != kPassLightPrePassBase)
+ continue;
+
+ const ChannelAssigns* channels = roData.material->SetPassWithShader(p, shader, ss);
+ if (channels)
+ {
+ SetupObjectMatrix (node->worldMatrix, node->transformType);
+ node->renderer->Render( roData.subsetIndex, *channels );
+ }
+ }
+ }
+
+ return rt;
+}
+#endif
+
+inline float MultiplyAbsVectorZ (const Matrix4x4f& m, const Vector3f& v)
+{
+ return Abs(m.m_Data[2]) * v.x + Abs(m.m_Data[6]) * v.y + Abs(m.m_Data[10]) * v.z;
+}
+
+
+RenderTexture* PrePassRenderLoop::RenderBasePass (
+ RenderTexture* rtMain,
+ const LightingLayers& lightingLayers,
+ RenderObjectDataContainer& outRemainingObjects,
+ MinMaxAABB& receiverBounds
+ )
+{
+ PROFILER_AUTO_GFX(gPrepassGeom, m_Context->m_Camera);
+ GPU_AUTO_SECTION(kGPUSectionDeferedPrePass);
+
+ const float shadowDistance = m_Context->m_ShadowCullData->shadowDistance;
+
+ GfxDevice& device = GetGfxDevice();
+ device.SetNormalizationBackface( kNormalizationDisabled, false );
+
+ GfxStencilState stRender;
+ stRender.writeMask = kStencilGeomWriteMask;
+ stRender.stencilEnable = true;
+ stRender.stencilPassOpFront = stRender.stencilPassOpBack = kStencilOpReplace;
+ DeviceStencilState* devStRender = device.CreateStencilState (stRender);
+
+ RenderTexture* rtNormalsSpec = GetRenderBufferManager().GetTempBuffer (RenderBufferManager::kFullSize, RenderBufferManager::kFullSize, kDepthFormatNone, kRTFormatARGB32, 0, kRTReadWriteLinear);
+ rtNormalsSpec->SetFilterMode (kTexFilterNearest);
+ if (!rtNormalsSpec->IsCreated())
+ rtNormalsSpec->Create();
+ RenderSurfaceHandle rtSurfaceColor = rtNormalsSpec->GetColorSurfaceHandle();
+ RenderSurfaceHandle rtSurfaceDepth = rtMain->GetDepthSurfaceHandle(); // reuse depth buffer from final pass
+
+ UInt32 rtFlags = RenderTexture::kFlagDontRestoreColor;
+ UInt32 gfxClearFlags = kGfxClearAll;
+ // do not clear depth/stencil if camera set to DontClear
+ if (m_Context->m_Camera->GetClearFlags() == Camera::kDontClear)
+ {
+ gfxClearFlags &= ~kGfxClearDepthStencil;
+ }
+ else
+ {
+ rtFlags |= RenderTexture::kFlagDontRestoreDepth;
+ }
+
+ // set base pass render texture
+ RenderTexture::SetActive (1, &rtSurfaceColor, rtSurfaceDepth, rtNormalsSpec, 0, kCubeFaceUnknown, rtFlags);
+
+ AddRenderLoopTempBuffer (m_Context->m_RenderLoop, rtNormalsSpec);
+
+ float black[] = {0,0,0,0};
+ GraphicsHelper::Clear (gfxClearFlags, black, 1.0f, 0);
+ GPU_TIMESTAMP();
+
+ device.SetViewMatrix (m_Context->m_CurCameraMatrix.GetPtr());
+
+ const ChannelAssigns* channels = NULL;
+
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+
+ int prevTransformType = -1;
+ Material* prevMaterial = 0;
+ Shader* prevShader = 0;
+ int prevSubshaderIndex = -1;
+ float prevInvScale = 0.0f;
+ float prevLodFade = 0.0f;
+ UInt32 prevCustomPropsHash = 0;
+ int prevPassIndex = -1;
+ int prevStencilRef = 0;
+
+ int canBatch = 0;
+
+ #endif
+
+ const bool directLightBakedInLightProbes = LightProbes::AreBaked() && GetLightmapSettings().GetLightmapsMode() != LightmapSettings::kDualLightmapsMode;
+
+ size_t ndata = m_Objects->size();
+ outRemainingObjects.reserve (ndata / 16);
+
+ for( size_t i = 0; i < ndata; ++i )
+ {
+ const PrePassRenderData& rpData = m_PlainRenderPasses[i];
+ const RenderObjectData& roData = (*m_Objects)[rpData.roIndex];
+ Shader* shader = roData.shader;
+
+ int ss = roData.subShaderIndex;
+ if (ss == -1)
+ ss = shader->GetShaderLabShader()->GetDefaultSubshaderIndex(kRenderPathExtPrePass);
+
+ const VisibleNode *node = roData.visibleNode;
+
+ bool withinShadowDistance = true;
+ float distanceAlongView = roData.distanceAlongView;
+ if (distanceAlongView > shadowDistance)
+ {
+ // check whether its bounds is actually further than shadow distance
+ // project extents onto camera forward axis
+ float z = MultiplyAbsVectorZ (m_Context->m_CurCameraMatrix, node->worldAABB.GetExtent());
+ Assert(z >= 0.0f);
+ if (distanceAlongView - z > shadowDistance)
+ withinShadowDistance = false;
+ }
+
+ if (ss == -1)
+ {
+ if (withinShadowDistance && node->renderer->GetReceiveShadows())
+ receiverBounds.Encapsulate (node->worldAABB);
+ outRemainingObjects.push_back() = roData;
+ continue;
+ }
+
+
+ const float invScale = node->invScale;
+ const float lodFade = node->lodFade;
+ const int transformType = node->transformType;
+ const UInt32 customPropsHash = node->renderer->GetCustomPropertiesHash();
+
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+
+ if (
+ node->renderer->GetStaticBatchIndex() == 0 ||
+ prevTransformType != transformType ||
+ prevMaterial != roData.material ||
+ prevShader != shader ||
+ prevSubshaderIndex != ss ||
+ !CompareApproximately(prevInvScale,invScale) ||
+ !CompareApproximately(prevLodFade,lodFade, LOD_FADE_BATCH_EPSILON) ||
+ prevCustomPropsHash != customPropsHash)
+ {
+ m_BatchRenderer.Flush();
+
+ prevTransformType = transformType;
+ prevMaterial = roData.material;
+ prevShader = shader;
+ prevSubshaderIndex = ss;
+ prevInvScale = invScale;
+ prevLodFade = lodFade;
+ prevCustomPropsHash = customPropsHash;
+
+ canBatch = 0;
+ }
+ else
+ ++canBatch;
+
+ #endif
+
+ SetObjectScale (device, lodFade, invScale);
+
+ node->renderer->ApplyCustomProperties(*roData.material, shader, ss);
+
+ const bool lightmapped = node->renderer->IsLightmappedForRendering();
+ const Renderer* renderer = static_cast<Renderer*>(node->renderer);
+ const bool directLightFromLightProbes = directLightBakedInLightProbes && node->renderer->GetRendererType() != kRendererIntermediate && renderer->GetUseLightProbes();
+
+ ShaderLab::SubShader& subshader = shader->GetShaderLabShader()->GetSubShader (ss);
+ int shaderPassCount = subshader.GetValidPassCount();
+ for (int p = 0; p < shaderPassCount; ++p)
+ {
+ ShaderPassType passType;
+ UInt32 passRenderOptions;
+ subshader.GetPass(p)->GetPassOptions (passType, passRenderOptions);
+ if (passType != kPassLightPrePassBase)
+ continue;
+
+ int stencilRef = kStencilMaskSomething;
+ if (!lightmapped && !directLightFromLightProbes)
+ {
+ stencilRef += kStencilMaskNonLightmapped;
+ }
+
+ if (!withinShadowDistance)
+ stencilRef += kStencilMaskBeyondShadowDistace;
+
+ int layerStencilBit = lightingLayers.layerToStencil[node->renderer->GetLayer()];
+ if (layerStencilBit != -1)
+ stencilRef |= 1<<layerStencilBit;
+
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+
+ if ((p != prevPassIndex) ||
+ (stencilRef != prevStencilRef))
+ {
+ m_BatchRenderer.Flush();
+ prevPassIndex = p;
+ prevStencilRef = stencilRef;
+ canBatch = 0;
+ }
+
+ if (canBatch <= 1)
+ #endif
+ {
+ device.SetStencilState (devStRender, stencilRef);
+ channels = roData.material->SetPassWithShader(p, shader, ss);
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+ prevPassIndex = p;
+ prevStencilRef = stencilRef;
+ #endif
+ }
+
+ receiverBounds.Encapsulate (node->worldAABB);
+
+ if (channels)
+ {
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+ m_BatchRenderer.Add (node->renderer, roData.subsetIndex, channels, node->worldMatrix, transformType);
+ #else
+ SetupObjectMatrix (node->worldMatrix, transformType);
+ node->renderer->Render (roData.subsetIndex, *channels);
+ #endif
+ }
+ }
+ }
+
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+ m_BatchRenderer.Flush();
+ #endif
+
+ return rtNormalsSpec;
+}
+
+void PrePassRenderLoop::RenderFinalPass (RenderTexture* rtMain,
+ RenderTexture* rtLight,
+#if SEPERATE_PREPASS_SPECULAR
+ RenderTexture* rtLightSpec,
+#endif
+ bool hdr,
+ bool linearLighting)
+{
+ PROFILER_AUTO_GFX(gPrepassFinal, m_Context->m_Camera);
+ GPU_AUTO_SECTION(kGPUSectionOpaquePass);
+
+ GfxDevice& device = GetGfxDevice();
+ device.SetNormalizationBackface( kNormalizationDisabled, false );
+
+ RenderTexture::SetActive (rtMain);
+
+ // Clear with background. Do not clear depth since we need the already
+ // filled one from the base pass.
+ device.SetSRGBWrite(!hdr && linearLighting && (!rtMain || rtMain->GetSRGBReadWrite()) );
+ m_Context->m_Camera->ClearNoSkybox(true);
+
+ if(rtLight)
+ rtLight->SetGlobalProperty (kSLPropLightBuffer);
+ else
+ {
+ ShaderLab::TexEnv *te = ShaderLab::g_GlobalProperties->SetTexture (kSLPropLightBuffer, hdr ? builtintex::GetBlackTexture() : builtintex::GetWhiteTexture());
+ te->ClearMatrix();
+ }
+
+#if SEPERATE_PREPASS_SPECULAR
+ if(rtLightSpec)
+ rtLightSpec->SetGlobalProperty (kSLPropLightSpecBuffer);
+ else
+ {
+ ShaderLab::TexEnv *te = ShaderLab::g_GlobalProperties->SetTexture (kSLPropLightSpecBuffer, hdr ? builtintex::GetBlackTexture() : builtintex::GetWhiteTexture());
+ te->ClearMatrix();
+ }
+#endif
+
+ const ChannelAssigns* channels = NULL;
+ const LightmapSettings& lightmapper = GetLightmapSettings();
+
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+
+ int prevPassIndex = -1;
+
+ int prevLightmapIndex = -1;
+ Vector4f prevLightmapST (0,0,0,0);
+ int prevTransformType = -1;
+ Material* prevMaterial = 0;
+ Shader* prevShader = 0;
+ int prevSubshaderIndex = -1;
+ float prevInvScale = 0.0f;
+ float prevLodFade = 0.0f;
+ UInt32 prevCustomPropsHash = 0;
+
+ int canBatch = 0;
+
+ #endif
+
+ if (hdr)
+ g_ShaderKeywords.Enable (kKeywordHDRLightPrepassOn);
+ else
+ g_ShaderKeywords.Disable (kKeywordHDRLightPrepassOn);
+
+ LightProbes* lightProbes = GetLightProbes();
+ const bool areLightProbesBaked = LightProbes::AreBaked();
+ BuiltinShaderParamValues& builtinParamValues = GetGfxDevice().GetBuiltinParamValues();
+ Vector3f ambientSH;
+ SHEvalAmbientLight(GetRenderSettings().GetAmbientLightInActiveColorSpace(), &ambientSH[0]);
+
+ size_t ndata = m_Objects->size();
+ for( size_t i = 0; i < ndata; ++i )
+ {
+ const PrePassRenderData& rpData = m_PlainRenderPasses[i];
+ const RenderObjectData& roData = (*m_Objects)[rpData.roIndex];
+
+ const VisibleNode *node = roData.visibleNode;
+ Shader* shader = roData.shader;
+
+ int ss = roData.subShaderIndex;
+ if (ss == -1)
+ ss = shader->GetShaderLabShader()->GetDefaultSubshaderIndex(kRenderPathExtPrePass);
+ if (ss == -1)
+ continue;
+
+ const Vector4f lightmapST = node->renderer->GetLightmapSTForRendering();
+ const int lightmapIndex = roData.lightmapIndex;
+ DebugAssert(lightmapIndex == node->renderer->GetLightmapIndex());
+
+ const float invScale = node->invScale;
+ const float lodFade = node->lodFade;
+ const int transformType = node->transformType;
+ const UInt32 customPropsHash = node->renderer->GetCustomPropertiesHash();
+
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+
+ if (
+ node->renderer->GetStaticBatchIndex() == 0 ||
+ prevTransformType != transformType ||
+ prevMaterial != roData.material ||
+ prevShader != shader ||
+ prevSubshaderIndex != ss ||
+ prevLightmapIndex != lightmapIndex ||
+ !CompareMemory(prevLightmapST, lightmapST) ||
+ !CompareApproximately(prevInvScale,invScale) ||
+ !CompareApproximately(prevLodFade,lodFade) ||
+ prevCustomPropsHash != customPropsHash)
+ {
+ m_BatchRenderer.Flush();
+
+ prevLightmapIndex = lightmapIndex;
+ prevLightmapST = lightmapST;
+ prevTransformType = transformType;
+ prevMaterial = roData.material;
+ prevShader = shader;
+ prevSubshaderIndex = ss;
+ prevInvScale = invScale;
+ prevLodFade = lodFade;
+ prevCustomPropsHash = customPropsHash;
+
+ canBatch = 0;
+ }
+ else
+ ++canBatch;
+
+ #endif
+
+ SetObjectScale (device, lodFade, invScale);
+
+ node->renderer->ApplyCustomProperties(*roData.material, roData.shader, ss);
+
+ ShaderLab::SubShader& subshader = roData.shader->GetShaderLabShader()->GetSubShader (ss);
+ int shaderPassCount = subshader.GetValidPassCount();
+ for (int p = 0; p < shaderPassCount; ++p)
+ {
+ ShaderPassType passType;
+ UInt32 passRenderOptions;
+ subshader.GetPass(p)->GetPassOptions (passType, passRenderOptions);
+ if (passType != kPassLightPrePassFinal)
+ continue;
+
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+ if (p != prevPassIndex)
+ {
+ m_BatchRenderer.Flush();
+ canBatch = 0;
+ }
+
+ if (canBatch <= 1)
+ #endif
+ {
+ // lightmap
+ SetupObjectLightmaps (lightmapper, lightmapIndex, lightmapST, false);
+
+ // light probes
+ // TODO: figure how does that interact with lightmaps and with batching;
+ // if we are about to use light probes and the renderer gets different coeffs (maybe a simpler check?) => can't batch
+ float lightProbeCoeffs[9][3];
+ memset (lightProbeCoeffs, 0, sizeof(lightProbeCoeffs));
+ if (areLightProbesBaked && node->renderer->GetRendererType() != kRendererIntermediate)
+ {
+ Renderer* renderer = static_cast<Renderer*>(node->renderer);
+ if (renderer && renderer->GetUseLightProbes())
+ lightProbes->GetInterpolatedLightProbe(renderer->GetLightProbeInterpolationPosition(node->worldAABB), renderer, &(lightProbeCoeffs[0][0]));
+ }
+ lightProbeCoeffs[0][0] += ambientSH[0];
+ lightProbeCoeffs[0][1] += ambientSH[1];
+ lightProbeCoeffs[0][2] += ambientSH[2];
+ SetSHConstants (lightProbeCoeffs, builtinParamValues);
+
+ // set pass
+ channels = roData.material->SetPassWithShader(p, shader, ss);
+ }
+
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+ prevPassIndex = p;
+ #endif
+
+ if (channels)
+ {
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+ m_BatchRenderer.Add (node->renderer, roData.subsetIndex, channels, node->worldMatrix, transformType);
+ #else
+ SetupObjectMatrix (node->worldMatrix, transformType);
+ node->renderer->Render (roData.subsetIndex, *channels);
+ #endif
+ }
+ }
+ }
+
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+ m_BatchRenderer.Flush();
+ #endif
+
+ GetGfxDevice().SetSRGBWrite(false);
+}
+
+
+PrePassRenderLoop* CreatePrePassRenderLoop()
+{
+ return new PrePassRenderLoop();
+}
+
+void DeletePrePassRenderLoop (PrePassRenderLoop* queue)
+{
+ delete queue;
+}
+
+
+static UInt32 CalculateLightingLayers ()
+{
+ // TODO: Use active lights instead
+ const LightManager::Lights& lights = GetLightManager().GetAllLights();
+ LightManager::Lights::const_iterator it, itEnd = lights.end();
+ UInt32 layers = ~0;
+ for (it = lights.begin(); it != itEnd; ++it)
+ {
+ UInt32 mask = it->GetCullingMask();
+ if (mask == 0)
+ continue;
+ layers &= mask;
+ }
+ return ~layers;
+}
+
+
+#if UNITY_EDITOR
+static void CheckLightLayerUsage (const LightingLayers& layers)
+{
+ static bool s_UsageWasOK = true;
+ bool usageIsOK = (layers.lightLayerCount <= kLightingLayerCount);
+
+ // Only log/remove warning message when broken vs. okay has changed
+ if (usageIsOK == s_UsageWasOK)
+ return;
+
+ s_UsageWasOK = usageIsOK;
+
+ // Remove any previous error
+ // Use instanceID of QualitySettings as log identifier
+ RemoveErrorWithIdentifierFromConsole (GetQualitySettings().GetInstanceID());
+
+ if (!usageIsOK)
+ {
+ std::string msg = Format(
+ "Too many layers used to exclude objects from lighting. Up to %i layers can be used to exclude lights, while your lights use %i:",
+ kLightingLayerCount,
+ layers.lightLayerCount);
+ for (int i = 0; i < LightingLayers::kLayerCount; ++i)
+ {
+ if (layers.lightingLayerMask & (1<<i))
+ {
+ std::string layerName = LayerToString (i);
+ if (layerName.empty())
+ layerName = "Unnamed " + IntToString (i);
+ msg += " '" + layerName + "'";
+ }
+ }
+ // Use instanceID of QualitySettings as log identifier
+ DebugStringToFile (msg, 0, __FILE__, __LINE__, kScriptingWarning, 0, GetQualitySettings().GetInstanceID());
+ }
+}
+#endif
+
+static void ResolveDepthIntoTextureIfNeeded (
+ GfxDevice& device,
+ RenderLoop& renderLoop,
+ DepthBufferFormat depthFormat,
+ RenderTexture*& outDepthRT,
+ TextureID* outDepthTextureID,
+ bool* outDepthWasCopied)
+{
+ // TODO FIXME!! Should add GLES20 here as well, but it's missing GfxDevice::ResolveDepthIntoTexture!
+
+#if GFX_SUPPORTS_D3D9 || GFX_SUPPORTS_D3D11 || GFX_SUPPORTS_OPENGL || GFX_SUPPORTS_OPENGLES30
+ bool needsDepthResolve = false;
+#if GFX_SUPPORTS_D3D9
+ // If doing depth tests & sampling as INTZ is very slow,
+ // do a depth resolve into a separate texture first.
+ needsDepthResolve |= (device.GetRenderer() == kGfxRendererD3D9 && gGraphicsCaps.hasStencilInDepthTexture && gGraphicsCaps.d3d.hasDepthResolveRESZ && gGraphicsCaps.d3d.slowINTZSampling);
+#endif
+#if GFX_SUPPORTS_D3D11
+ // Always needs resolve on D3D11.
+ needsDepthResolve |= (device.GetRenderer() == kGfxRendererD3D11);
+#endif
+#if GFX_SUPPORTS_OPENGL
+ // Needs resolve on OpenGL, unless we did the slow RenderBasePassDepth().
+ // TODO: get rid of buggyPackedDepthStencil
+ needsDepthResolve |= (device.GetRenderer() == kGfxRendererOpenGL) && !gGraphicsCaps.gl.buggyPackedDepthStencil;
+#endif
+#if GFX_SUPPORTS_OPENGLES30
+ // Always needs resolve on GLES30.
+ needsDepthResolve |= (device.GetRenderer() == kGfxRendererOpenGLES30);
+#endif
+
+ if (needsDepthResolve)
+ {
+ DebugAssert (depthFormat != kDepthFormatNone);
+ RenderTexture* depthCopy = GetRenderBufferManager().GetTempBuffer (RenderBufferManager::kFullSize, RenderBufferManager::kFullSize, depthFormat, kRTFormatDepth, RenderBufferManager::kRBSampleOnlyDepth, kRTReadWriteLinear);
+ depthCopy->SetFilterMode (kTexFilterNearest);
+ if (!depthCopy->IsCreated())
+ depthCopy->Create();
+ AddRenderLoopTempBuffer (&renderLoop, depthCopy);
+
+ device.ResolveDepthIntoTexture (depthCopy->GetColorSurfaceHandle (), depthCopy->GetDepthSurfaceHandle ());
+
+ outDepthRT = depthCopy;
+ *outDepthTextureID = depthCopy->GetTextureID ();
+ *outDepthWasCopied = true;
+ }
+
+#endif
+}
+
+#if ENABLE_PRE_PASS_LOOP_HASH_SORTING
+template<typename T>
+static UInt8* InstertToHashBufferPreLoop(const T* p, UInt8* buffer)
+{
+ Assert((sizeof(T) % 4) == 0); // unaligned write
+ *reinterpret_cast<T*>(buffer) = *p;
+ return buffer + sizeof(T);
+ }
+#endif
+
+void DoPrePassRenderLoop (
+ RenderLoopContext& ctx,
+ RenderObjectDataContainer& objects,
+ RenderObjectDataContainer& outRemainingObjects,
+ RenderTexture*& outDepthRT,
+ RenderTexture*& outDepthNormalsRT,
+ RenderTexture*& outMainShadowMap,
+ ActiveLights& activeLights,
+ bool linearLighting,
+ bool* outDepthWasCopied)
+{
+ outDepthRT = NULL;
+ outDepthNormalsRT = NULL;
+ *outDepthWasCopied = false;
+
+ // Allocated on the stack each time, uses temp allocators
+ PrePassRenderLoop loop;
+ loop.m_Context = &ctx;
+ loop.m_Objects = &objects;
+
+ loop.m_PlainRenderPasses.resize_uninitialized(0);
+
+ RenderObjectDataContainer::iterator itEnd = objects.end();
+ size_t roIndex = 0;
+ for (RenderObjectDataContainer::iterator it = objects.begin(); it != itEnd; ++it, ++roIndex)
+ {
+ RenderObjectData& odata = *it;
+ const VisibleNode *node = odata.visibleNode;
+ BaseRenderer* renderer = node->renderer;
+
+ PrePassRenderData rpData;
+ rpData.roIndex = roIndex;
+
+#if ENABLE_PRE_PASS_LOOP_HASH_SORTING
+
+ //hash state information for render object sorter
+ const int kHashBufferSize = 64;
+ UInt8 hashBuffer[kHashBufferSize];
+ UInt8* hashPtr = hashBuffer;
+
+ // Always write 32b granularity into the hash buffer to avoid unaligned writes
+ UInt32 transformType = static_cast<UInt32>(renderer->GetTransformInfo().transformType);
+ hashPtr = InstertToHashBufferPreLoop(&transformType, hashPtr);
+ hashPtr = InstertToHashBufferPreLoop(&node->invScale, hashPtr);
+ hashPtr = InstertToHashBufferPreLoop(&node->lodFade, hashPtr);
+ int materialID = odata.material->GetInstanceID();
+ hashPtr = InstertToHashBufferPreLoop(&materialID, hashPtr);
+ int shaderID = odata.shader->GetInstanceID();
+ hashPtr = InstertToHashBufferPreLoop(&shaderID, hashPtr);
+ int ss = odata.shader->GetShaderLabShader()->GetDefaultSubshaderIndex(kRenderPathExtPrePass);
+ hashPtr = InstertToHashBufferPreLoop(&ss, hashPtr);
+ #if GFX_ENABLE_DRAW_CALL_BATCHING
+ hashPtr = InstertToHashBufferPreLoop(&odata.staticBatchIndex, hashPtr);
+ #endif
+ Assert(hashPtr-hashBuffer <= kHashBufferSize);
+
+ rpData.hash = MurmurHash2A(hashBuffer, hashPtr-hashBuffer, 0x9747b28c);
+#endif
+ loop.m_PlainRenderPasses.push_back( rpData );
+ }
+
+ // Sort objects
+ {
+ PROFILER_AUTO(gPrepassSort, ctx.m_Camera);
+#if ENABLE_PRE_PASS_LOOP_HASH_SORTING
+ loop.SortPreRenderPassData(loop.m_PlainRenderPasses);
+#else
+ std::sort (objects.begin(), objects.end(), RenderPrePassObjectSorter());
+#endif
+ }
+
+ // Setup shadow distance, fade and ambient parameters
+ BuiltinShaderParamValues& params = GetGfxDevice().GetBuiltinParamValues();
+ Vector4f lightFade;
+ Vector4f fadeCenterAndType;
+ CalculateLightShadowFade (*ctx.m_Camera, 1.0f, lightFade, fadeCenterAndType);
+ params.SetVectorParam(kShaderVecLightmapFade, lightFade);
+ params.SetVectorParam(kShaderVecShadowFadeCenterAndType, fadeCenterAndType);
+ params.SetVectorParam(kShaderVecUnityAmbient, Vector4f(GetRenderSettings().GetAmbientLightInActiveColorSpace().GetPtr()));
+
+ GfxDevice& device = GetGfxDevice();
+
+ // Prepare for rendering
+ RenderTexture* rtMain = ctx.m_Camera->GetCurrentTargetTexture ();
+ Assert (rtMain);
+ if (!rtMain->IsCreated())
+ rtMain->Create();
+
+ LightingLayers lightingLayers (CalculateLightingLayers ());
+ #if UNITY_EDITOR
+ CheckLightLayerUsage (lightingLayers);
+ #endif
+
+ // Don't allow shaders to set their own stencil state from base pass until
+ // the end of light pass, since it would screw them up.
+ ShaderLab::g_GlobalAllowShaderStencil = false;
+
+ // Render Geometry base pass
+ MinMaxAABB receiverBounds;
+ RenderTexture* rtNormalsSpec = loop.RenderBasePass (rtMain, lightingLayers, outRemainingObjects, receiverBounds);
+ outDepthRT = rtNormalsSpec;
+
+ RenderSurfaceHandle colorSurfaceHandle = rtNormalsSpec->GetColorSurfaceHandle();
+ RenderSurfaceHandle depthTextureHandle = rtMain->GetDepthSurfaceHandle();
+ TextureID depthTextureID = rtMain->GetSecondaryTextureID();
+ DepthBufferFormat depthFormat = rtMain->GetDepthFormat();
+
+ #if GFX_SUPPORTS_OPENGL
+ if (device.GetRenderer() == kGfxRendererOpenGL && gGraphicsCaps.gl.buggyPackedDepthStencil)
+ {
+ // Separate pass to render depth into a separate target. And then use that texture to read depth
+ // in the lighting pass.
+ RenderTexture* rtDepth = RenderBasePassDepth (ctx, objects, loop.m_PlainRenderPasses);
+ depthTextureID = rtDepth->GetTextureID();
+ outDepthRT = rtDepth;
+ colorSurfaceHandle = rtDepth->GetColorSurfaceHandle();
+ depthTextureHandle = rtDepth->GetDepthSurfaceHandle();
+ *outDepthWasCopied = true;
+ }
+ #endif
+
+ if (gGraphicsCaps.hasStencilInDepthTexture)
+ {
+ const ActiveLight* mainActiveLight = GetMainActiveLight(activeLights);
+ Light* mainLight = mainActiveLight ? mainActiveLight->light : NULL;
+ const bool mainLightHasShadows = mainLight && mainLight->GetType() == kLightDirectional && mainLight->GetShadows() != kShadowNone;
+ const bool cameraNeedsDepthTexture = (ctx.m_Camera->GetDepthTextureMode() & Camera::kDepthTexDepthBit);
+ if (mainLightHasShadows || cameraNeedsDepthTexture)
+ {
+ RenderForwardObjectsIntoDepth (
+ ctx,
+ rtMain,
+ &outRemainingObjects,
+ colorSurfaceHandle,
+ depthTextureHandle,
+ rtMain->GetWidth(),
+ rtMain->GetHeight(),
+ cameraNeedsDepthTexture
+ );
+ }
+ }
+
+ ResolveDepthIntoTextureIfNeeded (device, *(ctx.m_RenderLoop), depthFormat, outDepthRT, &depthTextureID, outDepthWasCopied);
+
+ // Render Lighting pass
+ RenderTexture* rtLight = NULL;
+#if SEPERATE_PREPASS_SPECULAR
+ RenderTexture* rtLightSpec = NULL;
+#endif
+ loop.RenderLighting (activeLights,
+ rtMain,
+ depthTextureID,
+ rtNormalsSpec,
+ rtLight,
+#if SEPERATE_PREPASS_SPECULAR
+ rtLightSpec,
+#endif
+ lightFade,
+ lightingLayers,
+ receiverBounds,
+ &outMainShadowMap);
+
+ // It's again ok for shaders to set their stencil state now.
+ ShaderLab::g_GlobalAllowShaderStencil = true;
+
+ if (ctx.m_Camera->GetClearStencilAfterLightingPass())
+ {
+ float black[] = {0,0,0,0};
+ device.Clear (kGfxClearStencil, black, 1.0f, 0);
+ }
+
+ // Render final Geometry pass
+ loop.RenderFinalPass (rtMain,
+ rtLight,
+#if SEPERATE_PREPASS_SPECULAR
+ rtLightSpec,
+#endif
+ ctx.m_Camera->GetUsingHDR(),
+ linearLighting);
+
+ if (rtLight)
+ {
+ // Do not release the light buffer yet; so that image effects or whatever can access it later
+ // if needed (via _LightBuffer)
+ device.SetSurfaceFlags(rtLight->GetColorSurfaceHandle(), GfxDevice::kSurfaceDefault, ~GfxDevice::kSurfaceRestoreMask);
+ device.SetSurfaceFlags(rtLight->GetDepthSurfaceHandle(), GfxDevice::kSurfaceDefault, ~GfxDevice::kSurfaceRestoreMask);
+ AddRenderLoopTempBuffer (ctx.m_RenderLoop, rtLight);
+ }
+
+#if SEPERATE_PREPASS_SPECULAR
+ if (rtLightSpec)
+ {
+ device.SetSurfaceFlags(rtLightSpec->GetColorSurfaceHandle(), GfxDevice::kSurfaceDefault, ~GfxDevice::kSurfaceRestoreMask);
+ device.SetSurfaceFlags(rtLightSpec->GetDepthSurfaceHandle(), GfxDevice::kSurfaceDefault, ~GfxDevice::kSurfaceRestoreMask);
+ AddRenderLoopTempBuffer (ctx.m_RenderLoop, rtLightSpec);
+ }
+#endif
+
+ // Combine depth+normals if needed
+ if (ctx.m_Camera->GetDepthTextureMode() & Camera::kDepthTexDepthNormalsBit)
+ {
+ outDepthNormalsRT = CombineDepthNormalsTexture (ctx, outRemainingObjects);
+ RenderTexture::SetActive (rtMain);
+ }
+
+ device.SetViewMatrix( ctx.m_CurCameraMatrix.GetPtr() );
+ device.SetNormalizationBackface( kNormalizationDisabled, false );
+}
+
+#endif // GFX_SUPPORTS_RENDERLOOP_PREPASS