void SetDownsampleBlurOffsetParams(BlurParam blurH, BlurParam blurV, int w, int h) { float invW = 0.5f / w; float invH = 0.5f / h; float offsetX0 = invW * blurH.offset.x; float offsetX1 = invW * blurH.offset.y; float offsetY0 = invH * blurV.offset.x; float offsetY1 = invH * blurV.offset.y; m_downsampleShader.SetVector(s_downSampleBlurOffset0ParamID, new Vector4(offsetX0, offsetY0, -offsetX0, -offsetY0)); m_downsampleShader.SetVector(s_downSampleBlurOffset1ParamID, new Vector4(offsetX0, offsetY1, -offsetX0, -offsetY1)); m_downsampleShader.SetVector(s_downSampleBlurOffset2ParamID, new Vector4(offsetX1, offsetY0, -offsetX1, -offsetY0)); m_downsampleShader.SetVector(s_downSampleBlurOffset3ParamID, new Vector4(offsetX1, offsetY1, -offsetX1, -offsetY1)); }
void OnPostRender() { #if UNITY_EDITOR if (!(Application.isPlaying || m_isRenderingFromUpdate)) { return; } #endif m_camera.clearFlags = CameraClearFlags.Nothing; if (!m_isVisible) { return; } RenderTexture srcRT = m_camera.targetTexture; m_camera.targetTexture = null; if (m_superSampling != TextureSuperSample.x1 || HasShadowColor()) { m_downsampleShader.color = m_shadowColor; // downsample RenderTexture dstRT; if (0 < m_blurLevel) { dstRT = RenderTexture.GetTemporary(m_textureWidth, m_textureHeight, 0, m_shadowTexture.format, RenderTextureReadWrite.Linear); dstRT.filterMode = FilterMode.Bilinear; } else { dstRT = m_shadowTexture; } Graphics.SetRenderTarget(dstRT); int pass = m_superSampling == TextureSuperSample.x16 ? 0 : 2; Graphics.Blit(srcRT, dstRT, m_downsampleShader, HasShadowColor() ? pass + 1 : pass); RenderTexture.ReleaseTemporary(srcRT); srcRT = dstRT; } if (0 < m_blurLevel) { // adjust blur size according to texel aspect float texelAspect = (m_projector.aspectRatio * m_textureHeight) / (float)m_textureWidth; float blurSizeH = m_blurSize; float blurSizeV = m_blurSize; if (texelAspect < 1.0f) { blurSizeV *= texelAspect; } else { blurSizeH /= texelAspect; } // blur parameters BlurParam blurH = GetBlurParam(blurSizeH, m_blurFilter); BlurParam blurV = GetBlurParam(blurSizeV, m_blurFilter); blurH.tap = (blurH.tap - 3); // index of pass blurV.tap = (blurV.tap - 3) + 1; // index of pass m_blurShader.SetVector(s_blurOffsetHParamID, blurH.offset); m_blurShader.SetVector(s_blurOffsetVParamID, blurV.offset); m_blurShader.SetVector(s_blurWeightHParamID, blurH.weight); m_blurShader.SetVector(s_blurWeightVParamID, blurV.weight); RenderTexture dstRT = RenderTexture.GetTemporary(m_textureWidth, m_textureHeight, 0, m_shadowTexture.format, RenderTextureReadWrite.Linear); dstRT.filterMode = FilterMode.Bilinear; srcRT.wrapMode = TextureWrapMode.Clamp; dstRT.wrapMode = TextureWrapMode.Clamp; Graphics.Blit(srcRT, dstRT, m_blurShader, blurH.tap); if (1 < srcRT.antiAliasing) { RenderTexture.ReleaseTemporary(srcRT); srcRT = RenderTexture.GetTemporary(m_textureWidth, m_textureHeight, 0, m_shadowTexture.format, RenderTextureReadWrite.Linear); } else { srcRT.DiscardContents(); } for (int i = 1; i < m_blurLevel - 1; ++i) { Graphics.Blit(dstRT, srcRT, m_blurShader, blurV.tap); dstRT.DiscardContents(); Graphics.Blit(srcRT, dstRT, m_blurShader, blurH.tap); srcRT.DiscardContents(); } RenderTexture.ReleaseTemporary(srcRT); srcRT = m_shadowTexture; Graphics.Blit(dstRT, srcRT, m_blurShader, blurV.tap); RenderTexture.ReleaseTemporary(dstRT); } Graphics.SetRenderTarget(m_shadowTexture); if (srcRT != m_shadowTexture) { Graphics.Blit(srcRT, m_downsampleShader, 2); if (m_mipLevel == 0) { RenderTexture.ReleaseTemporary(srcRT); } } EraseShadowOnBoarder(m_textureWidth, m_textureHeight); if (0 < m_mipLevel) { // setup blur parameters BlurParam blurH = new BlurParam(), blurV = new BlurParam(); if (0.1f < m_mipmapBlurSize) { // adjust blur size according to texel aspect float texelAspect = (m_projector.aspectRatio * m_textureHeight) / (float)m_textureWidth; float blurSizeH = m_mipmapBlurSize; float blurSizeV = m_mipmapBlurSize; if (texelAspect < 1.0f) { blurSizeV *= texelAspect; } else { blurSizeH /= texelAspect; } // blur parameters if (m_singlePassMipmapBlur) { blurH = GetDownsampleBlurParam(2.0f + 2.0f * blurSizeH, m_blurFilter); blurV = GetDownsampleBlurParam(2.0f + 2.0f * blurSizeV, m_blurFilter); Vector4 weight = new Vector4(blurH.weight.x * blurV.weight.x, blurH.weight.x * blurV.weight.y, blurH.weight.y * blurV.weight.x, blurH.weight.y * blurV.weight.y); float a = 0.25f / (weight.x + weight.y + weight.z + weight.w); weight.x = Mathf.Round(255 * a * weight.x) / 255.0f; weight.y = Mathf.Round(255 * a * weight.y) / 255.0f; weight.z = Mathf.Round(255 * a * weight.z) / 255.0f; weight.w = 0.25f - weight.x - weight.y - weight.z; m_downsampleShader.SetVector(s_downSampleBlurWeightParamID, weight); } else { blurH = GetBlurParam(blurSizeH, m_blurFilter); blurV = GetBlurParam(blurSizeV, m_blurFilter); blurH.tap = (blurH.tap - 3); // index of pass blurV.tap = (blurV.tap - 3) + 1; // index of pass m_blurShader.SetVector(s_blurOffsetHParamID, blurH.offset); m_blurShader.SetVector(s_blurOffsetVParamID, blurV.offset); m_blurShader.SetVector(s_blurWeightHParamID, blurH.weight); m_blurShader.SetVector(s_blurWeightVParamID, blurV.weight); } } int w = m_textureWidth >> 1; int h = m_textureHeight >> 1; RenderTexture tempRT = RenderTexture.GetTemporary(w, h, 0, m_shadowTexture.format, RenderTextureReadWrite.Linear); tempRT.filterMode = FilterMode.Bilinear; bool downSampleWithBlur = m_singlePassMipmapBlur && 0.1f < m_mipmapBlurSize; if (downSampleWithBlur) { SetDownsampleBlurOffsetParams(blurH, blurV, w, h); } if (srcRT == m_shadowTexture) { if (downSampleWithBlur) { Graphics.Blit(srcRT, tempRT, m_downsampleShader, 5); } else { Graphics.Blit(srcRT, tempRT, m_copyMipmapShader, 1); } } else { Graphics.Blit(srcRT, tempRT, m_downsampleShader, downSampleWithBlur ? 4 : 0); RenderTexture.ReleaseTemporary(srcRT); } srcRT = tempRT; int i = 0; float falloff = 1.0f; for ( ; ;) { if (0.1f < m_mipmapBlurSize && !m_singlePassMipmapBlur) { tempRT = RenderTexture.GetTemporary(w, h, 0, m_shadowTexture.format, RenderTextureReadWrite.Linear); tempRT.filterMode = FilterMode.Bilinear; tempRT.wrapMode = TextureWrapMode.Clamp; srcRT.wrapMode = TextureWrapMode.Clamp; Graphics.Blit(srcRT, tempRT, m_blurShader, blurH.tap); srcRT.DiscardContents(); Graphics.Blit(tempRT, srcRT, m_blurShader, blurV.tap); RenderTexture.ReleaseTemporary(tempRT); } if (m_mipmapFalloff == MipmapFalloff.Linear) { falloff = ((float)(m_mipLevel - i)) / (m_mipLevel + 1.0f); } else if (m_mipmapFalloff == MipmapFalloff.Custom && m_customMipmapFalloff != null && 0 < m_customMipmapFalloff.Length) { falloff = m_customMipmapFalloff[Mathf.Min(i, m_customMipmapFalloff.Length - 1)]; } m_copyMipmapShader.SetFloat(s_falloffParamID, falloff); m_copyMipmapShader.SetFloat(s_falloffParamID, falloff); m_shadowTexture.DiscardContents(); // To avoid Tiled GPU perf warning. It just tells GPU not to copy back the rendered image to a tile buffer. It won't destroy the rendered image. ++i; Graphics.SetRenderTarget(m_shadowTexture, i); Graphics.Blit(srcRT, m_copyMipmapShader, 0); EraseShadowOnBoarder(w, h); w = Mathf.Max(1, w >> 1); h = Mathf.Max(1, h >> 1); if (i == m_mipLevel || w <= 4 || h <= 4) { RenderTexture.ReleaseTemporary(srcRT); break; } tempRT = RenderTexture.GetTemporary(w, h, 0, m_shadowTexture.format, RenderTextureReadWrite.Linear); tempRT.filterMode = FilterMode.Bilinear; if (downSampleWithBlur) { SetDownsampleBlurOffsetParams(blurH, blurV, w, h); Graphics.Blit(srcRT, tempRT, m_downsampleShader, 4); } else { Graphics.Blit(srcRT, tempRT, m_downsampleShader, 0); } RenderTexture.ReleaseTemporary(srcRT); srcRT = tempRT; } while (1 <= w || 1 <= h) { ++i; Graphics.SetRenderTarget(m_shadowTexture, i); GL.Clear(false, true, new Color(1, 1, 1, 0)); w = w >> 1; h = h >> 1; } } }
static BlurParam GetDownsampleBlurParam(float blurSize, BlurFilter filter) { BlurParam param = new BlurParam(); param.tap = 4; if (blurSize < 0.1f) { param.offset.x = 0.0f; param.offset.y = 0.0f; param.offset.z = 0.0f; param.offset.w = 0.0f; param.weight.x = 1.0f; param.weight.y = 0.0f; param.weight.z = 0.0f; param.weight.w = 0.0f; return(param); } // calculate weights if (filter == BlurFilter.Gaussian) { // gaussian filter float a = 1.0f / (2.0f * blurSize * blurSize); float totalWeight = 0.0f; for (int i = 0; i < param.tap; ++i) { float x = i + 0.5f; s_blurWeights[i] = Mathf.Exp(-x * x * a); totalWeight += 2.0f * s_blurWeights[i]; } float w = 1.0f / totalWeight; for (int i = 0; i < param.tap; ++i) { s_blurWeights[i] *= w; } } else { // uniform filter float a = 0.5f / blurSize; for (int i = 0; i < param.tap; ++i) { if (i + 1 <= blurSize) { s_blurWeights[i] = a; } else if (i < blurSize) { s_blurWeights[i] = a * (blurSize - i); } else { s_blurWeights[i] = 0.0f; } } } param.offset.x = 0.5f + s_blurWeights[1] / (s_blurWeights[0] + s_blurWeights[1]); param.offset.y = 2.5f + s_blurWeights[3] / (s_blurWeights[2] + s_blurWeights[3]); param.offset.z = 0.0f; param.offset.w = 0.0f; param.weight.x = s_blurWeights[0] + s_blurWeights[1]; param.weight.y = s_blurWeights[2] + s_blurWeights[3]; param.weight.z = 0.0f; param.weight.w = 0.0f; return(param); }
static BlurParam GetBlurParam(float blurSize, BlurFilter filter) { BlurParam param = new BlurParam(); if (blurSize < 0.1f) { param.tap = 3; param.offset.x = 0.0f; param.offset.y = 0.0f; param.offset.z = 0.0f; param.offset.w = 0.0f; param.weight.x = 1.0f; param.weight.y = 0.0f; param.weight.z = 0.0f; param.weight.w = 0.0f; return(param); } // calculate weights if (filter == BlurFilter.Gaussian) { // gaussian filter float a = 1.0f / (2.0f * blurSize * blurSize); float totalWeight = 1.0f; s_blurWeights[0] = 1.0f; for (int i = 1; i < s_blurWeights.Length; ++i) { s_blurWeights[i] = Mathf.Exp(-i * i * a); totalWeight += 2.0f * s_blurWeights[i]; } float w = 1.0f / totalWeight; for (int i = 0; i < s_blurWeights.Length; ++i) { s_blurWeights[i] *= w; } } else { // uniform filter float a = 0.5f / (0.5f + blurSize); for (int i = 0; i < s_blurWeights.Length; ++i) { if (i <= blurSize) { s_blurWeights[i] = a; } else if (i - 1 < blurSize) { s_blurWeights[i] = a * (blurSize - (i - 1)); } else { s_blurWeights[i] = 0.0f; } } } param.offset.x = 1.0f + s_blurWeights[2] / (s_blurWeights[1] + s_blurWeights[2]); param.offset.y = 3.0f + s_blurWeights[4] / (s_blurWeights[3] + s_blurWeights[4]); param.offset.z = 5.0f + s_blurWeights[6] / (s_blurWeights[5] + s_blurWeights[6]); param.offset.w = 0.0f; if (s_blurWeights[3] < 0.02f) { param.tap = 3; float a = 0.5f / (0.5f * s_blurWeights[0] + s_blurWeights[1] + s_blurWeights[2]); param.weight.x = Mathf.Round(255 * a * s_blurWeights[0]) / 255.0f; param.weight.y = 0.5f - 0.5f * param.weight.x; param.weight.z = 0.0f; param.weight.w = 0.0f; } else if (s_blurWeights[5] < 0.02f) { param.tap = 5; float a = 0.5f / (0.5f * s_blurWeights[0] + s_blurWeights[1] + s_blurWeights[2] + s_blurWeights[3] + s_blurWeights[4]); param.weight.x = Mathf.Round(255 * a * s_blurWeights[0]) / 255.0f; param.weight.y = Mathf.Round(255 * a * (s_blurWeights[1] + s_blurWeights[2])) / 255.0f; param.weight.z = 0.5f - (0.5f * param.weight.x + param.weight.y); param.weight.w = 0.0f; } else { param.tap = 7; param.weight.x = Mathf.Round(255 * s_blurWeights[0]) / 255.0f; param.weight.y = Mathf.Round(255 * (s_blurWeights[1] + s_blurWeights[2])) / 255.0f; param.weight.z = Mathf.Round(255 * (s_blurWeights[3] + s_blurWeights[4])) / 255.0f; param.weight.w = 0.5f - (0.5f * param.weight.x + param.weight.y + param.weight.z); } return(param); }