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

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

ただいまの
回答率

90.47%

  • GLSL

    51questions

    GLSL (OpenGL Shading Language) はC言語をベースとしたシェーディング言語です。

  • GLSL ES

    6questions

    GLSL ESは、GLSLの派生規格で、 組み込み環境向けのOpenGL ES用のシェーダー言語です。

GLSLを用いたfftの可視化について、glsl150からglsl120への書き換え

受付中

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 403

santaro

score 10

私はGLSLを使用してfftテクスチャを作成しようとしています。 しかし、現在のバージョン150で作成されたものの、バージョン120に書き直したいと思っていましたが、うまくいきません。下にソースコードを貼り付けますので、原因が分かっていれば教えてください。 

glsl150.vert

#version 150
uniform mat4 modelViewProjectionMatrix;
in vec4 position;
in vec2 texcoord;

out vec2 vTexCoord;

void main(){
    vTexCoord = texcoord;
    gl_Position = modelViewProjectionMatrix * position;
}

glsl150.frag

#version 150
uniform sampler2DRect tex;
uniform float rawFft[256];
in vec2 vTexCoord;

out vec4 outputColor;

void main(){

    if (vTexCoord.y < 1.0) {
        int i = int(vTexCoord.x);
        outputColor.r = rawFft[i];
    } else {
        vec2 st = vTexCoord;
        st.y -= 1.0;
        outputColor.r = texture(tex, st).r;
    }
    outputColor.a = 1.0;
}

イメージ説明

glsl120.vert

#version 120
#extension GL_ARB_texture_rectangle : enable

void main(){
    gl_TexCoord[0] = gl_MultiTexCoord0;
    gl_Position = gl_Vertex;
}

glsl120.frag

#version 120
#extension GL_ARB_texture_rectangle : enable

uniform sampler2D tex;
uniform float rawFft[256];

vec4 outputColor;

void main(){

    if (gl_TexCoord[0].y < 1.0) {
        int i = int(gl_TexCoord[0].x);
        outputColor.r = rawFft[i];
    } else {
        vec2 st = gl_TexCoord[0].st;
        st.y -= 1.0;
        outputColor.r = texture2D(tex, st).r;
    }
    outputColor.a = 1.0;

    gl_FragColor = outputColor;
}

イメージ説明

120に書き換えるとこのようになります。150と同じ結果を得るためにはどうしたら良いでしょうか?

[追記]
渡す頂点座標をgl_Position = gl_Vertex; からgl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;に変えてみたところ、全画面に色が出るようになりました、、
イメージ説明

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

0

動作実験はしていないのですが、GLSL 1.20なら頂点データの受け取りはattribute変数、バーテックス・フラグメントシェーダー間の値の伝達はvarying変数を使った記法となるのではないでしょうか?

GLSL 1.50版では頂点データ供給にVAOやVBOを使用し、MVP行列も自前で計算してuniform変数に投入していたかと思いますが、これらはGLSL 1.50版と同じままにして、シェーダーコードを下記のようにしてみるとどうでしょうか。

バーテックスシェーダー

#version 120
#extension GL_ARB_texture_rectangle : enable

uniform mat4 modelViewProjectionMatrix;

attribute vec4 position;
attribute vec2 texcoord;

varying vec2 vTexCoord;

void main(){
    vTexCoord = texcoord;
    gl_Position = modelViewProjectionMatrix * position;
}

フラグメントシェーダー

#version 120
#extension GL_ARB_texture_rectangle : enable

uniform sampler2DRect tex;
uniform float rawFft[256];

varying vec2 vTexCoord;

void main(){
    vec4 outputColor;

    if (vTexCoord.y < 1.0) {
        int i = int(vTexCoord.x);
        outputColor.r = rawFft[i];
    } else {
        vec2 st = vTexCoord.st;
        st.y -= 1.0;
        outputColor.r = texture2DRect(tex, st).r;
    }
    outputColor.a = 1.0;

    gl_FragColor = outputColor;
}

参考:The OpenGL® Shading Language Language Version: 1.20 Document Revision: 8 07-Sept-2006

