- 51
- 8
- 5
Привет! Я Столкнулся с проблемой при создании маски сущностей (EntityMaskRenderer.java) для тепловизора (ThermalVisionRenderer.java). Возникают визуальные артефакты, связанные с рендером: Рука игрока иногда отображается белой и полупрозрачной. В некоторых случаях модель руки ломается (деформируется или отсутствует часть геометрии), а также освещение не применяется корректно — рука выглядит "плоской" и не освещается, как должна. Облака при включённой маске и виде от третьего лица становятся слишком белыми, даже ночью, когда должны быть тёмными. Когда я взлетаю выше облаков все блоки снизу начинают выглядеть очень странно... Пожалуйста, помогите решить проблемы с рендерингом, уже неделю мучаюсь








Java:
package com.afp.handlers;
import com.afp.shaders.EntityMaskRenderer;
import com.afp.shaders.NightVisionRenderer;
import com.afp.shaders.ThermalVisionRenderer;
import net.minecraft.client.Minecraft;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class RenderWorldLastEventHandler {
// Кэшируем инстансы рендереров для уменьшения нагрузки на GC
private final ThermalVisionRenderer thermalVisionRenderer = new ThermalVisionRenderer();
private final NightVisionRenderer nightVisionRenderer = new NightVisionRenderer();
private final Minecraft mc = Minecraft.getMinecraft();
// Кэшируем строки для сравнения
private static final String THERMAL_VISION = "thermal_vision";
private static final String NIGHT_VISION = "night_vision";
@SubscribeEvent
public void onRenderWorldLast(RenderWorldLastEvent event) {
// Ранний выход при отсутствии необходимых условий
if (MC.player == null || MC.world == null) return;
// Получаем шлем из слота 3 (голова)
final ItemStack helmet = MC.player.inventory.armorInventory.get(3);
if (helmet.isEmpty() || !helmet.hasTagCompound()) return;
// Извлекаем тег видения из NBT
final NBTTagCompound tag = helmet.getTagCompound();
String visionTag = tag.getString("vision");
// Выбираем тип рендеринга на основе тега
if (THERMAL_VISION.equals(visionTag)) {
EntityMaskRenderer.renderEntityMasks(event.getPartialTicks());
thermalVisionRenderer.render();
} else if (NIGHT_VISION.equals(visionTag)) {
nightVisionRenderer.render();
}
}
}
Java:
package com.afp.shaders;
import com.afp.entities.EntityExoskeleton;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.entity.Render;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.client.shader.Framebuffer;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.item.EntityArmorStand;
import net.minecraft.entity.item.EntityBoat;
import net.minecraft.entity.item.EntityItem;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL30;
import java.util.List;
@SideOnly(Side.CLIENT)
public class EntityMaskRenderer {
private static final Minecraft MC = Minecraft.getMinecraft();
private static Framebuffer maskFramebuffer;
// Кэшируем часто используемые классы
private static final Class<?> EXOSKELETON_CLASS = EntityExoskeleton.Exoskeleton.class;
private static final Class<?> ARMOR_STAND_CLASS = EntityArmorStand.class;
public static boolean shouldRenderMask(Entity entity) {
Class<?> entityClass = entity.getClass();
return entity instanceof EntityLivingBase &&
entityClass != EXOSKELETON_CLASS &&
entityClass != ARMOR_STAND_CLASS;
}
private static void ensureMaskBufferInitialized() {
if (maskFramebuffer == null) {
maskFramebuffer = new Framebuffer(MC.displayWidth, MC.displayHeight, true);
maskFramebuffer.setFramebufferColor(0, 0, 0, 0);
} else if (maskFramebuffer.framebufferWidth != MC.displayWidth ||
maskFramebuffer.framebufferHeight != MC.displayHeight) {
maskFramebuffer.createBindFramebuffer(MC.displayWidth, MC.displayHeight);
}
}
public static void renderEntityMasks(float partialTicks) {
if (MC.player == null || MC.world == null) return;
ensureMaskBufferInitialized();
maskFramebuffer.bindFramebuffer(false);
// Очистка буфера цвета
GlStateManager.clearColor(0, 0, 0, 0);
GlStateManager.clear(GL11.GL_COLOR_BUFFER_BIT);
// Копирование буфера глубины
OpenGlHelper.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, MC.getFramebuffer().framebufferObject);
OpenGlHelper.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, maskFramebuffer.framebufferObject);
GL30.glBlitFramebuffer(0, 0, MC.displayWidth, MC.displayHeight, 0, 0, MC.displayWidth, MC.displayHeight, GL11.GL_DEPTH_BUFFER_BIT, GL11.GL_NEAREST);
// Настройка состояний рендеринга
OpenGlHelper.glBindFramebuffer(OpenGlHelper.GL_FRAMEBUFFER, maskFramebuffer.framebufferObject);
GlStateManager.enableDepth();
GlStateManager.depthMask(false);
GlStateManager.disableBlend();
GlStateManager.disableLighting();
GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f);
// Рендеринг сущностей
Entity viewEntity = MC.getRenderViewEntity();
RenderManager renderManager = MC.getRenderManager();
List<Entity> entities = MC.world.loadedEntityList;
for (Entity entity : entities) {
if (entity != viewEntity && shouldRenderMask(entity)) {
renderEntitySilhouette(entity, partialTicks, renderManager);
}
}
// Восстановление состояний
GlStateManager.depthMask(true);
GlStateManager.disableDepth();
GlStateManager.enableLighting();
MC.getFramebuffer().bindFramebuffer(false);
}
private static void renderEntitySilhouette(Entity entity, float partialTicks, RenderManager renderManager) {
// Интерполяция позиции
double x = entity.lastTickPosX + (entity.posX - entity.lastTickPosX) * partialTicks;
double y = entity.lastTickPosY + (entity.posY - entity.lastTickPosY) * partialTicks;
double z = entity.lastTickPosZ + (entity.posZ - entity.lastTickPosZ) * partialTicks;
// Перевод в пространство камеры
double renderX = x - renderManager.viewerPosX;
double renderY = y - renderManager.viewerPosY;
double renderZ = z - renderManager.viewerPosZ;
GlStateManager.pushMatrix();
GlStateManager.translate(renderX, renderY, renderZ);
renderEntityModel(entity, renderManager, partialTicks);
GlStateManager.popMatrix();
}
private static void renderEntityModel(Entity entity, RenderManager renderManager, float partialTicks) {
GlStateManager.pushAttrib();
Render<Entity> renderer = renderManager.getEntityRenderObject(entity);
if (renderer != null) {
// Интерполяция вращения
float interpolatedYaw = interpolateRotation(entity.prevRotationYaw, entity.rotationYaw, partialTicks);
renderer.doRender(entity, 0, 0, 0, interpolatedYaw, partialTicks);
}
GlStateManager.popAttrib();
}
private static float interpolateRotation(float prevRotation, float currentRotation, float partialTicks) {
float delta = currentRotation - prevRotation;
// Нормализация угла
delta = (delta + 180) % 360 - 180;
return prevRotation + partialTicks * delta;
}
public static int getMaskTexture() {
return maskFramebuffer != null ? maskFramebuffer.framebufferTexture : -1;
}
}
Java:
#version 120
uniform sampler2D DiffuseSampler;
uniform sampler2D NoiseSampler;
uniform sampler2D EntityMask;
uniform float VignetteRadius;
uniform float Brightness;
uniform float Time;
uniform float NoiseAmplification;
uniform float Contrast;
uniform vec2 InSize;
varying vec2 texCoord;
void main() {
// Чтение исходного цвета
vec4 texColor = texture2D(DiffuseSampler, texCoord);
vec4 mask = texture2D(EntityMask, texCoord.xy);
if (mask.a > 0.01) {
// Белый силуэт для сущностей
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
return;
}
// Генерация анимированного шума
vec2 uv;
uv.x = 0.35 * sin(Time * 10.0);
uv.y = 0.35 * cos(Time * 10.0);
vec3 noise = texture2D(NoiseSampler, texCoord.xy + uv).rgb * NoiseAmplification;
// Применение шума к цвету
texColor.rg += noise.rg * 0.05;
// Применяем яркость
texColor.rgb *= Brightness;
// Применение виньетирования
float dist = distance(texCoord, vec2(0.5));
float vignette = smoothstep(
VignetteRadius,
VignetteRadius - 0.25,
dist
);
texColor.rgb *= vignette;
// Переход в grayscale
float intensity = dot(texColor.rgb, vec3(0.299, 0.587, 0.114));
// Коррекция контраста
intensity = Contrast * (intensity - 0.5) + 0.5;
intensity = clamp(intensity, 0.0, 1.0);
//Формирование серого цвета
vec3 finalColor = vec3(intensity);
gl_FragColor = vec4(finalColor, 1.0);
}
Java:
package com.afp.shaders;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.shader.Framebuffer;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.ARBShaderObjects;
import org.lwjgl.opengl.GL11;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import static org.lwjgl.opengl.GL11.*;
@SideOnly(Side.CLIENT)
public abstract class BaseShaderRenderer {
protected static final Minecraft MC = Minecraft.getMinecraft();
// Статическая шумовая текстура (общая для всех шейдеров)
private static int noiseTextureId = -1;
private static boolean noiseTextureLoaded = false;
protected Framebuffer screenBuffer;
protected ShaderManager shaderManager;
protected boolean initializationFailed = false;
protected final Map<String, Integer> uniformCache = new HashMap<>(8);
protected abstract ResourceLocation getVertexShaderLocation();
protected abstract ResourceLocation getFragmentShaderLocation();
protected abstract void setupUniforms(int width, int height);
public void render() {
// Ранний выход при ошибке инициализации
if (initializationFailed) return;
if (shaderManager == null) {
initializeResources();
if (initializationFailed) return;
}
final int width = MC.displayWidth;
final int height = MC.displayHeight;
// Ресайз фреймбуфера при изменении размеров экрана
if (screenBuffer == null || screenBuffer.framebufferWidth != width || screenBuffer.framebufferHeight != height) {
resizeFrameBuffer(width, height);
}
// Копируем текущий буфер в текстуру
GlStateManager.bindTexture(screenBuffer.framebufferTexture);
GL11.glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, width, height);
// Настройка состояния рендера
GlStateManager.pushMatrix();
GlStateManager.pushAttrib();
setupRenderState(width, height);
// Активация шейдера
shaderManager.bind();
setupUniforms(width, height);
// Рендер полноэкранного квада
renderFullscreenQuad(width, height);
// Восстановление состояния
shaderManager.unbind();
restoreRenderState();
GlStateManager.popAttrib();
GlStateManager.popMatrix();
}
protected void initializeResources() {
if (!OpenGlHelper.shadersSupported) {
initializationFailed = true;
return;
}
try {
shaderManager = new ShaderManager(getVertexShaderLocation(), getFragmentShaderLocation());
loadNoiseTexture();
} catch (Exception e) {
initializationFailed = true;
System.err.println("Shader initialization failed");
e.printStackTrace();
}
}
protected synchronized void loadNoiseTexture() {
if (noiseTextureLoaded) return;
try {
ResourceLocation noiseRes = new ResourceLocation("afp", "textures/misc/noise.png");
try (InputStream in = MC.getResourceManager().getResource(noiseRes).getInputStream()) {
BufferedImage image = ImageIO.read(in);
int width = image.getWidth();
int height = image.getHeight();
int[] pixels = new int[width * height];
image.getRGB(0, 0, width, height, pixels, 0, width);
ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * 4);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int pixel = pixels[y * width + x];
buffer.put((byte) ((pixel >> 16) & 0xFF));
buffer.put((byte) ((pixel >> 8) & 0xFF));
buffer.put((byte) (pixel & 0xFF));
buffer.put((byte) ((pixel >> 24) & 0xFF));
}
}
buffer.flip();
noiseTextureId = GlStateManager.generateTexture();
GlStateManager.bindTexture(noiseTextureId);
GlStateManager.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
GlStateManager.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
GlStateManager.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
GlStateManager.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
GL11.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
}
noiseTextureLoaded = true;
} catch (Exception e) {
initializationFailed = true;
System.err.println("Failed to load noise texture");
e.printStackTrace(); }
}
protected void resizeFrameBuffer(int width, int height) {
if (screenBuffer != null) {
screenBuffer.deleteFramebuffer();
}
screenBuffer = new Framebuffer(width, height, false);
screenBuffer.setFramebufferFilter(GL_NEAREST);
screenBuffer.createFramebuffer(width, height);
}
protected void setupRenderState(int width, int height) {
GlStateManager.disableDepth();
GlStateManager.depthMask(false);
GlStateManager.matrixMode(GL_PROJECTION);
GlStateManager.pushMatrix();
GlStateManager.loadIdentity();
GlStateManager.ortho(0, width, height, 0, -1, 1);
GlStateManager.matrixMode(GL_MODELVIEW);
GlStateManager.pushMatrix();
GlStateManager.loadIdentity();
}
protected void cacheUniformLocation(String name, int textureUnit) {
Integer location = uniformCache.get(name);
if (location == null) {
location = shaderManager.getUniformLocation(name);
uniformCache.put(name, location);
}
if (location != null && location >= 0) {
ARBShaderObjects.glUniform1iARB(location, textureUnit);
}
}
protected void setUniformFloat(String name, float value) {
Integer location = uniformCache.get(name);
if (location == null) {
location = shaderManager.getUniformLocation(name);
uniformCache.put(name, location);
}
if (location != null && location >= 0) {
ARBShaderObjects.glUniform1fARB(location, value);
}
}
protected void setUniformVec2(String name, float x, float y) {
Integer location = uniformCache.get(name);
if (location == null) {
location = shaderManager.getUniformLocation(name);
uniformCache.put(name, location);
}
if (location != null && location >= 0) {
ARBShaderObjects.glUniform2fARB(location, x, y);
}
}
protected void renderFullscreenQuad(int width, int height) {
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
buffer.begin(GL_QUADS, DefaultVertexFormats.POSITION_TEX);
buffer.pos(0, height, 0).tex(0, 0).endVertex();
buffer.pos(width, height, 0).tex(1, 0).endVertex();
buffer.pos(width, 0, 0).tex(1, 1).endVertex();
buffer.pos(0, 0, 0).tex(0, 1).endVertex();
tessellator.draw();
}
protected void restoreRenderState() {
GlStateManager.matrixMode(GL_PROJECTION);
GlStateManager.popMatrix();
GlStateManager.matrixMode(GL_MODELVIEW);
GlStateManager.popMatrix();
GlStateManager.depthMask(true);
GlStateManager.enableDepth();
GlStateManager.setActiveTexture(33985);
GlStateManager.bindTexture(0);
GlStateManager.setActiveTexture(33984);
}
public static int getNoiseTexture() {
return noiseTextureId;
}
}
Java:
package com.afp.shaders;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.util.ResourceLocation;
import org.apache.commons.io.IOUtils;
import org.lwjgl.BufferUtils;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
public class ShaderManager {
private final int programId;
private boolean isValid;
public ShaderManager(ResourceLocation vertexShaderLoc, ResourceLocation fragmentShaderLoc) {
if (!OpenGlHelper.shadersSupported) {
programId = 0;
isValid = false;
return;
}
int vertexShader = 0;
int fragmentShader = 0;
programId = OpenGlHelper.glCreateProgram();
try {
// Компиляция вершинного шейдера
vertexShader = compileShader(vertexShaderLoc, OpenGlHelper.GL_VERTEX_SHADER);
OpenGlHelper.glAttachShader(programId, vertexShader);
// Компиляция фрагментного шейдера
fragmentShader = compileShader(fragmentShaderLoc, OpenGlHelper.GL_FRAGMENT_SHADER);
OpenGlHelper.glAttachShader(programId, fragmentShader);
// Линковка программы
OpenGlHelper.glLinkProgram(programId);
validateProgram();
isValid = true;
} catch (Exception e) {
System.err.println("Shader compilation failed");
e.printStackTrace();
isValid = false;
} finally {
// Очистка ресурсов
if (vertexShader != 0) OpenGlHelper.glDeleteShader(vertexShader);
if (fragmentShader != 0) OpenGlHelper.glDeleteShader(fragmentShader);
}
}
private int compileShader(ResourceLocation location, int shaderType) throws Exception {
String source = loadResource(location);
int shader = OpenGlHelper.glCreateShader(shaderType);
byte[] sourceBytes = source.getBytes(StandardCharsets.UTF_8);
ByteBuffer buffer = BufferUtils.createByteBuffer(sourceBytes.length + 1);
buffer.put(sourceBytes);
buffer.put((byte) 0);
buffer.flip();
OpenGlHelper.glShaderSource(shader, buffer);
OpenGlHelper.glCompileShader(shader);
validateShader(shader, shaderType);
return shader;
}
private String loadResource(ResourceLocation location) throws Exception {
try (InputStream in = Minecraft.getMinecraft().getResourceManager().getResource(location).getInputStream()) {
return IOUtils.toString(in, StandardCharsets.UTF_8);
}
}
private void validateShader(int shader, int shaderType) {
if (OpenGlHelper.glGetShaderi(shader, OpenGlHelper.GL_COMPILE_STATUS) == 0) {
String type = shaderType == OpenGlHelper.GL_VERTEX_SHADER ? "Vertex" : "Fragment";
String log = OpenGlHelper.glGetShaderInfoLog(shader, 1024);
throw new RuntimeException(type + " shader error: " + log);
}
}
private void validateProgram() {
if (OpenGlHelper.glGetProgrami(programId, OpenGlHelper.GL_LINK_STATUS) == 0) {
String log = OpenGlHelper.glGetProgramInfoLog(programId, 1024);
throw new RuntimeException("Shader link error: " + log);
}
}
public void bind() {
if (isValid) OpenGlHelper.glUseProgram(programId);
}
public void unbind() {
OpenGlHelper.glUseProgram(0);
}
public int getUniformLocation(String name) {
return isValid ? OpenGlHelper.glGetUniformLocation(programId, name) : -1;
}
public boolean isValid() {
return isValid;
}
}
Последнее редактирование: