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

Unity Shader编程】之高级纹理

一,立方体纹理 Cubemap 用途

用途说明
反射贴图表面镜面高光或金属反射
环境光采样模拟环境对物体的影响
天空盒背景使用六张图拼接场景背景
全景投影做360度相机渲染、投影等

二,创建立方体纹理

在 Unity 中创建和保存一个 立方体纹理(Cubemap) 有几种方式,具体看你是想要:


✅ 一、使用现成的六张图片创建 Cubemap(最常用)

步骤如下:
  1. 准备六张图像
    命名如下(Unity 会自动识别):

    myCubemap_front.png
    myCubemap_back.png
    myCubemap_left.png
    myCubemap_right.png
    myCubemap_up.png
    myCubemap_down.png
    
  2. 导入到 Unity
    将六张图拖入 Unity 的 Assets 目录中。

  3. 创建 Cubemap 资源

    • Assets 空白处右键 > Create > Legacy > Cubemap
    • 命名为 MyCubemap
    • 选中它后,在 Inspector 中点击 各个面,分别拖入上面对应的图(Unity 会有 Front、Back、Left、Right、Up、Down 的插槽)
  4. 保存 Cubemap
    这个 .cubemap 文件本身就会保存在你的项目里,可以直接拖入 Shader 中的 Cubemap 属性上。


✅ 二、将全景图(equirectangular)转换为 Cubemap(HDRI)

Unity 也支持将 全景 HDR 图 转换为 Cubemap 使用,适用于天空盒、环境反射等。

  1. 拖入 .hdr.exr 图像到项目中

  2. 在该图像的 Inspector 里设置:

    • Texture TypeCube
    • MappingLatitude-Longitude Layout
    • Generate Mip Maps → 勾选(反射需要)
    • Apply

这样 Unity 就会自动将它作为 Cubemap 处理。


✅ 三、运行时生成立方体纹理(程序生成)

如果你需要运行时创建一个 Cubemap 并保存(例如截图天空),可以这样做:

public class CubemapCapture : MonoBehaviour
{public Camera renderCamera;public int cubemapSize = 128;public string savePath = "Assets/CapturedCubemap.cubemap";void Start(){Cubemap cube = new Cubemap(cubemapSize, TextureFormat.RGBA32, false);renderCamera.RenderToCubemap(cube);// 保存#if UNITY_EDITORUnityEditor.AssetDatabase.CreateAsset(cube, savePath);UnityEditor.AssetDatabase.SaveAssets();#endif}
}

⚠️ 注意事项:

  • 你必须挂载一个摄像机并指向你想捕捉的中心。
  • 只能在 Editor 下使用保存功能(运行时不能创建 .cubemap 文件,除非导出为贴图数组等特殊形式)。

✅ 四、通过 Reflection Probe 生成 Cubemap

Unity 中的 Reflection Probe 本质上也在实时生成 Cubemap:

