山寨SSSSS

Author Avatar
Kanglai Qian 1月 10, 2015

主要参考了KlayGE里的SSSSS实现,在Unity里山寨了一下:

SSSSS打开 SSSSS关闭

然后不得不吐槽u3d的API太少了,怎么优化都不给力…

  • 参考MRT example用倒是用出来了,不过有点麻烦,而且后来根本没法跑3个Loop,删到只剩下x/y方向各一次也用不上了…
  • 在后处理里没法用stencil真是猎奇啊啊啊,Using the stencil-buffer in a post-process?这个帖子里描述的一模一样;
  • 替换掉Graphics.Blit也基本没有效果提升;
  • 没有factor blend真麻烦,还要自己在代码里算混合…
  • 最后还尝试了在OnPostRender()里搞,如果是相机有RT的话、这样能读取到stencil倒是,不过会有其他的问题

反正就是各种蛋疼,N5上都只有30fps;实在不行只能考虑纹理空间的SSS了,不然每次算整个屏幕太费了~

SSSSS.shaderview raw
Shader "Kanglai/SSSSS" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
}
CGINCLUDE

#include "UnityCG.cginc"

struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};

sampler2D _MainTex;
sampler2D _CameraDepthNormalsTexture; //built-in

float2 step;
float correction;

sampler2D _FrameTex;
float4 _BlendFactor;

v2f vert( appdata_img v ) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord.xy;

return o;
}

half4 GaussianBlur(v2f i){
// Gaussian weights for the six samples around the current pixel:
// -3 -2 -1 +1 +2 +3
float w[6] = { 0.006f, 0.061f, 0.242f, 0.242f, 0.061f, 0.006f };
float o[6] = { -1.0f, -0.6667f, -0.3333f, 0.3333f, 0.6667f, 1.0f };

// Fetch color and linear depth for current pixel:
float4 colorM = tex2D(_MainTex, i.uv);
float depthM = DecodeFloatRG(tex2D (_CameraDepthNormalsTexture, i.uv).zw);

// Accumulate center sample, multiplying it with its gaussian weight:
float4 colorBlurred = colorM;
colorBlurred.rgb *= 0.382f;

// Calculate the step that we will use to fetch the surrounding pixels,
// where "step" is:
// step = sssStrength * gaussianWidth * pixelSize * dir
// The closer the pixel, the stronger the effect needs to be, hence
// the factor 1.0 / depthM.
float2 finalStep = colorM.a * step / depthM * 0.0125f;
for (int j = 0; j < 6; ++ j)
{
// Fetch color and depth for current sample:
float2 offset = i.uv + o[j] * finalStep;
float3 color = tex2D(_MainTex, offset).rgb;
float depth = DecodeFloatRG(tex2D (_CameraDepthNormalsTexture, offset).zw);

// If the difference in depth is huge, we lerp color back to "colorM":
float s = min(correction * abs(depthM - depth), 1);
color = lerp(color, colorM.rgb, s);

// Accumulate:
colorBlurred.rgb += w[j] * color;
}
return colorBlurred;
}

half4 frag1(v2f i) : SV_Target{
half4 o = GaussianBlur(i);
return o;
}

half4 frag2(v2f i) : SV_Target{
half4 colorBlurred = GaussianBlur(i);
half4 frame_input = tex2D(_FrameTex, i.uv);
half4 o;
o.rgb = lerp(frame_input.rgb, colorBlurred.rgb, _BlendFactor.rgb);
o.a = frame_input.a + colorBlurred.a;
return o;
}

ENDCG
SubShader {
Pass {
ZTest Always Cull Off ZWrite Off
Fog { Mode off }

CGPROGRAM
#pragma fragmentoption ARB_precision_hint_fastest
#pragma glsl
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag1
ENDCG
}

Pass {
ZTest Always Cull Off ZWrite Off
Fog { Mode off }
CGPROGRAM
#pragma fragmentoption ARB_precision_hint_fastest
#pragma glsl
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag2
ENDCG
}
}
FallBack off
}
SSSSS.csview raw
using UnityEngine;
using System.Collections;

