博客
关于我
【实时渲染】Unity Shader实现视差贴图 法线贴图
阅读量:324 次
发布时间:2019-03-04

本文共 4685 字,大约阅读时间需要 15 分钟。

为了更好的理解本文所介绍的内容,建议您阅读:

    • 法线贴图 
    • 视差贴图
  • 《UnityShader入门精要》
    • 7.2凹凸纹理

 

所需图片     

 

Unity自带的着色器可以帮助我们实现视差贴图的效果

现在我们来了解一下其背后的原理

目录


视差贴图(Parallax Mapping)

视差贴图可以使物体表面更具有立体感,相对于法线贴图,视差贴图更加真实。

原理

向量V代表观察者观察方向,红色曲线代表高度图所表示的数值,底部黑色平面代表纹理贴图。

假设平面位移到高度曲线所表示的位置,那么观察者会看到B点(此处的B点不是高度图上的B点,而是B点对应纹理贴图的采样点,上图只是为了使读者更好的理解,下文同理)。可是实际上平面并没有位移,所以观察者所看到的是A点,而不是B点。那我们如何通过高度图寻找到一个适合的采样点呢?

这就是视差贴图的用处。我们要用V减去P,P的方向和V一致,大小与H(A)相等。此时观察者看到的是H(P)在贴图上对应的点,即(V-P)向量与H(P)处虚线交点处在贴图上对应的点,而此处和B点相距较近。

如果某点的高度越高,那么对其纹理坐标的偏移也就越大,记住,视差贴图并不能使表面看起来和高度图完全一致,因为它只是根据高度图来欺骗观察者视觉的一个trick。

实现

在真正实现时我们会采用深度图(即(1-height)所得)。

P的方向和V一致,大小与H(A)相等。A点纹理坐标就会偏移到H(P)点,和我们想要获得的P点相距较近。

float height = tex2D(_DepthMap, i.uv.zw);float2 p = viewDir.xy / viewDir.z * (height * _HeightScale);i.uv.zw = i.uv.zw - p;

这里的操作均是在切线空间中进行的。

附上代码

