当前位置: 首页 > news >正文

【光照】Unity如何在Cubemap中采样反射信息?

【从UnityURP开始探索游戏渲染】专栏-直达

介绍与发展历史

Cubemap(立方体贴图)是一种由六个独立的正方形纹理组成的集合,它将多个纹理组合起来映射到一个单一纹理。Cubemap包含6个2D纹理,每个2D纹理代表立方体的一个面,形成一个有贴图的立方体。

Cubemap技术起源于早期的3D图形学,最初用于实现天空盒效果。随着硬件性能的提升和图形API的发展,Cubemap逐渐被广泛应用于环境映射、反射和折射等高级渲染效果中。

应用领域

Cubemap在Unity中主要有以下应用场景:

  • 环境反射‌:用于模拟金属、玻璃等具有反射属性物体的反射效果
  • 天空盒‌:创建环境背景,提供场景的全局光照信息
  • 折射效果‌:模拟透明材质的折射现象
  • 全局光照‌:作为间接光照的来源之一

采样反射信息原理

基本原理

Cubemap采样的核心原理是使用方向向量进行索引和采样。想象一个1×1×1的单位立方体,中心位于原点。当从原点发出一个方向向量时,该向量会与立方体的某个面相交,交点处的纹理就是采样结果。

反射计算原理

  • 反射效果的计算过程如下:
  • 计算相机指向物体表面点的向量(视线向量)
  • 根据表面法线计算反射向量
  • 使用反射向量在Cubemap中进行采样(URP中使用宏SAMPLE_TEXTURECUBE实现采样,这个宏实际调用PLATFORM_SAMPLE_TEXTURECUBE宏来处理不同图形API的采样方法,下面有详细讲原理讲解。)
  • 将采样颜色与物体本身的颜色混合

数学上,反射向量R可以通过以下公式计算:

R = I - 2 * dot(N, I) * N

其中I是入射向量(视线向量取反),N是表面法线。

PLATFORM_SAMPLE_TEXTURECUBE的实现原理

PLATFORM_SAMPLE_TEXTURECUBE是Unity URP中用于跨平台Cubemap采样的关键宏,它封装了不同图形API(Direct3D、OpenGL、Metal)下Cubemap采样的实现差异,为开发者提供统一的采样接口。

数学原理基础

Cubemap是一个由6个2D纹理组成的立方体,每个面代表一个方向(±X, ±Y, ±Z)。使用反射向量作为方向索引,可以获取立方体相应位置的纹理颜色

1. 方向向量确定采样面

Cubemap采样基于3D方向向量,通过以下步骤确定采样面:

数学表达式:

主面 = max(|x|, |y|, |z|)
if (主面 == |x|)
if (x > 0) → +X面
else → -X面
else if (主面 == |y|)
if (y > 0) → +Y面
else → -Y面
else
if (z > 0) → +Z面
else → -Z面

## **2. 方向向量到UV坐标转换**确定采样面后,将3D方向向量转换为2D UV坐标的数学过程:对于+X面(右面):

u = 0.5 * (1 - (z / |x|))
v = 0.5 * (1 - (y / |x|))

对于-Y面(下面):

u = 0.5 * (1 - (x / |y|))
v = 0.5 * (1 + (z / |y|))

其他面的转换类似,但需要考虑不同面的坐标系差异。## **URP中的具体实现**### **PLATFORM_SAMPLE_TEXTURECUBE宏定义**在URP Core.hlsl中,该宏通常定义为:```c
hlsl
#define PLATFORM_SAMPLE_TEXTURECUBE(textureName, samplerName, coord3) \SampleTexture(textureName, samplerName, coord3)

实际采样函数会根据平台不同而有所区别,但对外提供统一接口。

反射向量计算示例

在URP Shader中计算反射向量并采样Cubemap的完整过程:

  • 计算视线向量(从表面点到相机):
hlsl
float3 viewDir = GetWorldSpaceViewDir(positionWS);
  • 计算反射向量:
hlsl
float3 reflectDir = reflect(-viewDir, normalWS);
  • 采样Cubemap:
hlsl
float4 cubemapColor = PLATFORM_SAMPLE_TEXTURECUBE(_Cubemap, sampler_Cubemap, reflectDir);
  • 解码HDR颜色(如果需要):
