【从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[视差遮挡贴图]原理剖析实践的更多相关文章

  1. Unity Shader-法线贴图(Normal)及其原理

    简介 以前经常听说“模型不好看啊,怎么办啊?”答曰“加法线”,”做了个高模,准备烘一下法线贴图”,“有的美术特别屌,直接画法线贴图”.....法线贴图到底是个什么鬼,当年天真的我真的被这个图形学的奇淫 ...

  2. 【Xamarin挖墙脚系列:Xamarin.IOS机制原理剖析】

    原文:[Xamarin挖墙脚系列:Xamarin.IOS机制原理剖析] [注意:]团队里总是有人反映卸载Xamarin,清理不完全.之前写过如何完全卸载清理剩余的文件.今天写了Windows下的批命令 ...

  3. 【Xamarin 跨平台机制原理剖析】

    原文:[Xamarin 跨平台机制原理剖析] [看了请推荐,推荐满100后,将发补丁地址] Xamarin项目从喊口号到现在,好几个年头了,在内地没有火起来,原因无非有三,1.授权费贵 2.贵 3.原 ...

  4. 【Xamain 跨平台机制原理剖析】

    原文:[Xamain 跨平台机制原理剖析] [看了请推荐,推荐满100后,将发补丁地址] Xamarin项目从喊口号到现在,好几个年头了,在内地没有火起来,原因无非有三,1.授权费贵 2.贵 3.原生 ...

  5. MapReduce/Hbase进阶提升(原理剖析、实战演练)

    什么是MapReduce? MapReduce是一种编程模型,用于大规模数据集(大于1TB)的并行运算.概念"Map(映射)"和"Reduce(归约)",和他们 ...

  6. 基本功 | Litho的使用及原理剖析

    1. 什么是Litho? Litho是Facebook推出的一套高效构建Android UI的声明式框架,主要目的是提升RecyclerView复杂列表的滑动性能和降低内存占用.下面是Litho官网的 ...

  7. ARouter原理剖析及手动实现

    ARouter原理剖析及手动实现 前言 路由跳转在项目中用了一段时间了,最近对Android中的ARouter路由原理也是研究了一番,于是就给大家分享一下自己的心得体会,并教大家如何实现一款简易的路由 ...

  8. 0000 - Spring 中常用注解原理剖析

    1.概述 Spring 框架核心组件之一是 IOC,IOC 则管理 Bean 的创建和 Bean 之间的依赖注入,对于 Bean 的创建可以通过在 XML 里面使用 <bean/> 标签来 ...

  9. 深入浅出!从语义角度分析隐藏在Unity协程背后的原理

    Unity的协程使用起来比较方便,但是由于其封装和隐藏了太多细节,使其看起来比较神秘.比如协程是否是真正的异步执行?协程与线程到底是什么关系?本文将从语义角度来分析隐藏在协程背后的原理,并使用C++来 ...

  10. Spring 中常用注解原理剖析

    前言 Spring 框架核心组件之一是 IOC,IOC 则管理 Bean 的创建和 Bean 之间的依赖注入,对于 Bean 的创建可以通过在 XML 里面使用 <bean/> 标签来配置 ...

随机推荐

  1. 电脑安装Win11专业版出现卡住不动的问题

    有很多深度官网的用户都想安装体验win11系统,但是因为win11专业版对系统配置有挺高的要求!这不有一位用户在安装win11系统的时候,就遇到了电脑安装时出现卡死不动的问题,接下来,深度技术系统小编 ...

  2. Unity 2D游戏开发优化技巧

    https://blog.unity.com/cn/technology/a-lightning-round-of-great-tips-for-2d-games

  3. springbean的作用域面试题

  4. PostgreSQL 分区最佳实践

    概述 分区的本质是将一张大的物理表从逻辑上拆分,为 N 个较小的物理表. 分区表按照官方的解释如下: The partitioned table itself is a "virtual&q ...

  5. mysql连接数,druid的连接数

    my.ini // 注册数据库驱动 Class.forName(driver); DriverManager.getConnection(url, user, password);  按max_con ...

  6. 小程序如何集成即构IM实现即时通讯发消息聊天

    之前的文章已经介绍了如何实现Web端的即时通讯IM,为了让大家全面的体验通信互动的快乐. 本文介绍如何使用 ZIM SDK 快速实现实现小程序端的基本的消息收发功能,在微信中实现一个mini版微信,也 ...

  7. OCI编程基础篇(二) 创建环境、分配句柄

    访问www.tomcoding.com网站,学习Oracle内部数据结构,详细文档说明,下载Oracle的exp/imp,DUL,logminer,ASM工具的源代码,学习高技术含量的内容. 创建OC ...

  8. AES加密,什么时候需要填充

    AES(高级加密标准)是一种分组密码,它将数据按照固定的块大小进行加密.AES标准规定了128位(16字节)的块大小.当使用AES加密时,如果输入的数据长度不是16字节的整数倍,就需要对最后一个数据块 ...

  9. QT信号槽: QT实现的观察者机制,可由信号触发槽方法。

    QT信号槽: QT实现的观察者机制,可由信号触发槽方法. QT里没有单独的关键词定义事件,因此:  1.在系统层面,一般说的事件对QT而言,都是指操作系统层面的事件.  2.在QT开发层面,事件更多是 ...

  10. MySQL Binlog Digger

    https://www.cnblogs.com/zqw111/p/12898819.html MySQL Binlog Digger是一个免费的,且基于图形界面的binlog挖掘分析工具.它可以为数据 ...