ReconCubed Posted April 30, 2020 Posted April 30, 2020 (edited) I've been working on the ability to add banner patterns to elytras, and I've actually gotten it to work. However, when an elytra has a banner attached the transparent parts of the wings are rendered with a gray color. Photos: Reveal hidden contents My layer renderer -> https://hastebin.com/roquzivade.hs My 'banner/elytra textures' cache -> https://hastebin.com/fusewojuva.hs My texture class (currently the same as a normal LayerColourMaskTexture class) -> https://hastebin.com/tazuwapabe.hs This only happens when I have a banner attached to the elytra, so it leads me to believe there's something I've missed in my texture class. I've tried adding a check on the multiplied colour, testing to see if it that shade of gray was present and then setting the pixel RGBA to a transparent pixel, but that didn't work. I think I'm a bit in over my head here, it'd be great if someone could help push me in the right direction. Cheers! Edited April 30, 2020 by ReconCubed added extra photo Quote
TheGreyGhost Posted April 30, 2020 Posted April 30, 2020 Howdy Are you sure this blend function is what you intend? GlStateManager.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO); Normally I would expect to see something like GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); Your texture generator doesn't appear to handle alpha values explicitly? It seems to be doing some sort of magic manipulations there, it would help if you named your variables more clearly. This in particular just doesn't look right; I presume it is trying to extract the alpha value but it's not clear to me why the alpha value is in the lowest 8 bits initially but needs to be shifted to the upper 8 bits int j1 = (i1 & 255) << 24 & -16777216; -TGG Quote
ReconCubed Posted April 30, 2020 Author Posted April 30, 2020 On 4/30/2020 at 10:43 AM, TheGreyGhost said: Howdy Are you sure this blend function is what you intend? GlStateManager.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO); Normally I would expect to see something like GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); Your texture generator doesn't appear to handle alpha values explicitly? It seems to be doing some sort of magic manipulations there, it would help if you named your variables more clearly. This in particular just doesn't look right; I presume it is trying to extract the alpha value but it's not clear to me why the alpha value is in the lowest 8 bits initially but needs to be shifted to the upper 8 bits int j1 = (i1 & 255) << 24 & -16777216; -TGG Expand Yeah, the texture generator is currently a direct copy+paste of vanillas LayeredColorMaskTexture, which is used to generate banners. I started off with just using that class, but that didn't work so I copied it over to my own to make tweaks. This is my first time playing with texture generators so I don't really understand it very well yet. I've managed to seperate the transparent pixels, even manipulate their colour. But I can't seem to get the alpha channel to actually work. https://hastebin.com/ogalajogux.hs I've trief 0x00 and all its counterparts I could find on the internet for just a transparent RGBA value in bytes. No luck there, just renders as white or black. As for the blend func change, I've switched my source factor and destination factor to match what you've suggested, but not changes. The original elytra layer uses source factor one & dest factor zero, so I figured it'd be fine. Quote
TheGreyGhost Posted April 30, 2020 Posted April 30, 2020 Are banners transparent (cutout)? That may be the problem if you've copied the texture gen code from banners. The vanilla elytra isn't really transparent, it's cutout so the blending function is irrelevant, the alpha test is the important function. i.e. if alpha is less than the cutoff it's transparent, otherwise it's opaque. I can't tell from your code whether cutout (alpha test) is enabled or not. But if it renders as opaque no matter what RGBA you write into the texture, then it's probably disabled. The first thing I'd try is something like this: for (int height = 0; height < nativeimage2.getHeight(); ++height) { for (int width = 0; width < nativeimage2.getWidth(); ++width) { int pixelRGBA = nativeimage2.getPixelRGBA(width, height); if (height == width) { nativeimage1.setPixelRGBA(width, height, 0); // will be transparent } else { nativeimage1.setPixelRGBA(width, height, pixelRGBA); } } } If that has cutout pixels in it, you know that the rendering is ok. Otherwise your rendering mode is wrong (alpha cutoff is not correct). I think your code for generating the texture is not right. The int RGBA value that you get is actually composed of four components bits 0 -> 7 are red bits 8 -> 15 are green bits 16 -> 23 are blue bits 24 -> 31 are the alpha value. so you calculate int alphaValue = (pixelRGBA >> 24) & 0xff; int colourWithoutAlpha = (pixelRGBA & 0xffffff); // blend your colours without Alpha here then int newRGBA = (alphaValue << 24) | (newColourWithoutAlpha); using blendPixel probably won't give you what you want because it blends the alpha channels as well which will probably mess up your culling. Perhaps if you describe how exactly you want the two textures to be combined it might help. Do you want partial blending of the banner texture onto the elytra? Or are you just wanting the elytra to be coloured to match the banner except where the elytra has a cutout (i.e. all-or-nothing blending)? In the second case I don't think you need blendPixel at all - just check every pixel in the elytra texture and if the alphaValue is above the cutoff, write the banner pixel to the output texture, otherwise write a transparent pixel (alpha of zero) -TGG Quote
ReconCubed Posted May 4, 2020 Author Posted May 4, 2020 On 4/30/2020 at 3:40 PM, TheGreyGhost said: Are banners transparent (cutout)? That may be the problem if you've copied the texture gen code from banners. The vanilla elytra isn't really transparent, it's cutout so the blending function is irrelevant, the alpha test is the important function. i.e. if alpha is less than the cutoff it's transparent, otherwise it's opaque. I can't tell from your code whether cutout (alpha test) is enabled or not. But if it renders as opaque no matter what RGBA you write into the texture, then it's probably disabled. The first thing I'd try is something like this: for (int height = 0; height < nativeimage2.getHeight(); ++height) { for (int width = 0; width < nativeimage2.getWidth(); ++width) { int pixelRGBA = nativeimage2.getPixelRGBA(width, height); if (height == width) { nativeimage1.setPixelRGBA(width, height, 0); // will be transparent } else { nativeimage1.setPixelRGBA(width, height, pixelRGBA); } } } If that has cutout pixels in it, you know that the rendering is ok. Otherwise your rendering mode is wrong (alpha cutoff is not correct). I think your code for generating the texture is not right. The int RGBA value that you get is actually composed of four components bits 0 -> 7 are red bits 8 -> 15 are green bits 16 -> 23 are blue bits 24 -> 31 are the alpha value. so you calculate int alphaValue = (pixelRGBA >> 24) & 0xff; int colourWithoutAlpha = (pixelRGBA & 0xffffff); // blend your colours without Alpha here then int newRGBA = (alphaValue << 24) | (newColourWithoutAlpha); using blendPixel probably won't give you what you want because it blends the alpha channels as well which will probably mess up your culling. Perhaps if you describe how exactly you want the two textures to be combined it might help. Do you want partial blending of the banner texture onto the elytra? Or are you just wanting the elytra to be coloured to match the banner except where the elytra has a cutout (i.e. all-or-nothing blending)? In the second case I don't think you need blendPixel at all - just check every pixel in the elytra texture and if the alphaValue is above the cutoff, write the banner pixel to the output texture, otherwise write a transparent pixel (alpha of zero) -TGG Expand Sorry it took a while to reply! Was away over the weekend. I tried adding alpha test in my custom elytra layer, and set the transparent pixels to 0. That ended up just rendering black for those pixels. It seems to be targetting the correct pixels from the original texture images, however just turning them black and making the entire thing a black square with the textures on it (like the photos in my OP). I also went back to have a 2nd look at the vanilla elytra layer, I believe they do use blendFunc, I cant see anything about the alpha test func in there. Also, to answer your question: I have made some textures using the vanilla Elytra & shield patterns as a base (much like how the banner & shield do it). I'm simply trying to render the proper elytra transparency, like the below picture, but with the custom textures I use above for all the banner patterns & colours. Cheers! Quote
TheGreyGhost Posted May 5, 2020 Posted May 5, 2020 Hmm ok If you put your code into a github repository I'll download it in the next few days and have a look, if you like. Cheers TGG Quote
ReconCubed Posted May 5, 2020 Author Posted May 5, 2020 On 5/5/2020 at 10:19 AM, TheGreyGhost said: Hmm ok If you put your code into a github repository I'll download it in the next few days and have a look, if you like. Cheers TGG Expand Thanks! Here's my repo: https://github.com/ReconCubed/thecommunityupdate The related files are: https://github.com/ReconCubed/thecommunityupdate/tree/master/src/main/java/me/reconcubed/communityupdate/client/render https://github.com/ReconCubed/thecommunityupdate/blob/master/src/main/java/me/reconcubed/communityupdate/util/BannerTextures.java I appreciate the help! Quote
TheGreyGhost Posted May 9, 2020 Posted May 9, 2020 Well after a bit of refactoring I figured out the problem It's more obvious when I replaced the magic numbers with the proper OpenGL constants TextureUtil.prepareImage(this.getGlTextureId(), overlaidElytra.getWidth(), overlaidElytra.getHeight()); GlStateManager.pixelTransfer(GL11.GL_ALPHA_BIAS, Float.MAX_VALUE); // Sets alpha of final image to max.... delete this line and it works fine. overlaidElytra.uploadTextureSub(0, 0, 0, false); GlStateManager.pixelTransfer(GL11.GL_ALPHA_BIAS, 0.0F); -TGG PS the refactored code FYI package me.reconcubed.communityupdate.client.render; import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.platform.TextureUtil; import java.io.IOException; import java.util.List; import net.minecraft.client.renderer.texture.NativeImage; import net.minecraft.client.renderer.texture.Texture; import net.minecraft.item.DyeColor; import net.minecraft.resources.IResource; import net.minecraft.resources.IResourceManager; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.MathHelper; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; import org.lwjgl.opengl.GL14; @OnlyIn(Dist.CLIENT) public class LayeredColorMaskTextureCustom extends Texture { private static final Logger LOGGER = LogManager.getLogger(); private final ResourceLocation textureLocation; private final List<String> listTextures; private final List<DyeColor> listDyeColors; public LayeredColorMaskTextureCustom(ResourceLocation textureLocationIn, List<String> p_i46101_2_, List<DyeColor> p_i46101_3_) { this.textureLocation = textureLocationIn; this.listTextures = p_i46101_2_; this.listDyeColors = p_i46101_3_; } public void loadTexture(IResourceManager manager) throws IOException { try ( IResource iresource = manager.getResource(this.textureLocation); NativeImage baseElytra = NativeImage.read(iresource.getInputStream()); NativeImage overlaidElytra = new NativeImage(baseElytra.getWidth(), baseElytra.getHeight(), false); ) { overlaidElytra.copyImageData(baseElytra); for (int i = 0; i < 17 && i < this.listTextures.size() && i < this.listDyeColors.size(); ++i) { String bannerTextureRL = this.listTextures.get(i); if (bannerTextureRL != null) { try ( NativeImage bannerLayer = net.minecraftforge.client.MinecraftForgeClient.getImageLayer(new ResourceLocation(bannerTextureRL), manager); ) { int bannerLayerColour = this.listDyeColors.get(i).getSwappedColorValue(); if (bannerLayer.getWidth() == overlaidElytra.getWidth() && bannerLayer.getHeight() == overlaidElytra.getHeight()) { for (int height = 0; height < bannerLayer.getHeight(); ++height) { for (int width = 0; width < bannerLayer.getWidth(); ++width) { int alphaBanner = bannerLayer.getPixelRGBA(width, height) & 0xff; // extract the red channel, could have used green or blue also. int alphaElytra = baseElytra.getPixelLuminanceOrAlpha(width, height) & 0xff; // algorithm is: // if elytra pixel is transparent, do nothing // otherwise: // the banner blend layer is a greyscale which is converted to a transparency: // blend the banner's colour into elytra pixel using the banner blend transparency if (alphaElytra != 0 && alphaBanner != 0) { int elytraPixelRGBA = baseElytra.getPixelRGBA(width, height); int multipliedColorRGB = MathHelper.multiplyColor(elytraPixelRGBA, bannerLayerColour) & 0xFFFFFF; int multipliedColorRGBA = multipliedColorRGB | (alphaBanner << 24); overlaidElytra.blendPixel(width, height, multipliedColorRGBA); } } } } } } } TextureUtil.prepareImage(this.getGlTextureId(), overlaidElytra.getWidth(), overlaidElytra.getHeight()); GlStateManager.pixelTransfer(GL11.GL_ALPHA_BIAS, 0.0F); overlaidElytra.uploadTextureSub(0, 0, 0, false); } catch (IOException ioexception) { LOGGER.error("Couldn't load layered color mask image", (Throwable) ioexception); } } } Quote
ReconCubed Posted May 9, 2020 Author Posted May 9, 2020 On 5/9/2020 at 7:09 AM, TheGreyGhost said: Well after a bit of refactoring I figured out the problem It's more obvious when I replaced the magic numbers with the proper OpenGL constants TextureUtil.prepareImage(this.getGlTextureId(), overlaidElytra.getWidth(), overlaidElytra.getHeight()); GlStateManager.pixelTransfer(GL11.GL_ALPHA_BIAS, Float.MAX_VALUE); // Sets alpha of final image to max.... delete this line and it works fine. overlaidElytra.uploadTextureSub(0, 0, 0, false); GlStateManager.pixelTransfer(GL11.GL_ALPHA_BIAS, 0.0F); -TGG PS the refactored code FYI package me.reconcubed.communityupdate.client.render; import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.platform.TextureUtil; import java.io.IOException; import java.util.List; import net.minecraft.client.renderer.texture.NativeImage; import net.minecraft.client.renderer.texture.Texture; import net.minecraft.item.DyeColor; import net.minecraft.resources.IResource; import net.minecraft.resources.IResourceManager; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.MathHelper; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; import org.lwjgl.opengl.GL14; @OnlyIn(Dist.CLIENT) public class LayeredColorMaskTextureCustom extends Texture { private static final Logger LOGGER = LogManager.getLogger(); private final ResourceLocation textureLocation; private final List<String> listTextures; private final List<DyeColor> listDyeColors; public LayeredColorMaskTextureCustom(ResourceLocation textureLocationIn, List<String> p_i46101_2_, List<DyeColor> p_i46101_3_) { this.textureLocation = textureLocationIn; this.listTextures = p_i46101_2_; this.listDyeColors = p_i46101_3_; } public void loadTexture(IResourceManager manager) throws IOException { try ( IResource iresource = manager.getResource(this.textureLocation); NativeImage baseElytra = NativeImage.read(iresource.getInputStream()); NativeImage overlaidElytra = new NativeImage(baseElytra.getWidth(), baseElytra.getHeight(), false); ) { overlaidElytra.copyImageData(baseElytra); for (int i = 0; i < 17 && i < this.listTextures.size() && i < this.listDyeColors.size(); ++i) { String bannerTextureRL = this.listTextures.get(i); if (bannerTextureRL != null) { try ( NativeImage bannerLayer = net.minecraftforge.client.MinecraftForgeClient.getImageLayer(new ResourceLocation(bannerTextureRL), manager); ) { int bannerLayerColour = this.listDyeColors.get(i).getSwappedColorValue(); if (bannerLayer.getWidth() == overlaidElytra.getWidth() && bannerLayer.getHeight() == overlaidElytra.getHeight()) { for (int height = 0; height < bannerLayer.getHeight(); ++height) { for (int width = 0; width < bannerLayer.getWidth(); ++width) { int alphaBanner = bannerLayer.getPixelRGBA(width, height) & 0xff; // extract the red channel, could have used green or blue also. int alphaElytra = baseElytra.getPixelLuminanceOrAlpha(width, height) & 0xff; // algorithm is: // if elytra pixel is transparent, do nothing // otherwise: // the banner blend layer is a greyscale which is converted to a transparency: // blend the banner's colour into elytra pixel using the banner blend transparency if (alphaElytra != 0 && alphaBanner != 0) { int elytraPixelRGBA = baseElytra.getPixelRGBA(width, height); int multipliedColorRGB = MathHelper.multiplyColor(elytraPixelRGBA, bannerLayerColour) & 0xFFFFFF; int multipliedColorRGBA = multipliedColorRGB | (alphaBanner << 24); overlaidElytra.blendPixel(width, height, multipliedColorRGBA); } } } } } } } TextureUtil.prepareImage(this.getGlTextureId(), overlaidElytra.getWidth(), overlaidElytra.getHeight()); GlStateManager.pixelTransfer(GL11.GL_ALPHA_BIAS, 0.0F); overlaidElytra.uploadTextureSub(0, 0, 0, false); } catch (IOException ioexception) { LOGGER.error("Couldn't load layered color mask image", (Throwable) ioexception); } } } Expand You're an absolute legend mate. Thank you! It worked perfectly. Have a good one Quote
TheGreyGhost Posted May 9, 2020 Posted May 9, 2020 no worries you're welcome, was an interesting debugging exercise for me Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.