質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.02%

ウィンドウを移動するまで正常に図形が描画されない(OpenGL、GLFW)

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 316

code413

score 6

環境:
MacOS 10.14.5
Xcode 10.3

床井研究室様の『GLFWによるOpenGL入門』(http://marina.sys.wakayama-u.ac.jp/~tokoi/GLFWdraft.pdf)を見て勉強しています。

第6章100Pまで行い、下記のソース(字数の都合上コメントを一部省略)ができたのですが、起動後は真ん中に(図1)、ウィンドウのサイズを変えると図形の位置が変わり、そこからウィンドウを移動すると再び図形の位置が戻ります。(図2,図3)
イメージ説明
---図1 起動直後---
イメージ説明
---図2 サイズ変更後---
イメージ説明
---図3 サイズ変更後、移動---

#include<cstdlib>
#include<iostream>
#include<fstream>
#include<vector>
#include<memory>
#include<GL/glew.h>
#include<GLFW/glfw3.h>
#include "Window.h"
#include "Shape.h"

// シェーダオブジェクトのコンパイル結果
GLboolean printShaderInfoLog(GLuint shader,const char *str){
    GLint status;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
    if(status == GL_FALSE) std :: cerr << "Compile Error in " << str << std::endl;

    GLsizei bufSize;
    glGetShaderiv(shader,GL_INFO_LOG_LENGTH,&bufSize);

    if(bufSize > 1){
        std::vector<GLchar> infoLog(bufSize);
        GLsizei length;
        glGetShaderInfoLog(shader,bufSize,&length,&infoLog[0]);
        std::cerr << &infoLog[0] << std::endl;
    }
    return static_cast<GLboolean>(status);
}

//プログラムオブジェクトのリンク結果
GLboolean printProgramInfoLog(GLuint program){
    GLint status;
    glGetProgramiv(program,GL_LINK_STATUS,&status);
    if(status == GL_FALSE) std::cerr << "Link Error" << std::endl;

    GLsizei bufsize;
    glGetProgramiv(program,GL_INFO_LOG_LENGTH,&bufsize);

    if(bufsize > 1){
        std::vector<GLchar> infoLog(bufsize);
        GLsizei length;
        glGetProgramInfoLog(program,bufsize,&length,&infoLog[0]);
        std::cerr << &infoLog[0] << std::endl;
    }

    return static_cast<GLboolean>(status);
}


//プログラムオブジェクト作成
    GLuint createProgram(const char *vsrc, const char *fsrc){
    const GLuint program(glCreateProgram());

    if(vsrc != NULL){
        //バーテックスシェーダのシェーダオブジェクトを作成、組込
        const GLuint vobj(glCreateShader(GL_VERTEX_SHADER));
        glShaderSource(vobj,1,&vsrc,NULL);
        glCompileShader(vobj);

        if(printShaderInfoLog(vobj,"vertex shader"))
            glAttachShader(program,vobj);
        glDeleteShader(vobj);
    }


    if(fsrc != NULL){
        //フラグメントシェーダのシェーダオブジェクトを作成、組込
        const GLuint fobj(glCreateShader(GL_FRAGMENT_SHADER));
        glShaderSource(fobj,1,&fsrc,NULL);
        glCompileShader(fobj);

        if(printShaderInfoLog(fobj,"fragment shader"))
            glAttachShader(program,fobj);
        glDeleteShader(fobj);
    }

    glBindAttribLocation(program,0,"position");
    glBindFragDataLocation(program,0,"fragment");
    glLinkProgram(program);

    if(printProgramInfoLog(program))
        return program;
    glDeleteProgram(program);
    return 0;
}



//シェーダのソースを読み込んだメモリを返す
bool readShaderSource(const char *name,std::vector<GLchar> &buffer){
    if(name == NULL) return false;

    std::ifstream file(name,std::ios::binary);
    if(file.fail()){
        std::cerr << "Error: Can't open source: " << name << std::endl;
        return false;
    }

    file.seekg(0L,std::ios::end);
    GLsizei length = static_cast<GLsizei>(file.tellg());

    //ファイルサイズのメモリ確保(+1では動かなかったので4に)
    buffer.resize(length + 4);

    file.seekg(0L,std::ios::beg);
    file.read(buffer.data(),length);
    buffer[length] = '\n';

    if(file.fail()){
        std::cerr << "Error: Couldn't read source:" << name << std::endl;
        file.close();
        return false;
    }
    file.close();
    return true;
}


//シェーダのソースを読み込んでプログラムオブジェクトを作成
GLuint loadProgram(const char *vert,const char *frag){
    std::vector<GLchar> vsrc;
    const bool vstat(readShaderSource(vert, vsrc));
    std::vector<GLchar> fsrc;
    const bool fstat(readShaderSource(frag, fsrc));

    return vstat && fstat ? createProgram(vsrc.data(), fsrc.data()) : 0;
}


constexpr Object::Vertex rectangleVertex[] =
{
    {-0.5f ,-0.5f },
    { 0.5f ,-0.5f },
    { 0.5f , 0.5f },
    {-0.5f , 0.5f }
};

int main() {

    if(glfwInit() == GL_FALSE){
        std::cerr << "Can't initialize GLFW" << std::endl;
        return 1;
    }

    atexit(glfwTerminate);

    // Version 3.2 Core Profile を選択
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    Window window;
    glClearColor(1.0f, 1.0f, 1.0f, 0.0f);

    //プログラムオブジェクト作成
    const GLuint program(loadProgram("point.vert","point.frag"));

    // uniform変数の場所を取得
    const GLint sizeLoc(glGetUniformLocation(program, "size"));
    const GLint scaleLoc(glGetUniformLocation(program,"scale"));

    // 図形データ作成
    std::unique_ptr<const Shape> shape(new Shape(2, 4, rectangleVertex));

    while(window.shouldClose() == GL_FALSE)
    {
        glClear(GL_COLOR_BUFFER_BIT);

        //シェーダプログラムの使用開始
        glUseProgram(program);

        // uniform変数に値を設定
        glUniform2fv(sizeLoc, 1, window.getSize());
        glUniform1f(scaleLoc, window.getScale());

        //図形を描画
        shape -> draw();

        window.swapBuffers();
    }
}
#pragma once
#include <iostream>
#include <GL/glew.h>
#include <GLFW/glfw3.h>

// ウィンドウ関連の処理
class Window{
    GLFWwindow *const window;

    //ウィンドウのサイズ
    GLfloat size[2];

    //ワールド座標系に対するデバイス座標系の拡大率
    GLfloat scale;

public:
    //コンストラクタ
    Window(int width = 640, int height = 480, const char *title = "Hello!")
    : window(glfwCreateWindow(width, height, title, NULL, NULL))
    , scale(100.0f)
    {
            if(window == NULL)
            {
                //ウィンドウが作成できなかった
                std::cerr << "Can't create GLFW window." << std::endl;
                exit(1);
            }

        //現在のウィンドウを処理対象にする
        glfwMakeContextCurrent(window);

        //GLEWを初期化する
        glewExperimental = GL_TRUE;
        if(glewInit() != GLEW_OK)
        {
            //GLEWの初期化に失敗した
            std::cerr << "Can't initialize GLEW" << std::endl;
            exit(1);
        }

        //垂直同期のタイミングを待つ
        glfwSwapInterval(1);

        //このインスタンスの this ポインタを記録しておく
        glfwSetWindowUserPointer(window, this);

        //ウィンドウのサイズ変更時に呼び出す処理の登録
        glfwSetWindowSizeCallback(window,resize);

        //開いたウィンドウの初期設定
        resize(window,width,height);
    }


    virtual ~Window(){
        glfwDestroyWindow(window);
    }

    //ウィンドウを閉じるべきかを判定する
    int shouldClose() const{
        return glfwWindowShouldClose(window);
    }

    //カラーバッファを入れ替えてイベントを取り出す
    void swapBuffers(){
        glfwSwapBuffers(window);
        glfwWaitEvents();
    }


    //ウィンドウのサイズを取り出す
    const GLfloat *getSize() const { return size; }

    //ワールド座標系に対するデバイス座標系の拡大率を取り出す
    GLfloat getScale() const{ return scale; }

    // ウィンドウのサイズ変更時の処理
    static void resize(GLFWwindow *const window,int width,int height)
    {
        //ウィンドウ全体をビューポートに設定する
        glViewport(0, 0, width, height);

        //このインスタンスのthisポインタを得る
        Window *const instance(static_cast<Window *>(glfwGetWindowUserPointer(window)));

        if(instance != NULL){
            //開いたウィンドウのサイズを保存する
            instance->size[0] = static_cast<GLfloat>(width);
            instance->size[1] = static_cast<GLfloat>(height);
        }
    }
};
#pragma once
#include<GL/glew.h>

//図形データ
class Object{
    //頂点配列、バッファ
    GLuint vao;
    GLuint vbo;

public:
    struct Vertex{
        GLfloat position[2];
    };

    //コンストラクタ
    Object(GLint size,GLsizei vertexcount,const Vertex *vertex){
        //頂点配列,バッファオブジェクト
        glGenVertexArrays(1,&vao);
        glBindVertexArray(vao);

        glGenBuffers(1,&vbo);
        glBindBuffer(GL_ARRAY_BUFFER,vbo);
        glBufferData(GL_ARRAY_BUFFER,vertexcount * sizeof(Vertex),vertex,GL_STATIC_DRAW);

        //結合されている頂点バッファオブジェクトをin変数から参照できる様にする
        glVertexAttribPointer(0,size,GL_FLOAT,GL_FALSE,0,0);
        glEnableVertexAttribArray(0);
    }

    virtual ~Object(){
        glDeleteBuffers(1,&vao);
        glDeleteBuffers(1,&vbo);
    }

private:
    Object(const Object &o);
    Object &operator = (const Object &o);

public:
    //頂点オブジェクトの統合
    void bind() const{
        //描画する頂点配列オブジェクトを指定
        glBindVertexArray(vao);
    }
};
#pragma once
#include<memory>

//図形データ
#include "Object.h"

//図形の描画
class Shape{
    std::shared_ptr<const Object> object;

protected:
    const GLsizei vertexcount;

public:
    Shape(GLint size,GLsizei vertexcount,const Object::Vertex *vertex)
    :object(new Object(size,vertexcount,vertex))
    ,vertexcount(vertexcount)
    {
    }

    //描画
    void draw() const{
        object -> bind();
        execute();
    }

    virtual void execute() const{
        glDrawArrays(GL_LINE_LOOP,0,vertexcount);
    }
};
#version 150 core
uniform vec2 size;
uniform float scale;
in vec4 position;

void main(){
    gl_Position = vec4(2.0 * scale/size, 1.0 , 1.0) * position;
    //二つ目の1.0のの前に空白を入れると動いた
#version 150 core
out vec4 fragment;

void main(){
  fragment = vec4(1.0, 0.0, 0.0, 1.0);
}


ウィンドウのサイズが切り替わっても真ん中に表示したいのですが、移動するまでは左下の位置のままです、どうかよろしくお願いします。

追伸
バーテックスシェーダに1つスペースを加えたら、図形が横長になる問題とサイズがウィンドウに比例する問題は自己解決したので質問の画像、文章などを差し替えさせていただきました。
現在は画面サイズ変更に伴う図形の移動について悩んでいます。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

check解決した方法

0

このページをSNS上で発信したところ、なんと床井先生本人からのリプライをいただいたのでWindow.hを以下のように修正して解決しました。
原因としては私の使用していたディスプレイがRetinaであることに依るものだそうです。
これに伴い、PDFの修正も行われたそうです(https://twitter.com/tokoik/status/1156837991005880321)……本当にありがとうございます。

また、この後の部分に関しても幾つか修正点があるそうなのですが、とりあえず質問の疑問となっていた部分は解決したので、いただいた修正案を解決方法として掲載させていただきます。
ちなみに画面の仕様の違いなので、PDFの抜けと呼べるものではないと思います……。(このPDFにはとてもお世話になったという私の個人的感情もありますが、修正部分があろうともOpenGL初心者の方にはおすすめします、とても良い教材です)

また、その際に質問のVertexShaderの部分は尻切れになっていたことを指摘されました、こちらに関しては私の不備です、申し訳ありません。

そして修正の詳細に関してですが、端的に言えば

glfwSetWindowSizeCallback()を
glfwSetFramebufferSizeCallback()に、

コンストラクタにあるresize()の前に
glfwGetFramebufferSize()をつけることです

なぜこの修正になるのかに関しては、修正されたpdfのP87辺り、及び以下の床井先生のツイートが一番参考になると思われます
https://twitter.com/tokoik/status/1156821288985362432

シャープが付いているのが変更点になります

<中略>



 //GLEWを初期化する
        glewExperimental = GL_TRUE;
        if(glewInit() != GLEW_OK)
        {
            //GLEWの初期化に失敗した
            std::cerr << "Can't initialize GLEW" << std::endl;
            exit(1);
        }

        //垂直同期のタイミングを待つ
        glfwSwapInterval(1);

        //このインスタンスの this ポインタを記録しておく
        glfwSetWindowUserPointer(window, this);

        //ウィンドウのサイズ変更時に呼び出す処理の登録
        ### glfwSetFramebufferSizeCallback(window,resize);

        //開いたウィンドウの初期設定
        ### glfwGetFramebufferSize(window, &width, &height);
        resize(window,width,height);

    }

また、尻切れトンボになっていたvertexShaderに関しても床井先生からいただいたソースコードを掲載しておきます。なお、上のVertexShaderの最後に"}"を付けるだけでも同様の挙動を示すことを確認済みです。

#version 150 core
uniform vec2 size;
uniform float scale;
uniform vec2 location;
in vec4 position;
void main()
{
    gl_Position = vec4(2.0 * scale / size, 1.0, 1.0) * position
    + vec4(location, 0.0, 0.0);
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.02%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る