目录
一、立方体贴图
立方体贴图即 Cube Map
1、基础理论
立方体纹理(Cubemap)是环境映射(Environment Mapping)一种实现方法。环境映射可以模拟物体周围的环境,而使用了环境映射的物体可以模拟反射周围环境的效果。比如金属、瓷器、塑料等带有反射特征的材质。 立方体贴图一共包含了6张图,这些图像对应了立方体的6个面。立方体的每个面表示沿着世界空间下的轴向(上、下、左、右、前、后)观察所得的图像。
立方体贴图中包含的6张图(图1)
和其它二维纹理坐标不同,对立方体纹理采样需要一个三维的纹理坐标(就是说采样立方体纹理的uv是一个三维的向量)。这个三维纹理坐标表示了在世界空间下的一个3D方向。这个方向矢量从立方体的中心出发,让它向外延申时会与立方体的6个纹理之一发生相交,而采样得到的结果就是由该交点计算而来。如下图所示给出了使用方向矢量对立方体纹理采样的过程。
对立方体贴图的采样(图2)
使用立方体贴图的优势是,实现简单快速,而得到的效果也比较好。它的缺点是当场景中引入了新的物体、光源、物体发生移动,我们就需要重新生成立方体贴图。它在实时渲染中有很多应用,最常见的就是用于天空盒子及环境映射。
创建立方体贴图有三种方法:第一种方法是直接由一些特殊布局的纹理创建;第二种方法是手动创建一个Cubemap资源,再把6张图赋给它;第三种方法是由脚本生成。
这篇文章主要介绍的就是第一种方法。第一种方法需要我们提供一张具有特殊布局的纹理,例如类似立方体展开图的交叉布局、全景布局等。
立方体贴图不同的排版形式(图3)
Unity中的设置:把立方体纹理贴图的Texture Shape设置为cube即可。
cubemap纹理贴图在unity中的设置(图4)
光照框架:直接光漫反射+直接光镜面反射+间接光漫反射+间接光镜面反射
在光照框架中,立方体贴图计算的就是间接光镜面反射的部分。
2、采样立方体贴图
(1)首先在属性代码块中声明以下变量
变量_ReflectColor 用于控制反射的颜色,_ReflectAmount 用于控制反射的强度,_CubeMap 采样的立方体贴图。为了增加效果我们还采样了_NormalMap法线贴图,这项的计算并不是本章讲的核心,所以不在这里过多做说明。完整代码里会给出完整的计算。
Properties
{
_ReflectColor("Reflect Color",color) = (1,1,1,1)
_Intensity("Intensity", float) = 1
_CubeMap("CubeMap",cube) = "whit"{}
_NormalMap("_NormalMap",2D) = "bump"{}
_Normal("Normal",range(0,1)) = 1
}
(2)顶点着色器
需要在顶点着色器中把顶点从模型空间转换到世界空间,即下边代码中worldPos的值,这一步是为后边的视方向的计算做准备。
同时在顶点着色器中计算出法线、切线和副法线。
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv*_NormalMap_ST.xy +_NormalMap_ST.zw;
//空间的顶点坐标
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
//法线方向
o.worldNormal = mul(float4(v.normal,0.0),unity_WorldToObject).xyz;
}
(3)片元着色器
在片元着色器中,利用反射方向来对立方体纹理采样(反射方向是一个三维的向量)。计算反射方向是由reflect函数实现的。里面需要输如两个参数;它们分别是法线方向和视方向。法线方向直接从片元着色器中传进来,视方向等于世界空间的摄像机位置 减去世界空间的顶点位置。
反射向量求出来后它就是我们采样Cubemap的uv。使用texCUBE函数对Cubemap进行采样的计算。
half4 frag(v2f i) : SV_Target
{
//法线方向
half3 normalDir= normalize(i.worldNormal);
//视方向
half3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos);
//反射向量
half3 reflectDir = reflect(-viewDir, normalDir);
// 采样环境光贴图
half4 cubeMap = texCUBE(_CubeMap, reflectDir);
}
在移动端默认不支持直接读取HDR数据,我们的Cubemap实际上就是一种高动态范围图,所以需要我们手动解码,Unity给我们提供了解码器函数(DecodeHDR),里面提供两个值,分别是采样好的立方体贴图的结果和一个uv值,只是这个uv值需要我们声明一个四维的变量。
这个四维变量的名称就是你采样的立方体贴图变量名后边加了大写的HDR。即_CubeMap_HDR
half3 cubeEnv = DecodeHDR(cubeMap, _CubeMap_HDR);
3、渲染结果
采样Cubemap的效果(图5)
加上法线的效果并调节了反射颜色(图6)
我们会发现采样的立方体贴图(Cubemap)高光是曝掉的,有些采样的立方体贴图会显得更明显 如(图7)所示。这会使最终的画面效果丢失一些细节。一般情况我们会配合使用后处理Tonemapping技术来处理丢失掉的高光里面细节问题。
引起这样的主要原因是采样的立方体贴图(Cubemap)属于高动态范围图(HDR),而我们的电脑屏幕对图像的亮度是限制在0~1的范围。但高动态范围图的亮度是超出了0~1范围的。所以在着色器计算中需要把HDR相关的图像最终控制在0~1的范围内。所以要配合后处理Tonemapping技术。
采样立方体贴图曝光严重(图7)
4、完整代码
Code
Shader "Example/sampling CubeMap"
{
Properties
{
_ReflectColor("Reflect Color",color) = (1,1,1,1)
_Intensity("Intensity", float) = 1
_CubeMap("CubeMap",cube) = "whit"{}
_NormalMap("_NormalMap",2D) = "bump"{}
_NormalIntensity("NormalIntensity",range(0,2)) = 1
}
Subshader
{
Tags{"RenderType" = "Opaque"}
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct a2v
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
float4 tangent : TANGENT;
};
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float3 worldNormal : TEXCOORD2;
float3 tangentDir : TEXCOORD3;
float3 binormal : TEXCOORD4;
};
samplerCUBE _CubeMap;
sampler2D _NormalMap;
float4 _NormalMap_ST;
float _NormalIntensity;
half4 _ReflectColor;
float _Intensity;
float4 _CubeMap_HDR;
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv*_NormalMap_ST.xy +_NormalMap_ST.zw;
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
o.worldNormal = mul(float4(v.normal,0.0),unity_WorldToObject).xyz;
o.tangentDir = mul(unity_ObjectToWorld, float4(v.tangent.xyz,0.0)).xyz;
o.binormal = cross(o.worldNormal,o.tangentDir) * v.tangent.w;
return o;
}
half4 frag(v2f i) : SV_Target
{
// dir
half3 tangentDir = normalize(i.tangentDir);
half3 binormal = normalize(i.binormal);
half3 worldNormal = normalize(i.worldNormal);
// 采样法线贴图
half3 normalMap = UnpackNormal(tex2D(_NormalMap, i.uv));
normalMap.xy *= _NormalIntensity;
float3x3 tbn = float3x3(tangentDir,binormal, worldNormal);
half3 normalDir = mul(normalMap,tbn);
// 视方向
half3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos);
// 反射向量
half3 reflectDir = reflect(-viewDir, normalDir);
// 采样环境光贴图
half4 cubeMap = texCUBE(_CubeMap, reflectDir);
half3 cubeEnv = DecodeHDR(cubeMap,_CubeMap_HDR);
//最终输出
half3 finalCol = cubeEnv * _ReflectColor * _Intensity;
return half4(finalCol, 1);
}
ENDCG
}
}
}
二、环境贴图生成工具
1、下载地址
HDRshop: 【免费】HDRshop软件一款处理环境图的软件资源-优快云文库
HDRLightStudio : 【免费】HDRLightStudio(3D渲染软件)V6.1.0英文安装版资源-优快云文库
2、软件简介
HDRshop:一个非常好用的处理HDR贴图的老工具,非常简洁易用。
官方网址:HDR Shop
CMFTStudio :一款强大的跨平台立方体贴图过滤工具,可根据需要进行二次开发。可以输出很多不同的格式,比如LatLong全景图格式、Hcross横向的十字形的格式、HStrip一字排开的格式等等,灵活的Cube Map类型支持。开源、跨平台、高效、易用适用于游戏开发/影视制作/虚拟现实等领域。
官方网址:cmftStudio - cubemap filtering tool — polycount
HDRLightStudio : 可以自己创造想要的全景图 可随意放置灯光的照射位置等 灵活性非常强。
官方网址:HDR Light Studio - Make and edit image-based lighting in your 3D software