ComputeScreenPos和ComputeGrabScreenPos/折射和反射/使用切线空间中法线对屏幕纹理坐标进行偏移

本文深入探讨Unity Shader中computeScreenPos和computeGrabScreenPos的使用,以及如何在切线空间中利用法线对屏幕纹理坐标进行偏移以实现折射效果。文章解释了reflect函数在高光反射模型和反射效果计算中的应用,并详细阐述了Unity Shader如何处理GrabPass以模拟玻璃的反射和折射。内容包括GrabPass的屏幕坐标处理、纹理翻转问题以及_Distortion参数对折射的影响。
摘要由CSDN通过智能技术生成

读本书第一遍的时候,好多知识点一知半解就过去了,没有仔细思考。读本书第二遍的时候发现很多知识点都能够串联起来了,也因此解决了很多之前对本书困惑的地方。

1.reflect(i,n)函数在高光反射模型和在计算反射的时候的应用
计算高光反射模型的时候,我们使用reflect(i,n),i为入射光线,n为法线。但是由于函数定义入射光线是由光源发出到点的,所以通常我们会写

fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));

但在实现反射效果的时候,我们写

fixed3 reflectDir = reflect(-worldViewDir, worldNormal);

理解起来应该是说,在计算高光反射模型的时候,是计算哪些沿着完全镜面反射方向被反射的光线。由于光是从四面八方射到物体的,其中当然也包括反射到摄像机的光线了。所以我们会用普遍的lightDir入射光线,来计算反射光线。

当在实现反射效果的时候,我们需要得到的是,从摄像机视角看过去的反射效果是正确的效果。所以我们需要的是,物体反射到摄像机的那个“光”,那个是我们需要的反射光线,而那个就是ViewDir。因此由入射光线由光路可逆原则,就是 -worldViewDir。

2.UnityShader中的玻璃效果

本书中,使用GrabPass来实现玻璃等透明材质的模拟。首先,书里实现了玻璃的反射+折射两个效果。并且使用GrabPass来对物体后面的图像进行复杂的处理,比如法线的模拟折射效果,不再是简单的与屏幕颜色进行混合。(之前是使用lerp函数进行混合,lerp(diffuse, refractionColor, _refarctionAmount))

先看反射,反射的实现方式与之前相似的。首先,我们使用reflect函数计算出反射方向,然后使用这个三维的纹理坐标来对cubemap进行纹理采样。采样的结果与主纹理颜色相乘就是反射的最终结果了。这就是只计算反射的结果。
在这里插入图片描述

然后是折射。之前我们也是用refract函数来计算折射方向,不过注意的是refract传入的是三个参数,refract(i,n,eta),eta就是入射ior/折射ior,i和n都是经过归一化的噢。以前是模拟不透明物体产生折射的时候上面的效果。现在要模拟的玻璃效果,就是说透过玻璃,我们看到里面的物体是有经过“折射”的感觉的,这点我们要模拟出来,就不能再是简单的把折射结果与屏幕颜色进行混合了。

然后我们看GrabPass,这边要清楚的是GrabPass是抓取当前的屏幕图像并把它储存在一张你命名的RT中。GrabPass通常是用来渲染透明物体的,但是我们需要在subshader的标签中写明,“Queue” = “Transparent”,但是"RenderType" = “Opaque”,为什么呢,这是因为我们希望不透明物体都在这个物体渲染之前被先渲染完了,这样渲染这个物体的时候抓取的屏幕图像才是正确的屏幕图像。

GrabPass把抓取到的屏幕图像储存在一张与屏幕分辨率相同的RT中。那么,屏幕图像当然要用屏幕坐标来采样啦。屏幕坐标怎么得到呢,记得之前我们学过如何获得片元的屏幕坐标,其中有一种就是利用ComputeScreenPos函数,但是利用这个函数我们还需要在片元着色器中手动进行齐次除法,才能得到最终的屏幕坐标/视口坐标(即左下角是(0,0),右上角是(1,1))。 在这个例子中ComputeGrabScreenPos函数来得到抓取屏幕图像的采样坐标。那这两个函数有什么区别呢?

