[OpenGL]
以下をざっと読んでLWJGLに適用する
- GLUとはOpenGLを補助するライブラリで、gluLookAtやgluPerspectiveなどがあります。
- それぞれ視点などを設定します。
- 結論から言いますと、GLUは現在でも使えないことは無いと思いますが、基本的により高機能なGLMを使います。
- 後で詳しく説明しますが、gluLookAtやgluPerspectiveはgl_ModelViewProjectionMatrixという組み込みの変数に値を設定しています。
- しかし、現在ではglm::LookAtやglm::Perspective等によってC++側でModelViewProjection行列を作成し、シェーダー側にuniform変数として送るのが主流のようです。
- この記事と同じことをClojureでやる
- GLEWは使わない
- GL/createCapabilities を呼び出す必要がある
+
|
スクリーンショット sample.glfw.png
|
|
|
|
+
|
ソースコード src/main/clojure/sample/glfw.clj
|
|
|
(ns sample.glfw
(:import
[org.lwjgl.opengl GL GL11]
[org.lwjgl.glfw GLFW]))
(defn -main []
;; GLFW初期化
(GLFW/glfwInit)
;; ウィンドウ生成
(let [window (GLFW/glfwCreateWindow 640 480 "OpenGL Simple" 0 0)]
(if-not window
;; 生成失敗
(GLFW/glfwTerminate)
;; 生成OK
(do
(GLFW/glfwWindowHint GLFW/GLFW_CONTEXT_VERSION_MAJOR 2)
(GLFW/glfwWindowHint GLFW/GLFW_CONTEXT_VERSION_MINOR 1)
;; コンテキストの作成
(GLFW/glfwMakeContextCurrent window)
(GLFW/glfwSwapInterval 1)
(GL/createCapabilities) ;; 追加
;; フレームループ
(while (not (GLFW/glfwWindowShouldClose window))
;; バッファのクリア
(GL11/glClearColor 0.2 0.2 0.2 0.0)
(GL11/glClear GL11/GL_COLOR_BUFFER_BIT)
;; ダブルバッファのスワップ
(GLFW/glfwSwapBuffers window)
(GLFW/glfwPollEvents)
))))
;; GLFWの終了処理
(GLFW/glfwTerminate))
$ lein compile && lein run -m sample.glfw
|
OpenGLが世界を描画するにあたって、描画までの一通りの流れをパイプラインといいます。
下の画像にはその流れをざっくりと示しています。
その中でも大切なのがバーテックスシェーダとフラグメントシェーダです。
これらは私たちプログラマーがGLSLという言語を使って記述していきます。
ただ実はglBeginやglEndはOpenGL 3.0で廃止予定、OpenGL 3.1で廃止されています。
じゃあなんで使ったのかというと、説明するのに便利だったからです。
OpenGLは現在VBOという仕組みを用いて描画を行うのが主流
+
|
スクリーンショット sample.old_gl.png
|
|
|
|
+
|
ソースコード src/main/clojure/sample/old_gl.clj
|
|
|
(ns sample.old-gl
(:import
[org.lwjgl.opengl GL GL11]
[org.lwjgl.glfw GLFW]))
(defn -main []
;; GLFW初期化
(GLFW/glfwInit)
;; ウィンドウ生成
(let [window (GLFW/glfwCreateWindow 640 480 "OpenGL Simple" 0 0)]
(if-not window
;; 生成失敗
(GLFW/glfwTerminate)
;; 生成OK
(do
(GLFW/glfwWindowHint GLFW/GLFW_CONTEXT_VERSION_MAJOR 2)
(GLFW/glfwWindowHint GLFW/GLFW_CONTEXT_VERSION_MINOR 1)
;; コンテキストの作成
(GLFW/glfwMakeContextCurrent window)
(GLFW/glfwSwapInterval 1)
(GL/createCapabilities) ;; 追加
;; フレームループ
(while (not (GLFW/glfwWindowShouldClose window))
;; バッファのクリア
(GL11/glClearColor 0.2 0.2 0.2 0.0)
(GL11/glClear GL11/GL_COLOR_BUFFER_BIT)
;; 色指定
(GL11/glColor4f 1.0 0.0 0.0 1.0)
;; 3つの頂点座標をGPUに転送
(GL11/glBegin GL11/GL_TRIANGLES)
(do
(GL11/glVertex2f 0, 0.5)
(GL11/glVertex2f -0.5, -0.5)
(GL11/glVertex2f 0.5, -0.5))
(GL11/glEnd)
;; ダブルバッファのスワップ
(GLFW/glfwSwapBuffers window)
(GLFW/glfwPollEvents)
))))
;; GLFWの終了処理
(GLFW/glfwTerminate))
$ lein compile && lein run -m sample.old-gl
|
GLSLはC言語を拡張した言語でGPU内での処理を記述していきます。
拡張子についてはシェーダーだと分かれば基本何でもいいです。
「.vert/.frag」としている人が多いですが、「.vertexshader/.fragmentshader」としている人もいたりします。
glCreateShader()でシェーダーオブジェクトを作成
作成したシェーダーオブジェクトにglShaderSource()でソースコードを読み込む
glCompileShaderでコンパイルします。
glCreateProgram()でプログラムオブジェクトを作成
glAttachShader()でシェーダーオブジェクトをシェーダープログラムへ登録
glDeleteShader()でシェーダーオブジェクトの削除
glLinkProgram()シェーダープログラムをリンク
glUseProgram()でシェーダーを有効化します
+
|
スクリーンショット sample.new_gl.png
|
|
|
|
+
|
ソースコード src/main/resources/shader.vert
|
|
|
#version 120
//
// shader.vert
//
void main(void)
{
gl_Position = gl_Vertex + vec4(0.0, 0.4 ,0.0, 0.0);
gl_FrontColor = gl_Color;
}
|
+
|
ソースコード src/main/resources/shader.frag
|
|
|
#version 120
//
// shader.frag
//
void main(void)
{
gl_FragColor = gl_Color + vec4(0.0, 0.0, 0.6, 0.0);
}
|
- 続けて、シェーダーを読み込んでオブジェクトにするソースをClojureで用意する
+
|
ソースコード src/main/clojure/sample/shader.clj
|
|
|
(ns sample.shader
(:require
[clojure.java.io :as io])
(:import
[org.lwjgl.opengl GL20])
(:use
[clojure.tools.logging]))
(defn shader-file-abs-path [^String file-name]
(let [shader-path (io/resource file-name)]
(debug (str "filepath: " shader-path))
shader-path))
(defn read-shader-src [^Integer shader-obj ^String file-name]
(let [shader-src (slurp (shader-file-abs-path file-name))]
(debug (str "shader: " shader-src))
(GL20/glShaderSource shader-obj shader-src)))
(defn make-shader [^String vertex-file-name ^String fragment-file-name]
;; シェーダーオブジェクト作成
(let [vert-shader-obj (GL20/glCreateShader GL20/GL_VERTEX_SHADER)
frag-shader-obj (GL20/glCreateShader GL20/GL_FRAGMENT_SHADER)]
;; シェーダーのソースプログラムの読み込み
(read-shader-src vert-shader-obj vertex-file-name)
(read-shader-src frag-shader-obj fragment-file-name)
;; バーテックスシェーダーのソースプログラムのコンパイル
(GL20/glCompileShader vert-shader-obj)
;; フラグメントシェーダーのソースプログラムのコンパイル
(GL20/glCompileShader frag-shader-obj)
;; プログラムオブジェクトの作成
(let [shader (GL20/glCreateProgram)]
;; シェーダーオブジェクトのシェーダープログラムへの登録
(GL20/glAttachShader shader vert-shader-obj)
(GL20/glAttachShader shader frag-shader-obj)
;; シェーダーオブジェクトの削除
(GL20/glDeleteShader vert-shader-obj)
(GL20/glDeleteShader frag-shader-obj)
;; シェーダープログラムのリンク
(GL20/glLinkProgram shader)
shader)))
|
+
|
ソースコード src/main/clojure/sample/new_gl.clj
|
|
|
(ns sample.new-gl
(:require
[sample.shader :as shader])
(:import
[org.lwjgl.opengl GL GL11 GL20]
[org.lwjgl.glfw GLFW]))
(defn -main []
;; GLFW初期化
(GLFW/glfwInit)
;; ウィンドウ生成
(let [window (GLFW/glfwCreateWindow 640 480 "OpenGL Simple" 0 0)]
(if-not window
;; 生成失敗
(GLFW/glfwTerminate)
;; 生成OK
(do
(GLFW/glfwWindowHint GLFW/GLFW_CONTEXT_VERSION_MAJOR 2)
(GLFW/glfwWindowHint GLFW/GLFW_CONTEXT_VERSION_MINOR 1)
;; コンテキストの作成
(GLFW/glfwMakeContextCurrent window)
(GLFW/glfwSwapInterval 1)
(GL/createCapabilities) ;; 追加
(let [shader (shader/make-shader "shader.vert" "shader.frag")]
;; フレームループ
(while (not (GLFW/glfwWindowShouldClose window))
;; バッファのクリア
(GL11/glClearColor 0.2 0.2 0.2 0.0)
(GL11/glClear GL11/GL_COLOR_BUFFER_BIT)
(GL20/glUseProgram shader)
;; 色指定
(GL11/glColor4f 1.0 0.0 0.0 1.0)
;; 3つの頂点座標をGPUに転送
(GL11/glBegin GL11/GL_TRIANGLES)
(do
(GL11/glVertex2f 0, 0.5)
(GL11/glVertex2f -0.5, -0.5)
(GL11/glVertex2f 0.5, -0.5))
(GL11/glEnd)
;; ダブルバッファのスワップ
(GLFW/glfwSwapBuffers window)
(GLFW/glfwPollEvents)
)))))
;; GLFWの終了処理
(GLFW/glfwTerminate))
|