Отображение картинки с экрана на блоке

Перед рендерингом мира чекаем, какие зеркала попадают в кадр. Для каждого зеркала в кадре нужно отрендерить всю сцену, отраженную относительно него.
При отрисовке мира для зеркала нужно чутка поменять установку камеры. Это может быть довольно сложно в зависимости от того, как поставлено зеркало, но я заморачиваться не стал. Зеркало всегда смотрит в одну сторону - отрицательное направление по оси Х. Поэтому, в моем случае достаточно просто было в конец EntityRenderer#setupCameraTransform() дописать вот это при отрисовке мира для зеркала:
Код:
GL11.glTranslated(MirrorRenderer.renderX, 0, 0);
GL11.glScalef(-1, 1, 1);
GL11.glTranslated(-MirrorRenderer.renderX, 0, 0);
Отрицательный скейл - это по сути и есть отражение. 
При этом нам не нужно рендерить ничего, что находится за зеркалом. Для этого в openGL есть фича под названием clip plane. Этот код можно дописать там же, в setupCameraTransform.
Код:
DoubleBuffer b = BufferUtils.createDoubleBuffer(4);
//это уравнение плоскости, с одной стороны от которой надо отсекать все вершины
b.put(new double[]{-1, 0, 0, MirrorRenderer.renderX});
b.flip();
glEnable(GL_CLIP_PLANE0);
glClipPlane(GL_CLIP_PLANE0, b);
Естественно, после отрисовки мира надо будет сделать glDisable(GL_CLIP_PLANE0).

Итак, перед отрисовкой мира нужно отрисовать отражённый мир. Нужно учесть, что у отрицательного скейла есть одна неприятность. Все полигоны начинают рисоваться наизнанку. Это фиксится так:
Код:
//этот код должен быть где-то непосредственно перед обычным вызовом renderWorld()
GL11.glCullFace(GL11.GL_FRONT); //меняем сторону, которая считается "изнанкой"
renderWorld(par1, 0); //рендерим мир для зеркала
GL11.glCullFace(GL11.GL_BACK); //возвращаем параметр назад

После отрисовки отраженного мира нужно сохранить результат в текстуру:
Код:
glBindTexture(GL_TEXTURE_2D, MirrorRenderer.blank.getGlTextureId());
glViewport(0, 0, mc.displayWidth, mc.displayHeight);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, mc.displayWidth, mc.displayHeight, 0);

Теперь у нас есть текстура, которая содержит отрисованный мир, отраженный относительно этого зеркало. Нам нужно просто отрисовать эту текстуру поверх экрана, "обрезав" по "трафарету" зеркала. Для этого используется stencil buffer. Дальше код отрисовки тайла зеркала, потому что раздробить его довольно сложно:

На этом моменте я понял, что никакого желания писать туториал по созданию зеркала у меня нет, это займет несколько часов. Просто держите код отрисщика тайла без пояснений, основные изменения в майне я расписал. У меня написан говнокод, который ломается при количестве зеркал, отличном от одного, и шлифовать код до приемлемого состояния очень долго.

Код:
package gloomyfolken.mods.mirrortest;

import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.BufferUtils;

import java.nio.DoubleBuffer;

import static org.lwjgl.opengl.GL11.*;

public class MirrorRenderer extends TileEntitySpecialRenderer {


    public static DynamicTexture blank = new DynamicTexture(1, 1);
    ResourceLocation texture = new ResourceLocation("mirror", "glass.png");

    public static boolean mirroring;
    public static boolean renderedMirror;
    public static double renderX;
    public static double renderY;
    public static double renderZ;

    static Minecraft mc = Minecraft.getMinecraft();

    static {
        blank.getTextureData()[0] = 0xFFFFFFFF;
        blank.updateDynamicTexture();
    }