Shader "Custom/ParallaxMapping"{    Properties    {		_Diffuse("Diffuse",Color) = (1,1,1,1)		_MainTex("Main Tex",2D)="White"{}		_Specular("Specular",Color) = (1,1,1,1)		_Gloss("Gloss",Range(8.0,256)) = 20		_Color("Color Tint",Color) = (1,1,1,1)		_BumpMap("Normal Map",2D)="bump"{}		_BumpScale("Bump Scale",Float)=1.0		//**********************************		_HeightMap("_DepthMap",2D)="white"{}		_HeightScale("HeightScale",Range(0.005,0.08)) = 1		//*******************************    }		SubShader{		pass {		Tags{"LightMode" = "ForwardBase"}			CGPROGRAM#pragma vertex vert#pragma fragment frag#include"Lighting.cginc"		fixed4 _Diffuse;		sampler2D _MainTex;		fixed4 _MainTex_ST;		sampler2D _BumpMap;		fixed4 _BumpMap_ST;		float _BumpScale;		fixed4 _Specular;		float _Gloss;		fixed4 _Color;		//*********************		sampler2D _DepthMap;		float _HeightScale;		//***********************		struct a2f {			float4 vertex:POSITION;			float3 normal:NORMAL;			float4 texcoord:TEXCOORD0;			float4 tangent:TANGENT;		};		struct v2f {			float4 pos:SV_POSITION;			float4 uv:TEXCOORD0;			float4 TtoW0:TEXCOORD1;			float4 TtoW1:TEXCOORD2;			float4 TtoW2:TEXCOORD3;		};		v2f vert(a2f v) {			v2f o;			o.pos = UnityObjectToClipPos(v.vertex);			o.uv.xy = v.texcoord.xy*_MainTex_ST.xy + _MainTex_ST.zw;			o.uv.zw = v.texcoord.xy*_BumpMap_ST.xy + _BumpMap_ST.zw;			float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;			fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);			fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);			fixed3 worldBinormal = cross(worldNormal, worldTangent)*v.tangent.w;			o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);			o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);			o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);			return o;		}		fixed4 frag(v2f i) :SV_Target{			float3 worldPos = float3(i.TtoW0.w,i.TtoW1.w,i.TtoW2.w);						fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));			fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));			//**************************************			float height = tex2D(_DepthMap, i.uv.zw);			float2 p = viewDir.xy / viewDir.z * (height * _HeightScale);			i.uv.zw = i.uv.zw - p;			//**************************************			fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv.zw));			bump.xy *= _BumpScale;			bump.z = sqrt(1 - saturate(dot(bump.xy, bump.xy)));			bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));			fixed3 albedo = tex2D(_MainTex, i.uv).rgb*_Color.rgb;			fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz*albedo;			fixed3 diffuse = _LightColor0.rgb*_Diffuse.rbg*albedo*max(dot(bump, lightDir),0);						fixed3 halfDir = normalize(lightDir + lightDir);						fixed3 specular = _LightColor0.rgb*_Specular.rgb*pow(max(0,dot(bump, halfDir)), _Gloss);									return fixed4(diffuse + ambient+ specular, 1.0);		}			ENDCG		}	}    FallBack "Specular"}

陡峭视差贴图(Steep Parallax Mapping)

陡峭视差贴图和视差贴图原理基本一致,但是效果更好。

我们从上到下遍历深度层,我们把每个深度层和储存在深度贴图中的它的深度值进行对比。如果这个层的深度值小于深度贴图的值,就意味着这一层的P向量部分在表面之下。我们继续这个处理过程直到有一层的深度高于储存在深度贴图中的值:这个点就在(经过位移的)表面下方。

这个例子中我们可以看到第二层(D(2) = 0.73)的深度贴图的值仍低于第二层的深度值0.4,所以我们继续。下一次迭代,这一层的深度值0.6大于深度贴图中采样的深度值(D(3) = 0.37)。我们便可以假设第三层向量P是可用的位移几何位置。我们可以用从向量P3的纹理坐标偏移T3来对fragment的纹理坐标进行位移。你可以看到随着深度曾的增加精确度也在提高。

最好的点是视线与高度图的交点,而最终我们获得了T3这个点,两者相差极小。

float numLayers = 10;float layerDepth = 1.0 / numLayers;float currentLayerDepth = 0.0;float2 p = viewDir.xy *_HeightScale;float deltaTexCoords = p / numLayers;float2 currentTexCoords = i.uv.zw;float currentDepthMapValue = tex2D(_DepthMap, currentTexCoords).r;			while(currentLayerDepth < currentDepthMapValue){	currentTexCoords -= deltaTexCoords;	currentDepthMapValue = tex2D(_DepthMap, currentTexCoords).r;	currentLayerDepth += layerDepth;}i.uv.zw =  currentTexCoords;

但是循环语句中,循环次数不能超过1024次,否则会报错。具体原因可参考

  • 《Unity Shader 入门精要》
    • 5.7.4慎用分支和循环语句
    • 16章 Unity中的渲染优化次数

 

 

 

转载地址:http://kduh.baihongyu.com/

你可能感兴趣的文章
NIFI1.21.0/NIFI1.22.0/NIFI1.24.0/NIFI1.26.0_2024-06-11最新版本安装_采用HTTP方式_搭建集群_实际操作---大数据之Nifi工作笔记0050
查看>>
NIFI1.21.0_java.net.SocketException:_Too many open files 打开的文件太多_实际操作---大数据之Nifi工作笔记0051
查看>>
NIFI1.21.0_Mysql到Mysql增量CDC同步中_日期类型_以及null数据同步处理补充---大数据之Nifi工作笔记0057
查看>>
NIFI1.21.0_Mysql到Mysql增量CDC同步中_补充_插入时如果目标表中已存在该数据则自动改为更新数据_Postgresql_Hbase也适用---大数据之Nifi工作笔记0058
查看>>
NIFI1.21.0_Mysql到Mysql增量CDC同步中_补充_更新时如果目标表中不存在记录就改为插入数据_Postgresql_Hbase也适用---大数据之Nifi工作笔记0059
查看>>
NIFI1.21.0_NIFI和hadoop蹦了_200G集群磁盘又满了_Jps看不到进程了_Unable to write in /tmp. Aborting----大数据之Nifi工作笔记0052
查看>>
NIFI1.21.0_Postgresql和Mysql同时指定库_指定多表_全量同步到Mysql数据库以及Hbase数据库中---大数据之Nifi工作笔记0060
查看>>
NIFI1.21.0最新版本安装_连接phoenix_单机版_Https登录_什么都没改换了最新版本的NIFI可以连接了_气人_实现插入数据到Hbase_实际操作---大数据之Nifi工作笔记0050
查看>>
NIFI1.21.0最新版本安装_配置使用HTTP登录_默认是用HTTPS登录的_Https登录需要输入用户名密码_HTTP不需要---大数据之Nifi工作笔记0051
查看>>
NIFI1.21.0通过Postgresql11的CDC逻辑复制槽实现_指定表多表增量同步_增删改数据分发及删除数据实时同步_通过分页解决变更记录过大问题_02----大数据之Nifi工作笔记0054
查看>>
NIFI1.21.0通过Postgresql11的CDC逻辑复制槽实现_指定表多表增量同步_增加修改实时同步_使用JsonPath及自定义Python脚本_03---大数据之Nifi工作笔记0055
查看>>
NIFI1.21.0通过Postgresql11的CDC逻辑复制槽实现_指定表多表增量同步_插入修改删除增量数据实时同步_通过分页解决变更记录过大问题_01----大数据之Nifi工作笔记0053
查看>>
NIFI1.21.0通过Postgresql11的CDC逻辑复制槽实现_指定表或全表增量同步_实现指定整库同步_或指定数据表同步配置_04---大数据之Nifi工作笔记0056
查看>>
NIFI1.23.2_最新版_性能优化通用_技巧积累_使用NIFI表达式过滤表_随时更新---大数据之Nifi工作笔记0063
查看>>
NIFI从MySql中增量同步数据_通过Mysql的binlog功能_实时同步mysql数据_根据binlog实现update数据实时同步_实际操作05---大数据之Nifi工作笔记0044
查看>>
NIFI从MySql中增量同步数据_通过Mysql的binlog功能_实时同步mysql数据_根据binlog实现数据实时delete同步_实际操作04---大数据之Nifi工作笔记0043
查看>>
NIFI从MySql中增量同步数据_通过Mysql的binlog功能_实时同步mysql数据_配置binlog_使用处理器抓取binlog数据_实际操作01---大数据之Nifi工作笔记0040
查看>>
NIFI从MySql中增量同步数据_通过Mysql的binlog功能_实时同步mysql数据_配置数据路由_实现数据插入数据到目标数据库_实际操作03---大数据之Nifi工作笔记0042
查看>>
NIFI从MySql中增量同步数据_通过Mysql的binlog功能_实时同步mysql数据_配置数据路由_生成插入Sql语句_实际操作02---大数据之Nifi工作笔记0041
查看>>
NIFI从MySql中离线读取数据再导入到MySql中_03_来吧用NIFI实现_数据分页获取功能---大数据之Nifi工作笔记0038
查看>>