全景图即HDRI贴图,能够代替6面cubemap,传统3D软件运用较为普遍。通常反射探针,天空盒等都会用到。git
可是体积过大是个问题,特别是移动端会对包体大小进行控制,虽然说能够经过球面贴图替换掉部分环境类贴图,但适用范围依然有限。github
而相较金字塔贴图又能够弥补图像质量低的问题,所需的分辨率却差很少。缺点是会产生接缝,穿帮问题,及镜像判断对GPU负担的增长。api
这里经过镜像的方式来作贴图大小的优化,可将贴图优化到原先的一半大小。网络
原图以下(网络收集):app
最终效果(左右上下镜像):函数
github上有一些Equirectangular map的转换函数,相似球面坐标,直接拿来主义了。测试
参考:优化
https://github.com/tolotratlt/UnityPhotosphericViewui
https://github.com/Mapiarz/CubemapToEquirectangularspa
通过测试是能够x,y轴镜像的,首先需裁剪原始HDRI图片。直接用Texture2D的Resize裁一下便可。ConvShader就是两个镜像函数的转换shader,不贴出来了
Material mat = new Material(Shader.Find("Hidden/ConvShader")); var rt = RenderTexture.GetTemporary(new RenderTextureDescriptor(tex.width, tex.height, RenderTextureFormat.ARGB32)); Graphics.Blit(tex, rt, mat); var instanceTex = Instantiate(tex); instanceTex.Resize(instanceTex.width, instanceTex.height / 2); instanceTex.ReadPixels(new Rect(0, 0, instanceTex.width, instanceTex.height), 0, 0); instanceTex.Apply(); ...
主要是转换全景图的两个函数,参考了github上的内容,顺带把常量改为了内置的UNITY_PI。
float3 UvToDir(float2 uv) { uv *= float2(UNITY_TWO_PI, UNITY_PI); float theta = uv.y; float phi = uv.x; float3 dir = float3(0, 0, 0); dir.x = sin(phi) * sin(theta) * -1; dir.y = cos(theta) * -1; dir.z = cos(phi) * sin(theta) * -1; return dir; } float2 DirToUV(float3 a_coords) { float3 a_coords_n = normalize(a_coords); float lon = atan2(a_coords_n.z, a_coords_n.x); float lat = acos(a_coords_n.y); float2 sphereCoords = float2(lon, lat) * (1.0 / UNITY_PI); return float2(1 - (sphereCoords.x * 0.5 - 0.5), 1 - sphereCoords.y); //must flip x }
转换以后就是在显示部分作修改,经过传入一个方向矢量来返回全景图的UV,并在其内部作镜像图片的修复
需注意输出x份量并不是0-1区间,而是0-2,估计因为全景图宽高2:1致使的,这里简单修复了下。
而y轴接缝较为明显,经过手动调节偏差。压缩,关闭mipmap等。接缝问题会获得缓解。
float2 DirToUV(float3 a_coords) { float3 a_coords_n = normalize(a_coords); float lon = atan2(a_coords_n.z, a_coords_n.x); float lat = acos(a_coords_n.y); float2 sphereCoords = float2(lon, lat) * (1.0 / UNITY_PI); float2 uv = float2(1 - (sphereCoords.x * 0.5 - 0.5), 1 - sphereCoords.y); //---------------------------- uv.x -= 1; if (uv.x > 0.5) uv.x = 0.5 - (uv.x - 0.5); uv.x *= 2; //----------------------------Mirror X. //---------------------------- uv.y *= 1.999; if (uv.y < 1) uv.y *= -0.97; else uv.y *= 1.03; //----------------------------Mirror Y. return uv; }
基本如上,另外不少状况下须要Cubemap转HDRI全景图,可直接参考维基百科上的Cubemaping映射函数: