【URP】Unity[视差遮挡贴图]原理剖析实践
【从UnityURP开始探索游戏渲染】专栏-直达
视差遮挡贴图(Parallax Occlusion Mapping, POM)介绍
视差遮挡贴图是视差贴图技术的高阶实现,通过光线步进(Raymarching)算法精确计算视线与高度图的交点,模拟复杂表面(如砖墙、岩石)的几何遮挡效果。相比标准视差贴图,POM能更真实地表现深度变化和自阴影,适用于高精度材质表现。
核心原理
- 分层深度检测将视线方向在切线空间分解为多层(通常8-15层),逐层采样高度图,通过二分法逼*视线与表面的交点。
- 动态采样优化根据视角与法线的夹角动态调整采样层数(**行视角增加层数,垂直视角减少层数),*衡精度与性能。
- 遮挡关系重建通过交点处的UV偏移修正纹理采样位置,模拟凹凸表面的光线遮挡效果。
Unity URP 实现示例与原理详解
代码关键点解析
动态采样层数
- 通过
lerp(_MaxSamples, _MinSamples, saturate(dot(float3(0,0,1), viewDirTS)))实现视角自适应分层,优化性能。
- 通过
光线步进循环
- 循环比较当前层高度与采样深度,找到首个交点区间,避免全精度遍历。
二分法优化
- 在初步交点区间内使用
lerp插值计算精确UV,减少采样次数。
- 在初步交点区间内使用
ParallaxOcclusionMapping.shader
Shader "Universal Render Pipeline/POM"
{
Properties
{
_MainTex("Albedo", 2D) = "white" {}
_NormalMap("Normal Map", 2D) = "bump" {}
_HeightMap("Height Map", 2D) = "white" {}
_ParallaxScale("Height Scale", Range(0, 0.1)) = 0.05
_MinSamples("Min Samples", Int) = 8
_MaxSamples("Max Samples", Int) = 15
} SubShader
{
Tags { "RenderPipeline"="UniversalPipeline" } HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" TEXTURE2D(_MainTex); SAMPLER(sampler_MainTex);
TEXTURE2D(_NormalMap); SAMPLER(sampler_NormalMap);
TEXTURE2D(_HeightMap); SAMPLER(sampler_HeightMap);
float _ParallaxScale;
int _MinSamples, _MaxSamples; float2 ParallaxOcclusionMapping(float3 viewDirTS, float2 uv)
{
// 动态计算采样层数
int numSamples = (int)lerp(_MaxSamples, _MinSamples, saturate(dot(float3(0,0,1), viewDirTS)));
float layerHeight = 1.0 / numSamples;
float2 deltaUV = _ParallaxScale * viewDirTS.xy / viewDirTS.z / numSamples; // 光线步进初始化
float currentLayerHeight = 0;
float2 currentUV = uv;
float currentDepth = 1 - SAMPLE_TEXTURE2D(_HeightMap, sampler_HeightMap, currentUV).r; // 分层检测
[loop]
for (int i = 0; i < 15; ++i) {
if (currentLayerHeight >= currentDepth) break;
currentUV -= deltaUV;
currentDepth = 1 - SAMPLE_TEXTURE2D(_HeightMap, sampler_HeightMap, currentUV).r;
currentLayerHeight += layerHeight;
} // 二分法精确交点
float2 prevUV = currentUV + deltaUV;
float prevDepth = currentDepth - layerHeight;
float weight = (currentLayerHeight - currentDepth) / (prevDepth - currentDepth);
return lerp(currentUV, prevUV, weight);
}
ENDHLSL Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag struct Attributes
{
float4 positionOS : POSITION;
float2 uv : TEXCOORD0;
float3 normalOS : NORMAL;
float4 tangentOS : TANGENT;
}; struct Varyings
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
float3 viewDirTS : TEXCOORD1;
}; Varyings vert(Attributes IN)
{
Varyings OUT;
VertexPositionInputs posInput = GetVertexPositionInputs(IN.positionOS.xyz);
OUT.positionCS = posInput.positionCS; // 转换视角方向到切线空间
VertexNormalInputs normInput = GetVertexNormalInputs(IN.normalOS, IN.tangentOS);
float3 viewDirWS = GetWorldSpaceViewDir(posInput.positionWS);
OUT.viewDirTS = TransformWorldToTangent(viewDirWS,
normInput.tangentWS, normInput.bitangentWS, normInput.normalWS); OUT.uv = IN.uv;
return OUT;
} half4 frag(Varyings IN) : SV_Target
{
// 计算POM偏移后的UV
float2 pomUV = ParallaxOcclusionMapping(normalize(IN.viewDirTS), IN.uv); // 采样最终纹理
half4 albedo = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, pomUV);
half3 normalTS = UnpackNormal(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, pomUV)); return half4(albedo.rgb, 1);
}
ENDHLSL
}
}
}
技术对比与性能建议
| 特性 | 标准视差贴图 | 陡峭视差贴图 | POM |
|---|---|---|---|
| 采样次数 | 单次 | 5-15次 | 8-15次+二分法 |
| 陡峭表面精度 | 低 | 中 | 高 |
| 适用*台 | 移动端 | PC/主机 | 高端PC |
| 推荐参数 | Scale=0.02 |
Scale=0.05 |
Scale=0.03-0.07 |
实际应用中需注意:
- 高度图建议使用法线贴图的Alpha通道节省资源
- 过高的
_ParallaxScale可能导致边缘拉伸,建议不超过0.1 - 移动端可减少
_MaxSamples至8层以降低开销
【从UnityURP开始探索游戏渲染】专栏-直达
(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,)
【URP】Unity[视差遮挡贴图]原理剖析实践的更多相关文章
- Unity Shader-法线贴图(Normal)及其原理
简介 以前经常听说“模型不好看啊,怎么办啊?”答曰“加法线”,”做了个高模,准备烘一下法线贴图”,“有的美术特别屌,直接画法线贴图”.....法线贴图到底是个什么鬼,当年天真的我真的被这个图形学的奇淫 ...
- 【Xamarin挖墙脚系列:Xamarin.IOS机制原理剖析】
原文:[Xamarin挖墙脚系列:Xamarin.IOS机制原理剖析] [注意:]团队里总是有人反映卸载Xamarin,清理不完全.之前写过如何完全卸载清理剩余的文件.今天写了Windows下的批命令 ...
- 【Xamarin 跨平台机制原理剖析】
原文:[Xamarin 跨平台机制原理剖析] [看了请推荐,推荐满100后,将发补丁地址] Xamarin项目从喊口号到现在,好几个年头了,在内地没有火起来,原因无非有三,1.授权费贵 2.贵 3.原 ...
- 【Xamain 跨平台机制原理剖析】
原文:[Xamain 跨平台机制原理剖析] [看了请推荐,推荐满100后,将发补丁地址] Xamarin项目从喊口号到现在,好几个年头了,在内地没有火起来,原因无非有三,1.授权费贵 2.贵 3.原生 ...
- MapReduce/Hbase进阶提升(原理剖析、实战演练)
什么是MapReduce? MapReduce是一种编程模型,用于大规模数据集(大于1TB)的并行运算.概念"Map(映射)"和"Reduce(归约)",和他们 ...
- 基本功 | Litho的使用及原理剖析
1. 什么是Litho? Litho是Facebook推出的一套高效构建Android UI的声明式框架,主要目的是提升RecyclerView复杂列表的滑动性能和降低内存占用.下面是Litho官网的 ...
- ARouter原理剖析及手动实现
ARouter原理剖析及手动实现 前言 路由跳转在项目中用了一段时间了,最近对Android中的ARouter路由原理也是研究了一番,于是就给大家分享一下自己的心得体会,并教大家如何实现一款简易的路由 ...
- 0000 - Spring 中常用注解原理剖析
1.概述 Spring 框架核心组件之一是 IOC,IOC 则管理 Bean 的创建和 Bean 之间的依赖注入,对于 Bean 的创建可以通过在 XML 里面使用 <bean/> 标签来 ...
- 深入浅出!从语义角度分析隐藏在Unity协程背后的原理
Unity的协程使用起来比较方便,但是由于其封装和隐藏了太多细节,使其看起来比较神秘.比如协程是否是真正的异步执行?协程与线程到底是什么关系?本文将从语义角度来分析隐藏在协程背后的原理,并使用C++来 ...
- Spring 中常用注解原理剖析
前言 Spring 框架核心组件之一是 IOC,IOC 则管理 Bean 的创建和 Bean 之间的依赖注入,对于 Bean 的创建可以通过在 XML 里面使用 <bean/> 标签来配置 ...
随机推荐
- 电脑安装Win11专业版出现卡住不动的问题
有很多深度官网的用户都想安装体验win11系统,但是因为win11专业版对系统配置有挺高的要求!这不有一位用户在安装win11系统的时候,就遇到了电脑安装时出现卡死不动的问题,接下来,深度技术系统小编 ...
- Unity 2D游戏开发优化技巧
https://blog.unity.com/cn/technology/a-lightning-round-of-great-tips-for-2d-games
- springbean的作用域面试题
- PostgreSQL 分区最佳实践
概述 分区的本质是将一张大的物理表从逻辑上拆分,为 N 个较小的物理表. 分区表按照官方的解释如下: The partitioned table itself is a "virtual&q ...
- mysql连接数,druid的连接数
my.ini // 注册数据库驱动 Class.forName(driver); DriverManager.getConnection(url, user, password); 按max_con ...
- 小程序如何集成即构IM实现即时通讯发消息聊天
之前的文章已经介绍了如何实现Web端的即时通讯IM,为了让大家全面的体验通信互动的快乐. 本文介绍如何使用 ZIM SDK 快速实现实现小程序端的基本的消息收发功能,在微信中实现一个mini版微信,也 ...
- OCI编程基础篇(二) 创建环境、分配句柄
访问www.tomcoding.com网站,学习Oracle内部数据结构,详细文档说明,下载Oracle的exp/imp,DUL,logminer,ASM工具的源代码,学习高技术含量的内容. 创建OC ...
- AES加密,什么时候需要填充
AES(高级加密标准)是一种分组密码,它将数据按照固定的块大小进行加密.AES标准规定了128位(16字节)的块大小.当使用AES加密时,如果输入的数据长度不是16字节的整数倍,就需要对最后一个数据块 ...
- QT信号槽: QT实现的观察者机制,可由信号触发槽方法。
QT信号槽: QT实现的观察者机制,可由信号触发槽方法. QT里没有单独的关键词定义事件,因此: 1.在系统层面,一般说的事件对QT而言,都是指操作系统层面的事件. 2.在QT开发层面,事件更多是 ...
- MySQL Binlog Digger
https://www.cnblogs.com/zqw111/p/12898819.html MySQL Binlog Digger是一个免费的,且基于图形界面的binlog挖掘分析工具.它可以为数据 ...