片段着色器 fmod,为什么不重复
Fragment shader fmod, why is this not repeating
我创建了以下片段着色器,它使用 frac
函数创建了一个大小为 _Size
的图块网格,并在每个图块之间绘制了一条小分隔线,我将图块的 ID 保存在它的 uv.z
值,这样我以后可以根据它的 id (uv.z
) 来访问磁贴。
_Size
和_CurrentID
可以通过inspector进行调整
Shader "Unlit/Fractals"
{
Properties
{
[HideInInspector] _MainTex ("Texture", 2D) = "white" {}
_Size ("Size", float) = 5
_CurrentID ("ID", float) = 0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _Size;
float _CurrentID;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
_CurrentID = floor(_CurrentID);
//Create a tile grid that is of _Size * _Size (5 in example), and create an ID for it in the .z value based on its grid position
float3 uv = float3(frac(i.uv * _Size), (floor(i.uv.y * _Size) * _Size) + (floor(i.uv.x * _Size)));
//Create lines to seperate the tiles
float4 col = float4(1, 1, 1, 1);
if ((uv.x > 0.98 && uv.x < 1) || (uv.y > .98 && uv.y < 1))
{
col *= float4(uv.x, uv.y, 0, 1);
}
else
{
col = float4(0, 0, 0, 1);
}
//Loop through all the tiles based on the ID
if (uv.z == fmod(_CurrentID, ((_Size) * (_Size))))
{
col = float4(0, 1, 1, 1);
}
//This correctly goes through every grid tile once, confirming that uv grid ID 5 corresponds to grid position (0,1)
/*if (uv.z == _CurrentID)
{
col = float4(0, 1, 1, 1);
}*/
return col;
}
ENDCG
}
}
}
(注意网格从左下角(0,0)开始到右上角(5,5))
为了确定我的 ID 设置正确,我循环遍历了每个 uv.z
值和检查员设置的 _CurrentID
的底线,当从 0 开始时,它会点亮每个瓷砖一次达到预期的 24(含)。
if (uv.z == _CurrentID)
{
col = float4(0, 1, 1, 1);
}
_CurrentID = 7
按预期点亮第 8 个图块的示例
现在只使用 _CurrentID
就意味着我只能遍历每个图块一次。无论 _CurrentID
有多大,为了使其可重复,我应该能够在 _CurrentID
上使用 fmod
(模数)(尽管使用 % 模数运算符也会发生同样的情况)所以它循环回来CurrnetID = 25
时为 0。我(尝试)使用以下代码来做:
if (uv.z == fmod(_CurrentID, ((_Size) * (_Size))))
{
col = float4(0, 1, 1, 1);
}
这对第一行很顺利(当 _CurrentId
>= 0 && < 5 时)。然而,一旦我点击 _CurrentID = 5
,事情就开始崩溃了,因为没有瓷砖会亮起,尽管之前能够确认 _CurrentID = 5
会点亮网格 (0, 1) 处的瓷砖。当我设置 _CurrentID = 6
时,正确的图块再次开始点亮(网格位置(1,1)),在网格(0,n)永远不会点亮的地方继续点亮,其中 n 大于 0。
_CurrentID = 5
使用 fmod 的示例。
一旦我的 CurrentID
高于 25,事情就开始变得更糟,它似乎根本没有模循环。 As seen in this gyazo gif。它似乎只是随机点亮瓷砖。
开始怀疑自己,我仔细检查了 WolframpAlpha 上的模数学,这似乎是正确的。
我可以通过执行 fmod(_CurrentID, ((_Size + 1) * (_Size + 1)))
来“解决”它跳过每一行的第一个图块的问题,这将正确循环遍历第一个 运行 上的每个图块(包括 (0, n) tiles), 但现在我的模从 36 开始循环,之后它仍会点亮随机图块,如 gif 中所示。
我做错了什么?
(Unity 版本 2020.1.1f1,在 2019.3.13 中确认了相同的行为)
这可能是一个浮点精度问题,因为您正在比较浮点数是否相等。
你可以这样写:
float id = _CurrentID % (_Size*_Size);
float epsilon = .0001f;
if (abs(uv.z - id) < epsilon)
{
col = float4(0, 1, 1, 1);
}
或者使用整数作为 ID。
我创建了以下片段着色器,它使用 frac
函数创建了一个大小为 _Size
的图块网格,并在每个图块之间绘制了一条小分隔线,我将图块的 ID 保存在它的 uv.z
值,这样我以后可以根据它的 id (uv.z
) 来访问磁贴。
_Size
和_CurrentID
可以通过inspector进行调整
Shader "Unlit/Fractals"
{
Properties
{
[HideInInspector] _MainTex ("Texture", 2D) = "white" {}
_Size ("Size", float) = 5
_CurrentID ("ID", float) = 0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _Size;
float _CurrentID;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
_CurrentID = floor(_CurrentID);
//Create a tile grid that is of _Size * _Size (5 in example), and create an ID for it in the .z value based on its grid position
float3 uv = float3(frac(i.uv * _Size), (floor(i.uv.y * _Size) * _Size) + (floor(i.uv.x * _Size)));
//Create lines to seperate the tiles
float4 col = float4(1, 1, 1, 1);
if ((uv.x > 0.98 && uv.x < 1) || (uv.y > .98 && uv.y < 1))
{
col *= float4(uv.x, uv.y, 0, 1);
}
else
{
col = float4(0, 0, 0, 1);
}
//Loop through all the tiles based on the ID
if (uv.z == fmod(_CurrentID, ((_Size) * (_Size))))
{
col = float4(0, 1, 1, 1);
}
//This correctly goes through every grid tile once, confirming that uv grid ID 5 corresponds to grid position (0,1)
/*if (uv.z == _CurrentID)
{
col = float4(0, 1, 1, 1);
}*/
return col;
}
ENDCG
}
}
}
(注意网格从左下角(0,0)开始到右上角(5,5))
为了确定我的 ID 设置正确,我循环遍历了每个 uv.z
值和检查员设置的 _CurrentID
的底线,当从 0 开始时,它会点亮每个瓷砖一次达到预期的 24(含)。
if (uv.z == _CurrentID)
{
col = float4(0, 1, 1, 1);
}
_CurrentID = 7
按预期点亮第 8 个图块的示例
现在只使用 _CurrentID
就意味着我只能遍历每个图块一次。无论 _CurrentID
有多大,为了使其可重复,我应该能够在 _CurrentID
上使用 fmod
(模数)(尽管使用 % 模数运算符也会发生同样的情况)所以它循环回来CurrnetID = 25
时为 0。我(尝试)使用以下代码来做:
if (uv.z == fmod(_CurrentID, ((_Size) * (_Size))))
{
col = float4(0, 1, 1, 1);
}
这对第一行很顺利(当 _CurrentId
>= 0 && < 5 时)。然而,一旦我点击 _CurrentID = 5
,事情就开始崩溃了,因为没有瓷砖会亮起,尽管之前能够确认 _CurrentID = 5
会点亮网格 (0, 1) 处的瓷砖。当我设置 _CurrentID = 6
时,正确的图块再次开始点亮(网格位置(1,1)),在网格(0,n)永远不会点亮的地方继续点亮,其中 n 大于 0。
_CurrentID = 5
使用 fmod 的示例。
一旦我的 CurrentID
高于 25,事情就开始变得更糟,它似乎根本没有模循环。 As seen in this gyazo gif。它似乎只是随机点亮瓷砖。
开始怀疑自己,我仔细检查了 WolframpAlpha 上的模数学,这似乎是正确的。
我可以通过执行 fmod(_CurrentID, ((_Size + 1) * (_Size + 1)))
来“解决”它跳过每一行的第一个图块的问题,这将正确循环遍历第一个 运行 上的每个图块(包括 (0, n) tiles), 但现在我的模从 36 开始循环,之后它仍会点亮随机图块,如 gif 中所示。
我做错了什么?
(Unity 版本 2020.1.1f1,在 2019.3.13 中确认了相同的行为)
这可能是一个浮点精度问题,因为您正在比较浮点数是否相等。 你可以这样写:
float id = _CurrentID % (_Size*_Size);
float epsilon = .0001f;
if (abs(uv.z - id) < epsilon)
{
col = float4(0, 1, 1, 1);
}
或者使用整数作为 ID。