OpenGL Skalowanie tekstur

Odpowiedz Nowy wątek
2016-09-29 18:50

Rejestracja: 4 lata temu

Ostatnio: 3 lata temu

0

Witam, od wczoraj zacząłem moją zabawę z opengl i mam pewien problem. Otóż napisałem metodę która ułatwia rysowanie obrazów podczas ustawiania wielkości zwykłego obrazu nie ma żadnego problemu, lecz przy próbie rysowania obrazu z kanałem alpha obraz jest mniejszy niż powinien.
Dla rozjaśnienia sytuacji podam dwa zdjęcia

<image>![9d8dec49ac.png](//static.4programmers.net/uploads/attachment/9d8dec49ac.png)</image>

Rycerz po lewej teoretycznie jest takiej samej wielkości jak zielony kwadrat.

<image>![e4c9206c38.png](//static.4programmers.net/uploads/attachment/e4c9206c38.png)</image>

Na drugim zdjęciu widać to dokładnie. Nie wiem w czym leży problem, zwykłym obrazom można ustawić wielkość bez problemu lecz tym z kanałem alpha już nie.
Będę wdzięczny za pomoc.


public static void DrawImage(Texture texture,int x,int y, int width, int height){
        if(texture != null){
            texture.bind();
            glEnable(GL_BLEND);
            glBlendFunc(GL_ONE, GL_ZERO);
            glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

            //set transparency
            glColor4f(1, 1, 1,1);
            //
            glTranslatef(x, y, 0);
            glBegin(GL_QUADS);
            glTexCoord2f(0, 0);
            glVertex2i(0, 0);
            glTexCoord2f(1, 0);
            glVertex2i(width, 0);
            glTexCoord2f(1, 1);
            glVertex2i(width, height);
            glTexCoord2f(0, 1);
            glVertex2i(0, height);
            glEnd();
            glLoadIdentity(); 

        }
    } 

        Texture tex = LoadTexture("res/1.png", "PNG");
        Texture t2 = LoadTexture("res/image.png", "PNG");

        while (!Display.isCloseRequested()) {
            int delta = getDelta();
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

            update(delta);
            renderGL();

             DrawImage(tex, 0, 0, 100 , 120);
             DrawImage(t2, 100, 0, 100, 120);

            Display.update();
            Display.sync(60); // cap fps to 60fps
        } 
edytowany 2x, ostatnio: Marcin902, 2016-09-29 18:51

Pozostało 580 znaków

2016-09-29 20:36

Rejestracja: 16 lat temu

Ostatnio: 23 minuty temu

0

Ja zawsze wywoływałem TexParameter podczas ładowania tekstury:

void Texture::Load(string filename,bool fixEdge)
{
    //load and decode
    std::vector<unsigned char> buffer, image;
    loadFile(buffer, filename);
    unsigned long w, h;
    int error = decodePNG(image, w, h, buffer.empty() ? 0 : &buffer[0], (unsigned long)buffer.size());

    if (!error) {
        glEnable(GL_TEXTURE_2D);
        width=w;
        height=h;
        glPixelStorei(GL_UNPACK_ALIGNMENT,4);
        glGenTextures(1,&texID);
        glBindTexture(GL_TEXTURE_2D,texID);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
        if (fixEdge) {
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE  );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE  );
        } else {
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT  );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT  );
        }

        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, &image[0]);
    } else {
        cout << "Error: " << filename << endl;
    }
}

CLAMP_TO_EDGE to ważny parametr dla sprite, bo przy nakładaniu tekstury na quada eliminujesz niedoskonałość liczb zmiennoprzecinkowych i nie będą dodawać się śmieci po bokach obrazka ;)

glBlendFunc poprawnie wykorzystujący kanał alpha robiłem zawsze w taki sposób:

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

Nie wiem, czemu dwa razy wywołujesz blend func, z różnymi parametrami.

BTW. przywitaj się z OpenGL 4, albo chociaż 3 ;)

edytowany 8x, ostatnio: Spine, 2016-09-29 20:45
Fakt, rzeczywiście powinienem najpierw więcej poczytać, zajmuje się opengl dopiero od wczoraj. Używam lwjgl 2.9.3 i u mnie nie ma takiego parametru CLAMP_TO_EDGE. I nie rozumiem metody decodePNG, na czym ona polega? - Marcin902 2016-09-29 21:29

Pozostało 580 znaków

2016-09-29 22:30

Rejestracja: 16 lat temu

Ostatnio: 23 minuty temu

0

Odpowiadaj w wątku, a nie w komentarzach.