首先我们要说的是,渲染平台的差异性。对于屏幕坐标系,OpenGL的左下角为(0,0),而DirectX的左上角为(0,0)
在这里插入图片描述
书中的原话是:

大多数情况下,这样的差异并不会对我们造成任何影响。但当我们要使用渲染到纹理技术,把屏幕图像渲染到一张渲染纹理中时,如果不采取任何措施的话,就会出现纹理翻转的情况。幸运的是,Unity在背后为我们处理了这种翻转问题——当在DirectX平台上使用渲染到纹理技术时,Unity会为我们翻转屏幕图像纹理,以便在不同平台上达到一致性。

Unity遵循OpenGL的规定,例如当我们使用渲染到RT技术的时候,且我们需要渲染到DirectX平台的时候,Unity在背后都为我们处理了这个翻转问题。具体操作是,Unity会通过翻转投影矩阵Projection Matrix 从而翻转 Ren

  • 6
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
以下是Unity Shader使用CG语言实现SAMPLE_DEPTH_TEXTURE的示例代码。 在vertex shader,我们需要将顶点坐标和投影矩阵相乘得到裁剪坐标,然后将裁剪坐标传递给fragment shader。 ``` v2f vert (appdata_base v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); return o; } ``` 在fragment shader,我们首先需要从深度纹理采样得到深度值,然后将其转换为线性深度值。转换方法可以根据具体场景进行调整。接着,我们可以根据深度值计算出该像素的位置,并将该位置作为颜色输出。 ``` half4 frag (v2f i) : SV_Target { float depth = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, UNITY_PROJ_COORD(i.vertex)).r); float linearDepth = 2.0 * _ZBufferParams.w / (_ZBufferParams.y + _ZBufferParams.x - depth * (_ZBufferParams.y - _ZBufferParams.x)); float4 clipPos = float4(ComputeGrabScreenPos(i.vertex.xy, linearDepth), linearDepth, 1.0); float4 viewPos = mul(_InvProjMatrix, clipPos); viewPos /= viewPos.w; return float4(viewPos.xyz, 1.0); } ``` 注意,这里我们还需要使用Unity提供的函数Linear01Depth将深度值从非线性转换为线性。同时,我们需要使用_ZBufferParams和_InvProjMatrix这两个Unity内置的变量来进行后续计算。 最后,在主程序,我们需要将深度纹理绑定到对应的纹理单元,并将投影矩阵和_ZBufferParams传递给shader。 ``` Shader "Custom/DepthShader" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" sampler2D _CameraDepthTexture; float4 _ZBufferParams; float4x4 _InvProjMatrix; struct appdata_base { float4 vertex : POSITION; }; struct v2f { float4 vertex : SV_POSITION; }; v2f vert (appdata_base v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); return o; } half4 frag (v2f i) : SV_Target { float depth = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, UNITY_PROJ_COORD(i.vertex)).r); float linearDepth = 2.0 * _ZBufferParams.w / (_ZBufferParams.y + _ZBufferParams.x - depth * (_ZBufferParams.y - _ZBufferParams.x)); float4 clipPos = float4(ComputeGrabScreenPos(i.vertex.xy, linearDepth), linearDepth, 1.0); float4 viewPos = mul(_InvProjMatrix, clipPos); viewPos /= viewPos.w; return float4(viewPos.xyz, 1.0); } ENDCG } } FallBack "Diffuse" } ``` 注意,我们需要使用Unity内置的宏UNITY_PROJ_COORD来将裁剪坐标转换为纹理坐标。另外,由于_CameraDepthTexture是Unity自动渲染深度纹理的结果,因此无需手动绑定纹理
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值