私は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;
に変えてみたところ、全画面に色が出るようになりました、、
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答2件
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~テクスチャ高さで指定することになります。この辺の設定でおかしいところはなさそうでしょうか?
バーテックスシェーダー
GLSL
1#version 120 2#extension GL_ARB_texture_rectangle : enable 3 4void main() { 5 gl_TexCoord[0] = gl_MultiTexCoord0; 6 gl_Position = gl_Vertex; 7}
フラグメントシェーダー
GLSL
1#version 120 2#extension GL_ARB_texture_rectangle : enable 3 4// もしGL_ARB_texture_rectangleを使わずにやるとすると、テクスチャは 5// GL_TEXTURE_2Dを使い、UV座標は0.0~1.0で設定し、サンプラー型は 6// sampler2D、サンプリング関数はtexture2Dに変えて、受け取ったUV座標は 7// 適宜256倍してy判定・i算出し、stずらし量は1.0 / 256.0にして...といった 8// 修正を加えることになるでしょう 9 10uniform sampler2DRect tex; // sampler2DRectを使用 11uniform float rawFft[256]; 12 13void main() { 14 vec4 outputColor; 15 16 if (gl_TexCoord[0].y < 1.0) { 17 int i = int(gl_TexCoord[0].x); 18 outputColor.r = rawFft[i]; 19 } else { 20 vec2 st = gl_TexCoord[0].st; 21 st.y -= 1.0; 22 outputColor.r = texture2DRect(tex, st).r; // texture2DRectを使用 23 } 24 outputColor.a = 1.0; 25 26 gl_FragColor = outputColor; 27} 28 29// 上記のコードでは、フレーム毎のFFT結果が下からせり上がってくるかと思います 30// 上から降りてくる方がお好みでしたら、一例としてはy位置判定の際のyを上下反転し、 31// さらにテクスチャサンプリング位置ずらし方向を上下反転する手があるでしょう 32/* 33void main() { 34 vec4 outputColor; 35 float yInverted = 256.0 - gl_TexCoord[0].y; 36 37 if (yInverted < 1.0) { 38 int i = int(gl_TexCoord[0].x); 39 outputColor.r = rawFft[i]; 40 } else { 41 vec2 st = gl_TexCoord[0].st; 42 st.y += 1.0; 43 outputColor.r = texture2DRect(tex, st).r; 44 } 45 outputColor.a = 1.0; 46 47 gl_FragColor = outputColor; 48} 49*/
メインコード
C++
1#include "stdafx.h" 2#include <gl/glew.h> 3#include <GLFW/glfw3.h> 4#include "Renderer.h" 5 6int main(int argc, char ** argv) { 7 // コンソールウィンドウが邪魔なので移動 8 SetWindowPos(GetConsoleWindow(), 0, 640, 0, 0, 0, SWP_NOZORDER | SWP_NOSIZE); 9 10 if (!glfwInit()) { 11 return -1; 12 } 13 // OpenGL 2.1までダウングレードしたかったのですが、私の環境では3.0までが限界のようでした 14 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 15 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); 16 GLFWwindow* mainWindow = glfwCreateWindow(256, 256, "OpenGL Test", NULL, NULL); 17 if (!mainWindow) { 18 glfwTerminate(); 19 return -1; 20 } 21 glfwMakeContextCurrent(mainWindow); 22 23 // 描画ループ 24 Renderer* renderer = new Renderer(); 25 while (!glfwWindowShouldClose(mainWindow)) { 26 // 動作確認をやりやすくするため、スペースキー押下時のみレンダリングするようにしました 27 if (glfwGetKey(mainWindow, GLFW_KEY_SPACE)) { 28 renderer->Render(); 29 glfwSwapBuffers(mainWindow); 30 } 31 glfwPollEvents(); 32 } 33 delete renderer; 34 35 glfwTerminate(); 36 return 0; 37}
Rendererヘッダ
C++
1#pragma once 2#include <gl/glew.h> 3#include <GLFW/glfw3.h> 4#include "Shader.h" 5 6class Renderer { 7private: 8 static const int kBufferSize = 256; 9 10 Shader* m_shader; // 描画に使うシェーダー 11 double m_angle; // ダミーデータ生成に使う 12 GLfloat m_fftBuffer[kBufferSize]; // FFTデータの代替物...これを毎フレーム新しいデータで埋める 13 GLuint m_texLoc; // シェーダー上のtexのuniformロケーション 14 GLuint m_rawFftLoc; // シェーダー上のrawFftのuniformロケーション 15 GLuint m_quadVbo; // ビューポートを覆う四角形の頂点データのVBO...各頂点は位置座標としてfloat×2、テクスチャ座標としてfloat×2を持つ 16 GLuint m_prevFrameTex; // 前回レンダリング時の結果を保管するテクスチャ 17 18 static void CheckGLErrors(); // OpenGLエラーをチェック、エラーがあればエラー出力に表示する 19 static const GLchar* GetShaderSource(WORD resourceId); // リソースとして仕込んだシェーダーファイルからコードを文字列として取り出す 20 21 void Init(); // コンストラクタ内で呼ばれる、レンダリングに先立って一回だけ行う諸々の処理 22 void UpdateFftBuffer(); // Render内の先頭で実行し、m_fftBufferを新しいダミーデータで埋める 23public: 24 Renderer(); 25 ~Renderer(); 26 27 void Render(); // フレームをレンダリングする 28};
Renderer実装
C++
1#include "stdafx.h" 2#include "Renderer.h" 3#include "resource.h" 4#include <iostream> 5 6static const double kPi = 3.141592653589793116; 7static const double kAngularSpeed = 1.0 / 64; 8static const double kWaveScale = 1.0; 9 10Renderer::Renderer() { 11 std::cout << "Hello!" << std::endl; 12 this->Init(); 13} 14 15Renderer::~Renderer() { 16 // 終了前に一応OpenGLオブジェクト類を削除 17 delete m_shader; 18 glDeleteBuffers(1, &m_quadVbo); 19 glDeleteTextures(1, &m_prevFrameTex); 20 std::cout << "Bye!" << std::endl; 21} 22 23void Renderer::Init() { 24 m_angle = 0.0; 25 std::fill_n(m_fftBuffer, std::size(m_fftBuffer), 0.0f); 26 27 // GLEWの初期化 28 GLenum glewError = glewInit(); 29 if (glewError) { 30 std::cerr << "GLEW error: " << glewGetErrorString(glewError) << std::endl; 31 } 32 std::cout 33 << glGetString(GL_VERSION) << std::endl 34 << glGetString(GL_RENDERER) << std::endl 35 << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl; 36 CheckGLErrors(); 37 38 // シェーダーを作成・使用、tex・rawFftのロケーションを取得 39 const GLchar* vertSource = GetShaderSource(IDR_GLSL120VERT); 40 const GLchar* fragSource = GetShaderSource(IDR_GLSL120FRAG); 41 m_shader = new Shader(vertSource, fragSource); 42 m_shader->Use(); 43 m_texLoc = m_shader->GetUniformLoc("tex"); 44 m_rawFftLoc = m_shader->GetUniformLoc("rawFft[0]"); 45 CheckGLErrors(); 46 47 // 頂点データを作成 48 // GLSL 1.50版コードではuniform変数としてMVP行列が渡されていましたが 49 // どのような変換がかかっているか不確かだったため、MVP行列を使わずに 50 // 生の頂点座標で画面全体を覆えるようにしました 51 // テクスチャ座標はtexture2DRectを使う前提の非正規化座標です 52 const int kVertexElementCount = 2 + 2; // 位置座標2要素、テクスチャ座標2要素 53 const GLsizei kVertexSize = kVertexElementCount * sizeof(GLfloat); // 一頂点のデータサイズ 54 GLfloat quadData[4 * kVertexElementCount] = { 55 -1.0f, 1.0f, 0.0f, 256.0f, // 左上隅 56 -1.0f, -1.0f, 0.0f, 0.0f, // 左下隅 57 1.0f, 1.0f, 256.0f, 256.0f, // 右上隅 58 1.0f, -1.0f, 256.0f, 0.0f // 右下隅 59 }; 60 glGenBuffers(1, &m_quadVbo); 61 glBindBuffer(GL_ARRAY_BUFFER, m_quadVbo); 62 glBufferData(GL_ARRAY_BUFFER, sizeof(quadData), quadData, GL_STATIC_DRAW); 63 glEnableClientState(GL_VERTEX_ARRAY); 64 glVertexPointer(2, GL_FLOAT, kVertexSize, reinterpret_cast<const void*>(0)); // 位置座標属性を設定 65 glEnableClientState(GL_TEXTURE_COORD_ARRAY); 66 glTexCoordPointer(2, GL_FLOAT, kVertexSize, reinterpret_cast<const void*>(2 * sizeof(GLfloat))); // テクスチャ座標属性を設定 67 // リファレンスによると、VBOがバインドされている場合は、これら属性設定関数の最後の引数はVBO先頭からのオフセットと解釈されるらしい 68 CheckGLErrors(); 69 70 // テクスチャを作成、メモリ確保は行うがデータ投入は不要 71 glGenTextures(1, &m_prevFrameTex); 72 glBindTexture(GL_TEXTURE_RECTANGLE, m_prevFrameTex); 73 glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGB8, kBufferSize, kBufferSize, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); 74 // glUniform1i(m_texLoc, 0); // texが参照するテクスチャユニットをGL_TEXTURE0...つまりm_prevFrameTexに設定 75 // texはデフォルトで0になっているはずなので、今回のケースでは上記設定は必須ではない 76 CheckGLErrors(); 77 78 // その他諸々の最初に一度だけ設定すれば十分な事項を設定 79 glViewport(0, 0, kBufferSize, kBufferSize); 80 glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 81 // 画面をクリア、その状態で描画結果をテクスチャにコピーし、テクスチャをまっさらにする 82 glClear(GL_COLOR_BUFFER_BIT); 83 glCopyTexSubImage2D(GL_TEXTURE_RECTANGLE, 0, 0, 0, 0, 0, kBufferSize, kBufferSize); 84 CheckGLErrors(); 85} 86 87void Renderer::Render() { 88 // m_fftBufferを更新、rawFftにデータを投入 89 this->UpdateFftBuffer(); 90 glUniform1fv(m_rawFftLoc, kBufferSize, m_fftBuffer); 91 92 // 領域全体を覆う四角形を描画 93 // glClear(GL_COLOR_BUFFER_BIT); // 画面全体が描き換わるので、画面クリアは必須ではない 94 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 95 96 // 描画結果をバインド中のテクスチャ...つまりm_prevFrameTexにコピー 97 glCopyTexSubImage2D(GL_TEXTURE_RECTANGLE, 0, 0, 0, 0, 0, kBufferSize, kBufferSize); 98} 99 100void Renderer::UpdateFftBuffer() { 101 // m_fftBufferをダミーのデータで満たす 102 // とりあえず時間とともに位相が回るサイン波を用意 103 // 描画結果として斜めの縞模様が描かれることを期待する 104 double phase = (m_angle += kAngularSpeed); 105 float* fftElement = m_fftBuffer; 106 for (int i = 0; i < kBufferSize; i++) { 107 fftElement[i] = static_cast<float>(sin(phase + (2.0 * kPi * i * kWaveScale) / kBufferSize) * 0.5 + 0.5); 108 } 109} 110 111void Renderer::CheckGLErrors() { 112 GLenum error = glGetError(); 113 if (error != GL_NO_ERROR) { 114 std::cerr << gluErrorString(error) << std::endl; 115 } 116} 117 118const GLchar* Renderer::GetShaderSource(WORD resourceId) { 119 return static_cast<const GLchar*>(LockResource(LoadResource(NULL, FindResourceA(NULL, MAKEINTRESOURCEA(resourceId), "GLSL")))); 120}
Shaderヘッダ
C++
1#pragma once 2#include <gl/glew.h> 3#include <GLFW/glfw3.h> 4 5class Shader { 6private: 7 GLuint m_programName; // シェーダープログラム名 8public: 9 Shader(const GLchar* vertSource, const GLchar* fragSource); 10 ~Shader(); 11 12 void Use(); // このシェーダープログラムを使用する 13 GLuint GetUniformLoc(const GLchar* name); // uniform変数名からロケーションを得る 14};
C++を使用してみたものの、不慣れでしてコード的にいまいちな部分が多いかと思います。あくまでも描画プロセスの参考程度と言うことでお願いします...
投稿2018/04/22 22:19
総合スコア10811
0
動作実験はしていないのですが、GLSL 1.20なら頂点データの受け取りはattribute変数、バーテックス・フラグメントシェーダー間の値の伝達はvarying変数を使った記法となるのではないでしょうか?
GLSL 1.50版では頂点データ供給にVAOやVBOを使用し、MVP行列も自前で計算してuniform変数に投入していたかと思いますが、これらはGLSL 1.50版と同じままにして、シェーダーコードを下記のようにしてみるとどうでしょうか。
バーテックスシェーダー
GLSL
1#version 120 2#extension GL_ARB_texture_rectangle : enable 3 4uniform mat4 modelViewProjectionMatrix; 5 6attribute vec4 position; 7attribute vec2 texcoord; 8 9varying vec2 vTexCoord; 10 11void main(){ 12 vTexCoord = texcoord; 13 gl_Position = modelViewProjectionMatrix * position; 14}
フラグメントシェーダー
GLSL
1#version 120 2#extension GL_ARB_texture_rectangle : enable 3 4uniform sampler2DRect tex; 5uniform float rawFft[256]; 6 7varying vec2 vTexCoord; 8 9void main(){ 10 vec4 outputColor; 11 12 if (vTexCoord.y < 1.0) { 13 int i = int(vTexCoord.x); 14 outputColor.r = rawFft[i]; 15 } else { 16 vec2 st = vTexCoord.st; 17 st.y -= 1.0; 18 outputColor.r = texture2DRect(tex, st).r; 19 } 20 outputColor.a = 1.0; 21 22 gl_FragColor = outputColor; 23}
参考: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/20 21:32
編集2018/04/20 23:45総合スコア10811
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/04/21 03:58
2018/04/21 04:57
2018/04/21 07:19
2018/04/21 07:27
2018/04/21 07:34
2018/04/22 22:24
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。