decodePNG to funkcja, której używałem w C++ (z "modułu" picopng - http://lodev.org/lodepng/picopng.cpp). To mało istotne w naszych rozważaniach ;)

Na Twoje szczęście lwjgl też używałem ze starym OpenGL. Żeby mieć CLAMP_TO_EDGE prawdopodobnie musisz zaimportować odpowiednie moduły, ja mam taką formułkę w pliku z loaderem tekstur:

import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL12.*;
import static org.lwjgl.opengl.GL13.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.PixelFormat.*;
import static org.lwjgl.util.glu.GLU.*;

Do ładowania PNG używałem takich modułów:

import de.matthiasmann.twl.utils.PNGDecoder;
import de.matthiasmann.twl.utils.PNGDecoder.Format;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.file.Paths;

Tak wygląda moja metoda ładująca PNG w Javie:

    public void load(String fileName,boolean fixEdge) throws IOException
    {
        InputStream in = null;
        try {
            in = new FileInputStream(Paths.get(fileName).toString());
            PNGDecoder decoder = new PNGDecoder(in);

            width=decoder.getWidth();
            height=decoder.getHeight();
            ByteBuffer buf = ByteBuffer.allocateDirect(4*width*height);
            decoder.decode(buf, width*4, Format.RGBA);
            buf.flip();
            texID=glGenTextures();
            glBindTexture(GL_TEXTURE_2D, texID);

            // All RGB bytes are aligned to each other and each component is 1 byte
            glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

            if (fixEdge) {
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE  );
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE  );
            } else {
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT  );
                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT  );
            }
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf);

            in.close(); // przy 250 uruchomieniach w jednej instancji, krzyczalo, ze za duzo otwartych plikow
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
edytowany 3x, ostatnio: Spine, 2016-09-29 22:37

Pozostało 580 znaków

2016-09-29 22:37

Rejestracja: 4 lata temu

Ostatnio: 3 lata temu

0

@Spine Dodałem ale to nadal nie rozwiązuje problemu, podam kod całej klasy.

 @SuppressWarnings("unused")
public class Start {

      float x = 400, y = 300;
    float rotation = 0;

    /** time at last frame */
    long lastFrame;

    /** frames per second */
    int fps;
    /** last fps time */
    long lastFPS;

    /** is VSync Enabled */
    boolean vsync;

    public void start() {
        try {
            Display.setDisplayMode(new DisplayMode(1280, 720));
            Display.create();
        } catch (LWJGLException e) {
            e.printStackTrace();
            System.exit(0);
        }

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(0, 1280, 720, 0, 1, -1);
        glMatrixMode(GL_MODELVIEW);
        glEnable(GL_TEXTURE_2D);

        getDelta(); // call once before loop to initialise lastFrame
        lastFPS = getTime(); // call before loop to initialise fps timer
        Texture tex = LoadTexture("res/1.png", "PNG");
        Texture t2 = LoadTexture("res/image.png", "PNG");
        Texture t3 = LoadTexture("res/atack1/1.png", "PNG");

        while (!Display.isCloseRequested()) {
            int delta = getDelta();
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

            update(delta);

                DrawImage(t2, 0, 0, 600, 420);
                DrawImage(t3, 0, 0, 307, 160);

             Display.update();
            Display.sync(60); // cap fps to 60fps
        }

        Display.destroy();
        System.exit(0);
    }

    public void update(int delta) {
        // rotate quad
        rotation += 0.15f * delta;

        if (Keyboard.isKeyDown(Keyboard.KEY_LEFT)) x -= 0.35f * delta;
        if (Keyboard.isKeyDown(Keyboard.KEY_RIGHT)) x += 0.35f * delta;

        if (Keyboard.isKeyDown(Keyboard.KEY_UP)) y -= 0.35f * delta;
        if (Keyboard.isKeyDown(Keyboard.KEY_DOWN)) y += 0.35f * delta;

        while (Keyboard.next()) {
            if (Keyboard.getEventKeyState()) {
                if (Keyboard.getEventKey() == Keyboard.KEY_F) {
                    setDisplayMode(1280, 720, !Display.isFullscreen());
                }
                else if (Keyboard.getEventKey() == Keyboard.KEY_V) {
                    vsync = !vsync;
                    Display.setVSyncEnabled(vsync);
                }
            }
        }

        // keep quad on the screen
        if (x < 0) x = 0;
        if (x > 800) x = 800;
        if (y < 0) y = 0;
        if (y > 600) y = 600;

        updateFPS(); // update FPS Counter
    }

