ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

每次都能让人头大的 Shader -- 从一次简单的功能说起

2019-11-25 16:01:33  阅读:447  来源: 互联网

标签:深度图 renderTexture 说起 float Shader 相机 depth 深度 人头


  最近有个功能, 要渲染从主相机视角看到的另一个相机的可视范围和不可见范围, 大概如下图 : 

  简单来说就是主相机视野和观察者相机视野重合的地方, 能标记出观察者相机的可见和不可见, 实现原理就跟 ShadowMap 一样, 就是有关深度图, 世界坐标转换之类的, 每次有此类的功能都会很悲催, 虽然它的逻辑很简单, 可是用Unity3D做起来很麻烦...

  原理 : 在观察者相机中获取深度贴图, 储存在某张 RenderTexture 中,  然后在主相机中每个片元都获取对应的世界坐标位置, 将世界坐标转换到观察者相机viewPort中, 如果在观察者相机视野内, 就继续把视口坐标转换为UV坐标然后取出储存在 RenderTexture 中的对应深度, 把深度转换为实际深度值后做比较, 如果深度小于等于深度图的深度, 就是观察者可见 否则就是不可见了.

 

  先来看怎样获取深度图吧, 不管哪种渲染方式, 给相机添加获取深度图标记即可:

        _cam = GetComponent<Camera>();
        _cam.depthTextureMode |= DepthTextureMode.Depth;

  

  然后深度图要精度高点的, 单通道图即可:

        renderTexture = RenderTexture.GetTemporary(Screen.width, Screen.height, 0, RenderTextureFormat.RFloat);
        renderTexture.hideFlags = HideFlags.DontSave;

  

  之后就直接把相机的贴图经过后处理把深度图渲染出来即可 : 

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (_material && _cam)
        {
            Shader.SetGlobalFloat("_cameraNear", _cam.nearClipPlane);
            Shader.SetGlobalFloat("_cameraFar", _cam.farClipPlane);

            Graphics.Blit(source, renderTexture, _material);
        }
        Graphics.Blit(source, destination);
    }

  材质就是一个简单的获取深度的shader : 

    sampler2D _CameraDepthTexture;    
    uniform float _cameraFar;
    uniform float _cameraNear;
    
    float DistanceToLinearDepth(float d, float near, float far)
    {
        float z = (d - near) / (far - near);
        return z;
    }
    
    fixed4 frag(v2f i) : SV_Target
    {
        float depth  = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);
        depth = LinearEyeDepth(depth);
        depth = DistanceToLinearDepth(depth, _cameraNear, _cameraFar);
        return float4(depth, depth, depth, 1);
    }
    

 

  这里有点奇怪吧, 为什么返回的不是深度贴图的深度, 也不是 Linear01Depth(depth) 的归一化深度, 而是自己计算出来的一个深度?

  这是因为缺少官方文档........其实看API里面有直接提供 RenderBuffer 给相机的方法 : 

        _cam.SetTargetBuffers(renderTexture.colorBuffer, renderTexture.depthBuffer);

  可是没有文档也没有例子啊, 鬼知道你渲染出来我怎么用啊, 还有 renderTexture.depthBuffer 到底怎样作为 Texture 传给 shader 啊... 我之前尝试过它们之间通过 IntPtr 作的转换操作, 都是失败...

  于是就只能用最稳妥的方法, 通过 Shader 渲染一张深度图出来吧, 然后通过 SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv); 获取到的深度值, 应该是 NDC 坐标, 深度值的范围应该是[0, 1]之间吧(非线性), 如果在其它相机过程中获取实际深度的话, 就需要自己实现 LinearEyeDepth(float z) 这个方法:

inline float LinearEyeDepth( float z )
{
    return 1.0 / (_ZBufferParams.z * z + _ZBufferParams.w);
}

  而在不同平台, _ZBufferParams 里面的值是不一样的, 所以如果实现它的话会很麻烦, 并且经过我的测试, 结果是不对的......

double x, y;

OpenGL would be this:
x = (1.0 - m_FarClip / m_NearClip) / 2.0;
y = (1.0 + m_FarClip / m_NearClip) / 2.0;

D3D is this:
x = 1.0 - m_FarClip / m_NearClip;
y = m_FarClip / m_NearClip;

_ZBufferParams = float4(x, y, x/m_FarClip, y/m_FarClip);

  

 

  最后做出来的只是基本功能, 并没有对距离深度做样本估计, 所以会像硬阴影一样有锯齿边, 还有像没开各项异性的贴图一样有撕扯感, 这些都需要进行处理...

标签:深度图,renderTexture,说起,float,Shader,相机,depth,深度,人头
来源: https://www.cnblogs.com/tiancaiwrk/p/11928333.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有