hlsl
float3 reflection = DecodeHDREnvironment(cubemapColor, _Cubemap_HDR);
  • 混合反射颜色与表面颜色:
hlsl
float3 finalColor = lerp(diffuseColor, reflection, _ReflectAmount);

不同图形API的实现差异

Direct3D

  • 面顺序:+X, -X, +Y, -Y, +Z, -Z
  • UV坐标系:左上角为(0,0),右下角为(1,1)
  • 需要特别注意Y轴的朝向

OpenGL

  • 面顺序:+X, -X, +Y, -Y, +Z, -Z
  • UV坐标系:左下角为(0,0),右上角为(1,1)
  • 通常需要翻转Y轴坐标

Metal

  • 使用MTLTextureTypeCube类型
  • 面顺序与Direct3D类似
  • 采样时需要考虑坐标系转换

数学示例:具体采样过程

假设有一个方向向量(0.5, -0.3, 0.8),计算其在Cubemap中的采样位置:

  • 确定主分量:
    • |x|=0.5, |y|=0.3, |z|=0.8 → 主分量为z
  • 确定采样面:
    • z=0.8 > 0 → +Z面
  • 计算UV坐标:
    • u = 0.5 * (1 + (x/|z|)) = 0.5 * (1 + (0.5/0.8)) ≈ 0.8125
    • v = 0.5 * (1 - (y/|z|)) = 0.5 * (1 - (-0.3/0.8)) ≈ 0.6875
  • 在+Z面纹理上采样(0.8125, 0.6875)处的颜色

性能优化考虑

  • 粗糙度与Mipmap‌:根据表面粗糙度选择适当的Mipmap级别,粗糙表面使用更高层级的Mipmap

    float perceptualRoughness = 1.0 - _Smoothness;
    float mip = PerceptualRoughnessToMipmapLevel(perceptualRoughness);
    float4 envColor = SAMPLE_TEXTURECUBE_LOD(_Cubemap, sampler_Cubemap, reflectDir, mip);
    

URP中的PLATFORM_SAMPLE_TEXTURECUBE宏通过统一这些复杂操作,使开发者能够专注于材质效果本身,而无需关心底层平台差异

实现示例