    /**
     * Set the display mode to be used 
     * 
     * @param width The width of the display required
     * @param height The height of the display required
     * @param fullscreen True if we want fullscreen mode
     */
    public void setDisplayMode(int width, int height, boolean fullscreen) {

        // return if requested DisplayMode is already set
                if ((Display.getDisplayMode().getWidth() == width) && 
            (Display.getDisplayMode().getHeight() == height) && 
            (Display.isFullscreen() == fullscreen)) {
            return;
        }

        try {
            DisplayMode targetDisplayMode = null;

            if (fullscreen) {
                DisplayMode[] modes = Display.getAvailableDisplayModes();
                int freq = 0;

                for (int i=0;i<modes.length;i++) {
                    DisplayMode current = modes[i];

                    if ((current.getWidth() == width) && (current.getHeight() == height)) {
                        if ((targetDisplayMode == null) || (current.getFrequency() >= freq)) {
                            if ((targetDisplayMode == null) || (current.getBitsPerPixel() > targetDisplayMode.getBitsPerPixel())) {
                                targetDisplayMode = current;
                                freq = targetDisplayMode.getFrequency();
                            }
                        }

                        // if we've found a match for bpp and frequence against the 
                        // original display mode then it's probably best to go for this one
                        // since it's most likely compatible with the monitor
                        if ((current.getBitsPerPixel() == Display.getDesktopDisplayMode().getBitsPerPixel()) &&
                            (current.getFrequency() == Display.getDesktopDisplayMode().getFrequency())) {
                            targetDisplayMode = current;
                            break;
                        }
                    }
                }
            } else {
                targetDisplayMode = new DisplayMode(width,height);
            }

            if (targetDisplayMode == null) {
                System.out.println("Failed to find value mode: "+width+"x"+height+" fs="+fullscreen);
                return;
            }

            Display.setDisplayMode(targetDisplayMode);
            Display.setFullscreen(fullscreen);

        } catch (LWJGLException e) {
            System.out.println("Unable to setup mode "+width+"x"+height+" fullscreen="+fullscreen + e);
        }
    }

    /** 
     * Calculate how many milliseconds have passed 
     * since last frame.
     * 
     * @return milliseconds passed since last frame 
     */
    public int getDelta() {
        long time = getTime();
        int delta = (int) (time - lastFrame);
        lastFrame = time;

        return delta;
    }

    /**
     * Get the accurate system time
     * 
     * @return The system time in milliseconds
     */
    public long getTime() {
        return (Sys.getTime() * 1000) / Sys.getTimerResolution();
    }

    /**
     * Calculate the FPS and set it in the title bar
     */
    public void updateFPS() {
        if (getTime() - lastFPS > 1000) {
            Display.setTitle("FPS: " + fps);
            fps = 0;
            lastFPS += 1000;
        }
        fps++;
    }

    public void initGL() {
        GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glLoadIdentity();
        GL11.glOrtho(0, 800, 0, 600, 1, -1);
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
    }

    public static void main(String[] argv) {
        Start fullscreenExample = new Start();
        fullscreenExample.start();

    }

     public static void DrawImage(Texture texture,float x,float y, float width, float height){
        if(texture != null){
            texture.bind();

            glEnable(GL_BLEND);

            glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT  );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT  );

            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE  );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE  );

            //set transparency
            glColor4f(1, 1, 1,1);
            //
            glTranslatef(x, y, 0);
            glBegin(GL_QUADS);
            glTexCoord2f(0, 0);
            glVertex2f(0, 0);
            glTexCoord2f(1, 0);
            glVertex2f(width, 0);
            glTexCoord2f(1, 1);
            glVertex2f(width, height);
            glTexCoord2f(0, 1);
            glVertex2f(0, height);
            glEnd();
            glLoadIdentity(); 

        }
    }

     public static Texture LoadTexture(String path,String fileType){
        Texture tex = null;
        InputStream in = ResourceLoader.getResourceAsStream(path);
        try {
            tex = TextureLoader.getTexture(fileType, in);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return tex;

    }

}

Pozostało 580 znaków

2016-09-29 22:42

Rejestracja: 16 lat temu

Ostatnio: 23 minuty temu

0

Powinno być:

GL12.GL_CLAMP_TO_EDGE

Ale import to załatwia, żeby nie trzeba było pisać nazwy modułu:

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

Poza tym jak podajesz kod "całej klasy", to nie obcinaj importów ;)

edytowany 2x, ostatnio: Spine, 2016-09-29 22:44

Pozostało 580 znaków

Odpowiedz

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0