Unity Shader Bitwise Float

效果

问题

制作Terrain时候需要解决Texture Blend问题,根本上来说就是需要在fragment里面知道当前是哪两张纹理需要混,用哪个贴图进行混. 所以就需要用到3个int值.

首先想到的是存到一个int里面用位运算直接取出来. 不过很不幸的是 首先shader里面就没有int(opengl es 4.1.3可用float模拟int),其次es中也没有位运算操作符.

所以需要解决的问题就是 如何把这3个int值存起来 并且在fragment里面再给取出来.

存哪里

研究了一下其他的Terrain System大部分应该都是生成一张数据纹理,然后将数据存入其中一个或多个通道内.用的时候Simpling出来. 对于支持笔刷的Terrain System这种方式无疑是最佳的选择.

但是对于我目前要制作的基于Grid Base Terrain System来说单独开一张纹理来存数据 有些浪费. 并且我担心视口远处Simpling时候可能会采错值(未实验).

所以我选择的是另外一种方式,直接将接将这部分信息存入mesh中.

怎么存

在介绍如何存之前,首先需要恶补一下基础知识. 也就是float是如何存数据的

Float的二进制表示法

参考资料:

float类型在内存中的表示
IEEE 754-1985
在线计算器

6.9

15:

中文教程,通俗易懂. 也就不用过多的解释了.

10进位 和 N进位 互相 转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
namespace Code
{
public class PackIntTestLogic : MonoBehaviour
{
public int N = 100;
public int z = 5;
public int y = 6;
public int x = 7;

[ReadOnly, LabelText("分子")]
public int sumValue;

[ReadOnly, LabelText("分母")]
public int sumScale;

[ReadOnly, LabelText("打包后:")]
public float packedNum;

[ReadOnly, LabelText("解压Z:")]
public float unpackedZ;

[ReadOnly, LabelText("解压Y:")]
public float unpackedY;

[ReadOnly, LabelText("解压X:")]
public float unpackedX;

[Button("Run", ButtonSizes.Large), GUIColor(0.066667f, 0.843137f, 0.435294f)]
private void RunLogic()
{
sumValue = z * N * N + y * N + x;
sumScale = N * N * N;
packedNum = (float) sumValue / sumScale;
unpackedZ = Mathf.Floor(packedNum * N);
unpackedY = Mathf.Floor(packedNum * N * N) - unpackedZ * N;
unpackedX = Mathf.Floor(packedNum * N * N * N) - unpackedZ * N * N - unpackedY * N;
}
}
}

根据公式 就可以直接将3个int值打入一个float中去. 因为我的纹理不会超过16个.所以就直接选择16进制

比如我要打包的值为5,6,7

sumValue = 5 x 16 x 16 + 6 x 16 + 7

这种格式最后再除掉sumScale将其变为小数.这这样在GPU中就只需要进行乘法操作而不用进行除法操作

怎么取

1
2
3
4
5
6
7
8
half4 frag (v2f i) : SV_Target
{
float packedNum = i.value.x;
float unpackedZ = round(packedNum * N);
float unpackedY = round(packedNum * N2) - unpackedZ * N;
float unpackedX = round(packedNum * N3) - unpackedZ * N2 - unpackedY * N;
...
}

需要注意的就是插值问题.

之前调试了半天 总是出现图片上这种裂纹问题.后来想起来肯定是float后面几位精度问题

比如0.199999999990.200000000001 其实差别很小的.但是一但 x10 floor以后 一个是1一个是2 也就会出现上图这种情况了.

所以直接使用Round就可以解决这个问题.

坚持原创技术分享,您的支持将鼓励我继续创作!