山寨SSSSS

Author Avatar
Kanglai Qian 1月 10, 2015
  • 在其它设备中阅读本文章

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

SSSSS打开 SSSSS关闭
unity_sssss_on unity_sssss_off

然后不得不吐槽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_);
}
}