  1. GameObject > Light > Reflection Probe
  2. 设置为 Baked 或 Realtime
  3. 勾选 Box Projection(可选)
  4. Bake 后它会自动生成一个 Cubemap,用于环境反射

你可以通过 Shader 中 _ReflectionProbe0 获取它,或绑定到材质里。

其中,Reflection Probe bake的时候,你可能会发现它只bake进去天空盒,那是因为Reflection Probe只会bake进去场景中为static的物体

✅ 小结:创建 Cubemap 的常用方法

方式适用场景是否支持保存
手动导入六图拼接天空盒、自定义 Cubemap
HDRI 贴图转 Cube天空盒、环境光
Camera.RenderToCubemap动态反射、截图环境✔(仅 Editor)
Reflection Probe物理反射、全局光照自动生成

三,反射,折射贴图

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'Shader "Unity Shaders Book/Chapter 10/Refraction" {Properties {// 材质颜色,用于混合漫反射颜色_Color ("Color Tint", Color) = (1, 1, 1, 1)// 折射颜色,可用于调节 Cubemap 显示色调_RefractColor ("Refraction Color", Color) = (1, 1, 1, 1)// 折射混合权重:0表示完全漫反射,1表示完全折射_RefractAmount ("Refraction Amount", Range(0, 1)) = 1// 折射率比(η),用于控制折射方向,通常设置为空气到玻璃的折射率比,如 1.0/1.33_RefractRatio ("Refraction Ratio", Range(0.1, 1)) = 0.5// 用于折射的 Cubemap 环境贴图,通常使用场景天空盒_Cubemap ("Refraction Cubemap", Cube) = "_Skybox" {}}SubShader {// 使用 Opaque 渲染队列,并归类为 Geometry 类型Tags { "RenderType"="Opaque" "Queue"="Geometry" }Pass {// 使用前向渲染的主光源通道Tags { "LightMode"="ForwardBase" }CGPROGRAM// 开启前向光照编译宏(支持多个光源和阴影)#pragma multi_compile_fwdbase	// 指定使用的顶点/片元函数#pragma vertex vert#pragma fragment frag// 引入 Unity 的光照与阴影宏定义#include "Lighting.cginc"#include "AutoLight.cginc"// 声明从 Properties 中传入的变量fixed4 _Color;fixed4 _RefractColor;float _RefractAmount;fixed _RefractRatio;samplerCUBE _Cubemap;// 顶点输入结构体:包含位置和法线struct a2v {float4 vertex : POSITION;float3 normal : NORMAL;};// 顶点到片元的传输结构体(插值器)struct v2f {float4 pos : SV_POSITION;         // 裁剪空间顶点位置(用于屏幕映射)float3 worldPos : TEXCOORD0;      // 顶点世界坐标fixed3 worldNormal : TEXCOORD1;   // 世界法线fixed3 worldViewDir : TEXCOORD2;  // 世界视线方向(摄像机指向像素点)fixed3 worldRefr : TEXCOORD3;     // 世界折射方向SHADOW_COORDS(4)                  // Unity 内置阴影坐标宏};// 顶点着色器:计算空间变换、折射方向等v2f vert(a2v v) {v2f o;// 将顶点从本地空间转换为裁剪空间,作为 SV_POSITION 输出o.pos = UnityObjectToClipPos(v.vertex);// 将法线从本地空间转换为世界空间,并归一化o.worldNormal = UnityObjectToWorldNormal(v.normal);// 将顶点位置从对象空间转换到世界空间o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;// 计算视线方向:摄像机 → 当前像素(世界空间)o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);//反射,模拟镜面效果,和折射相冲突,保留一个// o.worldRefr=reflect(-o.worldViewDir,o.worldNormal);// 使用 HLSL 内置函数 refract 计算折射方向// 输入方向 I = -视线方向(从像素指向相机),法线 N,折射率 etao.worldRefr = refract(-normalize(o.worldViewDir), normalize(o.worldNormal), _RefractRatio);// 阴影贴图相关数据插值初始化(支持阴影衰减)TRANSFER_SHADOW(o);return o;}// 片元着色器:执行漫反射、折射采样、颜色混合等fixed4 frag(v2f i) : SV_Target {// 标准化输入的法线fixed3 worldNormal = normalize(i.worldNormal);// 获取主光源方向(世界空间)fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));// 获取标准化视线方向(摄像机 → 当前像素)fixed3 worldViewDir = normalize(i.worldViewDir);// 获取环境光颜色fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;// 标准 Lambert 漫反射计算fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(worldNormal, worldLightDir));// 用折射方向采样 Cubemap 环境贴图,并乘上折射色调fixed3 refraction = texCUBE(_Cubemap, i.worldRefr).rgb * _RefractColor.rgb;// 计算阴影衰减(基于阴影贴图)UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);// 最终颜色混合:将漫反射与折射按权重混合fixed3 color = ambient + lerp(diffuse, refraction, _RefractAmount) * atten;// 返回最终颜色,alpha = 1(不透明)return fixed4(color, 1.0);}ENDCG}}// 降级 Shader,如果目标平台不支持此 Shader 则使用FallBack "Reflective/VertexLit"
}

四,渲染纹理

在unity中有一种特殊的pass,GrabPass,定义后,unity会把当前屏幕的图像绘制在一张纹理中,然后我们可以通过对这个纹理来进行操作

4.1 玻璃效果

// Upgrade NOTE: replaced ‘_Object2World’ with ‘unity_ObjectToWorld’
// Upgrade NOTE: replaced ‘mul(UNITY_MATRIX_MVP,)’ with 'UnityObjectToClipPos()’

