Is somehow possible to tell specific material/renderer or whatever to do NOT use texture mip level 0 and force it to start from 1st mip?
How about this? Unity - Scripting API: Texture.mipMapBias
I affraid that mip map bias is solution per texture - but Iam looking for per renderer or per material solution
You’d have to write a custom shader that uses tex2Dbias
to sample the texture, or possibly even tex2Dlod
and manually calculate the appropriate mip level in the shader.
Though I prefer using tex2Dgrad
and clamping the derivatives instead as it doesn’t break anisotropic filtering and is a little easier to implement.
Here’s an example shader that implements min & max mip + bias using tex2Dlod
. I realized my simple tex2Dgrad
implemenation I had was only handling a limited use case and didn’t work when generalized to a min and max clamp.
Shader "Unlit/MipClamping"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_MinLOD ("Mip Min", Range(0, 12)) = 0
_MaxLOD ("Mip Max", Range(0, 12)) = 12
_BiasLOD ("Mip Bias", 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
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _MainTex_TexelSize;
float _MinLOD, _MaxLOD, _BiasLOD;
float CalcMipLevel(float2 texture_coord)
{
float2 dx = ddx(texture_coord);
float2 dy = ddy(texture_coord);
float delta_max_sqr = max(dot(dx, dx), dot(dy, dy));
return max(0.0, 0.5 * log2(delta_max_sqr));
}
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
{
float mipLevel = CalcMipLevel(i.uv * _MainTex_TexelSize.zw);
fixed4 col = tex2Dlod(_MainTex, float4(i.uv, 0.0, clamp(mipLevel + _BiasLOD, _MinLOD, _MaxLOD)));
return col;
}
ENDCG
}
}
}
Ohhh I see… Shader solution is great idea! Thanks…
Lastly I want to hear your opinion about what I want to achive, because I am not sure if is it good idea. I have FPS game where I am using 2k textures for fps gun models. I want to also use same texture for gun TPV models (to reduce some build size) of my guns where 2k texture is useless - so I want to skip first mip from texture to reduce texture overhead on gpu. Is it good idea, or would be better to just duplicate texture → set size to 1k and use it for TPV guns?
Don’t do anything. If you’re already reusing the texture, just do that and nothing else. Don’t even bother with using a shader like above.
There’s no need to prevent the top mip from being used as it’ll already never be used, assuming the camera doesn’t get close enough to see it, and there won’t be any performance penalty for having the top mip sitting in memory for when it is needed for the first person view.
Is there really no way to get the mipbias back into the tex2Dgrad function? (And how limited is the hack you were talking about?)
I’m sampling a 2darray from an index texture and want to apply bilinear filtering to that. I get the same artifacts, because of a frac, but dont think i can get around the frac ( https://discussions.unity.com/t/769628 ) You also mentioned the 2Dgrad method there, which solves the problem with the artifacts, but removes the mipbias-
I was just saying my specific hack that I was using in some existing shader was limited. You can absolutely implement the equivalent of tex2Dbias
/ tex2Dlod
using tex2Dgrad
.
Do you have an idea on how you’d get the normal auto mipbias back? (Now that i think about it tho, wouldn’t that bring back the artifacts aswell, as the mip than can again change rapidly in the 0.9999 → 0.0001 areas?)
Edit: Maybe to clarify, i want the normal automatic mipbias (so that textures change mips automatically), but remove these artifacts!
Funny enough, I was planning on writing an article about this exact topic soon.
// get derivatives
float4 uv_df = float4(ddx(uv), ddy(uv));
// mip bias
uv_df *= pow(2.0, _MipBias);
// sample
float4 col = tex2Dgrad(_Tex, uv, uv_df.xy, uv_df.zw);
In the case of something like atlas UVs with discontinuities, like when using frac()
on previously continuous UVs, the trick is very simply to use the derivatives from the original UVs before applying frac()
, and possibly scaled by the same amount as the sample position UV.
float4 uv_df = float4(ddx(uv), ddy(uv));
float2 atlasUV = frac(uv) * _TileScaleOffset.xy + _TileScaleOffset.zw;
float4 col = tex2D(_Tex, atlasUV, uv_df.xy * _TileScaleOffset.xy, uv_df.zw * _TileScaleOffset.xy);
If you’re getting discontinuities from procedural circular UVs like from using atan()
, then you need to calculate two sets of UVs with the discontinuity in different positions and use the smallest magnitude derivatives. For atan()
it returns a value between -pi and +pi, so if you divide it by tau you have a range from -0.5 to +0.5, and you can then use frac()
to get a 0.0 to 1.0 range and you can compare them like this:
float2 uv = atan(y, x) / (UNITY_PI * 2.0);
float2 uvB = frac(uv);
uv = fwidth(uv) < fwidth(uvB) ? uv : uvB;
float4 uv_df = float4(ddx(uv), ddy(uv));
float4 col = tex2Dgrad(_Tex, uv, uv_df.xy, uv_df.zw);
@bgolus Just got around to try it out and the small changes solved the artifacts problem! Thank you!! (Now my terrain shader finally doesn’t have any artifacts anymore~ Also i can change the bias now, so thats neat!)
[quote=“bgolus, post:11, topic: 803572, username:bgolus”]
Funny enough, I was planning on writing an article about this exact topic soon.
[/quote] Also for some reasons this feels like a dejavu, I think we had a similar situation once, where I had questions about a topic you were planning on writing aboutxD Thx for helping again!