- Home /
I wanted to ask again to get another opinion, and I wanted to word it better.
How to make pixel art textures for a 3D game look better?
I'm having an issue where I can't get my pixelated textures to look good. I'm using 32x32 textures and tiling them for a basic test scene that I made. I set their filter mode as "Point" to preserve the hard edges, but this causes sparkly visuals from a distance/at an angle. I've heard about using mip maps to solve this issue, but the mip map edges are overly noticeable, compared to if I were using bilinear filtering rather than point.
Here is what it looks like with point-filtering, WITHOUT mip maps: https://imgur.com/a/F9Cz8UA
This is with point-filtering, WITH mip maps: https://imgur.com/a/vyeRvlT
And this is bilinear filterning, WITH mip maps: https://imgur.com/a/7M0lZi9
The last one looks the best, but the bilinear filtering blurs my textures up close which is not what I want. What can I do to make this look better?
Thanks in advance!
Add an AA image effect to the camera and see if the quality improves.
Answer by Remy_Unity · May 03, 2018 at 12:03 PM
What would help you is to be able to sample by point at the pixel level, but sample bi-linear between mips. For comparison, tri-linear filtering samples bi-linear per pixel and bi-linear per mips.
This is not exposed, but you could fake it with a custom shader and point filtering on the texture + Mips. In the shader you then manualy sample two consecutive mip levels and blend them.
Thanks for the reply! I think you're right, that would probably achieve what I'm trying to do. Unfortunately, I'm not experienced with writing shaders like at all. Do you know if there is some kind of tutorial floating around to achieve something similar to this? Or is it just a matter of being more experienced with writing shaders?
Also you may want to crank up the anisotropic filtering, especially for the floor.
I don't have the option for that when using the "Point" filter mode. The aniso level slider is just greyed out, it only appears with bilinear or trilinear filtering.
Answer by doctordingus · May 04, 2018 at 12:33 AM
For anybody who comes across this, I have a solution that I'm happy with that doesn't involve messing with shaders. I made a script that sets the Texture.mipMapBias of the textures to -1. A negative mip map bias results in the mips being "sharper". This, at least for me, has resulted in the edges of the mips less noticeable.
I took some time and did a shader that implements what I said :
Shader "Unlit/Pixel$$anonymous$$ipBlend"
{
Properties
{
_$$anonymous$$ainTex ("Texture", 2D) = "white" {}
_BlendFactor ("Blend Factor", Range(0,1)) = 1
_$$anonymous$$ipPower ("$$anonymous$$ip Level Power", float) = 1
_$$anonymous$$ipOffset("$$anonymous$$ip Level Offset", float) = 1
_$$anonymous$$ip$$anonymous$$in ("$$anonymous$$ip $$anonymous$$in", float) = 0
_$$anonymous$$ip$$anonymous$$ax ("$$anonymous$$ip $$anonymous$$ax", float) = 5
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRA$$anonymous$$
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _$$anonymous$$ainTex;
float4 _$$anonymous$$ainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFOR$$anonymous$$_TEX(v.uv, _$$anonymous$$ainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
float _$$anonymous$$ip$$anonymous$$in;
float _$$anonymous$$ip$$anonymous$$ax;
float _BlendFactor;
float _$$anonymous$$ipOffset;
float _$$anonymous$$ipPower;
fixed4 frag (v2f i) : SV_Target
{
// Calculate uv derivatives
float2 uv_dx = ddx( i.uv );
float2 uv_dy = ddy( i.uv );
// Calculate mip level to be used
float mipLevel = max (dot(uv_dx, uv_dx), dot(uv_dy, uv_dy));
// $$anonymous$$ip Level tweak
mipLevel = _$$anonymous$$ipOffset + mipLevel * _$$anonymous$$ipPower;
// Clamp
mipLevel = clamp(mipLevel, _$$anonymous$$ip$$anonymous$$in, _$$anonymous$$ip$$anonymous$$ax);
// Blending factor for smooth mips transition
float mipBlend = frac(mipLevel);
// Truncate the mip level value
mipLevel = mipLevel-mipBlend;
// Sample two times the texture, for the current mip and the one just before
float4 tex$$anonymous$$ipA = tex2Dlod( _$$anonymous$$ainTex, float4(i.uv, 0, mipLevel) );
float4 tex$$anonymous$$ipB = tex2Dlod( _$$anonymous$$ainTex, float4(i.uv, 0, mipLevel - 1) );
// Interpolate between the two mips for nice transition
fixed4 col = lerp(tex$$anonymous$$ipB, tex$$anonymous$$ipA, mipBlend * _BlendFactor);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
Whit exaggerated settings, it allowed to go from this (no mips blending):
To this:
Oh wow, I didn't expect any more replies for this. Thank you so much, it's very much appreciated. I'll make sure to study your code to hopefully learn a bit more about the process.
Follow this Question
Related Questions
Making mipmaps with pixel art look better? 2 Answers
How can I prevent my Texture2D filter mode from reverting to Bilinear from Point? 0 Answers
Blurred sprite borders - Quick question 0 Answers
My pixel sprite is really blury 0 Answers
Anisotropic texturing not working and making textures blurry 1 Answer