Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SpriteUtil to API package #2747

Closed
wants to merge 12 commits into from
Prev Previous commit
Next Next commit
Adjust texcoord epsilon depending on atlas size
This fixes some texture precision issues with very large
atlases, and with hardware that has limited sub-texel
precision.
jellysquid3 committed Jan 18, 2025
commit 763bb3df1e323f6e9e7b2b68bfabe90ba7a11876
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package net.caffeinemc.mods.sodium.client.gl.shader.uniform;

import org.lwjgl.opengl.GL30C;

public class GlUniformFloat2v extends GlUniform<float[]> {
public GlUniformFloat2v(int index) {
super(index);
}

@Override
public void set(float[] value) {
if (value.length != 2) {
throw new IllegalArgumentException("value.length != 2");
}

GL30C.glUniform2fv(this.index, value);
}

public void set(float x, float y) {
GL30C.glUniform2f(this.index, x, y);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package net.caffeinemc.mods.sodium.client.render.chunk.shader;

import com.mojang.blaze3d.platform.GlStateManager;
import net.caffeinemc.mods.sodium.client.gl.shader.uniform.GlUniformFloat2v;
import net.caffeinemc.mods.sodium.client.gl.shader.uniform.GlUniformFloat3v;
import net.caffeinemc.mods.sodium.client.gl.shader.uniform.GlUniformInt;
import net.caffeinemc.mods.sodium.client.gl.shader.uniform.GlUniformMatrix4f;
import net.caffeinemc.mods.sodium.client.util.TextureUtil;
import net.caffeinemc.mods.sodium.mixin.core.render.texture.TextureAtlasAccessor;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.TextureAtlas;
import org.joml.Matrix4fc;
import org.lwjgl.opengl.GL32C;

@@ -15,11 +19,30 @@
* A forward-rendering shader program for chunks.
*/
public class DefaultShaderInterface implements ChunkShaderInterface {
// Direct3D specifies at least 8 bits of sub-texel precision for texture fetches. OpenGL specifies at least
// 4 bits of sub-texel precision. Most OpenGL-capable graphics are Direct3D-capable as well, so we could
// *probably* assume 8 bits of precision.
//
// However, in practice, this seems to be a complete mess. The rounding behavior for point-filtering seems to
// be defined inconsistently and depends on the shader compiler and hardware implementation. Apple's GL-on-Metal
// implementation is the worst of all of them, with a very large epsilon (1.0 / 32.0) being needed to cure
// texture seams between blocks.
//
// Unless we implemented texture filtering in the shader ourselves (i.e. using texelFetch(..)), it is unlikely
// we could avoid these issues. And that would not help much in the case of linear interpolation across
// mip layers.
//
// So in other words, this constant is the lowest common denominator we found through evaluation on the target
// hardware. It is rather pessimistic to accommodate for Apple's implementation, but does seem to reliably fix
// texture seams.
private static final int SUB_TEXEL_PRECISION_BITS = 5;

private final Map<ChunkShaderTextureSlot, GlUniformInt> uniformTextures;

private final GlUniformMatrix4f uniformModelViewMatrix;
private final GlUniformMatrix4f uniformProjectionMatrix;
private final GlUniformFloat3v uniformRegionOffset;
private final GlUniformFloat2v uniformTexCoordShrink;

// The fog shader component used by this program in order to setup the appropriate GL state
private final ChunkShaderFogComponent fogShader;
@@ -28,6 +51,7 @@ public DefaultShaderInterface(ShaderBindingContext context, ChunkShaderOptions o
this.uniformModelViewMatrix = context.bindUniform("u_ModelViewMatrix", GlUniformMatrix4f::new);
this.uniformProjectionMatrix = context.bindUniform("u_ProjectionMatrix", GlUniformMatrix4f::new);
this.uniformRegionOffset = context.bindUniform("u_RegionOffset", GlUniformFloat3v::new);
this.uniformTexCoordShrink = context.bindUniform("u_TexCoordShrink", GlUniformFloat2v::new);

this.uniformTextures = new EnumMap<>(ChunkShaderTextureSlot.class);
this.uniformTextures.put(ChunkShaderTextureSlot.BLOCK, context.bindUniform("u_BlockTex", GlUniformInt::new));
@@ -38,9 +62,21 @@ public DefaultShaderInterface(ShaderBindingContext context, ChunkShaderOptions o

@Override // the shader interface should not modify pipeline state
public void setupState() {
// TODO: Bind to these textures directly rather than using fragile RenderSystem state
this.bindTexture(ChunkShaderTextureSlot.BLOCK, TextureUtil.getBlockTextureId());
this.bindTexture(ChunkShaderTextureSlot.LIGHT, TextureUtil.getLightTextureId());

var textureAtlas = (TextureAtlasAccessor) Minecraft.getInstance()
.getTextureManager()
.getTexture(TextureAtlas.LOCATION_BLOCKS);

// There is a limited amount of sub-texel precision when using hardware texture sampling. The mapped texture
// area must be "shrunk" by at least one sub-texel to avoid bleed between textures in the atlas.
this.uniformTexCoordShrink.set(
(1.0f / textureAtlas.getWidth()) / (1 << SUB_TEXEL_PRECISION_BITS),
(1.0f / textureAtlas.getHeight()) / (1 << SUB_TEXEL_PRECISION_BITS)
);

this.fogShader.setup();
}

Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package net.caffeinemc.mods.sodium.mixin.core.render.texture;

import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.resources.ResourceLocation;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;

import java.util.Map;

@Mixin(TextureAtlas.class)
public interface TextureAtlasAccessor {
@Accessor
Map<ResourceLocation, TextureAtlasSprite> getTexturesByName();
int getWidth();

@Accessor
int getHeight();
}
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ out float v_FragDistance;

uniform int u_FogShape;
uniform vec3 u_RegionOffset;
uniform vec2 u_TexCoordShrink;

uniform sampler2D u_LightTex; // The light map texture sampler

@@ -47,7 +48,7 @@ void main() {

// Add the light color to the vertex color, and pass the texture coordinates to the fragment shader
v_Color = _vert_color * texture(u_LightTex, _vert_tex_light_coord);
v_TexCoord = _vert_tex_diffuse_coord;
v_TexCoord = _vert_tex_diffuse_coord - (_vert_tex_diffuse_coord_bias * u_TexCoordShrink);

v_MaterialMipBias = _material_mip_bias(_material_params);
#ifdef USE_FRAGMENT_DISCARD
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ vec3 _vert_position;

// The block texture coordinate of the vertex
vec2 _vert_tex_diffuse_coord;
vec2 _vert_tex_diffuse_coord_bias;

// The light texture coordinate of the vertex
vec2 _vert_tex_light_coord;
@@ -28,10 +29,6 @@ const uint TEXTURE_MAX_VALUE = TEXTURE_MAX_COORD - 1u;
const float VERTEX_SCALE = 32.0 / float(POSITION_MAX_COORD);
const float VERTEX_OFFSET = -8.0;

// The amount of inset the texture coordinates from the edges of the texture, to avoid texture bleeding
const float TEXTURE_FUZZ_AMOUNT = 1.0 / 64.0;
const float TEXTURE_GROW_FACTOR = (1.0 - TEXTURE_FUZZ_AMOUNT) / TEXTURE_MAX_COORD;

in uvec2 a_Position;
in vec4 a_Color;
in uvec2 a_TexCoord;
@@ -49,13 +46,14 @@ vec2 _get_texcoord() {
}

vec2 _get_texcoord_bias() {
return mix(vec2(-TEXTURE_GROW_FACTOR), vec2(TEXTURE_GROW_FACTOR), bvec2(a_TexCoord >> TEXTURE_BITS));
return mix(vec2(-1.0), vec2(1.0), bvec2(a_TexCoord >> TEXTURE_BITS));
}

void _vert_init() {
_vert_position = (_deinterleave_u20x3(a_Position) * VERTEX_SCALE) + VERTEX_OFFSET;
_vert_color = a_Color;
_vert_tex_diffuse_coord = _get_texcoord() + _get_texcoord_bias();
_vert_tex_diffuse_coord = _get_texcoord();
_vert_tex_diffuse_coord_bias = _get_texcoord_bias();

_vert_tex_light_coord = vec2(a_LightAndData.xy) / vec2(256.0);