Shader “Unity Shaders Book/Chapter 10/Glass Refraction” {
Properties {
_MainTex (“Main Tex”, 2D) = “white” {} // 主纹理贴图
_BumpMap (“Normal Map”, 2D) = “bump” {} // 法线贴图
_Cubemap (“Environment Cubemap”, Cube) = “_Skybox” {} // 环境反射贴图(立方体)
_Distortion (“Distortion”, Range(0, 100)) = 10 // 法线扰动强度(影响折射扭曲)
_RefractAmount (“Refract Amount”, Range(0.0, 1.0)) = 1.0 // 折射/反射混合比例
}

SubShader {Tags { "Queue"="Transparent" "RenderType"="Opaque" }// 捕捉当前屏幕图像到 _RefractionTex(背景图像用于模拟折射)GrabPass { "_RefractionTex" }Pass {CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"sampler2D _MainTex;float4 _MainTex_ST;sampler2D _BumpMap;float4 _BumpMap_ST;samplerCUBE _Cubemap;float _Distortion;fixed _RefractAmount;sampler2D _RefractionTex;             // 被 GrabPass 捕捉到的背景图像float4 _RefractionTex_TexelSize;      // 屏幕图像的像素大小(用于精确偏移)struct a2v {float4 vertex : POSITION;float3 normal : NORMAL;float4 tangent : TANGENT;float2 texcoord : TEXCOORD0;};struct v2f {float4 pos : SV_POSITION;float4 scrPos : TEXCOORD0;     // 屏幕坐标,用于 GrabPass UV 采样float4 uv : TEXCOORD1;         // uv.xy = 主纹理UV,uv.zw = 法线纹理UVfloat4 TtoW0 : TEXCOORD2;      // TBN矩阵第1行 + worldPos.xfloat4 TtoW1 : TEXCOORD3;      // TBN矩阵第2行 + worldPos.yfloat4 TtoW2 : TEXCOORD4;      // TBN矩阵第3行 + worldPos.z};v2f vert (a2v v) {v2f o;o.pos = UnityObjectToClipPos(v.vertex);   // 模型 → 裁剪空间// 计算当前像素在屏幕上的 GrabPass 采样位置(自动透视修正)o.scrPos = ComputeGrabScreenPos(o.pos);// 主纹理、法线纹理 UV 变换o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);o.uv.zw = TRANSFORM_TEX(v.texcoord, _BumpMap);// 构建 TBN 矩阵(切线空间 → 世界空间)float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;float3 worldNormal = UnityObjectToWorldNormal(v.normal);float3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);float3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w;// 每行存 TBN basis 的 xyz,w 分量存 worldPos 对应轴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 {// 恢复世界坐标(从每个 w 分量提取)float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);// 计算从像素指向摄像机的视线方向(世界空间)fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));// 从法线贴图采样切线空间法线(范围[-1, 1])fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv.zw));// ✅【产生玻璃折射扭曲感】的核心代码:float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy;i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy;// 采样 GrabPass 纹理作为“背景折射”颜色fixed3 refrCol = tex2D(_RefractionTex, i.scrPos.xy / i.scrPos.w).rgb;// 法线从切线空间转换到世界空间(TBN乘切线法线)bump = normalize(half3(dot(i.TtoW0.xyz, bump),dot(i.TtoW1.xyz, bump),dot(i.TtoW2.xyz, bump)));// 计算反射方向fixed3 reflDir = reflect(-worldViewDir, bump);// 从 Cubemap 中采样反射环境颜色fixed4 texColor = tex2D(_MainTex, i.uv.xy);fixed3 reflCol = texCUBE(_Cubemap, reflDir).rgb * texColor.rgb;// ✅【反射 + 折射】混合(_RefractAmount 控制)fixed3 finalColor = reflCol * (1 - _RefractAmount) + refrCol * _RefractAmount;return fixed4(finalColor, 1); // 不透明(你可以改为支持透明混合)}ENDCG}
}Fallback "Diffuse"

}

bump.xy 决定了你往哪个方向“挤压/偏移”

_Distortion 放大效果

texel size 保证单位一致(从像素偏移变成 UV 偏移)
i.scrPos.xy 原本就是当前像素在屏幕中的 UV 坐标(GrabPass用)
i.scrPos.z 透视深度校正因子(从 ComputeGrabScreenPos() 中得)
offset * i.scrPos.z 根据深度远近决定偏移强度,防止透视拉伸过度

  • i.scrPos.xy 添加偏移量,实现“错位采样”
    这是一个折射的功能shader,为什么要添加cubemap来作为反射呢?
    ➤ 它是模拟玻璃表面上的环境反射(如天空、高光、远处光源反光)
    不影响背景的错位采样

不影响整体透视扭曲

仅仅为表面提供一种“微妙的光泽感”

🧪 如果 Cubemap 是纯黑图,你会发现玻璃表面失去了高光反射感,变“哑光”

http://www.xdnf.cn/news/786223.html

相关文章:

  • C++ TCP传输心跳信息
  • 秋招Day12 - 计算机网络 - IP
  • Kafka集群部署(docker容器方式)SASL认证(zookeeper)
  • 用HTML5 Canvas打造交互式心形粒子动画:从基础到优化实战
  • 软件工程的定义与发展历程
  • 关于FPGA软核的仿真(一)
  • 队列的讲解:C++队列的使用
  • 12.7 LangChain实战:1.2秒响应!用LCEL构建高效RAG系统,准确率提升41%
  • docker离线镜像下载
  • Nginx配置Ollama 访问api服务
  • TablePlus:一个跨平台的数据库管理工具
  • AI Coding 资讯 2025-06-03
  • 垂起固定翼无人机应用及技术分析
  • godwork_ AT 5.2 摄影测量空三数据处理软件。
  • 闲谈PMIC和SBC
  • FTXUI::Screen 模块
  • 《Effective Python》第六章 推导式和生成器——总结(基于智能物流仓储监控系统的数据处理)
  • 1. 引言
  • 《DeepSeek使用指南》开源知识库正式上线啦!
  • Spring AI开发跃迁指南(第二章:精进之道1——花样玩转LLM对话记忆功能)
  • 小巧实用,Windows文件夹着色软件推荐
  • OD 算法题 B卷【矩阵稀疏扫描】
  • 【知识点】openai请求参数如何转为大模型的字符串?
  • 如何将 iOS 性能调试融入日常开发流程?构建“默认监控机制”的实战经验(含 KeyMob 工具搭配)
  • 【高等数学】(1)映射
  • 二分查找的边界艺术:LeetCode 34 题深度解析
  • WebFuture:启动服务提示Job webfuture.service/start failed with result ‘dependency‘处理办法
  • 大模型 提示模板 设计
  • 动态规划(2)
  • 从测试角度看待CI/CD,敏捷开发