# Kleine GL Texturen auf grosse GL Textur kopieren?



## Friedhelm (31. Aug 2010)

Ich habe da einen Wunsch  . Nämlich möchte ich gerne z.B. 4 kleine Texturen in eine groessere Textur kopieren :bahnhof:. Und das inherhalb der Grafikkarte, also irgendwie mit GL Befehlen.

Wie mache ich das? Hat jemand ein Beispiel? :rtfm:


----------



## Empire Phoenix (31. Aug 2010)

Beispiel nicht aber logic

Bilder a,b,c,d seien als Bytearray dargestellt, und auf das selbe Format(unkomprimiert) gesetzt.
Dann erstellt man bilde indem man ein 4 mal größeres Bytearraynimmt was im selben Format interpretiert wird und man die arrays stückelt: 
A,B,C,D seien arraystücke der bilddaten die x*breite der kleinen bilder an bytes enthalten. x ist Formatspezifisch
Große bild siht dan so aus:

ABCD
ABCD
ABCD
... (das ganze entsprechend oft für die größe des Bildes.

Benutzt werden dafür keine GL befehle sondern einfache arraybefehle. anschliesind wird das ergebnis als texture hochgeladen in den vram.


----------



## Marco13 (31. Aug 2010)

Es war ja explizit nach GL Befehlen gefragt. Vermutlich geht es darum, Texturen, die schon auf der Grafikkarte liegen, zu kombinieren, ohne sie vorher auf den host runterkopieren zu müssen.
Warte mal bis Fancy vorbeischaut, der schüttelt dir das aus dem Ärmel


----------



## Empire Phoenix (31. Aug 2010)

hm naja man könnte se orthogonal auf eine größere texture rendern... (stichwort render to texture)


----------



## Evil-Devil (31. Aug 2010)

Prinzipielle Frage an den TE: Wieso werden die vier kleineren Texturen nicht gleich direkt als eine große abgespeichert und zb. via glTexSubImage verwendet?


----------



## Friedhelm (31. Aug 2010)

Evil-Devil hat gesagt.:


> Prinzipielle Frage an den TE: Wieso werden die vier kleineren Texturen nicht gleich direkt als eine große abgespeichert und zb. via glTexSubImage verwendet?



Ah, das ist garnicht mal so eine schlechte Idee 

Ok, und wie muss ich dass dann machen, dass ich aus der einen großen Textur, ausgewählte Bereiche dieser, in eine neue Textur kopiere? Ein kleines Beispiel wäre nett :rtfm:


----------



## Evil-Devil (31. Aug 2010)

Warum denn überhaupt kopieren?

Eine Textur hat die Größe 0 bis 1 und alles zwischen 0 und 1 kannst du ansprechen und auf deine Vertices legen.

Kopieren in eine Datei mittels Render To Texture brauch man imho nur bei speziellen Effekten.


----------



## Friedhelm (31. Aug 2010)

Nett gemeint  aber ich brauche das reale kopieren in eine neue Textur. Ich habe gedacht das geht einfach irgendwie mit Pixel-Copy innerhalb der Grafikkarte mit GL Befehlen.


----------



## Guest2 (31. Aug 2010)

Moin,



Marco13 hat gesagt.:


> Warte mal bis Fancy vorbeischaut, der schüttelt dir das aus dem Ärmel



lol 

Ich befürchte allerdings, dass das hier ehr wieder ein Fall von "den Nagel mit der Tasse in die Wand treiben" sein wird. 




Friedhelm hat gesagt.:


> Nett gemeint  aber ich brauche das reale kopieren in eine neue Textur. Ich habe gedacht das geht einfach irgendwie mit Pixel-Copy innerhalb der Grafikkarte mit GL Befehlen.



Mir würde kein Szenario einfallen bei dem es Sinn machen würde Texturen direkt kopieren zu wollen. Es würde auch dem Wesen von OpenGL widersprechen. Das ARB dürfte das wohl ähnlich sehen, den bei allen 2066 OpenGL Befehlen ist keiner dabei der direkt von einer Textur auf eine andere Textur kopiert (zumindest kein mir bekannter). Bei den glCopy* ist Ziel oder Quelle immer ein Framebuffer, bei allen anderen ein Buffer im normalen RAM.

Allgemein geht der Trend auch zu Mega- bzw. Gigatexturen, dabei werden im Wesentlichen alle Texturen aller Objekte als ein komprimierter Block zur Grafikkarte übertragen und dann nur diese eine einzige Textur verwendet (praktisch ist es ein wenig komplizierter ). Rage soll eine Textur von 128000x128000 Texel verwenden können!

Deswegen sind die Aussagen hier im Thread schon vollkommen korrekt, wenn man Texturen kopieren will, geht das nur über einen Framebuffer, was eben render to texture bedeutet. Idealerweise über einen extra angelegten Framebuffer, damit das unabhängig von der Auflösung des Anzeigefensters bleibt. 

Das sieht dann in etwa so aus:


```
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import javax.imageio.ImageIO;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.awt.GLCanvas;
import javax.media.opengl.glu.GLU;

import com.jogamp.opengl.util.Animator;
import com.jogamp.opengl.util.GLBuffers;


public class Merge implements GLEventListener {

    private static final String  IMAGES[]       = { "test.png", "test2.png" };

    private static final float[] QUAD_XYZ       = new float[] { -0.5f, -0.5f, 0f, +0.5f, -0.5f, 0f, +0.5f, +0.5f, 0f, -0.5f, +0.5f, 0f };
    private static final float[] QUAD_ST        = new float[] { 0f, 0f, 1f, 0f, 1f, 1f, 0f, 1f };

    private final FloatBuffer    vaXYZ          = GLBuffers.newDirectFloatBuffer(QUAD_XYZ);
    private final FloatBuffer    vaST           = GLBuffers.newDirectFloatBuffer(QUAD_ST);

    private final IntBuffer      buffer         = GLBuffers.newDirectIntBuffer(1);

    private final int[]          textureHandles = new int[IMAGES.length + 1];

    private GL2                  gl;
    private GLU                  glu;


    private int createTexture(final String resource) {

        try {

            final BufferedImage image = ImageIO.read(getClass().getClassLoader().getResourceAsStream(resource));
            final byte[] data = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
            final ByteBuffer texture = GLBuffers.newDirectByteBuffer(data);

            gl.glGenTextures(1, buffer);
            final int textureHandle = buffer.get(0);

            gl.glBindTexture(GL2.GL_TEXTURE_2D, textureHandle);
            gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MAG_FILTER, GL2.GL_LINEAR);
            gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MIN_FILTER, GL2.GL_LINEAR);

            gl.glPixelStorei(GL2.GL_UNPACK_ALIGNMENT, 1);
            gl.glTexImage2D(GL2.GL_TEXTURE_2D, 0, GL2.GL_RGB, image.getWidth(), image.getHeight(), 0, GL2.GL_BGR, GL2.GL_UNSIGNED_BYTE, texture);

            return textureHandle;

        } catch (final IOException e) {

            throw new RuntimeException(e);

        }

    }


    private int merge(final int textureHandle1, final int textureHandle2, final int width, final int height) {

        gl.glGenFramebuffers(1, buffer);
        final int frameBufferHandle = buffer.get(0);

        gl.glGenRenderbuffers(1, buffer);
        final int renderBufferHandle = buffer.get(0);

        gl.glGenTextures(1, buffer);
        final int textureHandle = buffer.get(0);

        gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, frameBufferHandle);
        gl.glBindRenderbuffer(GL2.GL_RENDERBUFFER, renderBufferHandle);
        gl.glBindTexture(GL2.GL_TEXTURE_2D, textureHandle);
        gl.glTexImage2D(GL2.GL_TEXTURE_2D, 0, GL2.GL_RGB, width, height, 0, GL2.GL_RGB, GL2.GL_UNSIGNED_BYTE, null);
        gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MAG_FILTER, GL2.GL_LINEAR);
        gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MIN_FILTER, GL2.GL_LINEAR);
        gl.glFramebufferTexture2D(GL2.GL_FRAMEBUFFER, GL2.GL_COLOR_ATTACHMENT0, GL2.GL_TEXTURE_2D, textureHandle, 0);
        gl.glRenderbufferStorage(GL2.GL_RENDERBUFFER, GL2.GL_DEPTH_COMPONENT24, width, height);
        gl.glFramebufferRenderbuffer(GL2.GL_FRAMEBUFFER, GL2.GL_DEPTH_ATTACHMENT, GL2.GL_RENDERBUFFER, renderBufferHandle);

        gl.glMatrixMode(GL2.GL_PROJECTION);
        gl.glPushMatrix();
        gl.glLoadIdentity();
        gl.glOrthof(-1f, 1f, -1f, 1f, -1f, 1f);

        gl.glMatrixMode(GL2.GL_MODELVIEW);
        gl.glPushMatrix();
        gl.glLoadIdentity();

        gl.glViewport(0, 0, width, height);

        gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);

        gl.glScalef(1f, 2f, 1f);
        gl.glTranslatef(-0.5f, 0.0f, 0.0f);
        gl.glBindTexture(GL2.GL_TEXTURE_2D, textureHandle1);
        gl.glTexCoordPointer(2, GL2.GL_FLOAT, 0, vaST);
        gl.glVertexPointer(3, GL2.GL_FLOAT, 0, vaXYZ);
        gl.glDrawArrays(GL2.GL_QUADS, 0, vaXYZ.capacity());

        gl.glTranslatef(1.0f, 0.0f, 0.0f);
        gl.glBindTexture(GL2.GL_TEXTURE_2D, textureHandle2);
        gl.glTexCoordPointer(2, GL2.GL_FLOAT, 0, vaST);
        gl.glVertexPointer(3, GL2.GL_FLOAT, 0, vaXYZ);
        gl.glDrawArrays(GL2.GL_QUADS, 0, vaXYZ.capacity());

        gl.glMatrixMode(GL2.GL_PROJECTION);
        gl.glPopMatrix();
        gl.glMatrixMode(GL2.GL_MODELVIEW);
        gl.glPopMatrix();

        gl.glBindRenderbuffer(GL2.GL_RENDERBUFFER, 0);
        gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, 0);

        buffer.put(0, renderBufferHandle);
        gl.glDeleteRenderbuffers(GL2.GL_RENDERBUFFER, buffer);

        buffer.put(0, frameBufferHandle);
        gl.glDeleteFramebuffers(GL2.GL_FRAMEBUFFER, buffer);

        return textureHandle;

    }


    @Override
    public void init(final GLAutoDrawable drawable) {

        gl = drawable.getGL().getGL2();
        glu = new GLU();

        gl.glEnable(GL2.GL_DEPTH_TEST);
        gl.glEnable(GL2.GL_TEXTURE_2D);
        gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL2.GL_TEXTURE_COORD_ARRAY);


        for (int i = 0; i < IMAGES.length; i++)
            textureHandles[i] = createTexture(IMAGES[i]);

        textureHandles[IMAGES.length] = merge(textureHandles[0], textureHandles[1], 1024, 1024);

    }


    @Override
    public void display(final GLAutoDrawable drawable) {

        gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
        gl.glLoadIdentity();

        gl.glTranslatef(-3f, 0.0f, -5.0f);

        for (int i = 0; i < IMAGES.length + 1; i++) {

            gl.glTranslatef(+1.5f, 0.0f, 0.0f);
            gl.glBindTexture(GL2.GL_TEXTURE_2D, textureHandles[i]);
            gl.glTexCoordPointer(2, GL2.GL_FLOAT, 0, vaST);
            gl.glVertexPointer(3, GL2.GL_FLOAT, 0, vaXYZ);
            gl.glDrawArrays(GL2.GL_QUADS, 0, vaXYZ.capacity());

        }

    }


    @Override
    public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, int height) {

        if (height <= 0)
            height = 1;

        gl.glMatrixMode(GL2.GL_PROJECTION);
        gl.glLoadIdentity();
        glu.gluPerspective(45.0f, (float) width / (float) height, 1.0, 20.0);
        gl.glMatrixMode(GL2.GL_MODELVIEW);
        gl.glLoadIdentity();

    }


    @Override
    public void dispose(final GLAutoDrawable arg0) {

    }


    public static void main(final String[] args) throws IOException {

        final Merge base = new Merge();
        final Frame frame = new Frame();
        final GLCanvas canvas = new GLCanvas();
        final Animator animator = new Animator(canvas);

        canvas.addGLEventListener(base);
        frame.add(canvas);
        frame.setSize(500, 500);
        frame.addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosing(final WindowEvent e) {

                new Thread(new Runnable() {
                    public void run() {

                        animator.stop();
                        System.exit(0);

                    }

                }).start();
            }
        });

        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        animator.start();

    }

}
```

Wenn Du Lust hast, kannst Du aber auch gerne etwas ausführlicher beschreiben was Du vorhast, das würde die Wahrscheinlichkeit, dass Dir jemand einen sinnvolleren Tipp geben kann steigern.

Gruß,
Fancy


----------



## Friedhelm (1. Sep 2010)

Wow, sehr nice :toll:

Es ist mehr als ich mir erhofft hatte. Zwar kann ich den Code nicht ausführen (habe kein com.jogamp), aber ich sehe schon das Prinzip. Also Primär dachte ich daran, etwa ein grafisches Alphabet in einer Textur zu haben... und wenn der User etwas schreibt, dann den entsprechenden Buchstaben in eine art OpenGL TextArea zu schreiben (mit 1 Textur). Es gibt ja schon Textlösungen wie etwa bei Spielen, die jedoch dazu für jeden Buchstaben ein Rechteck auf dem Screen zeichnen. Das geht natürlich bei langem Text nicht. Denn bei spätestens 2.500-5.000 wirds eng (jedenfalls bei meinem Rechner). Ich habe keine Ahnung ob das funktionieren wird, aber ich probiers einfach mal.

Kann mir einer einen Link zu aktuellen compilierten JOGL 2 (July 2010) Dateien geben (Mac)?

Ich habe mit Eclipse und dem GIT Plugin versucht aktuelle JOGL Dateien zu holen, doch da geht garnichts (bekomme nur Sourcen die ich nicht compilieren kann). Warum wird das aktuelle JOGL nicht mehr als fertiges Paket angeboten(.jar und .jnilib) , so wie es mit JOGL 1.1 und JOGL 2.0 der Fall war?

Wenn ich das obige Beispiel unter LWJGL laufen lassen möcht... was ist die Alternative zu "GLBuffers" ?


----------



## Empire Phoenix (1. Sep 2010)

DisplayListen für die renderbefehle der text zeichen.


----------



## Evil-Devil (1. Sep 2010)

Wer kommt denn auf so kranke Ideen Texte in Texturen zu speichern? Mehr als eine Textur mit dem Alphabet und Zahlen brauchst du doch nicht. Den Text neu zu zeichnen oder in einen Buffer abzulegen sollte nicht das Problem darstellen. Es gibt viele andere Dinge die weitaus mehr Performance fressen würden


----------



## Empire Phoenix (1. Sep 2010)

theoretisch könnte man das auch per shader machen den man das asci alphabet (für adavnced das unicode) mithilfe on uniforms übergibt ^^ der das dann selber auf den fragmentshader berechnet. Wäre mal nen interessantes Projekt, aber wie über mir schon gesagt, text ist eigentlich nicht das problem. Ich empfehle halt alle befelhe als Displaylist zu speichern, dann gehen die aufrufe auch super schnell  Performance Probleme bei 2-3k zeichen wenn du die als eigene texture hast entstehen nur durch die von dir manuell aufgerufenen draw calls und texturestate changes.


----------



## Friedhelm (1. Sep 2010)

Empire Phoenix hat gesagt.:


> theoretisch könnte man das auch per shader machen den man das asci alphabet (für adavnced das unicode) mithilfe on uniforms übergibt ^^ der das dann selber auf den fragmentshader berechnet. Wäre mal nen interessantes Projekt, aber wie über mir schon gesagt, text ist eigentlich nicht das problem. Ich empfehle halt alle befelhe als Displaylist zu speichern, dann gehen die aufrufe auch super schnell  Performance Probleme bei 2-3k zeichen wenn du die als eigene texture hast entstehen nur durch die von dir manuell aufgerufenen draw calls und texturestate changes.



Leider habe ich von dem keine Ahnung. Was sind Displaylisten? Wie sehen diese in der Praxis aus (Codeschnipsel)?

Aber ist es nicht performanter, wenn die Grafikkarte nur 1 Textur mit dem ganzen Text darstellen muss? Da spart man sich doch den ganzen Darstelleungskram von 2-3K Buchstaben die durch die Grafikkarte sausen müssten. Man stelle sich eine Textverarbeitung in OpenGL vor. Ich denke da ist es mit einer Textur besser.



> Wenn ich das obige Beispiel unter LWJGL laufen lassen möcht... was ist die Alternative zu "GLBuffers" ?


Hat jemand dafür eine Lösung?


----------



## Empire Phoenix (1. Sep 2010)

Sorry habe keine Lowlevel codeschnipsel, da ich einige layer höher arbeite mit der jmonkeyengine.

Der hier sollte aber helfen:
twl: src/de/matthiasmann/twl/renderer/lwjgl/LWJGLFontCache.java@e91eeeea2155


----------



## Evil-Devil (1. Sep 2010)

Friedhelm hat gesagt.:


> Aber ist es nicht performanter, wenn die Grafikkarte nur 1 Textur mit dem ganzen Text darstellen muss? Da spart man sich doch den ganzen Darstelleungskram von 2-3K Buchstaben die durch die Grafikkarte sausen müssten. Man stelle sich eine Textverarbeitung in OpenGL vor. Ich denke da ist es mit einer Textur besser.


Du hast doch nur eine Textur und deinen Text. Auf der Textur liegen die Buchstaben und Zahlen und Sonderzeichen herum. Mittels derer Position kannst du dir die gewünschten Texel nehmen und auf einem Quad zeichnen.

Wenn man vom einfachsten Fall ausgeht das ein Buchstaben Feld 16 Texel breit ist, dann kannst du bei einer Textur die 256 Texel breit ist, 16 Buchstaben pro Zeile unterbringen. Alles was du dann noch machen musst ist die entsprechende Koordinate anzugeben. Im LWJGL Forum (das down ist), gibt es im Documentation Board eine dafür passende Klasse.


----------



## Guest2 (1. Sep 2010)

Friedhelm hat gesagt.:


> jeden Buchstaben ein Rechteck auf dem Screen zeichnen. Das geht natürlich bei langem Text nicht. Denn bei spätestens 2.500-5.000 wirds eng (jedenfalls bei meinem Rechner).



In der Größenordnung sollte das kein Problem darstellen, auch auf Deinem Rechner nicht. Du kannst auf jeden Fall deutlich mehr Rechtecke darstellen, als Du Buchstaben auf dem Bildschirm erkennen kannst. Wenn Du das versucht haben solltest und es zu langsam wurde, hast Du wahrscheinlich noch ein anderes Problem. (Code zeigen hilft da meistens -> KSKB )




Friedhelm hat gesagt.:


> Kann mir einer einen Link zu aktuellen compilierten JOGL 2 (July 2010) Dateien geben (Mac)?



Wie kommst Du ausgerechnet auf Juli? Die aktuellsten und zurzeit sinnvollsten Pakete gibt es hier. Die sind von April und enthalten die com.jogamp Sachen. Noch aktuellere gibt es z.B. hier (aber nicht jetzt ). Ganz aktuelle gibt es vom Buildserver, aber da gibt es keine MacOS Versionen. Ich nutze normalerweise die aus dem ersten Link, das sind die "Last Release". Siehe auch z.B. hier.




Friedhelm hat gesagt.:


> Wenn ich das obige Beispiel unter LWJGL laufen lassen möcht... was ist die Alternative zu "GLBuffers" ?



Zu Fuß mit der reinen Java API, z.B.:


```
FloatBuffer     vaXYZ          = GLBuffers.newDirectFloatBuffer(QUAD_XYZ);
```

Wird zu:


```
FloatBuffer vaXYZ = ByteBuffer.allocateDirect(Float.SIZE / Byte.SIZE * QUAD_XYZ.length).order(ByteOrder.nativeOrder()).asFloatBuffer();
vaXYZ.put(QUAD_XYZ);
vaXYZ.rewind();
```




Empire Phoenix hat gesagt.:


> theoretisch könnte man das auch per shader machen



Shader ist (fast) immer eine gute Lösung! 
Ich würde es allerdings erstmal einfacher angehen:

1.	Das TextFeld ist ein Gitter aus Dreiecken. Dieses Gitter hat eine feste Dimension und wird zum Programmstart erzeugt. Leere Positionen im Gitter werden als Leerzeichen dargestellt. Es werden keine Texturkoordinaten definiert.
2.	Eine Textur welche alle darstellbaren Zeichen enthält: ABCDE...
3.	Ein Vertexshader der den Text per uniforme Variable bekommt und aus Text und VertexID die Texturkoordinaten berechnet


Schnell runtergeschrieben sieht das dann so aus:


```
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import javax.imageio.ImageIO;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.awt.GLCanvas;
import javax.media.opengl.glu.GLU;

import com.jogamp.opengl.util.Animator;
import com.jogamp.opengl.util.GLBuffers;


public class Text implements GLEventListener {

    private static int           WIDTH       = 10;
    private static int           HEIGHT      = 1;

    private static final String staticVS = "" +
    "#version 130\n" +
    "uniform int text[" + WIDTH * HEIGHT + "];" +
    "out vec2 st;" +
    "void main(void) " +
    "{" +
    "  int i = text[gl_VertexID / 6];" +
    "  float s1 = i * (1 / 26.0) - 0.002;" +
    "  float s2 = s1 + (1 / 26.0 - 0.002);" +
    "  st = vec2(s1, 1);" +
    "  if((gl_VertexID % 6) == 1) st = vec2(s2, 1);" +
    "  else if((gl_VertexID % 6) == 2) st = vec2(s2, 0);" +
    "  else if((gl_VertexID % 6) == 3) st = vec2(s1, 1);" +
    "  else if((gl_VertexID % 6) == 4) st = vec2(s2, 0);" +
    "  else if((gl_VertexID % 6) == 5) st = vec2(s1, 0);" +
    "  gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;" +
    "}";


    private static final String staticFS = "" +
    "uniform sampler2D tex;" +
    "in vec2 st;" +
    "void main (void)" +
    "{" +
    "  gl_FragColor = texture2D(tex, st);" +
    "}";


    private static final String  IMAGE       = "abc.png";
    private static final float[] TRIANGLE_XY = { -0.5f, -0.5f, +0.5f, -0.5f, +0.5f, +0.5f, -0.5f, -0.5f, +0.5f, +0.5f, -0.5f, +0.5f };

    private final IntBuffer      buffer      = GLBuffers.newDirectIntBuffer(1);
    private final IntBuffer      text        = GLBuffers.newDirectIntBuffer(WIDTH * HEIGHT);

    private final FloatBuffer    va;

    private int                  shaderHandle;
    private int                  textureHandle;
    private int                  textHandle;

    private GL2                  gl;
    private GLU                  glu;


    public Text() {

        va = GLBuffers.newDirectFloatBuffer(TRIANGLE_XY.length * WIDTH * HEIGHT);

        for (int y = 0; y < HEIGHT; y++)
            for (int x = 0; x < WIDTH; x++)
                for (int i = 0; i < TRIANGLE_XY.length; i++)
                    if (i % 2 == 0)
                        va.put((TRIANGLE_XY[i] + x + 0.5f - WIDTH / 2.0f) / WIDTH);
                    else
                        va.put((TRIANGLE_XY[i] + y + 0.5f - HEIGHT / 2.0f) / HEIGHT);

        va.rewind();

    }


    private int createTexture(final String resource) {

        try {

            final BufferedImage image = ImageIO.read(getClass().getClassLoader().getResourceAsStream(resource));
            final byte[] data = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
            final ByteBuffer texture = GLBuffers.newDirectByteBuffer(data);

            gl.glGenTextures(1, buffer);
            final int textureHandle = buffer.get(0);

            gl.glBindTexture(GL2.GL_TEXTURE_2D, textureHandle);
            gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MAG_FILTER, GL2.GL_LINEAR);
            gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MIN_FILTER, GL2.GL_LINEAR);

            gl.glPixelStorei(GL2.GL_UNPACK_ALIGNMENT, 1);
            gl.glTexImage2D(GL2.GL_TEXTURE_2D, 0, GL2.GL_RGBA, image.getWidth(), image.getHeight(), 0, GL.GL_RGBA, GL2.GL_UNSIGNED_INT_8_8_8_8, texture);

            return textureHandle;

        } catch (final IOException e) {

            throw new RuntimeException(e);

        }

    }


    private int createShader() {

        final int vHandle = gl.glCreateShader(GL2.GL_VERTEX_SHADER);
        gl.glShaderSource(vHandle, 1, new String[] { staticVS }, new int[] { staticVS.length() }, 0);
        gl.glCompileShader(vHandle);

        final int fHandle = gl.glCreateShader(GL2.GL_FRAGMENT_SHADER);
        gl.glShaderSource(fHandle, 1, new String[] { staticFS }, new int[] { staticFS.length() }, 0);
        gl.glCompileShader(fHandle);

        final int spHandle = gl.glCreateProgram();
        gl.glAttachShader(spHandle, vHandle);
        gl.glAttachShader(spHandle, fHandle);
        gl.glLinkProgram(spHandle);
        gl.glValidateProgram(spHandle);

        gl.glUseProgram(spHandle);

        textHandle = gl.glGetUniformLocation(spHandle, "text");

        return spHandle;

    }


    @Override
    public void init(final GLAutoDrawable drawable) {

        gl = drawable.getGL().getGL2();
        glu = new GLU();

        gl.glEnable(GL2.GL_DEPTH_TEST);
        gl.glEnable(GL2.GL_TEXTURE_2D);

        gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);

        shaderHandle = createShader();
        textureHandle = createTexture(IMAGE);

    }


    private void setText(final String value) {

        for (int i = 0; i < value.length() && i < text.capacity(); i++)
            text.put(i, value.charAt(i) - 'A');

        gl.glUseProgram(shaderHandle);
        gl.glUniform1iv(textHandle, value.length(), text);

    }


    @Override
    public void display(final GLAutoDrawable drawable) {

        gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
        gl.glLoadIdentity();

        gl.glTranslatef(0f, 0.0f, -2.0f);
        gl.glScalef(1f, 0.2f, 1f);

        setText("HELLOWORLD");
        gl.glBindTexture(GL2.GL_TEXTURE_2D, textureHandle);
        gl.glVertexPointer(2, GL2.GL_FLOAT, 0, va);
        gl.glDrawArrays(GL2.GL_TRIANGLES, 0, va.capacity());

    }


    @Override
    public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, int height) {

        if (height <= 0)
            height = 1;

        gl.glMatrixMode(GL2.GL_PROJECTION);
        gl.glLoadIdentity();
        glu.gluPerspective(45.0f, (float) width / (float) height, 1.0, 20.0);
        gl.glMatrixMode(GL2.GL_MODELVIEW);
        gl.glLoadIdentity();

    }


    @Override
    public void dispose(final GLAutoDrawable arg0) {

    }


    public static void main(final String[] args) throws IOException {

        final Text base = new Text();
        final Frame frame = new Frame();
        final GLCanvas canvas = new GLCanvas();
        final Animator animator = new Animator(canvas);

        canvas.addGLEventListener(base);
        frame.add(canvas);
        frame.setSize(500, 500);
        frame.addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosing(final WindowEvent e) {

                new Thread(new Runnable() {
                    public void run() {

                        animator.stop();
                        System.exit(0);

                    }

                }).start();
            }
        });

        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        animator.start();

    }

}
```


Das Ergebnis ist dann:







(Man sieht, ich habe mir beim erstellen der ABC Textur nicht sonderlich viel Mühe gegeben.  Unter anderem auch nur Großbuchstaben und kein Leerzeichen. Wollte man das ordentlich machen, werden insbesondere nicht monospace Schriftarten schwierig.)

Wer es trotzdem ausprobieren möchte, hier die abc.png

Gruß,
Fancy


----------



## Friedhelm (1. Sep 2010)

Danke für die sehr guten Anregungen :meld: Ich denke für die große Textausgabe "HELLOWORD" sollte man große Texturbuchstaben nehmen (28 oder sogar 48). Dann sind diese auch nicht so unscharf.

Noch besser ist es natürlich Pixelgenau zu arbeiten, sprich man hat nicht nur 1 Basis-Textur mit den Buchstaben, sondern für jede Größe eine Buchstaben-Textur (8,10,11,12,14,16,18,20) ... dann braucht man die Buchstaben nicht skalieren.

Ich werde alles mal durchprobieren und mich für das effektivste entscheiden.

Ps.: Übrigens ein spezielles Danke an Guest2... Dein Engagement, gerade was Beipspiel-Code angeht, ist sehr sehr sehr bemerkenswert :applaus:


----------



## Friedhelm (1. Sep 2010)

Guest2 hat gesagt.:


> Moin,
> 
> Gruß,
> Fancy



zum Merge Code:

Da bekomme ich das was Du auf dem Screenshot siehst (ein zerstörtes Bild). Woran könnte das liegen? Ich habe zwei 1024x1024 PNG's verwendet.


----------



## Friedhelm (1. Sep 2010)

Guest2 hat gesagt.:


> Das Ergebnis ist dann:
> 
> 
> 
> ...



Auch beim 2. Beispiel "Text" sehe ich nur Schwarz (leer).


----------



## Guest2 (2. Sep 2010)

Friedhelm hat gesagt.:


> zum Merge Code:
> 
> Da bekomme ich das was Du auf dem Screenshot siehst (ein zerstörtes Bild). Woran könnte das liegen? Ich habe zwei 1024x1024 PNG's verwendet.



Grobe Vermutung, die PNGs haben Alpha Kanäle. Die createTexture() des ersten Beispiels erwartet PNGs ohne. Die des zweiten Beispiels mit. Erkennen kann man das an den Parametern von glTexImage2D (und auch darüber anpassen). Sind halt beides nur KSKB, also immer genau hinsehen. 

Du kannst die PNGs auch mal online stellen, dann kann ich ausprobieren ob es wirklich daran liegt. 




Friedhelm hat gesagt.:


> Auch beim 2. Beispiel "Text" sehe ich nur Schwarz (leer).



Ersetze die createShader() mal mit diesem Codesegment und poste die Ausgabe:


```
private void printShaderError(final int handle) {

        gl.glGetShaderiv(handle, GL2.GL_COMPILE_STATUS, buffer);
        if (buffer.get(0) != 1) {

            gl.glGetShaderiv(handle, GL2.GL_INFO_LOG_LENGTH, buffer);

            final ByteBuffer byteBuffer = GLBuffers.newDirectByteBuffer(buffer.get(0));
            gl.glGetShaderInfoLog(handle, byteBuffer.capacity(), buffer, byteBuffer);

            System.err.println("\nshader compile error: " + handle);
            for (int i = 0; i < buffer.get(0); i++)
                System.err.print((char) byteBuffer.get(i));
        }

    }

    private int createShader() {

        final int vHandle = gl.glCreateShader(GL2.GL_VERTEX_SHADER);
        gl.glShaderSource(vHandle, 1, new String[] { staticVS }, new int[] { staticVS.length() }, 0);
        gl.glCompileShader(vHandle);

        printShaderError(vHandle);

        final int fHandle = gl.glCreateShader(GL2.GL_FRAGMENT_SHADER);
        gl.glShaderSource(fHandle, 1, new String[] { staticFS }, new int[] { staticFS.length() }, 0);
        gl.glCompileShader(fHandle);

        printShaderError(fHandle);

        final int spHandle = gl.glCreateProgram();
        gl.glAttachShader(spHandle, vHandle);
        gl.glAttachShader(spHandle, fHandle);
        gl.glLinkProgram(spHandle);
        gl.glValidateProgram(spHandle);

        gl.glUseProgram(spHandle);

        textHandle = gl.glGetUniformLocation(spHandle, "text");

        return spHandle;

    }
```

Gruß,
Fancy


----------



## Friedhelm (2. Sep 2010)

Hier die Ausgabe von "Text":


shader compile error: 1
ERROR: 0:1: '' :  Version number not supported by GL2
ERROR: 0:2: 'out' : syntax error syntax error
ERROR: Parser found no code to compile in source strings.

shader compile error: 2
ERROR: 0:1: 'in' : syntax error syntax error
ERROR: Parser found no code to compile in source strings.


Anbei die 2 PNG's für "Merge" (im Original sind diese 1024x1024)

Schalte ich Transparenz bei beien PNG's aus, erhalte ich Screenshot 3


----------



## Guest2 (2. Sep 2010)

Friedhelm hat gesagt.:


> Anbei die 2 PNG's für "Merge" (im Original sind diese 1024x1024)
> 
> Schalte ich Transparenz bei beien PNG's aus, erhalte ich Screenshot 3



Ok und was genau wundert Dich nun? Das die Texturen kopf stehen? Oder das das Programm etwas merkwürdiges macht? Das Programm stellt 3 Flächen dar. Die erste mit der ersten Textur, die zweite mit der zweiten und die dritte mit einer Textur die sich aus Textur1 und Textur2 ergibt. Versuche es doch mal mit Texturen die nicht dieselbe Farbe wie der GL Hintergrund haben, dann erkennst Du auch etwas.

Die Auflösung der dritten Textur hängt von den letzten beiden Parametern bei merge() ab. Das höhen/breiten Verhältnis hängt von der Fläche ab auf der die Textur gelegt wird. Diese Fläche wird in dem Beispiel aus einem VertexArray erzeugt. 




Friedhelm hat gesagt.:


> Hier die Ausgabe von "Text":
> 
> 
> shader compile error: 1
> ...



Ok, normalerweise ist es kein Problem aus einem GL2 Kontext heraus einen GLSL 1.3 Shader zu laden. Da Du schriebst das sich in Deinem Rechner eine GeForce befindet und es seitens NVIDIA erlaubt wäre es so zu handhaben, vermute ich das der Treiber nicht von NVIDIA sondern von Apple stammt? Dann wäre natürlich so erstmal Essig, aber immerhin konsequent, Apple schreibt seinen Nutzern ja gerne vor was er darf und was nicht. 

GL3 ist vermutlich keine sinnvolle Alternative für Dich. Allerdings kann man den Shader auch merkbefreit nach java kopieren und ihn dort ausführen. Dann braucht man keinen Shader mehr, es wäre reines GL2 und Apple wieder glücklich.

Aussehen würde das dann etwa so:
(die setText() sollte normalerweise nur aufgerufen werden wenn sich der Text auch ändert)


```
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import javax.imageio.ImageIO;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.awt.GLCanvas;
import javax.media.opengl.glu.GLU;

import com.jogamp.opengl.util.Animator;
import com.jogamp.opengl.util.GLBuffers;


public class Text2 implements GLEventListener {

    private static int           WIDTH       = 5;
    private static int           HEIGHT      = 2;

    private static final String  IMAGE       = "abc.png";
    private static final float[] TRIANGLE_XY = { -0.5f, -0.5f, +0.5f, -0.5f, +0.5f, +0.5f, -0.5f, -0.5f, +0.5f, +0.5f, -0.5f, +0.5f };

    private final IntBuffer      buffer      = GLBuffers.newDirectIntBuffer(1);

    private final FloatBuffer    vaXY;
    private final FloatBuffer    vaST;

    private int                  textureHandle;

    private GL2                  gl;
    private GLU                  glu;


    public Text2() {

        vaXY = GLBuffers.newDirectFloatBuffer(TRIANGLE_XY.length * WIDTH * HEIGHT);
        vaST = GLBuffers.newDirectFloatBuffer(TRIANGLE_XY.length * WIDTH * HEIGHT);

        for (int y = HEIGHT - 1; y >= 0; y--)
            for (int x = 0; x < WIDTH; x++)
                for (int i = 0; i < TRIANGLE_XY.length; i++)
                    if (i % 2 == 0)
                        vaXY.put((TRIANGLE_XY[i] + x + 0.5f - WIDTH / 2.0f) / WIDTH);
                    else
                        vaXY.put((TRIANGLE_XY[i] + y + 0.5f - HEIGHT / 2.0f) / HEIGHT);

        vaXY.rewind();

    }


    private int createTexture(final String resource) {

        try {

            final BufferedImage image = ImageIO.read(getClass().getClassLoader().getResourceAsStream(resource));
            final byte[] data = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
            final ByteBuffer texture = GLBuffers.newDirectByteBuffer(data);

            gl.glGenTextures(1, buffer);
            final int textureHandle = buffer.get(0);

            gl.glBindTexture(GL2.GL_TEXTURE_2D, textureHandle);
            gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MAG_FILTER, GL2.GL_LINEAR);
            gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MIN_FILTER, GL2.GL_LINEAR);

            gl.glPixelStorei(GL2.GL_UNPACK_ALIGNMENT, 1);
            gl.glTexImage2D(GL2.GL_TEXTURE_2D, 0, GL2.GL_RGBA, image.getWidth(), image.getHeight(), 0, GL.GL_RGBA, GL2.GL_UNSIGNED_INT_8_8_8_8, texture);

            return textureHandle;

        } catch (final IOException e) {

            throw new RuntimeException(e);

        }

    }


    @Override
    public void init(final GLAutoDrawable drawable) {

        gl = drawable.getGL().getGL2();
        glu = new GLU();

        gl.glEnable(GL2.GL_DEPTH_TEST);
        gl.glEnable(GL2.GL_TEXTURE_2D);

        gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL2.GL_TEXTURE_COORD_ARRAY);

        textureHandle = createTexture(IMAGE);

    }


    private void setText(final String value) {

        for(int gl_VertexID = 0; gl_VertexID  < value.length() * 6 && gl_VertexID < vaXY.capacity(); gl_VertexID++){

            final int i = value.charAt(gl_VertexID / 6) - 'A';
            final float s1 = (float) (i * (1 / 26.0) - 0.002);
            final float s2 = (float) (s1 + (1 / 26.0 - 0.002));

            float s = s1;
            float t = 1;

            if((gl_VertexID % 6) == 1) { s = s2; t = 1; }
            else if((gl_VertexID % 6) == 2) { s = s2; t = 0; }
            else if((gl_VertexID % 6) == 3) { s = s1; t = 1; }
            else if((gl_VertexID % 6) == 4) { s = s2; t = 0; }
            else if((gl_VertexID % 6) == 5) { s = s1; t = 0; }

            vaST.put(gl_VertexID * 2, s);
            vaST.put(gl_VertexID * 2 + 1, t);

        }

    }


    @Override
    public void display(final GLAutoDrawable drawable) {

        gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
        gl.glLoadIdentity();

        gl.glTranslatef(0f, 0.0f, -2.0f);
        gl.glScalef(1f, 0.2f, 1f);

        setText("HELLOWORLD");
        gl.glBindTexture(GL2.GL_TEXTURE_2D, textureHandle);
        gl.glTexCoordPointer(2, GL2.GL_FLOAT, 0, vaST);
        gl.glVertexPointer(2, GL2.GL_FLOAT, 0, vaXY);
        gl.glDrawArrays(GL2.GL_TRIANGLES, 0, vaXY.capacity());

    }


    @Override
    public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, int height) {

        if (height <= 0)
            height = 1;

        gl.glMatrixMode(GL2.GL_PROJECTION);
        gl.glLoadIdentity();
        glu.gluPerspective(45.0f, (float) width / (float) height, 1.0, 20.0);
        gl.glMatrixMode(GL2.GL_MODELVIEW);
        gl.glLoadIdentity();

    }


    @Override
    public void dispose(final GLAutoDrawable arg0) {

    }


    public static void main(final String[] args) throws IOException {

        final Text2 base = new Text2();
        final Frame frame = new Frame();
        final GLCanvas canvas = new GLCanvas();
        final Animator animator = new Animator(canvas);

        canvas.addGLEventListener(base);
        frame.add(canvas);
        frame.setSize(500, 500);
        frame.addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosing(final WindowEvent e) {

                new Thread(new Runnable() {
                    public void run() {

                        animator.stop();
                        System.exit(0);

                    }

                }).start();
            }
        });

        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        animator.start();

    }

}
```

Gruß,
Fancy


----------



## Friedhelm (3. Sep 2010)

Super, das funktioniert. Naja, stimmt schon, bei Apple ist alles ein wenig wie auf Schienen... aber dafür hat man auch nicht so viel Probleme. Jedenfalls empfinde ich das so nach 11 Jahren Windows und 4 Jahren Mac :autsch:


----------



## Evil-Devil (8. Sep 2010)

Das LWJGL Forum ist wieder online und somit war der Thread auch auffindbar den ich meinte.
Complete TrueTypeFont class that only requires LWJGL


----------