public class SSSSS : MonoBehaviour {
public Shader sssssShader = null;

static Material sssssMaterial = null;

protected void OnEnable () {
camera.depthTextureMode |= DepthTextureMode.DepthNormals;
}

protected void OnDisable() {
if( sssssMaterial ) {
DestroyImmediate( sssssMaterial );
}
}

protected void CreateMaterials() {
if (sssssMaterial == null) {
sssssMaterial = new Material(sssssShader);
sssssMaterial.hideFlags = HideFlags.DontSave;
}
}

protected void Start()
{
// Disable if we don't support image effects
if (!SystemInfo.supportsImageEffects) {
enabled = false;
Debug.Log("PostProcess SSSSS: Image Effects not supported");
return;
}
if (!SystemInfo.SupportsRenderTextureFormat (RenderTextureFormat.Depth)) {
enabled = false;
Debug.Log("PostProcess SSSSS: Depth Texture not supported");
return;
}

CreateMaterials();
// Disable if the shader can't run on the users graphics card
if (!sssssShader || !sssssMaterial.shader.isSupported) {
enabled = false;
Debug.Log("PostProcess SSSSS: shader or material not found");
return;
}
}

public float sss_strength = 1.0f;
public float correction = 1.0f;


void OnRenderImage (RenderTexture source, RenderTexture destination)
{
RenderTexture blur_x_tex_ = RenderTexture.GetTemporary (source.width/2, source.height/2, 0);

// We may disable/enable the script when running in Editor
CreateMaterials();

sssssMaterial.SetTexture("_MainTex", source);
sssssMaterial.SetFloat("correction", correction);
sssssMaterial.SetVector("step", new Vector4(1.0f * sss_strength / source.width, 0, 0, 0));
Graphics.Blit(source, blur_x_tex_, sssssMaterial, 0);

sssssMaterial.SetTexture("_MainTex", blur_x_tex_);
sssssMaterial.SetFloat("correction", correction);
sssssMaterial.SetVector("step", new Vector4(0, 1.0f * sss_strength / source.height, 0, 0));
sssssMaterial.SetTexture("_FrameTex", source);
sssssMaterial.SetVector("_BlendFactor", new Vector4(0.3251f, 0.45f, 0.3584f, 1.0f));
Graphics.Blit(blur_x_tex_, destination, sssssMaterial, 1);

/*
GL.PushMatrix();
GL.LoadOrtho();

RenderTexture.active = blur_x_tex_;

sssssMaterial.SetTexture("_MainTex", source);
sssssMaterial.SetFloat("correction", correction);
sssssMaterial.SetVector("step", new Vector4(1.0f * sss_strength / source.width, 0, 0, 0));
sssssMaterial.SetPass(0);
GL.Begin(GL.QUADS);
GL.TexCoord2(0.0f, 0.0f); GL.Vertex3(0.0f, 0.0f, 0.1f);
GL.TexCoord2(1.0f, 0.0f); GL.Vertex3(1.0f, 0.0f, 0.1f);
GL.TexCoord2(1.0f, 1.0f); GL.Vertex3(1.0f, 1.0f, 0.1f);
GL.TexCoord2(0.0f, 1.0f); GL.Vertex3(0.0f, 1.0f, 0.1f);
GL.End();

RenderTexture.active = destination;

sssssMaterial.SetTexture("_MainTex", blur_x_tex_);
sssssMaterial.SetFloat("correction", correction);
sssssMaterial.SetVector("step", new Vector4(0, 1.0f * sss_strength / source.height, 0, 0));
sssssMaterial.SetTexture("_FrameTex", source);
sssssMaterial.SetVector("_BlendFactor", new Vector4(0.3251f, 0.45f, 0.3584f, 1.0f));
sssssMaterial.SetPass(1);
GL.Begin(GL.QUADS);
GL.TexCoord2(0.0f, 0.0f); GL.Vertex3(0.0f, 0.0f, 0.1f);
GL.TexCoord2(1.0f, 0.0f); GL.Vertex3(1.0f, 0.0f, 0.1f);
GL.TexCoord2(1.0f, 1.0f); GL.Vertex3(1.0f, 1.0f, 0.1f);
GL.TexCoord2(0.0f, 1.0f); GL.Vertex3(0.0f, 1.0f, 0.1f);
GL.End();

GL.PopMatrix();
*/
RenderTexture.ReleaseTemporary (blur_x_tex_);
}
}