[追記]
後で気付いたのですが、ご提示のGLSL 1.20版コードではMVP変換を行っていない頂点座標を後段に送っているようですね。この違いが原因だとすると、渡す頂点座標をgl_Position = gl_Vertex;からgl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;に変えてみるというのも試す価値がありそうです。
なにぶん生のOpenGLをいじるのはご無沙汰でして、他にも見落としがあるかもしれません。もし「この部分も怪しそうだがどう思うか」などお気付きの点があればぜひご指摘ください。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/04/21 12:09 編集

    参考にさせていただき、実装してみましたが、動きませんでした、、、
    さきほど元質問に追記しましたが、画像のように右下ではなく全画面に色がつくようにはなりました、

    キャンセル

  • 2018/04/21 12:58

    異常動作の原因として他にありそうなのは、元コードのglsl120.fragでoutputColorをグローバルスコープで宣言し、しかもそれをmain内で書き換えようとしている点かもしれません。
    「vec4 outputColor;」の位置を「void main(){」と「if (gl_TexCoord[0].y < 1.0) {」の間の行に移動して、outputColorをローカル変数にしてみるとどうなるでしょうか。

    もしこれでもダメな場合、次に気になるのはCPU側のコード(glDraw~などの描画コマンドや、行列をOpenGLに渡す処理を記述している方のコード)ですね。こちらはGLSL 1.50の場合と1.20の場合で何か書き換えをしているでしょうか?
    もし書き換えている場合は、それら1.50用コードと1.20用コードも見てみたいです。1.50用コードをそれと同等な1.20用コードに書き換える際に、何かミスをしてしまったという可能性はないでしょうか?

    キャンセル

  • 2018/04/21 13:57

    教えていただいた通りoutputColorをローカル変数にしてみましたが同じく変わりませんでした。

    CPU側で変更したのは、シェーダーの参照先(パス)と使うOpenGLを2.1に変更しただけなので、、何回も見直しましたがミスはないはずです、、

    キャンセル

  • 2018/04/21 16:19

    うーん、今はちょっとプログラミングできる環境が手元にないのですが、後で私もOpenGL 2.1の範囲内のコードを書いてみて実験してみたいと思います。実際にやってみれば問題点の手がかりがつかめるかもしれません。
    なるべくご質問者さんと近い条件でやってみたいので確認したいのですが、そのGLSL 1.20用のCPU側コードでは「glBeginやglEndを使っている箇所はない」、「glMatrixModeやglLoadIdentity、glPushMatrix、glTranslatedなど行列操作系関数を使っている箇所もない」、「glGenBuffers、glBindBuffer、glBufferDataやglVertexAttribPointerを使っている箇所がある」ということでいいでしょうか?

    キャンセル

  • 2018/04/21 16:27

    もう一つ確認しますが、

    #version 120
    #extension GL_ARB_texture_rectangle : enable

    uniform mat4 modelViewProjectionMatrix;

    void main(){
    gl_TexCoord[0] = gl_MultiTexCoord0;
    gl_Position = modelViewProjectionMatrix * gl_Vertex;
    }

    という風に、1.50版と同じようにMVP行列をuniform変数として渡すというパターンはすでに試されたでしょうか。もしCPU側コードで行列操作系関数を使っていないのであれば、これも原因の可能性として怪しく思えてきました。

    キャンセル

  • 2018/04/21 16:34

    ありがとうございます!

    そうです、行列操作系の関数は使用しておらず、「glGenBuffers、glBindBuffer、glBufferDataやglVertexAttribPointer」は使用しています。

    MVP行列をuniform変数として渡すパターンもすでに試していますが、効果なしでした、、!

    キャンセル

  • 2018/04/23 07:24

    試した結果を別回答に書きました。ご質問者さんの場合と比べてみて、怪しそうな所を見つける手がかりになりますでしょうか?
    コードはC++を選びましたが、すみませんがC++の知識不足で変なコードになっている可能性が高いです。ご容赦ください...

    キャンセル

0

[追記]
字数制限につき別回答で投稿しますが、ちょっと試してみた結果を追記します。下記のような方針でやってみました。

  • 画面サイズはとりあえず256×256とした
  • 前の描画結果をとっておくためのテクスチャを用意する
  • FFT結果はダミーデータで毎フレーム埋める(ご質問者さんの場合は実際のFFT結果を使えるかと思います)
  • 画面全体を覆う四角形を描画...端の一行はFFT結果を使い、残りは前の描画結果を一行ずらして描画する
  • 描画結果をテクスチャにコピーし、次回の描画に備える

ご質問者さんがGLSL 1.20版でうまくいかなかった可能性としては、描画結果を見ますに描画自体は行われているものの、やはり何か位置座標・テクスチャ座標周りで問題があったように思います。

MVP変換については、ご質問者さんのコードの場合どのような変換がかかっているか不確かでしたので、描画する四角形の頂点座標を最初から画面を覆う位置にして、MVP変換不要な状態にすることにしました。

テクスチャ座標についてハマりそうだと思ったのは、GL_TEXTURE_2Dを使うかGL_TEXTURE_RECTANGLEを使うかでテクスチャ座標が変わる点でしょうか。
GL_TEXTURE_2Dの場合は正規化座標...つまり0.0~1.0で指定しますが、GL_TEXTURE_RECTANGLEの場合は非正規化座標...つまり0.0~テクスチャ幅、0.0~テクスチャ高さで指定することになります。この辺の設定でおかしいところはなさそうでしょうか?

バーテックスシェーダー

#version 120
#extension GL_ARB_texture_rectangle : enable

void main() {
    gl_TexCoord[0] = gl_MultiTexCoord0;
    gl_Position = gl_Vertex;
}

フラグメントシェーダー

#version 120
#extension GL_ARB_texture_rectangle : enable

// もしGL_ARB_texture_rectangleを使わずにやるとすると、テクスチャは
// GL_TEXTURE_2Dを使い、UV座標は0.0~1.0で設定し、サンプラー型は
// sampler2D、サンプリング関数はtexture2Dに変えて、受け取ったUV座標は
// 適宜256倍してy判定・i算出し、stずらし量は1.0 / 256.0にして...といった
// 修正を加えることになるでしょう

uniform sampler2DRect tex; // sampler2DRectを使用
uniform float rawFft[256];

void main() {
    vec4 outputColor;

    if (gl_TexCoord[0].y < 1.0) {
        int i = int(gl_TexCoord[0].x);
        outputColor.r = rawFft[i];
    } else {
        vec2 st = gl_TexCoord[0].st;
        st.y -= 1.0;
        outputColor.r = texture2DRect(tex, st).r; // texture2DRectを使用
    }
    outputColor.a = 1.0;

    gl_FragColor = outputColor;
}

// 上記のコードでは、フレーム毎のFFT結果が下からせり上がってくるかと思います
// 上から降りてくる方がお好みでしたら、一例としてはy位置判定の際のyを上下反転し、
// さらにテクスチャサンプリング位置ずらし方向を上下反転する手があるでしょう
/*
void main() {
    vec4 outputColor;
    float yInverted = 256.0 - gl_TexCoord[0].y;

    if (yInverted < 1.0) {
        int i = int(gl_TexCoord[0].x);
        outputColor.r = rawFft[i];
    } else {
        vec2 st = gl_TexCoord[0].st;
        st.y += 1.0;
        outputColor.r = texture2DRect(tex, st).r;
    }
    outputColor.a = 1.0;

    gl_FragColor = outputColor;
}
*/

メインコード

#include "stdafx.h"
#include <gl/glew.h>
#include <GLFW/glfw3.h>
#include "Renderer.h"

int main(int argc, char ** argv) {
    // コンソールウィンドウが邪魔なので移動
    SetWindowPos(GetConsoleWindow(), 0, 640, 0, 0, 0, SWP_NOZORDER | SWP_NOSIZE);

    if (!glfwInit()) {
        return -1;
    }
    // OpenGL 2.1までダウングレードしたかったのですが、私の環境では3.0までが限界のようでした
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
    GLFWwindow* mainWindow = glfwCreateWindow(256, 256, "OpenGL Test", NULL, NULL);
    if (!mainWindow) {
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(mainWindow);

    // 描画ループ
    Renderer* renderer = new Renderer();
    while (!glfwWindowShouldClose(mainWindow)) {
        // 動作確認をやりやすくするため、スペースキー押下時のみレンダリングするようにしました
        if (glfwGetKey(mainWindow, GLFW_KEY_SPACE)) {
            renderer->Render();
            glfwSwapBuffers(mainWindow);
        }
        glfwPollEvents();
    }
    delete renderer;

    glfwTerminate();
    return 0;
}

Rendererヘッダ

#pragma once
#include <gl/glew.h>
#include <GLFW/glfw3.h>
#include "Shader.h"

class Renderer {
private:
    static const int kBufferSize = 256;

    Shader* m_shader; // 描画に使うシェーダー
    double m_angle; // ダミーデータ生成に使う
    GLfloat m_fftBuffer[kBufferSize]; // FFTデータの代替物...これを毎フレーム新しいデータで埋める
    GLuint m_texLoc; // シェーダー上のtexのuniformロケーション
    GLuint m_rawFftLoc; // シェーダー上のrawFftのuniformロケーション
    GLuint m_quadVbo; // ビューポートを覆う四角形の頂点データのVBO...各頂点は位置座標としてfloat×2、テクスチャ座標としてfloat×2を持つ
    GLuint m_prevFrameTex; // 前回レンダリング時の結果を保管するテクスチャ

    static void CheckGLErrors(); // OpenGLエラーをチェック、エラーがあればエラー出力に表示する
    static const GLchar* GetShaderSource(WORD resourceId); // リソースとして仕込んだシェーダーファイルからコードを文字列として取り出す

    void Init(); // コンストラクタ内で呼ばれる、レンダリングに先立って一回だけ行う諸々の処理
    void UpdateFftBuffer(); // Render内の先頭で実行し、m_fftBufferを新しいダミーデータで埋める
public:
    Renderer();
    ~Renderer();

    void Render(); // フレームをレンダリングする
};

Renderer実装

#include "stdafx.h"
#include "Renderer.h"
#include "resource.h"
#include <iostream>

static const double kPi = 3.141592653589793116;
static const double kAngularSpeed = 1.0 / 64;
static const double kWaveScale = 1.0;

Renderer::Renderer() {
    std::cout << "Hello!" << std::endl;
    this->Init();
}

Renderer::~Renderer() {
    // 終了前に一応OpenGLオブジェクト類を削除
    delete m_shader;
    glDeleteBuffers(1, &m_quadVbo);
    glDeleteTextures(1, &m_prevFrameTex);
    std::cout << "Bye!" << std::endl;
}

void Renderer::Init() {
    m_angle = 0.0;
    std::fill_n(m_fftBuffer, std::size(m_fftBuffer), 0.0f);

    // GLEWの初期化
    GLenum  glewError = glewInit();
    if (glewError) {
        std::cerr << "GLEW error: " << glewGetErrorString(glewError) << std::endl;
    }
    std::cout
        << glGetString(GL_VERSION) << std::endl
        << glGetString(GL_RENDERER) << std::endl
        << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;
    CheckGLErrors();

    // シェーダーを作成・使用、tex・rawFftのロケーションを取得
    const GLchar* vertSource = GetShaderSource(IDR_GLSL120VERT);
    const GLchar* fragSource = GetShaderSource(IDR_GLSL120FRAG);
    m_shader = new Shader(vertSource, fragSource);
    m_shader->Use();
    m_texLoc = m_shader->GetUniformLoc("tex");
    m_rawFftLoc = m_shader->GetUniformLoc("rawFft[0]");
    CheckGLErrors();

    // 頂点データを作成
    // GLSL 1.50版コードではuniform変数としてMVP行列が渡されていましたが
    // どのような変換がかかっているか不確かだったため、MVP行列を使わずに
    // 生の頂点座標で画面全体を覆えるようにしました
    // テクスチャ座標はtexture2DRectを使う前提の非正規化座標です
    const int kVertexElementCount = 2 + 2; // 位置座標2要素、テクスチャ座標2要素
    const GLsizei kVertexSize = kVertexElementCount * sizeof(GLfloat); // 一頂点のデータサイズ
    GLfloat quadData[4 * kVertexElementCount] = {
        -1.0f,  1.0f,   0.0f, 256.0f, // 左上隅
        -1.0f, -1.0f,   0.0f,   0.0f, // 左下隅
         1.0f,  1.0f, 256.0f, 256.0f, // 右上隅
         1.0f, -1.0f, 256.0f,   0.0f  // 右下隅
    };
    glGenBuffers(1, &m_quadVbo);
    glBindBuffer(GL_ARRAY_BUFFER, m_quadVbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(quadData), quadData, GL_STATIC_DRAW);
    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(2, GL_FLOAT, kVertexSize, reinterpret_cast<const void*>(0)); // 位置座標属性を設定
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glTexCoordPointer(2, GL_FLOAT, kVertexSize, reinterpret_cast<const void*>(2 * sizeof(GLfloat))); // テクスチャ座標属性を設定
    // リファレンスによると、VBOがバインドされている場合は、これら属性設定関数の最後の引数はVBO先頭からのオフセットと解釈されるらしい
    CheckGLErrors();

    // テクスチャを作成、メモリ確保は行うがデータ投入は不要
    glGenTextures(1, &m_prevFrameTex);
    glBindTexture(GL_TEXTURE_RECTANGLE, m_prevFrameTex);
    glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGB8, kBufferSize, kBufferSize, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
    // glUniform1i(m_texLoc, 0); // texが参照するテクスチャユニットをGL_TEXTURE0...つまりm_prevFrameTexに設定
    // texはデフォルトで0になっているはずなので、今回のケースでは上記設定は必須ではない
    CheckGLErrors();

    // その他諸々の最初に一度だけ設定すれば十分な事項を設定
    glViewport(0, 0, kBufferSize, kBufferSize);
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    // 画面をクリア、その状態で描画結果をテクスチャにコピーし、テクスチャをまっさらにする
    glClear(GL_COLOR_BUFFER_BIT);
    glCopyTexSubImage2D(GL_TEXTURE_RECTANGLE, 0, 0, 0, 0, 0, kBufferSize, kBufferSize);
    CheckGLErrors();
}

void Renderer::Render() {
    // m_fftBufferを更新、rawFftにデータを投入
    this->UpdateFftBuffer();
    glUniform1fv(m_rawFftLoc, kBufferSize, m_fftBuffer);

    // 領域全体を覆う四角形を描画
    // glClear(GL_COLOR_BUFFER_BIT); // 画面全体が描き換わるので、画面クリアは必須ではない
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    // 描画結果をバインド中のテクスチャ...つまりm_prevFrameTexにコピー
    glCopyTexSubImage2D(GL_TEXTURE_RECTANGLE, 0, 0, 0, 0, 0, kBufferSize, kBufferSize);
}

void Renderer::UpdateFftBuffer() {
    // m_fftBufferをダミーのデータで満たす
    // とりあえず時間とともに位相が回るサイン波を用意
    // 描画結果として斜めの縞模様が描かれることを期待する
    double phase = (m_angle += kAngularSpeed);
    float* fftElement = m_fftBuffer;
    for (int i = 0; i < kBufferSize; i++) {
        fftElement[i] = static_cast<float>(sin(phase + (2.0 * kPi * i * kWaveScale) / kBufferSize) * 0.5 + 0.5);
    }
}

void Renderer::CheckGLErrors() {
    GLenum error = glGetError();
    if (error != GL_NO_ERROR) {
        std::cerr << gluErrorString(error) << std::endl;
    }
}

const GLchar* Renderer::GetShaderSource(WORD resourceId) {
    return static_cast<const GLchar*>(LockResource(LoadResource(NULL, FindResourceA(NULL, MAKEINTRESOURCEA(resourceId), "GLSL"))));
}

Shaderヘッダ

#pragma once
#include <gl/glew.h>
#include <GLFW/glfw3.h>

class Shader {
private:
    GLuint m_programName; // シェーダープログラム名
public:
    Shader(const GLchar* vertSource, const GLchar* fragSource);
    ~Shader();

    void Use(); // このシェーダープログラムを使用する
    GLuint GetUniformLoc(const GLchar* name); // uniform変数名からロケーションを得る
};

実行結果
プレビュー

C++を使用してみたものの、不慣れでしてコード的にいまいちな部分が多いかと思います。あくまでも描画プロセスの参考程度と言うことでお願いします...

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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

関連した質問

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

  • GLSL

    51questions

    GLSL (OpenGL Shading Language) はC言語をベースとしたシェーディング言語です。

  • GLSL ES

    6questions

    GLSL ESは、GLSLの派生規格で、 組み込み環境向けのOpenGL ES用のシェーダー言語です。