Unity3D中的深度纹理和法线纹理

1.什么是深度纹理?

深度纹理实际就是一张渲染纹理,只不过它里面存储的像素值不是颜色值,而是一个高精度的深度值。由于被存储在一张纹理中,深度纹理的深度值范围是[0,1],而且通常是非线性分布的,那么,这些深度值是从哪里得到的呢?总体来说,这些深度值来自于顶点变换后得到的归一化的设备坐标(Normalized Device Coordinates,NDC)。一个模型要想要最终被绘制到屏幕上,需要把它的顶点从模型空间变换到齐次裁剪坐标系下,这是通过在顶点着色器中乘以MVP变换矩阵得到的。在变换的最后一步,我们需要使用一个投影矩阵来变换顶点,当我们使用的是透视投影类型的摄像机时,这个投影矩阵就是非线性的。

2.在Unity中如何生成一张深度纹理?

在Unity中,当用前向渲染路径时,由于无法直接获取深度缓存,深度和法线纹理是通过一个单独的Pass渲染而得的。要想让物体能够出现在深度和法线纹理中,就必须在Shader中设置正确的RenderType标签。Unity背后的具体实现是,它会使用着色器替换技术选择那些渲染类型(即SubShader的RenderType标签)为Opaque的物体,判断它们使用的渲染队列是否小于2500(内置的Background,Geometry和AlphaTest渲染队列均在此渲染范围内),如果满足条件,就把它渲染到深度和法线纹理中。

在Unity中,我们可以选择让一个摄像机生成一张深度纹理或是一张深度+法线纹理。当选择前者,Unity会按着色器替换技术,选取需要的不透明物体,并使用它投射阴影时使用的Pass来得到深度纹理(即LightMode被设置为ShadowCaster的Pass)。如果Shader中不包含这样一个Pass,那么这个物体就不会出现在深度纹理中(当然,它也不能向其它物体投射阴影)。当选择生成一张深度+法线纹理,Unity底层使用了一个单独的Pass把整个场景渲染一遍来完成。这个Pass被包含在Unity内置的一个Unity Shader中,我们可以在内置的buildin_shaders-xxx/DefaultResources/Camera-DepthNormalTexture.shader文件中找到这个用于渲染深度和法线信息的Pass。其中观察空间下的法线信息会被编码进纹理的R和G通道,而深度信息会被编进B和A通道。

3.在Unity中如何获取深度纹理中的信息?

在Unity中,获取深度纹理是非常简单的,我们可以通过设置摄像机的depthTextureMode来完成:camera.depthTextureMode = DepthTextureMode.Depth,然后再在Shader中通过声明CameraDepthTexture变量访问它。同理,如果想要获得深度+法线纹理,我们只需要在代码中这样设置:camera.depthTextureMode = DepthTextureMode.DepthNormals,然后再Shader中通过声明CameraDepthNormalsTexture变量来访问它。

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

camera.depthTextureMode =DepthTextureMode.Depth;

camera.depthTextureMode = DepthTextureMode.DepthNormals;

在Unity5中,我们还可以在摄像机的Camera组件上看到当前摄像机是否需要渲染深度或深度+法线纹理。当在Shader中访问到深度纹理cameraDepthTexture后,我们就可以使用当前像素的纹理坐标对它进行采样。绝大多数情况下,我们直接使用tex2D函数采样即可,但是Unity为我们提供了一个宏SAMPLE_DEPTH_TEXTURE,用来处理由于平台的不同造成的差异问题。而我们只需要在Shader中使用SAMPLE_DEPTH_TEXTURE宏对深度纹理进行采样:float d = SAMPLE_DEPTH_TEXTURE(_cameraDepthTexture,i.uv).其中i.uv是一个float2类型的变量,对应了当前像素的纹理坐标。类似的宏还有SAMPLE_DEPTH_TEXTURE_PROJ。

SAMPLE_DEPTH_TEXTURE_PROJ宏接受两个参数-深度纹理和一个float3或float4类型的纹理坐标,它的内部使用了tex2Dproj这样的函数进行投影纹理采样,纹理坐标的前两个分量首先会除以最后一个分量再进行纹理采样。如果提供了第四个分量,还会进行一次比较,通常用于阴影的实现中。SAMPLE_DEPTH_TEXTURE_PROJ的第二个参数通常是由顶点着色器输出插值而得的屏幕坐标:float d = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture,UNITY_PROJ_COORD(i.scrPos)),其中i.scrPos是在顶点着色器中通过调用ComputeScreenPos(o.pos)得到的屏幕坐标。

当通过纹理采样得到深度值后,这些深度值往往是非线性的,这种非线性来自于透视投影使用的裁剪矩阵。我们需要把投影后的深度值变换到线性空间下,例如视角空间下的深度值。Unity提供了两个辅助函数来为我们计算上述的计算过程-LinearEyeDepth和Linear01Depth.

LinearEyeDepth负责把深度纹理的采样结果转换到视角空间下的深度值,而Linear01Depth则会返回一个范围在[0,1]的线性深度值。

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

inline void DecodeDepthNormal(float4 enc,out float depth,out float3 normal)

{depth = DecodeFloatRG(enc.zw)

normal = DecodeViewNormalStereo(enc)

}

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容