精华内容
下载资源
问答
  • unity-shader-屏幕空间反射

    千次阅读 2019-06-19 21:24:56
    title: unity-shader-屏幕空间反射ssr categories: Unity3d-Shader tags: [unity, shader, ssr, 屏幕空间反射] date: 2019-06-18 22:22:28 comments: false 顾名思义, 是基于屏幕 rt 的. 前篇 unity学习笔记之...

    title: unity-shader-屏幕空间反射
    categories: Unity3d-Shader
    tags: [unity, shader, 屏幕空间反射]
    date: 2019-06-18 22:22:28
    comments: false

    顾名思义, 是基于屏幕 rt 的.
    这个 shader 并不适用于 移动平台, 只能跑在 游戏主机 及 桌面pc 上, 因为使用了 延迟渲染 gbuffer 中的 法线图.


    前篇

    Raymarch

    Raymarch 叫光线步进,是光线追踪在光栅化中运行的一个形式,当光线沿着他的方向向前走一个单位距离,检查一下是否碰到物体。有则停下,返回颜色或者深度结果。没有则继续向前,直到走到最大步数并终止。

    下图是raymarch示意图

    第四个步进的时候才碰撞到物体


    效果


    原理

    前置条件

    使用 Deferred Shading, 因为要使用到 法线图 及 高光图中.a 通道, 另外在使用到 深度图

    流程

    参考: https://github.com/bodhid/UnityScreenSpaceReflections 的流程分析

    笔记仓库中的 SSR-Simple

    总的流程分两步, 异步是正常的 延迟渲染, 然后通过 后处理, 计算出 反射 部分的 RT, 然后根据 光滑度 及 反射因子 进行 lerp 插值得到最终的像素

    1. 后处理计算出 反射 部分的 RT

      1. 利用 深度图, 计算出像素点的世界坐标位置.

        利用深度图 构建 ndc 坐标 (注意 z 值应该做平台区分, dx (0~1) 和 opengl(-1~1) 不一样), 再用 vp矩阵的 逆矩阵 (有 csharp 中计算好传入 shader) 算出 世界坐标

        (xyzMatrix = mul(_CAMERA_XYZMATRIX, float4(uv_depth*2.0 - 1.0, SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv_depth), 1.0))) / xyzMatrix.w;
        
      2. 使用 gbuff 中的 法线图, 算出 反射 方向

        // 从 gbuffer 的 法线图 获取 世界空间下的 法线值, 并转到 [-1, 1] 区间下
        float3 worldNormal = tex2D(_CameraGBufferTexture2, i.uv).rgb * 2.0 - 1.0;
        // dir 是 观察方向的 反射方向, 且有 步进
        dir = reflect(viewDir, worldNormal) * sampleDistance;//sample dis
        
      3. 进行 ray 碰撞算法

    2. 将 反射 部分的 RT 进行 高斯模糊 一下, 效果会好点

      //blur SSR result
      for (int i = 0; i < blurAmount; ++i) {
          blurMaterial.SetVector("_Direction", new Vector4(1, 0, 0, 0));
          Graphics.Blit(tempA, tempB, blurMaterial);
          blurMaterial.SetVector("_Direction", new Vector4(0, 1, 0, 0));
          Graphics.Blit(tempB, tempA, blurMaterial);
      }
      
    3. 然后根据 光滑度 及 反射因子 进行 lerp 插值得到最终的像素

      float4 ssr = tex2D(_SSR,i.uv); // _SSR 反射 部分的 RT
      
      // 取出 gbuffer 的 光滑度
      float smoothness = tex2D(_CameraGBufferTexture1,i.uv).a;
      float4 col = tex2D(_MainTex, i.uv); // _MainTex 正常渲染的 后处理 原图
      
      // ssr.a 是 碰撞的 步进次数相关, 步进越大表示应该反射的效果越差
      return col * lerp(1, ssr, smoothness * ssr.a);
      

    unity 内置 Post-processing 中的 ssr

    官网文档: Screen Space Reflection - https://docs.unity3d.com/Manual/PostProcessing-ScreenSpaceReflection.html

    for Projects running on current-gen consoles and desktop computers. It’s not suitable for mobile development. Because it relies on the Normals G-Buffer, it is only available in the deferred rendering path.

    可以跑在 游戏主机 及 桌面pc, 不适用于 移动平台. 因为使用了 延迟渲染 gbuffer 中的 法线图.

    展开全文
  • unity屏幕空间坐标shader

    千次阅读 2016-05-21 16:04:09
    屏幕坐标效果不受物体在空间位置的影响, 只受到物体本身和视口的影响,有时候可以做一些特殊效果 比如2D游戏平铺纹理,调试屏幕位置,屏幕特效等等 http://docs.unity3d.com/Manual/SL-ShaderSemantics.html ...

    屏幕坐标效果不受物体在空间位置的影响,

    只受到物体本身和视口的影响,有时候可以做一些特殊效果

    比如2D游戏平铺纹理,调试屏幕位置,屏幕特效等等


    下面是根据官方例子修改的

    http://docs.unity3d.com/Manual/SL-ShaderSemantics.html

    Shader "Unlit/Show UVs"
    {    Properties
    {
    	[NoScaleOffset]_MainTex("Texture", 2D) = "white" {}
    }
    SubShader
    {
    	Pass
    {
    	CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag
    #pragma target 3.0
    
    struct v2f {
    	float2 uv : TEXCOORD0;
    	//out float4 outpos2 : SV_POSITION;
    	//float4 pos : SV_POSITION;
    };
    sampler2D _MainTex;
    struct ccc {
    	float4 vertex : POSITION; // vertex position input
    	float2 uv : TEXCOORD0;// first texture coordinate input
    
    };
    
    v2f vert(ccc c,
    	out float4 outpos : SV_POSITION // clip space position output
    	)
    {
    	v2f o;
    	//o.outpos2 = mul(UNITY_MATRIX_MVP, vertex);
    	outpos = mul(UNITY_MATRIX_MVP, c.vertex);
    	o.uv = c.uv;
    	return o;
    
    }
    
       
    
     
    然后是测试代码 
    

    可以通过改动红色代码看到效果

    Shader "Unlit/Show UVs"
    {    Properties
    {
    	[NoScaleOffset]_MainTex("Texture", 2D) = "white" {}
    }
    SubShader
    {
    	Pass
    {
    	CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag
    #pragma target 3.0
    
    struct v2f {
    	float2 uv : TEXCOORD0;
    	//out float4 outpos2 : SV_POSITION;
    	//float4 pos : SV_POSITION;
    };
    
    sampler2D _MainTex;
    struct ccc {
    	float4 vertex : POSITION; // vertex position input
    	float2 uv : TEXCOORD0;// first texture coordinate input
    
    };
    
    v2f vert(ccc c,
    	out float4 outpos : SV_POSITION // clip space position output
    	)
    {
    	v2f o;
    	outpos = mul(UNITY_MATRIX_MVP, c.vertex);
    	o.uv = c.uv;
    	return o;
    }
    //screenPos.xy就是讲像素点0.5具象化
    //每个格子相当于一个像素 这是其他博主的理解 我再次理解下就是说半像素问题0.5像素是半透明的
    //整个不再边缘的像素才是原本设置的颜色
    //像素坐标的类型是不同的,所以为了方便使用 UNITY_VPOS_TYPE 类型
    
    fixed4 frag(v2f i,
    	UNITY_VPOS_TYPE screenPos : VPOS
    	) : SV_Target
    {
    //clip(checker);//停止渲染负值部分,0和正的会被渲染
    fixed4 c = fixed4(screenPos.x-2, screenPos.y -2,0,0);
    return c;
    }
    ENDCG
    }
    }
    }



    可以看到如上面设置的时候,在边缘可以看到半透明的红色和绿色的像素线,这个就是
    screenPos.x  和 screenPos.y的具像化

    其他地方是黄色因为其他地方的值超过了1也就是说绿色和红色都是1,所以加起来是黄色

    如果你使用

    fixed4 c = fixed4(screenPos.x-1 , screenPos.y-1,0,0);
    会看到下面效果(注意这是放大了的结果)


    发现有红色和绿色偏移了

    但是通过取色,发现颜色并不准确,因为最边缘一个像素会被窗口边界上色(大概)

    如果偏移2个像素就会发现中间的一行是正常的颜色


    另外说下默认状态可能ps取色有色彩,需要改一下取色器的采样设置






    展开全文
  • UnityShader屏幕空间全息扫描效果

    千次阅读 多人点赞 2017-10-28 21:49:00
    前段时间刚玩《使命召唤11》的时候发现里面新增了一种很高科技的扫描手榴弹,可以产生一圈类似全息效果的扫描圈,并显示出墙后的敌人,类似这样: 最近打算实现一个用在第三...3.计算屏幕空间世界坐标到扫描发起

    前段时间刚玩《使命召唤11》的时候发现里面新增了一种很高科技的扫描手榴弹,可以产生一圈类似全息效果的扫描圈,并显示出墙后的敌人,类似这样:



    最近打算实现一个用在第三人称中类似的效果,如下:



    实现方案:

    1.根据_CameraDepthTexture计算屏幕空间像素点的世界坐标

    2.将扫描发起位置的世界坐标传入shader

    3.计算屏幕空间世界坐标到扫描发起位置世界坐标的距离

    4.根据相关参数渲染出扫描区域


    1._CameraDepthTexture中记录了投影空间的深度信息,通过如下方式可以得到世界坐标:

    fixed depth = tex2D(_CameraDepthTexture, i.uv).r;
    fixed4 projPos = fixed4(i.uv.x * 2 - 1, i.uv.y * 2 - 1, -depth * 2 + 1, 1);
    worldPos = mul(internalCameraToWorld, worldPos);
    worldPos /= worldPos.w;


    2.计算传入的初始位置和屏幕空间世界坐标距离:

    fixed dis = length(internalCentPos.xyz - worldPos.xyz);
     
    fixed a = 1 - saturate((abs(dis - internalArg.x) - internalArg.y) / internalArg.z);
    a = a * internalFade.x + c * internalFade.y;
    最终可以得到如下效果:

    3.保存上一步的渲染结果,使用CommandBuffer,将需要标记为持续显示的目标(例如敌人)也渲染到该纹理,注意需要判断目标是否在摄像机内,效果如下:


    public static void CallRender(Vector3 worldPosition, Renderer[] renderer)
        {
            if (!IsInitialized())
                return;
            if (instance.m_IsShowingEffect)
            {
                if (renderer == null)
                    return;
                Vector3 pjpos = instance.m_Camera.worldToCameraMatrix.MultiplyPoint(worldPosition);
                pjpos = instance.m_Camera.projectionMatrix.MultiplyPoint(pjpos);
                if (pjpos.x < -1 || pjpos.x > 1 || pjpos.y < -1 || pjpos.y > 1 || pjpos.z < -1 || pjpos.z > 1)
                    return;
                for (int i = 0; i < renderer.Length; i++)
                {
                    instance.m_CommandBuffer.DrawRenderer(renderer[i], instance.m_ReplaceMaterial);
                }
            }
        }


    4.根据屏幕uv信息将屏幕uv栅格化,并计算每个格子中采样到的颜色值,可以得到如下结果:



    float2 fl = floor(i.uv * _EffectScale);
    float dp = tex2D(_PreTex, (fl + float2(0.5, 0.5)) / _EffectScale);
                     
    float4 led = tex2D(_EffectTex, i.uv * _EffectScale - fl);
                     
    col.rgb += led.rgb*dp;

    5.同样根据刚刚栅格的结果,可以计算出每一小格的uv,根据该uv来采样用于作为全息扫描效果的纹理,得到如下结果:



    6.叠加最终结果:



    Git地址请点击博客原文


    展开全文
  • UnityShader——屏幕空间反射(一)

    千次阅读 2018-06-05 19:20:00
    的方法,即每次步进都至少移动一个像素点,以避免在同一个像素点重复采样,作者还贴心的给出了完整代码,该方法通过DDA在屏幕空间绘制直线以避免同一个像素多次采样,然后将直线转换到 view space 得到 z 值并与该...

    从Crytek提出SSR后,基本上成了各种大作标配,而自己不知道有意还是无意居然也一直没有自己写过SSR实现,正好最近有点时间,自己写了写试了试,啃了啃别人的源码,博客内容主要是自己在学习过程中的一些踩坑试错过程和分析,写在这权当笔记了,想直接看成熟方案的看官可以绕道了。

    SSR本质上就是 screen space raytracing 的一种应用, 通过深度和法线贴图重建视锥内的世界,之前的博客我们扒过 Unity 的 global fog 重建世界的方法,其通过一个自定义的 blit 函数将视锥远截面的四个顶点传入 Shader 来插值,但实际上我们可以不用那么麻烦,我们直接插值内置的 Blit 函数所绘制的 Quad 顶点的 UV 就可以了,因为 Blit 函数所构造的 quad 在经过坐标转换和裁剪后, 是恰好覆盖屏幕的(要不然怎么叫屏幕特效呢),其顶点的屏幕位置和顶点 UV 是恰好对应的:

    v2f vert (appdata v)
    {
        v2f o;
        o.vertex = UnityObjectToClipPos(v.vertex);
        o.test = v.uv;
        o.test1 = o.vertex;
        return o;
    }
    fixed4 frag (v2f i) : SV_Target
    {               
        return fixed4(i.test1.xy/i.test1.w * 0.5 + 0.5, 0, 1);
        return fixed4(i.test.xy,0,1);
    }

    上面代码中两个返回的结果是一模一样的

    因此,我们直接构造

    o.rayDirVS = mul(unity_CameraInvProjection, float4((float2(v.uv.x, v.uv.y) - 0.5) * 2, 1, 1));

    便可在片元着色器中得到相机出发、穿过每一个像素点到达相机远截面的射线,结合深度贴图,便可得到 view space 中每个像素的位置:

    float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);
    depth = Linear01Depth (depth);
    float3 view_pos = i.rayDirVS.xyz/i.rayDirVS.w * depth;  

    然后我们获取法线并转换至相机空间:

    float3 normal = tex2D (_CameraGBufferTexture2, i.uv).xyz * 2.0 - 1.0;
    normal = mul((float3x3)_NormalMatrix, normal);

    注意到这里使用了自己传过来的矩阵而不是内置的矩阵,因为 blit 函数生成的 quad 在绘制时已经调用了 GL.LoadOrtho 来进行坐标转换,因此在 Shader 中, UNITY_MATRIX_M、UNITY_MATRIX_V 都是单位矩阵, UNITY_MATRIX_P 则是:

    2000020000001111 [ 2 0 0 − 1 0 2 0 − 1 0 0 0 1 0 0 0 1 ]

    接下来便正式开始计算反射了,原始思路非常简单,有了 normal 和 ray, 可以非常简单的计算出反射后的光线,计算该光线与 depth 的交点,拿到该交点处的着色信息叠加到反射点就行了,重点就在于光线与 depth 求交,方法都是光线步进,基于这个思路,我们马上可以写出最简单的实现:

    #define MAX_TRACE_DIS 50
    #define MAX_IT_COUNT 50         
    #define EPSION 0.1
    
    float2 PosToUV(float3 vpos)
    {
        float4 proj_pos = mul(unity_CameraProjection, float4(vpos ,1));
        float3 screenPos = proj_pos.xyz/proj_pos.w; 
        return float2(screenPos.x,screenPos.y) * 0.5 + 0.5;
    }
    
    float compareWithDepth(float3 vpos, out bool isInside)
    {                   
        float2 uv = PosToUV(vpos);
                float depth = tex2D (_CameraDepthTexture, uv);
                depth = LinearEyeDepth (depth);   
                isInside =  uv.x > 0 && uv.x < 1 && uv.y > 0 && uv.y < 1;           
                return depth + vpos.z;
    }
    
    bool rayTrace(float3 o, float3 r, out float3 hitp)
    {
        float3 start = o;
        float3 end = o;
        float stepSize = 0.15;//MAX_TRACE_DIS / MAX_IT_COUNT;
        for (int i = 1; i <= MAX_IT_COUNT; ++i)
        {
            end = o + r * stepSize * i;
            if(length(end - start) > MAX_TRACE_DIS)
                return false;
    
            bool isInside = true;
            float diff = compareWithDepth(end, isInside);
            if(isInside)
            {
                if(abs(diff) < EPSION)
                {
                    hitp = end;
                    return true;
                }
            }
            else
            {
                return false;
            }
        }
        return false;
    }
    
    fixed4 frag (v2f i) : SV_Target
    {
        fixed4 col = tex2D(_MainTex, i.uv);
    
        float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);
        depth = Linear01Depth (depth);
        float3 view_pos = i.rayDirVS.xyz/i.rayDirVS.w * depth;                              
        float3 normal = tex2D (_CameraGBufferTexture2, i.uv).xyz * 2.0 - 1.0;
        normal = mul((float3x3)_NormalMatrix, normal);
        float3 reflectedRay = reflect(normalize(view_pos), normal);
    
        float3 hitp = 0;
        if(rayTrace(view_pos, reflectedRay, hitp))
        {
            float2 tuv = PosToUV(hitp); 
            float3 hitCol = tex2D (_CameraGBufferTexture0, tuv);
            col += fixed4(hitCol, 1);
        }
        return col;
    }


    至于效果嘛,也非常屎,而且,在特定角度下,我们还能看到这样的现象:


    原因如下图所示:


    在第一步时,如果 diff < EPSION, 则反射光线与反射点所在的平面相交了,然后就出现了自己反射自己的现象,那么解决方法有两个:
    1、增加第一个采样点的距离
    2、减小阈值 EPSION

    第一个方法显然不行,例如我们把代码改成:

    for (int i = 3; i <= MAX_IT_COUNT; ++i)

    结果就会变成这样:


    近处的相交完全检测不到了

    但如果在步长和采样方法不变的时候直接减小阈值的话,一样会漏掉许多相交点:


    如果不是要完全舍弃性能的话,剩下的就只有在步进过程中变动步长了,Binary Search 呼之欲出:

    先定义一个步进,当发现某个step与几何体相交时,将step的步进/2,并以该step的起点为总体起点重新进行step march,并找到新的相交点;以此循环N次,N=iteration;

    根据这个思路我们把代码改成这样:

    #define MAX_TRACE_DIS 50
    #define MAX_IT_COUNT 10
    #define MAX_BS_IT 5         
    uniform float EPSION;
    uniform float _StepSize;
    
    bool rayTrace(float3 o, float3 r, float stepSize, out float3 _end, out float _diff)
    {
        float3 start = o;
        float3 end = o;         
        for (int i = 1; i <= MAX_IT_COUNT; ++i)
        {
            end = o + r * stepSize * i;
            if(stepSize * i > MAX_TRACE_DIS)
                return false;
    
            bool isInside = true;
            float diff = compareWithDepth(end, isInside);
            if(!isInside)
                return false;
    
            _diff = diff;
            _end = end;
    
            if(_diff < 0)
                return true;
        }
        return false;
    }
    
    bool BinarySearch(float3 o, float3 r, out float3 hitp, out float debug)
    {
        float3 start = o;
        float3 end = o;
        float sign = 1;
        float stepSize = _StepSize;
        float diff = 0;
        for (int i = 0; i < MAX_BS_IT; ++i)
        {
            if(rayTrace(start, r, stepSize, end, diff))
            {
                if(abs(diff) < EPSION)
                {
                    debug = diff;
                    hitp = end;
                    return true;
                }
                start = end - stepSize * r;
                stepSize /= 2;
            }
            else
            {
                return false;
            }
        }
        return false;
    }

    最大循环次数保持和之前一样,初始步长0.3, 阈值0.03的效果如图:


    效果比之前好上不少,但依然是一坨 Shit,但我们毕竟下官方 Post Processing Pack 中的SSR的相同迭代次数类似步长下模糊降至最低的效果:


    嗯。。貌似也好不到哪去,先忍住不看官方的源码,要不然快乐就少了一倍(哈哈,反正也没人催我弄),那么,接下来从哪开始进行优化呢?

    依然是先从光线步进的算法下手,这些年发展下来,大佬们不断的提出了优化的方法,这里主要参考的是 Morgan McGuire 的方法,即每次步进都至少移动一个像素点,以避免在同一个像素点重复采样,作者还贴心的给出了完整代码,该方法通过DDA在屏幕空间绘制直线以避免同一个像素多次采样,然后将直线转换到 view space 得到 z 值并与该点的深度进行比较,这里要注意的就是 z 值是按照 1/z 进行线性插值的,否则计算出来的 z 值和 depthbuffer 中的深度是对不上的,具体证明可以参考我之前的博客,原代码中有一些我觉得莫名其妙的地方,基本上就按照自己的理解写了,代码如下:

    #define MAX_IT_COUNT 50                 
    uniform float EPSION;
    uniform float _StepSize;
    uniform float _MaxLength;
    
    inline bool isIntersectWithDepth(float zb, float2 hitPixel)
    {
        float depth = tex2D (_CameraDepthTexture, hitPixel * 0.5 + 0.5);
        depth = LinearEyeDepth (depth);
        return abs(zb + depth) < EPSION;
    }
    
    bool DDARayTrace(float3 o, float3 r, out float2 hitPixel)
    {
        float3 end = o + r * _MaxLength;
    
        float4 h0 = mul(unity_CameraProjection, float4(o, 1));
        float4 h1 = mul(unity_CameraProjection, float4(end, 1));
    
        float k0 = 1/h0.w, k1 = 1/h1.w;
    
        //screen space
        float2 p0 = h0.xy * k0, p1 = h1.xy * k1;
    
        //DDA
        float2 delta = p1 - p0;
        bool permute = false;
        if(abs(delta.x) < abs(delta.y))
        {
            permute = true;
            delta = delta.yx;
            p0 = p0.yx;
            p1 = p1.yx;
        }
    
        float stepDir = sign(delta.x);
        float invdx = stepDir / delta.x;
    
        //derivatives
        float dk = (k1 - k0) * invdx * _StepSize / _MaxLength;
        float2 dp = float2(stepDir, delta.y * invdx) * _StepSize / _MaxLength;
    
        float pixelSize = min(_MainTex_TexelSize.x, _MainTex_TexelSize.y);
        if(length(dp) < pixelSize)
            dp *= pixelSize / min(dp.x, dp.y);
    
        bool intersect = false;
        float zb = 0;
        float2 p = p0;
        float k = k0;
        for (int i = 0; i < MAX_IT_COUNT && intersect == false; ++i)
        {
            p += dp;
            k += dk;
            zb = -1/k;
            hitPixel = permute? p.yx : p;
            intersect = isIntersectWithDepth(zb, hitPixel);
        }
        return intersect;
    }

    阈值0.025, 步长1, 最大距离50的情况下效果如图所示:


    虽然出现了一些莫名其妙的反射,但…但至少排线整齐了呀……好吧依然是一坨 Shit,我们先来解决这些莫名其妙的反射,输出交点法线如图:


    好嘛,又是自己反射自己了,但既然是这样,干脆把反射点和交点法线非常接近的反射点直接排除掉,把相交判断改成下面这样:

    inline bool isIntersectWithDepth(float zb, float3 worldnormal, float2 hitPixel)
    {
        float depth = tex2D (_CameraDepthTexture, hitPixel * 0.5 + 0.5);
        depth = LinearEyeDepth (depth);
        float3 pNormal = tex2D (_CameraGBufferTexture2, hitPixel * 0.5 + 0.5).xyz * 2.0 - 1.0;
        return abs(zb + depth) < EPSION && dot(worldnormal, pNormal) < 0.9;
    }

    效果如下:


    嗯,简单粗暴,一下子干净了,貌似也很少能出现反射点和交点法线方向一样的情况,等等,这里又是什么鬼:


    我们分析一波,大佬的算法是不可能出现这种低级错误的,那就是我改错了,一波观察后把 end 定义改成大佬的模样:

    float rayLength = ((o.z + r.z * _MaxLength) > -_ProjectionParams.y) ?
                    (-_ProjectionParams.y - o.z) / r.z : _MaxLength;
    float3 end = o + r * rayLength;

    问题解决:


    我们继续,现在这一条一条的看着很突兀,之前体积光那边博客的时候就接触过在 raytrace 过程中加噪声来均匀采样的技巧,但是懒得没去自己实现,DDA这个算法的作者源代码里也是加入了噪声的,正好这里试一试:

    float2 p = p0 + jitter * dp;
    float k = k0 + jitter * dk;

    嗯。。肉眼居然完全看不出有啥变化,那就只能上大招了,模糊走起,我们将反射结果单独输出并进行高斯模糊后再与原场景 buffer 合并,代码如下:

    private void OnRenderImage(RenderTexture src, RenderTexture dst)
    {
        if (combineMat != null)
        {
            RenderTexture gbuffer3 = RenderTexture.GetTemporary(src.width, src.height, 0);
            Graphics.Blit(src, gbuffer3);
    
            if (reflectMat == null)
            {
                Graphics.Blit(src, dst);
            }
            else
            {
                reflectMat.SetFloat("_MaxLength", maxLength);
                reflectMat.SetFloat("EPSION", epsion);
                reflectMat.SetFloat("_StepSize", stepSize);
                reflectMat.SetMatrix("_NormalMatrix", Camera.current.worldToCameraMatrix);
                Graphics.Blit(src, dst, reflectMat, 0);
            }
    
            if (blurMat != null)
            {
                int rtW = src.width / downSample;
                int rtH = src.height / downSample;
    
                RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);
                buffer0.filterMode = FilterMode.Bilinear;
    
                Graphics.Blit(dst, buffer0);
    
                for (int i = 0; i < iterations; i++)
                {
                    blurMat.SetFloat("_BlurSize", 1.0f + i * blurSpread);
                    RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
    
                    Graphics.Blit(buffer0, buffer1, blurMat, 0);
    
                    RenderTexture.ReleaseTemporary(buffer0);
                    buffer0 = buffer1;
                    buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
    
                    Graphics.Blit(buffer0, buffer1, blurMat, 1);
    
                    RenderTexture.ReleaseTemporary(buffer0);
                    buffer0 = buffer1;
                }
    
                Graphics.Blit(buffer0, src);
                RenderTexture.ReleaseTemporary(buffer0);
    
                combineMat.SetTexture("_gbuffer3", gbuffer3);
                Graphics.Blit(src, dst, combineMat);
                RenderTexture.ReleaseTemporary(gbuffer3);
            }
        }
        else
        {
            Graphics.Blit(src, dst);
        }
    }
    SubShader {
            CGINCLUDE
    
            #include "UnityCG.cginc"
    
            sampler2D _MainTex;  
            half4 _MainTex_TexelSize;
            float _BlurSize;
    
            struct v2f {
                float4 pos : SV_POSITION;
                half2 uv[5]: TEXCOORD0;
            };
    
            v2f vertBlurVertical(appdata_img v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
    
                half2 uv = v.texcoord;
    
                o.uv[0] = uv;
                o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
                o.uv[2] = uv - float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
                o.uv[3] = uv + float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
                o.uv[4] = uv - float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
    
                return o;
            }
    
            v2f vertBlurHorizontal(appdata_img v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
    
                half2 uv = v.texcoord;
    
                o.uv[0] = uv;
                o.uv[1] = uv + float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
                o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
                o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
                o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
    
                return o;
            }
    
            fixed4 fragBlur(v2f i) : SV_Target {
                float weight[3] = {0.4026, 0.2442, 0.0545};
    
                fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];                      
    
                for (int it = 1; it < 3; it++) {                
                    sum += tex2D(_MainTex, i.uv[it*2-1]).rgb * weight[it];
                    sum += tex2D(_MainTex, i.uv[it*2]).rgb * weight[it];
                }
    
                return fixed4(sum, 1.0);
            }
    
            ENDCG
    
            ZTest Always Cull Off ZWrite Off
    
            Pass {
                NAME "GAUSSIAN_BLUR_VERTICAL"
    
                CGPROGRAM
    
                #pragma vertex vertBlurVertical  
                #pragma fragment fragBlur
    
                ENDCG  
            }
    
            Pass {  
                NAME "GAUSSIAN_BLUR_HORIZONTAL"
    
                CGPROGRAM  
    
                #pragma vertex vertBlurHorizontal  
                #pragma fragment fragBlur
    
                ENDCG
            }
        } 
    SubShader
        {
            // No culling or depth
            Cull Off ZWrite Off ZTest Always
    
            Pass
            {
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
    
                #include "UnityCG.cginc"
    
                sampler2D _gbuffer3;
    
                struct appdata
                {
                    float4 vertex : POSITION;
                    float2 uv : TEXCOORD0;
                };
    
                struct v2f
                {
                    float2 uv : TEXCOORD0;
                    float4 vertex : SV_POSITION;
                };
    
                v2f vert (appdata v)
                {
                    v2f o;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    o.uv = v.uv;
                    return o;
                }
    
                sampler2D _MainTex;
    
                fixed4 frag (v2f i) : SV_Target
                {
                    fixed4 col = tex2D(_MainTex, i.uv);
                    fixed4 ref = tex2D(_gbuffer3, i.uv);
                    return col + ref;
                }
                ENDCG
            }
        }

    加上一张贴图后效果如下:


    这里写图片描述

    感觉勉强能看了,接下来我们把模糊程度和反射距离关联上,将 raytrace 距离存储在 a 通道,通过模糊后的 a 通道对原始反射与模糊后的反射进行插值:

    private void OnRenderImage(RenderTexture src, RenderTexture dst)
    {
        if (combineMat != null)
        {
            RenderTexture original_scene = RenderTexture.GetTemporary(src.width, src.height, 0);
            Graphics.Blit(src, original_scene);
    
            RenderTexture b4_blur_refl = RenderTexture.GetTemporary(src.width, src.height, 0, RenderTextureFormat.ARGB32);
    
            if (reflectMat == null)
            {
                Graphics.Blit(src, dst);
            }
            else
            {
                reflectMat.SetFloat("_MaxLength", maxLength);
                reflectMat.SetFloat("EPSION", epsion);
                reflectMat.SetFloat("_StepSize", stepSize);
                reflectMat.SetMatrix("_NormalMatrix", Camera.current.worldToCameraMatrix);               
                Graphics.Blit(src, b4_blur_refl, reflectMat, 0);
            }
    
            if (blurMat != null)
            {
                int rtW = b4_blur_refl.width / downSample;
                int rtH = b4_blur_refl.height / downSample;
    
                RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0, RenderTextureFormat.ARGB32);
                buffer0.filterMode = FilterMode.Bilinear;
    
                Graphics.Blit(b4_blur_refl, buffer0);
    
                for (int i = 0; i < iterations; i++)
                {
    
                    blurMat.SetFloat("_BlurSize", 1.0f + i * blurSpread);
                    RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0, RenderTextureFormat.ARGB32);
    
                    Graphics.Blit(buffer0, buffer1, blurMat, 0);
    
                    RenderTexture.ReleaseTemporary(buffer0);
                    buffer0 = buffer1;
                    buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
    
                    Graphics.Blit(buffer0, buffer1, blurMat, 1);
    
                    RenderTexture.ReleaseTemporary(buffer0);
                    buffer0 = buffer1;
                }
                RenderTexture after_blur_refl = RenderTexture.GetTemporary(buffer0.width, buffer0.height, 0, RenderTextureFormat.ARGB32);
    
                Graphics.Blit(buffer0, after_blur_refl);
                RenderTexture.ReleaseTemporary(buffer0);
    
                combineMat.SetTexture("_b4Blur", b4_blur_refl);
                combineMat.SetTexture("_gbuffer3", original_scene);
                Graphics.Blit(after_blur_refl, dst, combineMat);
    
                RenderTexture.ReleaseTemporary(original_scene);
                RenderTexture.ReleaseTemporary(b4_blur_refl);
                RenderTexture.ReleaseTemporary(after_blur_refl);
            }
        }
        else
        {
            Graphics.Blit(src, dst);
        }
    }
    for (int i = 0; i < MAX_IT_COUNT && intersect == false; ++i)
    {
        p += dp;
        k += dk;
        zb = -1/k;
        hitPixel = permute? p.yx : p;
        length = i * _StepSize * invdx / rayLength * r;
        intersect = isIntersectWithDepth(zb, normal, hitPixel);
    }
    fixed4 fragBlur(v2f i) : SV_Target {
        float weight[3] = {0.4026, 0.2442, 0.0545};
    
        fixed4 col = tex2D(_MainTex, i.uv[0]);
        fixed4 sum = col * weight[0];                   
    
        for (int it = 1; it < 3; it++) {                
            sum += tex2D(_MainTex, i.uv[it*2-1]) * weight[it];
            sum += tex2D(_MainTex, i.uv[it*2]) * weight[it];
        }           
    return sum;
    }
    fixed4 frag (v2f i) : SV_Target
    {
        fixed4 col_after = tex2D(_MainTex, i.uv);//blured reflect
        fixed4 col_b4 = tex2D(_b4Blur, i.uv);//unblured reflect
        fixed4 ref = tex2D(_gbuffer3, i.uv);//original scene
        return fixed4(ref.rgb + lerp(col_b4.rgb, col_after.rgb, clamp(0,1, col_after.a * 2)), 1);
    }

    但效果却并没有想象中的美好:

    但至少像个反射的样子了哎,莫名其妙已经挺长了,还有好多情况没涉及到,很多优秀的算法也还没去尝试,慢慢来吧,to be continue…

    展开全文
  • 主要的原理是:屏幕空间深度图,纹理动画,遮罩纹理,Phong光照模型。 所需数学知识: 模型空间到裁剪空间的变换,详情请见冯乐乐的Shader入门到精通4.6.7章节,因为冯乐乐在自己的github上已经
  • unity屏幕shader之油画屏幕

    千次阅读 2016-03-14 09:48:39
    本篇博客仅仅供本人参考学习,转载自浅墨shader。...油画屏幕shaderShader "Custom/oilScreen" { //------------------------------------【属性值】------------------------------------ Properties
  • unity屏幕shader之水屏幕

    千次阅读 2016-03-09 20:55:56
    本篇博客仅仅供本人参考学习,转载自浅墨shader。 首先创建一个顶点片段shader,将内容修改如下: Shader "Custom/waterCamera" { ... //屏幕水滴的素材图 _ScreenWaterDropTex("Base (RGB)",2D)=
  • Shader "Custom/Sample" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _Glossiness ("Smoothness", Range(0,1)) = 0.5 _Metallic ("Metallic", Range(0,1...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,939
精华内容 1,975
关键字:

shaderunity屏幕空间