片段着色器fmod,为什么不重复

问题描述

我创建了以下片段着色器,该片段着色器使用_Size函数创建了大小为frac的图块网格,并在每个图块之间绘制了一条小的分隔线,我将图块的ID保存在其{ {1}}值,以便以后可以根据其ID(uv.z)来平铺图块。

uv.z_Size可以通过检查器进行调整

_CurrentID

(请注意,网格从(0,0)左下角开始到(5,5)右上角开始)

要确定我的ID设置正确,我通过检查器设置了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); if ((uv.x > 0.98 && uv.x < 1) || (uv.y > .98 && uv.y < 1)) { col *= float4(uv.x,uv.y,1); } else { col = float4(0,1); } //Loop through all the tiles based on the ID if (uv.z == fmod(_CurrentID,((_Size) * (_Size)))) { col = float4(0,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); }*/ return col; } ENDCG } } } 的底值,遍历每个uv.z值,当从0变为24时,该值会点亮每个图块一次(包括)。

_CurrentID

_CurrentID 7 lights up the 8th tile as expected


if (uv.z == _CurrentID) { col = float4(0,1); } 照亮第8个图块的示例

现在仅使用_CurrentID = 7意味着我只能浏览每个图块一次。为了使此重复性无论_CurrentID有多大,我都应该能够在_CurrentID上使用fmod(以模为单位)(尽管使用%模运算符也会发生这种情况),这样它就可以循环_CurrentID时为0。我(尝试)使用以下代码

CurrnetID = 25

第一行(if (uv.z == fmod(_CurrentID,((_Size) * (_Size)))) { col = float4(0,1); } > = 0 && _CurrentId,事情就会开始破裂,因为没有瓷砖会亮起,尽管以前能够确认_CurrentID = 5会点亮网格(0,1)的瓷砖。当我设置_CurrentID = 5时,适当的图块再次开始亮起(网格pos(1,1)),直到n(大于0)的网格(0,n)永远不会亮起。

fmod = 5 won't light up the proper square


_CurrentID = 6使用fmod的示例。

一旦我的_CurrentID = 5升至25以上,一切似乎开始破裂,那里似乎根本没有模循环。 As seen in this gyazo gif。似乎只是照亮了随机图块。

我开始怀疑自己,我仔细检查了WolframpAlpha上的模数学,这似乎是正确的。

我可以通过执行CurrentID来“解决”它跳过每一行的第一个图块的问题,这将在第一次运行时正确遍历每个图块(包括(0,n)图块),但是现在我的模数开始在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);
}

或将整数用作ID。