    @Override
    public void renderTileEntityAt(TileEntity tileentity, double x, double y, double z, float f) {
        if (mirroring) return;
        renderedMirror = true;
        renderX = x; renderY = y; renderZ = z;

        //выключаем затенение по лайтмапе
        OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240.0F, 0.0F);
        glDisable(GL_LIGHTING);
        glPushMatrix();
        glTranslated(x, y, z);
        glDisable(GL_ALPHA_TEST);

        //включам стенсил буфер, вырубаем запись в остальные
        glEnable(GL_STENCIL_TEST);
        glColorMask(false, false, false, false);
        glDepthMask(false);
        glStencilFunc(GL_ALWAYS, 0x8, 0x8);
        glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
        glStencilMask(0x8);
        glClear(GL_STENCIL_BUFFER_BIT);
        drawMirror();
        glStencilFunc(GL_EQUAL, 0x8, 0x8);
        glStencilMask(0x00);
        glColorMask(true, true, true, false);

        saveMatrices();
        setupOverlayRendering();
        glBindTexture(GL_TEXTURE_2D, blank.getGlTextureId());
        glDisable(GL_DEPTH_TEST);
        int width = mc.displayWidth;
        int height = mc.displayHeight;

        Tessellator t = Tessellator.instance;
        glDisable(GL_FOG);
        t.startDrawingQuads();
        t.addVertexWithUV(0, 0, -1, 0, 1);
        t.addVertexWithUV(0, height, -1, 0, 0);
        t.addVertexWithUV(width, height, -1, 1, 0);
        t.addVertexWithUV(width, 0, -1, 1, 1);
        t.draw();
        glEnable(GL_FOG);
        glEnable(GL_DEPTH_TEST);
        glDisable(GL_STENCIL_TEST);
        restoreMatrices();

        glDepthMask(true);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        Minecraft.getMinecraft().renderEngine.bindTexture(texture);
        drawMirror();
        glDisable(GL_BLEND);
        glEnable(GL_ALPHA_TEST);
        glPopMatrix();
    }

    DoubleBuffer mv = BufferUtils.createDoubleBuffer(16);
    DoubleBuffer p = BufferUtils.createDoubleBuffer(16);

    private void saveMatrices(){
        glGetDouble(GL_PROJECTION_MATRIX, p);
        glGetDouble(GL_MODELVIEW_MATRIX, mv);
    }

    private void restoreMatrices(){
        glMatrixMode(GL_PROJECTION);
        glLoadMatrix(p);
        glMatrixMode(GL_MODELVIEW);
        glLoadMatrix(mv);
    }

    private void setupOverlayRendering(){
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(0.0D, mc.displayWidth, mc.displayHeight, 0.0D, 1000.0D, 3000.0D);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        glTranslatef(0.0F, 0.0F, -2000.0F);
    }

    private void drawMirror(){
        Tessellator t = Tessellator.instance;
        t.startDrawingQuads();
        t.addVertexWithUV(0, 0, -1, 0, 1);
        t.addVertexWithUV(0, 0, 1, 1, 1);
        t.addVertexWithUV(0, 2, 1, 1, 0);
        t.addVertexWithUV(0, 2, -1, 0, 0);
        t.draw();
    }
}
 
Да проблема в том, что ни разу не полно, просто залил кучу своего тестового говнокода)
 
Вопросик такой.
Сколько ты с OpenGL'ом и рендерингом (вдруг ещё DX) в целом работаешь?
Если не секрет, хехе.
 
Примерно столько же, сколько с майном - года два. Почти сразу же заинтересовался рендерингом. Активно изучать openGL стал где-то год назад. С DX не работал, разве что идеи из HLSL-шейдеров воровал :D
 
Как же тогда IChun сделал?
snBgZeVhSGk.jpg
 
У айчуна полигоны наизнанку, лол.
А так порталы ничем особо не отличаются от зеркал.
Единственное отличие: камера перемещается в место, где второй портал (сзади портала, наверн), и отражение ПО ФАКТУ всего один раз рисуется, сохраняется в текстуру, а потом мы видим уже повторения отражений, не новые.
 
Назад
Сверху