关于Unity特效热扰动的特别优化(伪)

发表于2018-10-25
评论5 1.01w浏览

最近公司在做优化,,之前特效为了做效果用了各种增强选项比如HDR,Bloom,热扰动之类..反正就是为了炫酷吊炸天各种效果各种加..但是..如今,这个债是时候还了.....
其实就现在的硬件而言HDR,Bloom其实都还好,但是热扰动这个东西吧..emmm.就有点儿emmmm了...基本上就是如下图......
当然我们也可以很直观的看到主要消耗还是RenderTexture.GrabPixels....虽说是这个shader用了Cull off双面显示了...但是即使变成单面显示..也依旧峰值很高......所以就要打开shader去查看了...
你们可以去查看下你们工程里特效常用的shader里如果有扰动效果,大部分在SubShader下都有一个GrabPass{}...而它就是万恶的源头...
GrabPass其实就是一个截屏的pass...输出RenderTexture....可以看到官方文档中GrabPass是由两种用法的..一种是GrabPass{}一种是GrabPass{"Name"}....这两种方法的区别文档也很清楚的告诉了你..很明显GrabPass{"Name"}要优于GrabPass{}.....你假设下...一帧中有3个热扰动的特效..若是GrabPass{}..那么就很完美的你会得到3张命名为_GrabPass的RT图.....而GrabPass{"Name"}的话你在Frame Debugger中只会看到1张RT图...这张RT图就是你自己命名的...但是实际测试还是有点儿高..那么可能就是GrabPass这个Pass干嘛了.....
反正是张RT图,那么就把它换了得了..刚开始直接用Bloom生成的RT图去调用使用..发现其实是有问题的...因为Bloom的RT图是整个摄像机的输出图,其实并没有所谓的透明部分...如果这张图直接使用那么这个扰动其实是很奇怪的.....emmm当时忘了截图..你们自行感受....反正就是....很!奇!怪!....
咋办?
文章正题来了....

程序小哥哥发来一篇帖子,让看看参考下...点开链接粗略扫了一眼,,当时不以为意...因为大致一看比较麻烦就先忙别的去了.等稍微有时间去看的时候.....卧槽?这个效果?emmm....好像直接能用啊....
在这儿直接上博主链接:https://blog.csdn.net/puppet_master/article/details/70199330
博主的思路就是跟屏幕后处理一样,先给全屏来个扰动...然后根据热扰动特效的那个粒子面片去生成一张mask的RT图,然后把全屏扰动mask下特效的扰动就出来了....在这里需要在主相机下生成一个辅摄像机..layer层只照扰动特效的那一层...Target里直接给一张RT图用来做mask...那么这张rt图就可以自定义了..比如这个rt图我让他256*256.....这个在GrabPass中是不可能的,因为它生成的图的大小与你摄像机分辨率有关..你摄像机要是适应屏幕,你要是4K屏就很刺激了...

-----------------------------------------------分界线----------------------------------------------

下面就直接上博主的代码...也是可以用的.

/********************************************************************
 FileName: DistortEffect.cs
 Description: 屏幕扭曲效果
 Created: 2017/04/27
 by: puppet_master
*********************************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DistortEffect : PostEffectBase {
    //扭曲的时间系数
    [Range(0.0f, 1.0f)]
    public float DistortTimeFactor = 0.15f;
    //扭曲的强度
    [Range(0.0f, 0.2f)]
    public float DistortStrength = 0.01f;
    //噪声图
    public Texture NoiseTexture = null;
    //渲染Mask图所用的shader
    public Shader maskObjShader = null;
    //降采样系数
    public int downSample = 4;
    private Camera mainCam = null;
    private Camera additionalCam = null;
    private RenderTexture renderTexture = null;
    public void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (_Material)
        {
            _Material.SetTexture("_NoiseTex", NoiseTexture);
            _Material.SetFloat("_DistortTimeFactor", DistortTimeFactor);
            _Material.SetFloat("_DistortStrength", DistortStrength);
            _Material.SetTexture("_MaskTex", renderTexture);
            Graphics.Blit(source, destination, _Material);
        }
        else
        {
            Graphics.Blit(source, destination);
        }
    }
    void Awake()
    {
        //创建一个和当前相机一致的相机
        InitAdditionalCam();
    }
    private void InitAdditionalCam()
    {
        mainCam = GetComponent<Camera>();
        if (mainCam == null)
            return;
        Transform addCamTransform = transform.FindChild("additionalDistortCam");
        if (addCamTransform != null)
            DestroyImmediate(addCamTransform.gameObject);
        GameObject additionalCamObj = new GameObject("additionalDistortCam");
        additionalCam = additionalCamObj.AddComponent<Camera>();
        SetAdditionalCam();
    }
    private void SetAdditionalCam()
    {
        if (additionalCam)
        {
            additionalCam.transform.parent = mainCam.transform;
            additionalCam.transform.localPosition = Vector3.zero;
            additionalCam.transform.localRotation = Quaternion.identity;
            additionalCam.transform.localScale = Vector3.one;
            additionalCam.farClipPlane = mainCam.farClipPlane;
            additionalCam.nearClipPlane = mainCam.nearClipPlane;
            additionalCam.fieldOfView = mainCam.fieldOfView;
            additionalCam.backgroundColor = Color.clear;
            additionalCam.clearFlags = CameraClearFlags.Color;
            additionalCam.cullingMask = 1 << LayerMask.NameToLayer("Distort");
            additionalCam.depth = -999;
            //分辨率可以低一些
            if (renderTexture == null)
                renderTexture = RenderTexture.GetTemporary(Screen.width >> downSample, Screen.height >> downSample, 0);
        }
    }
    void OnEnable()
    {
        SetAdditionalCam();
        additionalCam.enabled = true;
    }
    void OnDisable()
    {
        additionalCam.enabled = false;
    }
    void OnDestroy()
    {
        if (renderTexture)
        {
            RenderTexture.ReleaseTemporary(renderTexture);
        }
        DestroyImmediate(additionalCam.gameObject);
    }
    //在真正渲染前的回调,此处渲染Mask遮罩图
    void OnPreRender()
    {
        //maskObjShader进行渲染
        if (additionalCam.enabled)
        {
            additionalCam.targetTexture = renderTexture;
            additionalCam.RenderWithShader(maskObjShader, "");
        }
    }
}
他继承于PostEffectBase类.这个在很多后处理例子中都有,就不上了.直接挂在主摄像机下.上面代码很好理解,运行下在主摄像机下会创建一个子相机只照Distort层..许多你可以自定义的..
它依赖以下2个shader,一个基础..就是扰动..一个是mask...当然这个也是可以避免的,在下文会提到.
//by:puppet_master
//2017.5.3
Shader "ApcShader/MaskObjPrepass"
{
	//子着色器	
	SubShader
	{
		Pass
		{	
			Cull Off
			CGPROGRAM
			#include "UnityCG.cginc"
			struct v2f
			{
				float4 pos : SV_POSITION;
			};
			v2f vert(appdata_full v)
			{
				v2f o;
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				return o;
			}
			fixed4 frag(v2f i) : SV_Target
			{
				//这个Pass直接输出颜色
				return fixed4(1,1,1,1);
			}
			//使用vert函数和frag函数
			#pragma vertex vert
			#pragma fragment frag
			ENDCG
		}
	}
}
上面是mask图shader.下面是扰动shader.
//全屏幕扭曲Shader
//by:puppet_master
//2017.5.3
Shader "Custom/DistortPostEffect"
{
	Properties
	{
		_MainTex("Base (RGB)", 2D) = "white" {}
		_NoiseTex("Noise", 2D) = "black" {}//默认给黑色,也就是不会偏移
		_MaskTex("Mask", 2D) = "black" {}//默认给黑色,权重为0
	}
	CGINCLUDE
	#include "UnityCG.cginc"
	uniform sampler2D _MainTex;
	uniform sampler2D _NoiseTex;
	uniform sampler2D _MaskTex;
	uniform float _DistortTimeFactor;
	uniform float _DistortStrength;
	fixed4 frag(v2f_img i) : SV_Target
	{
		//根据时间改变采样噪声图获得随机的输出
		float4 noise = tex2D(_NoiseTex, i.uv - _Time.xy * _DistortTimeFactor);
		//以随机的输出*控制系数得到偏移值
		float2 offset = noise.xy * _DistortStrength;
		//采样Mask图获得权重信息
		fixed4 factor = tex2D(_MaskTex, i.uv);
		//像素采样时偏移offset,用Mask权重进行修改
		float2 uv = offset * factor.r + i.uv;
		return tex2D(_MainTex, uv);
	}
	ENDCG
	SubShader
	{
		Pass
		{
			ZTest Always
			Cull Off
			ZWrite Off
			Fog{ Mode off }
			CGPROGRAM
			#pragma vertex vert_img
			#pragma fragment frag
			#pragma fragmentoption ARB_precision_hint_fastest 
			ENDCG
		}
	}
	Fallback off
}
---------------下面这个是针对完全不会的同学写的用法,大佬直接跳过-----------------------------
用法很简单,1.主相机挂上DistortEffect 2.DistortEffect Texture NoiseTexture挂上一张扰动图. maskObjShader上拖进MaskObjPrepass.shader..3.把特效中扰动的粒子改下layer还有shader改成随便一个透明的shader即可....这个layer要和DistorEffect.cs中的生成摄像机的culling mask层要一样.
----------------------------------------------------------------------------------------------------

进入正题....
其实上述做法就已经能达到优化效果了....
左边的是用上述做法的..右边的是GrabPass的.....
然而要是注意的话,上述做法能做到的扰动只能有一张扰动图....也就是说,场景中所有特效的扰动其实类型都一样.....特效讲究的 会有多中扰动特效,,,虽然get不太到扰动图中的区别能有多大....感觉不是很大.........但是.既然看到特效中还是有区分的..那就分开吧....
于是.在上述思路上把mask图从黑白改成了RGB..这样就首先有了3种了....
/********************************************************************
 FileName: DistortEffect.cs
 Description: 屏幕扭曲效果
 Created: 2017/04/27
 by: puppet_master
*********************************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DistortEffect : MonoBehaviour {
	public Shader shader = null;
	private Material _material = null;
	public Material _Material
	{
		get
		{
			if (_material == null)
				_material = GenerateMaterial(shader);
			return _material;
		}
	}
	//根据shader创建用于屏幕特效的材质
	protected Material GenerateMaterial(Shader shader)
	{
		if (shader == null)
			return null;
		//需要判断shader是否支持
		if (shader.isSupported == false)
			return null;
		Material material = new Material(shader);
		material.hideFlags = HideFlags.DontSave;
		if (material)
			return material;
		return null;
	}
	//扭曲的时间系数
	[Range(0.0f, 1.0f)]
	public float DistortTimeFactor = 0.5f;
	//扭曲的强度
	[Range(0.0f, 0.2f)]
	public float DistortStrength = 0.01f;
	//噪声图1 对应R
	public Texture NoiseTexture1 = null;
	//噪声图2 对应G
	public Texture NoiseTexture2 = null;
	//噪声图3 对应B
	public Texture NoiseTexture3 = null;
	//降采样系数
	public int downSample = 1;
	private Camera mainCam = null;
	private Camera additionalCam = null;
	private RenderTexture renderTexture = null;
	public void OnRenderImage(RenderTexture source, RenderTexture destination)
	{
        if(additionalCam)
        {
            additionalCam.targetTexture = renderTexture;
            additionalCam.Render();
            if (_Material)
            {
                _Material.SetTexture("_NoiseTex1", NoiseTexture1);
                _Material.SetTexture("_NoiseTex2", NoiseTexture2);
                _Material.SetTexture("_NoiseTex3", NoiseTexture3);
                _Material.SetFloat("_DistortTimeFactor", DistortTimeFactor);
                _Material.SetFloat("_DistortStrength", DistortStrength);
                _Material.SetTexture("_MaskTex", renderTexture);
                Graphics.Blit(source, destination, _Material);
            }
            else
            {
                Graphics.Blit(source, destination);
            }
        }
	}
	void Awake()
	{
		//创建一个和当前相机一致的相机
		InitAdditionalCam();
	}
	private void InitAdditionalCam()
	{
		mainCam = GetComponent<Camera>();
		if (mainCam == null)
			return;
		mainCam.depthTextureMode |= DepthTextureMode.Depth;
		Transform addCamTransform = transform.Find("additionalDistortCam");
		if (addCamTransform != null)
			DestroyImmediate(addCamTransform.gameObject);
		GameObject additionalCamObj = new GameObject("additionalDistortCam");
		additionalCam = additionalCamObj.AddComponent<Camera>();
        additionalCam.transform.parent = mainCam.transform;
        additionalCam.transform.localPosition = Vector3.zero;
        additionalCam.transform.localRotation = Quaternion.identity;
        additionalCam.transform.localScale = Vector3.one;
        additionalCam.backgroundColor = Color.clear;
        additionalCam.clearFlags = CameraClearFlags.Color;
        additionalCam.cullingMask = 1 << LayerMask.NameToLayer("DistortEffect");
        additionalCam.depth = -999;
        additionalCam.allowHDR = false;
        additionalCam.allowMSAA = false;
        SetAdditionalCam();
    }
    private void SetAdditionalCam()
	{
		additionalCam.farClipPlane = mainCam.farClipPlane;
		additionalCam.nearClipPlane = mainCam.nearClipPlane;
		additionalCam.fieldOfView = mainCam.fieldOfView;
		//分辨率可以低一些
		if (renderTexture == null)
			renderTexture = RenderTexture.GetTemporary(Screen.width >> downSample, Screen.height >> downSample, 0);
	}
	void OnEnable()
	{
        if(additionalCam)
        {
            additionalCam.gameObject.SetActive(true);
            additionalCam.enabled = true;
            SetAdditionalCam();
        }
    }
    void OnDisable()
	{
        if (additionalCam)
        {
            additionalCam.gameObject.SetActive(false);
            additionalCam.enabled = false;
        }
    }
	void OnDestroy()
	{
		if (renderTexture)
		{
			RenderTexture.ReleaseTemporary(renderTexture);
		}
		DestroyImmediate(additionalCam.gameObject);
	}
}
//全屏幕扭曲Shader
//by:puppet_master
//2017.5.3
Shader "Custom/DistortPostEffect"
{
	Properties
	{
		_MainTex("Base (RGB)", 2D) = "white" {}
		_NoiseTex1("Noise1", 2D) = "black" {}//默认给黑色,也就是不会偏移
		_NoiseTex2("Noise2", 2D) = "black" {}//默认给黑色,也就是不会偏移
		_NoiseTex3("Noise3", 2D) = "black" {}//默认给黑色,也就是不会偏移
		_MaskTex("Mask", 2D) = "black" {}//默认给黑色,权重为0
	}
	CGINCLUDE
	#include "UnityCG.cginc"
	uniform sampler2D _MainTex;
	uniform sampler2D _NoiseTex1;
	uniform sampler2D _NoiseTex2;
	uniform sampler2D _NoiseTex3;
	uniform sampler2D _MaskTex;
	uniform float _DistortTimeFactor;
	uniform float _DistortStrength;
	fixed4 frag(v2f_img i) : SV_Target
	{
		//根据时间改变采样噪声图获得随机的输出
		float4 noise1 = tex2D(_NoiseTex1, i.uv - _Time.xy * _DistortTimeFactor);
		float4 noise2 = tex2D(_NoiseTex2, i.uv - _Time.xy * _DistortTimeFactor);
		float4 noise3 = tex2D(_NoiseTex3, i.uv - _Time.xy * _DistortTimeFactor);
		//以随机的输出*控制系数得到偏移值
		float2 offset1 = noise1.xy * _DistortStrength;
		float2 offset2 = noise2.xy * _DistortStrength;
		float2 offset3 = noise3.xy * _DistortStrength;
		//采样Mask图获得权重信息
		fixed4 factor = tex2D(_MaskTex, i.uv);
		//像素采样时偏移offset,用Mask权重进行修改
		float2 uv = offset1 * factor.r + offset2 * factor.g + offset3 * factor.b + i.uv;
		return tex2D(_MainTex, uv);
	}
	ENDCG
	SubShader
	{
		Pass
		{
			ZTest Always
			Cull Off
			ZWrite Off
			Fog{ Mode off }
			CGPROGRAM
			#pragma vertex vert_img
			#pragma fragment frag
			#pragma fragmentoption ARB_precision_hint_fastest 
			ENDCG
		}
	}
	Fallback off
}

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "ApcShader/MaskObjPrepass"
{
	//子着色器
	Properties
	{
		_RGBColor("RGB Color", Color) = (1,0,0,1)
	}
	SubShader
	{
		Pass
		{
			Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
			Blend One OneMinusSrcAlpha // note, we use premultiplied alpha, so 1 (1-src)
			Cull Off Lighting Off ZWrite Off
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"
			struct appdata_t
			{
				float4 vertex     : POSITION;
				float2 texcoord   : TEXCOORD0;
			};
			struct v2f
			{
				float4 vertex        : SV_POSITION;
				float2 texcoord      : TEXCOORD0;
				float4 projPos       : TEXCOORD1;
			};
			fixed4 _RGBColor;
			sampler2D_float _CameraDepthTexture;
			v2f vert(appdata_t v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				float3 wvp = mul(unity_ObjectToWorld, v.vertex);
				o.projPos = ComputeScreenPos(o.vertex);
				COMPUTE_EYEDEPTH(o.projPos.z);
				o.texcoord = v.texcoord;
				return o;
			}
			fixed4 frag(v2f i) : SV_Target
			{
				fixed4 col = _RGBColor;
				// Do Z clip
				float zbuf = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)));
				float partZ = i.projPos.z;
				float zalpha = saturate((zbuf - partZ + 1e-2f) * 10000);
				col.a = col.a * zalpha;
				// premultiply alpha
				col.rgb = col.rgb * col.a;
				return col;
			}		
			ENDCG
		}
	}
}
这个跟上面那种用法其实大致相同,,不同的就是mask这块儿....这个是直接在扰动的那个粒子上把材质球shader改成MaskObjPrepass..通过RGB纯色颜色去区分他用哪张扰动图...这样...场景中就可以有多重扰动效果了..

在这儿MaskObjPrepass这个shader和原本博主最大的不同之处就是算得深度...通过摄像机与粒子发射的距离去区别了深度让扰动不影响靠近摄像机的物体...因为博主的做法是全屏shader再通过mask去抠出扰动区域...其实那块儿扰动区域就没有深度了...假如特效在远处 而摄像机近处有个模型...那这个模型也会受到扰动 ..这就不对了.....
所以刚好在冯乐乐女神的书中有提到深度的做法在这儿我就直接复制了...

---------------------------------------------------------------------------------------------------

在Unity中,获取深度纹理是非常简单的,我们只需要告诉Unity“把深度纹理给我!”然后再在Shader中直接访问特定的纹理属性即可。这个与Unity沟通的过程是通过在脚本中设置摄像机的depthTextureMode来完成的,例如我们可以通过下面的代码来获取深度纹理:

camera.depthTextureMode = DepthTextureMode.Depth;
一旦设置好了上面的摄像机模式后,我们就可以在Shader中通过声明_CameraDepthTexture变量来访问它。这个过程非常简单,但我们需要知道两行代码的背后,Unity为我们做了许多工作。

同理,如果想要获取深度+法线纹理,我们只需要在代码中这样设置:

camera.depthTextureMode = DepthTextureMode.DepthNormals;
然后在Shader中通过声明_CameraDepthNormalsTexture变量来访问它。

我们还可以组合这些模式,让一个摄像机同时产生一张深度和深度+法线纹理:

  1. camera.depthTextureMode |= DepthTextureMode.Depth;
  2. camera.depthTextureMode |= DepthTextureMode.DepthNormals;
在Unity5中,我们还可以在摄像机的Camera组件上看到当前摄像机是否需要渲染深度或深度+法线纹理。当在Shader中访问到深度纹理_CameraDepthTexture 后,我们就可以使用当前像素的纹理坐标对它进行采样。绝大多数情况下,我们直接使用tex2D函数采样即可,但在某些平台上,我们需要一些特殊处理。Unity为我们提供了一个统一的宏SAMPLE_DEPTH_TEXTURE,用来处理这些由于平台差异造成的问题。而我们只需要在Shader中使用SAMPLE_DEPTH_TEXTURE宏对深度纹理进行采样,例如:

float d = SMAPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);
其中,i.uv 是一个float2类型的变量,对应了当前像素的纹理坐标。类似的宏还有SAMPLE_DEPTH_TEXTURE_PROJ 和 SAMPLE_DEPTH_TEXTURE_LOD。SAMPLE_DEPTH_TEXTURE_PROJ 宏同样接受两个参数——深度纹理和一个float3或float4类型的纹理坐标,它的内部使用了tex2Dproj这样的函数进行投影纹理采样,纹理坐标的前两个分量首先会除以最后一个分量,再进行纹理采样。如果提供了第四个分量,还会进行一次比较, 通常用于阴影的实现中。SAMPLE_DEPTH_TEXTURE_PROJ 的第二个参数通常是由顶点着色器输出插值而得的屏幕坐标,例如:
float d = SMAPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.srcPos));
其中,i.srcPos 是在顶点着色器中通过调用ComputeScreenPos(o.pos)得到的屏幕坐标。上述这些宏,可以在Unity内置的HLSLSupport.cginc文件中找到。

当通过纹理采样得到深度值后,这些深度值往往是非线性的,这种非线性来自于透视投影使用的裁剪矩阵。然而,在我们的计算过程中通常是需要线性的深度值,也就是说,我们需要把投影后的深度值变换到线性空间下,例如视角空间下的深度值。那么,我们应该如何进行这个转换呢?实际上,我们只需要倒推顶点变换的过程即可。下面我们以透视投影为例,推导如何由深度纹理中的深度信息计算得到视角空间下的深度值。

我们之前已知,当我们使用透视投影的裁剪矩阵P(clip)对视角空间下的一个顶点进行变换后,裁剪空间下顶点的z和w分量为:


其中,Far 和 Near 分别是远近裁剪平面的距离。然后,我们通过齐次除法就可以得到NDC下的z分量:


之前我们知道,深度纹理中的深度值是 通过下面的公式由NDC计算而得的:


由上面的这些式子,我们可以推导出用d表示而得的Z(visw)的表达式:


由于在Unity使用的视角空间中,摄像机正向对应的z值均为负值,因此为了得到深度值的正数表示,我们需要对上面的结果取反,最后得到的结果如下:


它的取值范围就是视锥体深度范围,即[Near,Far]。如果我们想要得到范围在[0, 1]之间的深度值,只需要把上面得到的结果除以Far即可。这样,0就表示该点与摄像机位于同一位置,1表示该点位于视锥体的远裁剪平面上。结果如下:


幸运的是,Unity提供了两个辅助函数来为我们进行上述的计算过程——LinearEyeDepth 和 Linear01Depth。LinearEyeDepth 负责把深度纹理的采样结果转换到视角空间下的深度值,也 就是我们上面得到的Z(visw)。而 Linear01Depth 则会返回一个范围在[0, 1]的线性深度值,也就是我们上面得到的Z(01),这两个函数内部使用了内置的_ZBufferParams变量来得到远近裁剪平面的距离。

如果我们需要获取深度+法线纹理,可以直接使用tex2D函数对_CameraDepthNormalsTexture 进行采样,得到里面存储的深度和法线信息。Unity提供了辅助函数来为我们队这个采样结果进行解码,从而得到深度值和法线方向。这个函数是DecodeDepthNormal,它在UnityCG.cginc里被定义:

  1. inline void DecodeDepthNormal(float4 enc, out float depth,out float3 normal){
  2. depth = DecodeFloatRG(enc.zw);
  3. normal = DecodeViewNormalStereo(enc);
DecodeDepthNormal 的第一个参数是对深度+法线纹理的采样结果,这个采样结果是Unity对深度和法线信息编码后的结果,它的xy分量存储的是视角空间下的法线信息,而深度信息被编码进了zw分量。通过调用DecodeDepthNormal 函数对采样结果解码后,我们就可以得到解码后的深度值和法线。这个深度值是范围在[0, 1]的线性深度值(这与单独的深度纹理中存储的深度值不同),而得到的法线则是视角空间下的法线方向。同样,我们也可以通过调用DecodeFloatRG 和 DecodeViewNormalStereo来解码深度+法线纹理中的深度和法线信息。


----------------------------------------------------------------------------------------------------

然后...就解决了....


不想码字了..先这么着吧.....

上面已经说得很明白了我觉得...恩...

掰掰!






如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引