以下是一个简单的反射Cubemap的Shader实现:

  • 定义Shader属性,包括颜色、反射颜色、反射强度和Cubemap纹理

  • 顶点着色器计算世界空间的位置、法线、视线方向和反射方向

  • 片元着色器计算环境光、漫反射和反射颜色

  • 使用lerp函数混合漫反射和反射颜色,控制反射强度

  • 最终输出混合后的颜色

  • URPReflection.shader

    Shader "Custom/URPReflection"
    {Properties{_BaseColor("Base Color", Color) = (1,1,1,1)_ReflectColor("Reflection Color", Color) = (1,1,1,1)_ReflectAmount("Reflect Amount", Range(0,1)) = 0.5_Cubemap("Reflection Cubemap", Cube) = "_Skybox" {}_Smoothness("Smoothness", Range(0,1)) = 0.5}SubShader{Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" }HLSLINCLUDE#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"struct Attributes{float4 positionOS : POSITION;float3 normalOS : NORMAL;};struct Varyings{float4 positionHCS : SV_POSITION;float3 positionWS : TEXCOORD0;float3 normalWS : TEXCOORD1;float3 viewDirWS : TEXCOORD2;float3 reflectDir : TEXCOORD3;};CBUFFER_START(UnityPerMaterial)half4 _BaseColor;half4 _ReflectColor;half _ReflectAmount;half _Smoothness;CBUFFER_ENDTEXTURECUBE(_Cubemap);SAMPLER(sampler_Cubemap);Varyings vert(Attributes IN){Varyings OUT;// 顶点变换OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);OUT.positionWS = TransformObjectToWorld(IN.positionOS.xyz);OUT.normalWS = TransformObjectToWorldNormal(IN.normalOS);// 计算视线方向(从表面点到相机)OUT.viewDirWS = GetWorldSpaceViewDir(OUT.positionWS);// 计算反射方向float3 viewDir = normalize(OUT.viewDirWS);OUT.reflectDir = reflect(-viewDir, normalize(OUT.normalWS));return OUT;}half4 frag(Varyings IN) : SV_Target{// 标准化法线和反射方向float3 normalWS = normalize(IN.normalWS);float3 reflectDir = normalize(IN.reflectDir);// 计算漫反射光照Light light = GetMainLight();float3 lightDir = normalize(light.direction);float NdotL = saturate(dot(normalWS, lightDir));half3 diffuse = _BaseColor.rgb * light.color * NdotL;// 采样Cubemaphalf perceptualRoughness = 1.0 - _Smoothness;half mip = PerceptualRoughnessToMipmapLevel(perceptualRoughness);half4 cubemapColor = SAMPLE_TEXTURECUBE_LOD(_Cubemap, sampler_Cubemap, reflectDir, mip);half3 reflection = DecodeHDREnvironment(cubemapColor, unity_SpecCube0_HDR) * _ReflectColor.rgb;// 混合漫反射和反射half3 color = lerp(diffuse, reflection, _ReflectAmount);return half4(color, 1.0);}ENDHLSL}
    }
    

Cubemap生成方法

在Unity中可以通过以下步骤生成Cubemap:

  • 创建空物体作为观察位置
  • 在Project视图右键创建Legacy/Cubemap
  • 使用脚本调用Camera.RenderToCubemap方法生成
  • 确保Cubemap勾选Readable选项以便脚本读取

Cubemap技术为Unity中的环境反射和高级材质效果提供了强大的支持,通过合理使用可以显著提升场景的真实感和视觉质量


【从UnityURP开始探索游戏渲染】专栏-直达
(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)

http://www.zskr.cn/news/16088.html

相关文章:

  • 深入解析:[论文阅读]Poisonprompt: Backdoor attack on prompt-based large language models
  • 箭头
  • 2025广告喷绘公司最新推荐榜单, 覆盖广告喷绘广告牌,广告喷绘写真,广告喷绘广告牌写真,广告喷绘门头服务!
  • 实用指南:24.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--单体转微服务--认证微服务
  • 详细介绍:STM32 串口通信①:USART 全面理解 + 代码详解
  • 2025夹丝玻璃厂家最新企业品牌推荐排行榜,艺术夹丝玻璃,淋浴房夹丝玻璃,极简门夹丝玻璃,金属夹丝玻璃公司推荐!
  • 详细介绍:性能优化 - 案例篇:缓存_Guava#LoadingCache设计
  • 2025年X射线管厂家最新企业品牌推荐排行榜,工业用金属陶瓷,波长色散荧光分析,应力衍射分析,管板角焊缝,轮胎检测,辐照,固定阳极波纹陶瓷,测厚,食品检测 X 射线管公司推荐
  • 深入解析:Guava限频器RateLimiter的使用示例
  • Photoshop 在线网页版?是的,它来了!免费使用指南
  • 鲲鹏Arm+麒麟V10 K8s 离线部署教程 - 教程
  • 线段树模板1
  • 20多年前李敖告訴你美國為什麼不可靠?
  • 2025数控铣床厂家最新企业品牌推荐排行榜, 双头数控铣床,双面数控铣床,龙门数控铣床,双侧数控铣床推荐这十家公司!
  • 题解:2025.10.信友队.智灵班选拔面试题目
  • MX WEEK4
  • 实用指南:【25软考网工】第十章 网络规划与设计(1)综合布线
  • 深入解析:Java基础(二):八种基本数据类型详解
  • 物理_备忘
  • 详细介绍:静态资源js,css免费CDN服务比较
  • 在AI技术唾手可得的时代,挖掘JavaScript学习资源的新需求成为关键
  • 读人形机器人31未来30年
  • 【java面试】redis篇 - 指南
  • NLP学习路线图(十四):词袋模型(Bag of Words) - 详解
  • 2025 年搅拌器厂家最新推荐排行榜:涵盖立式、不锈钢、侧入式等多类型设备,深度解析实力厂商
  • 2025 年最新推荐承烧板厂家排行榜:筛选优质企业,破解采购难题,赋能高温工业生产
  • 一文看懂AI SoC芯片
  • Python 在自动化测试与质量保障中的应用
  • 玩转树莓派屏幕之一:LCD屏幕显示
  • Python离群值检测实战:使用distfit库实现基于分布拟合的异常检测