本格的な矢印はポリゴン数も計算も大変なので簡易的な矢印を実装する。
なおそろそろ拡張子.hppを割と意識して使うことにする
#pragma once #include <Windows.h> #include <gl/GL.h> namespace numath { typedef double real_t; //! @brief ラジアンを度に変換 //! @param [in] radian ラジアンの値 //! @return 度の値 inline real_t toDegree(const real_t radian) { return radian * 180 / 3.1415926535897932384626; } //! @brief 三次元のベクトルを正規化する //! @param [in,out] pV 三次元ベクトル1 要素数3 //! @retval true 成功 //! @retval false 失敗 inline bool normalize(real_t* const pV) { const int X = 0; const int Y = 1; const int Z = 2; real_t len; real_t& x = pV[X]; real_t& y = pV[Y]; real_t& z = pV[Z]; len = static_cast<real_t>(sqrt(x * x + y * y + z * z)); if (len < static_cast<real_t>(1e-6)) return false; len = static_cast<real_t>(1.0) / len; x *= len; y *= len; z *= len; return true; } //! @brief ベクトルの長さを求める //! @param [in] vec 三次元ベクトル1 要素数3 //! @return vecの長さを表すスカラー値 inline real_t length(real_t const* const vec) { const int X = 0; const int Y = 1; const int Z = 2; return sqrt(vec[X] * vec[X] + vec[Y] * vec[Y] + vec[Z] * vec[Z]); } //! @brief 二つの三次元ベクトルの内積を求める //! @param [in] vec1 三次元ベクトル1 要素数3 //! @param [in] vec1 三次元ベクトル2 要素数3 //! @return ベクトルの内積 inline real_t inner(real_t const* const vec1, real_t const* const vec2) { const int X = 0; const int Y = 1; const int Z = 2; return ((vec1[X]) * (vec2[X]) + (vec1[Y]) * (vec2[Y]) + (vec1[Z]) * (vec2[Z])); } //! @brief 二つの三次元ベクトルの角度を求める //! @param [in] vec1 三次元ベクトル1 要素数3 //! @param [in] vec1 三次元ベクトル2 要素数3 //! @return ベクトルの角度(ラジアン) inline real_t vectorAngle(real_t const* const vec1, real_t const* const vec2) { real_t x = inner(vec1, vec2) / (length(vec1) * length(vec2)); if (x <= -1) x = -0.9999999; if (x >= 1) x = 0.99999999; return acos(x); } //! @brief ベクトルにスカラー値をかける //! @param [in,out] 対象の三次元ベクトル1 要素数3 //! @param scalar スカラー値 //! @return なし inline void vectorMult3(real_t * dst, const real_t scalar) { dst[0] *= scalar; dst[1] *= scalar; dst[2] *= scalar; } //! @brief 始点終点の線分をベクトルに変換 //! @param [out] vec3 結果を格納する三次元ベクトル 要素数3 //! @param [out] from 始点を表す三次元ベクトル 要素数3 //! @param [out] to 終点三次元ベクトル 要素数3 //! @return vec3のポインタ inline real_t* lineToVector3(real_t * const vec3, const real_t * const from, const real_t * const to) { vec3[0] = to[0] - from[0]; vec3[1] = to[1] - from[1]; vec3[2] = to[2] - from[2]; return vec3; } //! @brief 与えられた二つのベクトルの外積を求める //! @param [out] dvec 結果を格納する三次元ベクトル 要素数3 //! @param [in] svec 三次元ベクトル1 要素数3 //! @param [in] svec 三次元ベクトル2 要素数3 //! @return なし inline void outer(real_t * const dvec, real_t const* const svec1, real_t const* const svec2) { const int X = 0; const int Y = 1; const int Z = 2; const real_t& x1 = svec1[X]; const real_t& y1 = svec1[Y]; const real_t& z1 = svec1[Z]; const real_t& x2 = svec2[X]; const real_t& y2 = svec2[Y]; const real_t& z2 = svec2[Z]; dvec[X] = static_cast<real_t>(y1 * z2 - z1 * y2); dvec[Y] = static_cast<real_t>(z1 * x2 - x1 * z2); dvec[Z] = static_cast<real_t>(x1 * y2 - y1 * x2); } //! @brief 与えられたベクトルと垂直なベクトルを計算する //! @param dst13 結果を表す三次元ベクトル 要素数3(一つ目 必須) //! @param dst23 結果を表す三次元ベクトル 要素数3(二つ目 いらないならnullptrを指定) //! @param src3 入力する三次元ベクトル 要素数3 //! @return なし inline void rightAngleVector(real_t * const dst13, real_t * const dst23, const real_t * const src3) { real_t tmp[3] = { 1,0,0 }; //tmpとsrc3の角度0またはそれに限りなく近いなら、別なベクトルを用意 if (toDegree(vectorAngle(tmp, src3)) < 0.1) { tmp[0] = 0; tmp[1] = 1; tmp[2] = 0; } //外積を求める outer(dst13, tmp, src3); if (dst23 != nullptr) { outer(dst23, src3, dst13); } } } //! @brief 矢印を描画 //! @param [in] 矢印の始点 //! @param [out] 矢印の終点 //! @return なし inline void drawArrow(const double* from3, const double* to3) { glBegin(GL_LINES); glVertex3dv(from3); glVertex3dv(to3); glEnd(); numath::real_t v[3]; numath::lineToVector3(v, from3, to3); double len = numath::length(v); numath::real_t p3[3] = { from3[0], from3[1], from3[2] }; numath::vectorMult3(v, 0.8); numath::real_t v1[3], v2[3]; numath::rightAngleVector(v1, v2, v); numath::normalize(v1); numath::normalize(v2); numath::vectorMult3(v1, len * 0.03); numath::vectorMult3(v2, len * 0.03); const double* f = from3; const double* t = to3; glBegin(GL_LINE_STRIP); glVertex3d(f[0] + v[0], f[1] + v[1], f[2] + v[2]); glVertex3d(f[0] + v[0] + v1[0], f[1] + v[1] + v1[1], f[2] + v[2] + v1[2]); glVertex3d(t[0], t[1], t[2]); glEnd(); glBegin(GL_LINE_STRIP); glVertex3d(f[0] + v[0], f[1] + v[1], f[2] + v[2]); glVertex3d(f[0] + v[0] + v2[0], f[1] + v[1] + v2[1], f[2] + v[2] + v2[2]); glVertex3d(t[0], t[1], t[2]); glEnd(); glBegin(GL_LINE_STRIP); glVertex3d(f[0] + v[0], f[1] + v[1], f[2] + v[2]); glVertex3d(f[0] + v[0] - v1[0], f[1] + v[1] - v1[1], f[2] + v[2] - v1[2]); glVertex3d(t[0], t[1], t[2]); glEnd(); glBegin(GL_LINE_STRIP); glVertex3d(f[0] + v[0], f[1] + v[1], f[2] + v[2]); glVertex3d(f[0] + v[0] - v2[0], f[1] + v[1] - v2[1], f[2] + v[2] - v2[2]); glVertex3d(t[0], t[1], t[2]); glEnd(); }
#include <iostream> #include <Windows.h> #include <gl/GL.h> #include <gl/GLU.h> #include <gl/freeglut.h> // freeglut: // http://freeglut.sourceforge.net/ //矢印描画関数 #include "drawarrow.hpp" //ウィンドウの幅と高さ int width, height; double rotate_angle=0; //描画関数 void disp(void) { glClearColor(0.2, 0.2, 0.2, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glViewport(0, 0, width, height); //カメラの設定 glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60, width / (double)height, 0.1, 3); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslated(0, 0, -2); //表示用の回転 glRotated(rotate_angle, 0, 1, 0); glRotated(rotate_angle, 1, 0, 0); //矢印を三つ描画 glLineWidth(2); double from[3] = { 0,0,0 }; double tox[3] = { 1,0,0 }; double toy[3] = { 0,1,0 }; double toz[3] = { 0,0,1 }; glColor3d(1, 0, 0); drawArrow(from, tox); glColor3d(0, 1, 0); drawArrow(from, toy); glColor3d(0, 0, 1); drawArrow(from, toz); glLineWidth(1); double v = 0.2; glBegin(GL_QUADS); glColor3d(0, 0, 1); glVertex2d(-v, -v); glColor3d(1, 0, 1); glVertex2d(v, -v); glColor3d(1, 1, 1); glVertex2d(v, v); glColor3d(0, 1, 1); glVertex2d(-v, v); glEnd(); glFlush(); } //ウィンドウサイズの変化時に呼び出される void reshape(int w, int h) { width = w; height = h; disp(); } void timer(int value) { rotate_angle += 5; disp(); glutTimerFunc(100, timer, 0); } //エントリポイント int main(int argc, char** argv) { glutInit(&argc, argv); glutInitWindowPosition(100, 50); glutInitWindowSize(500, 500); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA); glutCreateWindow("sample"); glutDisplayFunc(disp); glutReshapeFunc(reshape); glutTimerFunc(1000, timer, 0);//タイマー glutMainLoop(); return 0; }
現在管理人はゼノブレイド2をやっていてブログを書く暇がガチで無い(←)。
いい機会なので暫く小物をアップロードしてお茶を濁す。正直どこかに置いておかないと無くしてしまうのでGitHubにでも捨てておきたいのだが色々と思うところがあってできないでいる。
以下はOpenGLの基本のプログラム。いつも某所からコピペをしていた。
#include <iostream> #include <Windows.h> #include <gl/GL.h> #include <gl/GLU.h> #include <gl/freeglut.h> // freeglut: // http://freeglut.sourceforge.net/ //ウィンドウの幅と高さ int width, height; //描画関数 void disp(void) { glViewport(0, 0, width, height); glClearColor(0.2, 0.2, 0.2,1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_CULL_FACE); double v = 0.7; glBegin(GL_QUADS); glColor3d(0, 0, 1); glVertex2d(-v, -v); glColor3d(1, 0, 1); glVertex2d(v, -v); glColor3d(1, 1, 1); glVertex2d(v, v); glColor3d(0, 1, 1); glVertex2d(-v, v); glEnd(); glFlush(); } //ウィンドウサイズの変化時に呼び出される void reshape(int w, int h) { width = w; height = h; disp(); } //エントリポイント int main(int argc, char** argv) { glutInit(&argc, argv); glutInitWindowPosition(100, 50); glutInitWindowSize(500, 500); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA); glutCreateWindow("sample"); glutDisplayFunc(disp); glutReshapeFunc(reshape); glutMainLoop(); return 0; }
#include <iostream> #include <Windows.h> #include <gl/GL.h> #include <gl/GLU.h> #include <gl/freeglut.h> // freeglut: // http://freeglut.sourceforge.net/ //ウィンドウの幅と高さ int width, height; double rotate_angle=0; //描画関数 void disp(void) { glClearColor(0.2, 0.2, 0.2, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport(0, 0, width, height); //カメラの設定 glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60, width / (double)height, 0.1, 3); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslated(0, 0, -2); glRotated(rotate_angle, 0, 1, 0); //回転するならカリングはOffにしたほうがいい。 //glEnable(GL_CULL_FACE); double v = 0.7; glBegin(GL_QUADS); glColor3d(0, 0, 1); glVertex2d(-v, -v); glColor3d(1, 0, 1); glVertex2d(v, -v); glColor3d(1, 1, 1); glVertex2d(v, v); glColor3d(0, 1, 1); glVertex2d(-v, v); glEnd(); glFlush(); } //ウィンドウサイズの変化時に呼び出される void reshape(int w, int h) { width = w; height = h; disp(); } void timer(int value) { rotate_angle += 5; disp(); glutTimerFunc(10, timer, 0); } //エントリポイント int main(int argc, char** argv) { glutInit(&argc, argv); glutInitWindowPosition(100, 50); glutInitWindowSize(500, 500); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA); glutCreateWindow("sample"); glutDisplayFunc(disp); glutReshapeFunc(reshape); glutTimerFunc(1000, timer, 0);//タイマー glutMainLoop(); return 0; }
次回はもう少しオリジナリティあるのものを載せる
12:00~
1.Object Modeへ行き、新たなParticleを追加する。
2.Particleを以下のように設定
この時、元のFoliage01_colオブジェクトを選択し[H]キーでHiddenにしておく。
3.Weight Paintする。
この時、ペンの種類を選ぶ場所が見当たらない時は、[View]→[Tool Settings]にチェックを入れる
青の部分にはParticleが生じないので、緑~赤の領域が全体を占めるように塗る
4.Object Modeへ戻り、Particleの設定から、Vertex Group→Densityに、今塗りつぶした"Group"を選択する
5.Particleのその他の設定を以下のようにする
最後に、hairの数を5000にする
6.Viewport ShaderをRenderedに設定する
7.Renderを以下のように設定。動画ではSubsurface Scatteringにチェックを付けることになっている。自分の使っているバージョンにはそのチェックがない。
8.Materialへ行き、Materialを一つ追加する。BaseColorを濃い茶色に設定する。
9.Sunを夕暮れ時を想定して急な角度に配置し、色を赤みがける
9.その他、Shadowの設定を以下のように行う
10.cloud_layers_4K.hdrを以下からダウンロードする
https://hdrihaven.com/hdri/?h=cloud_layers
11.World Settingsへ行き、Color→Environment Textureを選択し、上記hdrファイルを設定する。
12.[Shift+A]→[Mesh]→CubeでCubeを配置し、[S][Z]で縦に伸ばす
影を付け、カメラを適切な場所に設置すると木が近くにあるように見える
RenderのLookをHigh Contrastにすると結果が変わる。
08:44
1.[Tab]でEditモードへ行く
2.[Ctrl+R]でループカットを開始
3.ホイールを回し、草のポリゴンを7等分する
4.エッジ選択モードにする
5.Proportional EditingをSharpにする
6.ポリゴンの一番上のエッジを選択し、[G]で曲げる
7.Smooth Shadingに切り替える
[F3]で検索窓でShade Smoothを検索する
※あるいは、Object→Shade Smoothを選択する
09:24~
8.Editモードで[Shift+D]でDuplicateし、すぐ隣に移動する
9.再び画面を二分割し、左側をUV Imageにする
10.[D]でUVを移動し、別の葉に移動する
11.回転・拡大縮小を加え、同様のものを複数設置し、草の株を作る
12.複製などを利用すると比較的早くできる。最後に、originが草の根元にあることを確認する
11:23~
13.[Shift+A]→[Mesh]→Plane で新たなPlaneを追加し、[S]で約6弱の大きさに広げる
14.Sculpt Modeへ移行する
16.DyntopoをOnにする。(チェックを入れると警告が出るので"OK"を押す)
Sculptで地面を盛り上げる
区切りがいいので次は次回
草を作るチュートリアル約17分。Particleはたくさん生やすために使うけれど基本的にローポリ+テクスチャのやつです。
1:20~
Edit→Preferences...で、Node WranglerとImages As Planesをそれぞれ追加する
https://www.cc0textures.com/view.php?tex=Foliage01
から、2Kの画像をダウンロードする。
File → Import→Images As Planes で、Foliage01_col.jpgを開く
Planeが生成されるので、[Num 1]→[Num 3]でPlaneを正面から表示し、3D ViewをRenderedモードにすると、テクスチャが既に貼り付けられているのがわかる
・[Tab]でエディットモードへ行く
・[Ctrl+R]でLoop Cutを繰り返し、不要部分をメッシュで区切る
・面選択に変更し、[Shift+左クリック]で不要部分を選択し、[X]で削除する
Editモードのまま、[a]で全頂点選択、[G]で移動し、originが草の根元になるように頂点を移動する
[a]で全選択・解除ができないときは、Edit→Preferences...→Keymap→Select All TogglesのチェックをOnにする
画面を二つに分割し、左側をShader Editorにする
ライトを以下のように設定。動画ではEnergyになっているが該当項目は恐らくStrenghtで問題ない。また2.8のバージョン次第だと思うが著者の環境ではStrengthのデフォルト値が1000だった。
以下のようにノードを設定する
gluPerspectiveのソースコードをhttps://www.khronos.org/opengl/wiki/GluPerspective_codeからコピペ移植して透視投影変換をします。
<!DOCTYPE html> <html> <head> <title>四角形描画</title> <style> </style> </head> <body> <!-- HTML5のキャンバス作成 --> <canvas id="MyCanvas" width="400" height="400"></canvas> <!-- シェーダ --><!-- シェーダはGLSL (OpenGL Shading Language) で書く。つまり、ここはHTMLでもJavaScriptでもない --> <!-- scriptタグのid「vshader」を識別子として、コンパイル時にgetElementByIDを使って文字列としてこのシェーダプログラムを参照する --> <!-- ************************************** --> <!-- ** 頂点シェーダ*********************** --> <!-- ************************************** --> <script id="vshader" type="x-shader/x-vertex"> attribute vec3 position; attribute vec3 color; uniform mat4 mmodel;//モデルビュー行列 uniform mat4 mperspective;//透視投影変換行列 varying lowp vec3 vColor; void main(void){ gl_Position = mperspective * mmodel * vec4(position, 1.0); vColor = color; } </script> <!-- ここもGLSLで書く。 --> <!-- 上と同様、コンパイル時にscriptタグのid「fshader」で参照する --> <!-- ************************************** --> <!-- ** フラグメントシェーダ*************** --> <!-- ************************************** --> <script id="fshader" type="x-shader/x-fragment"> precision mediump float;//これがないとコンパイルエラーになる varying lowp vec3 vColor; void main(void){ gl_FragColor = vec4(vColor, 1.0); } </script>
<!-- シェーダのコンパイル。ここはJavaScriptで書く --> <!-- ************************************** --> <!-- ** シェーダコンパイルとリンク********* --> <!-- ************************************** --> <script> var programID;// グローバル変数としてプログラム名の格納先を定義する function prepare_shaders(gl){ /////////////////////////////// /////////////////////////////// // 頂点シェーダコンパイル var vs = document.getElementById ("vshader"); var vertexShader = gl.createShader (gl.VERTEX_SHADER); gl.shaderSource (vertexShader, vs.text);//シェーダのプログラムをテキストで取得 gl.compileShader (vertexShader); if ( !gl.getShaderParameter (vertexShader, gl.COMPILE_STATUS) ){ throw new Error (gl.getShaderInfoLog(vertexShader)); } /////////////////////////////// /////////////////////////////// // フラグメントシェーダコンパイル var fs = document.getElementById ("fshader"); var fragmentShader = gl.createShader (gl.FRAGMENT_SHADER); gl.shaderSource (fragmentShader, fs.text);//シェーダのプログラムをテキストで取得 gl.compileShader (fragmentShader); if ( !gl.getShaderParameter (fragmentShader, gl.COMPILE_STATUS) ){ throw new Error (gl.getShaderInfoLog(fragmentShader)); } /////////////////////////////// /////////////////////////////// // プログラムのリンク programID = gl.createProgram (); gl.attachShader (programID, vertexShader); gl.attachShader (programID, fragmentShader); gl.linkProgram (programID); if ( !gl.getProgramParameter(programID, gl.LINK_STATUS) ) { throw new Error (gl.getProgramInfoLog(programID)); } } </script><!-- 回転行列 --><!-- データの準備。ここもJavaScriptで書く --> <!-- ************************************** --> <!-- ** 三角形の準備********* --> <!-- ************************************** --> <!-- ************************************** --> <script> //各変数をグローバル変数として定義 var vertexesVboID; var colorsVboID; function prepare_data(gl){ //頂点バッファ作成 var vertexes = [ -0.7, 0.7, 0.0, 0.7, 0.7, 0.0, -0.7, -0.7, 0.0, 0.7, -0.7, 0.0 ]; vertexesVboID = gl.createBuffer (); gl.bindBuffer (gl.ARRAY_BUFFER, vertexesVboID); gl.bufferData (gl.ARRAY_BUFFER, new Float32Array (vertexes), gl.STATIC_DRAW); //カラーバッファ作成 var colors = [ 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 ]; colorsVboID = gl.createBuffer (); gl.bindBuffer (gl.ARRAY_BUFFER, colorsVboID); gl.bufferData (gl.ARRAY_BUFFER, new Float32Array (colors), gl.STATIC_DRAW); } </script><!-- ************************************** --> <!-- ** アフィン変換行列を作る関数 ******** --> <!-- ************************************** --> <script> function rotate_z16(rad){ var m = new Float32Array(16); m[ 0] = Math.cos(rad); m[ 1] = Math.sin(rad); m[ 2] = 0; m[ 3] = 0; m[ 4] = -Math.sin(rad); m[ 5] = Math.cos(rad); m[ 6] = 0; m[ 7] = 0; m[ 8] = 0; m[ 9] = 0; m[10] = 1; m[11] = 0; m[12] = 0; m[13] = 0; m[14] = 0; m[15] = 1; return m; } function rotate_x16(rad){ var m = new Float32Array(16); m[ 0] = 1; m[ 1] = 0; m[ 2] = 0; m[ 3] = 0; m[ 4] = 0; m[ 5] = Math.cos(rad); m[ 6] = Math.sin(rad); m[ 7] = 0; m[ 8] = 0; m[ 9] = -Math.sin(rad); m[10] = Math.cos(rad); m[11] = 0; m[12] = 0; m[13] = 0; m[14] = 0; m[15] = 1; return m; } </script><!-- 透視投影変換行列 --> <!-- https://www.khronos.org/opengl/wiki/GluPerspective_code --> <script> function glhFrustumf2( matrix, left, right, bottom, top, znear, zfar ){ var temp, temp2, temp3, temp4; temp = 2.0 * znear; temp2 = right - left; temp3 = top - bottom; temp4 = zfar - znear; matrix[0] = temp / temp2; matrix[1] = 0.0; matrix[2] = 0.0; matrix[3] = 0.0; matrix[4] = 0.0; matrix[5] = temp / temp3; matrix[6] = 0.0; matrix[7] = 0.0; matrix[8] = (right + left) / temp2; matrix[9] = (top + bottom) / temp3; matrix[10] = (-zfar - znear) / temp4; matrix[11] = -1.0; matrix[12] = 0.0; matrix[13] = 0.0; matrix[14] = (-temp * zfar) / temp4; matrix[15] = 0.0; } function glhPerspectivef2( matrix, fovyInDegrees, aspectRatio, znear, zfar){ var ymax, xmax; var temp, temp2, temp3, temp4; ymax = znear * Math.tan(fovyInDegrees * 3.14159 / 360.0); xmax = ymax * aspectRatio; glhFrustumf2(matrix, -xmax, xmax, -ymax, ymax, znear, zfar); } </script><!-- やっと描画部分 --> <!-- ここもJavaScriptで書く --> <!-- ////////////////////////////////////// --> <!-- // 初回のみ実行 ////////////////////// --> <!-- ////////////////////////////////////// --> <!-- ////////////////////////////////////// --> <script> var mycanv; // 初回表示時に呼び出される window.onload = function(){ mycanv = document.getElementById("MyCanvas"); // クリックイベントを追加 今回はマウスを動かすと回転する mycanv.addEventListener('mousemove', onMouseMove, false); // GLコンテキストを取得 var gl = mycanv.getContext('webgl') || mycanv.getContext('experimental-webgl'); prepare_shaders(gl);//シェーダの準備 prepare_data(gl);//描画データの準備 //最初の描画 render(0); } </script><script> var radangle = 0; var posz = -3;///////////////////////////////////////////// //マウスイベント本体 function onMouseMove(e) { console.log("mousemove"); var x = e.clientX - mycanv.offsetLeft; var y = e.clientY - mycanv.offsetTop; console.log("x:", x, "y:", y,"radang:",radangle); radangle+=0.1; render(radangle); }//////////////////////////////////////////// //レンダリング関数 function render(angle){ // GLコンテキストを取得 var gl = mycanv.getContext('webgl') || mycanv.getContext('experimental-webgl'); // 画面を黒でクリア gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); gl.useProgram(programID); //行列の設定 var rotmat = rotate_x16(angle); rotmat[14] = posz;//モデルを奥に移動 var pspmat = new Float32Array(16); glhPerspectivef2(pspmat,45,1.0/1.0,0.1,50); var matmodel = gl.getUniformLocation(programID, 'mmodel'); var matperspective = gl.getUniformLocation(programID, 'mperspective'); gl.uniformMatrix4fv(matmodel, false, rotmat); gl.uniformMatrix4fv(matperspective, false, pspmat); //バッファへアクセス var vertex_location = gl.getAttribLocation (programID, 'position'); var color_location = gl.getAttribLocation (programID, 'color'); //頂点バッファを有効にする gl.enableVertexAttribArray (vertex_location); gl.bindBuffer (gl.ARRAY_BUFFER, vertexesVboID); gl.vertexAttribPointer (vertex_location, 3, gl.FLOAT,false, 0, 0); //カラーバッファを有効にする gl.enableVertexAttribArray (color_location); gl.bindBuffer (gl.ARRAY_BUFFER, colorsVboID); gl.vertexAttribPointer (color_location, 3, gl.FLOAT,false, 0, 0); //四角形を描く gl.drawArrays (gl.TRIANGLE_STRIP, 0, 4); //バッファを無効にする gl.disableVertexAttribArray(vertex_location); gl.disableVertexAttribArray(color_location); gl.flush (); }</script> </body> </html>
OpenGL知っている所からWebGL入門(1) – 画面クリア
OpenGL知っている所からWebGL入門(2) – 図形描画
OpenGL知っている所からWebGL入門(3) – 図形を回転
CanvasにaddEventListenerでOnClickを追加して、マウスがクリックされるたびに回転行列を更新します。
<!DOCTYPE html> <html> <head> <title>四角形描画</title> <style> </style> </head> <body> <!-- HTML5のキャンバス作成 --> <canvas id="MyCanvas" width="400" height="400"></canvas><!-- シェーダはGLSL (OpenGL Shading Language) で書く。つまり、ここはHTMLでもJavaScriptでもない --> <!-- scriptタグのid「vshader」を識別子として、コンパイル時にgetElementByIDを使って文字列としてこのシェーダプログラムを参照する --> <!-- ************************************** --> <!-- ** 頂点シェーダ*********************** --> <!-- ************************************** --> <script id="vshader" type="x-shader/x-vertex"> attribute vec3 position;//描画時にgetAttribLocationでアクセスするのでわかりやすい名前にしておくことが望ましい attribute vec3 color;//同上 uniform mat4 rot_matrix; varying lowp vec3 vColor; void main(void){ gl_Position = rot_matrix * vec4(position, 1.0); vColor = color; } </script> <!-- ここもGLSLで書く。 --> <!-- 上と同様、コンパイル時にscriptタグのid「fshader」で参照する --> <!-- ************************************** --> <!-- ** フラグメントシェーダ*************** --> <!-- ************************************** --> <script id="fshader" type="x-shader/x-fragment"> precision mediump float;//これがないとコンパイルエラーになる varying lowp vec3 vColor; void main(void){ gl_FragColor = vec4(vColor, 1.0); } </script><!-- シェーダのコンパイル。ここはJavaScriptで書く --> <!-- ************************************** --> <!-- ** シェーダコンパイルとリンク********* --> <!-- ************************************** --> <script> var programID;// グローバル変数としてプログラム名の格納先を定義する function prepare_shaders(gl){ /////////////////////////////// /////////////////////////////// // 頂点シェーダコンパイル var vs = document.getElementById ("vshader"); var vertexShader = gl.createShader (gl.VERTEX_SHADER); gl.shaderSource (vertexShader, vs.text);//シェーダのプログラムをテキストで取得 gl.compileShader (vertexShader); if ( !gl.getShaderParameter (vertexShader, gl.COMPILE_STATUS) ){ throw new Error (gl.getShaderInfoLog(vertexShader)); } /////////////////////////////// /////////////////////////////// // フラグメントシェーダコンパイル var fs = document.getElementById ("fshader"); var fragmentShader = gl.createShader (gl.FRAGMENT_SHADER); gl.shaderSource (fragmentShader, fs.text);//シェーダのプログラムをテキストで取得 gl.compileShader (fragmentShader); if ( !gl.getShaderParameter (fragmentShader, gl.COMPILE_STATUS) ){ throw new Error (gl.getShaderInfoLog(fragmentShader)); } /////////////////////////////// /////////////////////////////// // プログラムのリンク programID = gl.createProgram (); gl.attachShader (programID, vertexShader); gl.attachShader (programID, fragmentShader); gl.linkProgram (programID); if ( !gl.getProgramParameter(programID, gl.LINK_STATUS) ) { throw new Error (gl.getProgramInfoLog(programID)); } } </script><!-- データの準備。ここもJavaScriptで書く --> <!-- ************************************** --> <!-- ** 三角形の準備********* --> <!-- ************************************** --> <!-- ************************************** --> <script> //各変数をグローバル変数として定義 var vertexesVboID; var colorsVboID; function prepare_data(gl){ //頂点バッファ作成 var vertexes = [ -0.7, 0.7, 0.0, 0.7, 0.7, 0.0, -0.7, -0.7, 0.0, 0.7, -0.7, 0.0 ]; vertexesVboID = gl.createBuffer (); gl.bindBuffer (gl.ARRAY_BUFFER, vertexesVboID); gl.bufferData (gl.ARRAY_BUFFER, new Float32Array (vertexes), gl.STATIC_DRAW); //カラーバッファ作成 var colors = [ 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 ]; colorsVboID = gl.createBuffer (); gl.bindBuffer (gl.ARRAY_BUFFER, colorsVboID); gl.bufferData (gl.ARRAY_BUFFER, new Float32Array (colors), gl.STATIC_DRAW); } </script><!-- やっと描画部分 --> <!-- ここもJavaScriptで書く --> <!-- ////////////////////////////////////// --> <!-- // 初回のみ実行 ////////////////////// --> <!-- ////////////////////////////////////// --> <!-- ////////////////////////////////////// --> <script> var mycanv; // 初回表示時に呼び出される window.onload = function(){ mycanv = document.getElementById("MyCanvas"); // クリックイベントを追加 mycanv.addEventListener('click', onClick, false); // GLコンテキストを取得 var gl = mycanv.getContext('webgl') || mycanv.getContext('experimental-webgl'); prepare_shaders(gl);//シェーダの準備 prepare_data(gl);//描画データの準備 //最初の描画 render(0); } </script> <script><!-- ************************************** --> <!-- ** アフィン変換行列を作る関数 ******** --> <!-- ************************************** --> <script> function rotate_z16(rad){ var m = new Float32Array(16); m[ 0] = Math.cos(rad); m[ 1] = -Math.sin(rad); m[ 2] = 0; m[ 3] = 0; m[ 4] = Math.sin(rad); m[ 5] = Math.cos(rad); m[ 6] = 0; m[ 7] = 0; m[ 8] = 0; m[ 9] = 0; m[10] = 1; m[11] = 0; m[12] = 0; m[13] = 0; m[14] = 0; m[15] = 1; return m; } </script>var radangle = 0; ///////////////////////////////////////////// //クリックイベント本体 function onClick(e) { console.log("click"); var x = e.clientX - mycanv.offsetLeft; var y = e.clientY - mycanv.offsetTop; console.log("x:", x, "y:", y,"radang:",radangle); radangle+=0.1; render(radangle); }//////////////////////////////////////////// //レンダリング関数 function render(angle){ // GLコンテキストを取得 var gl = mycanv.getContext('webgl') || mycanv.getContext('experimental-webgl'); // 画面を黒でクリア gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); gl.useProgram(programID); //行列の設定 (ここを追加) var rotmat = rotate_z16(angle); var rotation_location = gl.getUniformLocation(programID, 'rot_matrix'); gl.uniformMatrix4fv(rotation_location, false, rotmat); //バッファへアクセス var vertex_location = gl.getAttribLocation (programID, 'position'); var color_location = gl.getAttribLocation (programID, 'color'); //頂点バッファを有効にする gl.enableVertexAttribArray (vertex_location); gl.bindBuffer (gl.ARRAY_BUFFER, vertexesVboID); gl.vertexAttribPointer (vertex_location, 3, gl.FLOAT,false, 0, 0); //カラーバッファを有効にする gl.enableVertexAttribArray (color_location); gl.bindBuffer (gl.ARRAY_BUFFER, colorsVboID); gl.vertexAttribPointer (color_location, 3, gl.FLOAT,false, 0, 0); //四角形を描く gl.drawArrays (gl.TRIANGLE_STRIP, 0, 4); //バッファを無効にする gl.disableVertexAttribArray(vertex_location); gl.disableVertexAttribArray(color_location); gl.flush (); }</script> </body> </html>
クリックすると回転します。
OpenGL知っている所からWebGL入門(1) – 画面クリア
OpenGL知っている所からWebGL入門(2) – 図形描画
OpenGL知っている所からWebGL入門(3) – 図形を回転
回転行列を使ってアフィン変換を行います。
前回と変わっていないところは折りたたんでいますがコピペをすると展開されて張り付くので注意してください。
<!DOCTYPE html> <html> <head> <title>四角形描画</title> <style> </style> </head> <body> <!-- HTML5のキャンバス作成 --> <canvas id="MyCanvas" width="400" height="400"></canvas><!-- シェーダはGLSL (OpenGL Shading Language) で書く。つまり、ここはHTMLでもJavaScriptでもない --> <!-- scriptタグのid「vshader」を識別子として、コンパイル時にgetElementByIDを使って文字列としてこのシェーダプログラムを参照する --> <!-- ************************************** --> <!-- ** 頂点シェーダ*********************** --> <!-- ************************************** --> <script id="vshader" type="x-shader/x-vertex"> attribute vec3 position;//描画時にgetAttribLocationでアクセスするのでわかりやすい名前にしておくことが望ましい attribute vec3 color;//同上 uniform mat4 rot_matrix; varying lowp vec3 vColor; void main(void){ gl_Position = rot_matrix * vec4(position, 1.0); vColor = color; } </script><!-- ここもGLSLで書く。 --> <!-- 上と同様、コンパイル時にscriptタグのid「fshader」で参照する --> <!-- ************************************** --> <!-- ** フラグメントシェーダ*************** --> <!-- ************************************** --> <script id="fshader" type="x-shader/x-fragment"> precision mediump float;//これがないとコンパイルエラーになる varying lowp vec3 vColor; void main(void){ gl_FragColor = vec4(vColor, 1.0); } </script><!-- シェーダのコンパイル。ここはJavaScriptで書く --> <!-- ************************************** --> <!-- ** シェーダコンパイルとリンク********* --> <!-- ************************************** --> <script> var programID;// グローバル変数としてプログラム名の格納先を定義する function prepare_shaders(gl){ /////////////////////////////// /////////////////////////////// // 頂点シェーダコンパイル var vs = document.getElementById ("vshader"); var vertexShader = gl.createShader (gl.VERTEX_SHADER); gl.shaderSource (vertexShader, vs.text);//シェーダのプログラムをテキストで取得 gl.compileShader (vertexShader); if ( !gl.getShaderParameter (vertexShader, gl.COMPILE_STATUS) ){ throw new Error (gl.getShaderInfoLog(vertexShader)); } /////////////////////////////// /////////////////////////////// // フラグメントシェーダコンパイル var fs = document.getElementById ("fshader"); var fragmentShader = gl.createShader (gl.FRAGMENT_SHADER); gl.shaderSource (fragmentShader, fs.text);//シェーダのプログラムをテキストで取得 gl.compileShader (fragmentShader); if ( !gl.getShaderParameter (fragmentShader, gl.COMPILE_STATUS) ){ throw new Error (gl.getShaderInfoLog(fragmentShader)); } /////////////////////////////// /////////////////////////////// // プログラムのリンク programID = gl.createProgram (); gl.attachShader (programID, vertexShader); gl.attachShader (programID, fragmentShader); gl.linkProgram (programID); if ( !gl.getProgramParameter(programID, gl.LINK_STATUS) ) { throw new Error (gl.getProgramInfoLog(programID)); } } </script><!-- データの準備。ここもJavaScriptで書く --> <!-- ************************************** --> <!-- ** 三角形の準備********* --> <!-- ************************************** --> <!-- ************************************** --> <script> //各変数をグローバル変数として定義 var vertexesVboID; var colorsVboID; function prepare_data(gl){ //頂点バッファ作成 var vertexes = [ -0.7, 0.7, 0.0, 0.7, 0.7, 0.0, -0.7, -0.7, 0.0, 0.7, -0.7, 0.0 ]; vertexesVboID = gl.createBuffer (); gl.bindBuffer (gl.ARRAY_BUFFER, vertexesVboID); gl.bufferData (gl.ARRAY_BUFFER, new Float32Array (vertexes), gl.STATIC_DRAW); //カラーバッファ作成 var colors = [ 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 ]; colorsVboID = gl.createBuffer (); gl.bindBuffer (gl.ARRAY_BUFFER, colorsVboID); gl.bufferData (gl.ARRAY_BUFFER, new Float32Array (colors), gl.STATIC_DRAW); } </script><!-- ************************************** --> <!-- ** アフィン変換行列を作る関数 ******** --> <!-- ************************************** --> <script> function rotate_z16(rad){ var m = new Float32Array(16); m[ 0] = Math.cos(rad); m[ 1] = -Math.sin(rad); m[ 2] = 0;m[ 3] = 0; m[ 4] = Math.sin(rad); m[ 5] = Math.cos(rad); m[ 6] = 0; m[ 7] = 0; m[ 8] = 0; m[ 9] = 0; m[10] = 1; m[11] = 0; m[12] = 0; m[13] = 0; m[14] = 0; m[15] = 1; return m; } </script><!-- やっと描画部分 --> <!-- ここもJavaScriptで書く --> <!-- ////////////////////////////////////// --> <!-- // 初回のみ実行 ////////////////////// --> <!-- ////////////////////////////////////// --> <!-- ////////////////////////////////////// --> <script> // 初回表示時に呼び出される window.onload = function(){ var mycanv = document.getElementById("MyCanvas"); // GLコンテキストを取得 var gl = mycanv.getContext('webgl') || mycanv.getContext('experimental-webgl'); // 画面を黒でクリア gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); prepare_shaders(gl);//シェーダの準備 prepare_data(gl);//描画データの準備 gl.useProgram(programID);//行列の設定 (ここを追加) var rotmat = rotate_z16(0.4); var rotation_location = gl.getUniformLocation(programID, 'rot_matrix'); gl.uniformMatrix4fv(rotation_location, false, rotmat);//バッファへアクセス var vertex_location = gl.getAttribLocation (programID, 'position'); var color_location = gl.getAttribLocation (programID, 'color'); //頂点バッファを有効にする gl.enableVertexAttribArray (vertex_location); gl.bindBuffer (gl.ARRAY_BUFFER, vertexesVboID); gl.vertexAttribPointer (vertex_location, 3, gl.FLOAT,false, 0, 0); //カラーバッファを有効にする gl.enableVertexAttribArray (color_location); gl.bindBuffer (gl.ARRAY_BUFFER, colorsVboID); gl.vertexAttribPointer (color_location, 3, gl.FLOAT,false, 0, 0); //四角形を描く gl.drawArrays (gl.TRIANGLE_STRIP, 0, 4); //バッファを無効にする gl.disableVertexAttribArray(vertex_location); gl.disableVertexAttribArray(color_location); gl.flush (); } </script></body> </html>
OpenGL知っている所からWebGL入門(1) – 画面クリア
OpenGL知っている所からWebGL入門(2) – 図形描画
OpenGL知っている所からWebGL入門(3) – 図形を回転
前回に引き続きWebGLをやってみる。
一つのファイルにHTML , GLSL , JavaScript という三つの言語が混在していてとても分かりにくい。
そして長さの秘訣はやはりシェーダーのコンパイルとリンクだ(褒めてない)。シェーダー自体は長くないし、バッファ作成と描画はまあしょうがないとして、シェーダのコンパイルはどうせ使いまわしなのに無駄に長い。
<!DOCTYPE html> <html> <head> <title>四角形描画</title> <style> </style> </head> <body> <!-- HTML5のキャンバス作成 --> <canvas id="MyCanvas" width="640" height="480"></canvas><!-- シェーダはGLSL (OpenGL Shading Language) で書く。つまり、ここはHTMLでもJavaScriptでもない -->
<!-- ************************************** --> <!-- ** 頂点シェーダ*********************** --> <!-- ************************************** --> <script id="vshader" type="x-shader/x-vertex"> attribute vec3 position;//描画時にgetAttribLocationでアクセスするのでわかりやすい名前にしておくことが望ましい attribute vec3 color;//同上 varying lowp vec3 vColor; void main(void){ gl_Position = vec4(position, 1.0); vColor = color; } </script><!-- scriptタグのid「vshader」を識別子として、コンパイル時にgetElementByIDを使って文字列としてこのシェーダプログラムを参照する --><!-- ************************************** --> <!-- ** フラグメントシェーダ*************** --> <!-- ************************************** --> <script id="fshader" type="x-shader/x-fragment"> precision mediump float;//これがないとコンパイルエラーになる varying lowp vec3 vColor; void main(void){ gl_FragColor = vec4(vColor, 1.0); } </script><!-- ここもGLSLで書く。 --><!-- 上と同様、コンパイル時にscriptタグのid「fshader」で参照する --><!-- ************************************** --> <!-- ** シェーダコンパイルとリンク********* --> <!-- ************************************** --> <script> var programID;// グローバル変数としてプログラム名の格納先を定義する function prepare_shaders(gl){ /////////////////////////////// /////////////////////////////// // 頂点シェーダコンパイル var vs = document.getElementById ("vshader"); var vertexShader = gl.createShader (gl.VERTEX_SHADER); gl.shaderSource (vertexShader, vs.text);//シェーダのプログラムをテキストで取得 gl.compileShader (vertexShader); if ( !gl.getShaderParameter (vertexShader, gl.COMPILE_STATUS) ){ throw new Error (gl.getShaderInfoLog(vertexShader)); } /////////////////////////////// /////////////////////////////// // フラグメントシェーダコンパイル var fs = document.getElementById ("fshader"); var fragmentShader = gl.createShader (gl.FRAGMENT_SHADER); gl.shaderSource (fragmentShader, fs.text);//シェーダのプログラムをテキストで取得 gl.compileShader (fragmentShader); if ( !gl.getShaderParameter (fragmentShader, gl.COMPILE_STATUS) ){ throw new Error (gl.getShaderInfoLog(fragmentShader)); } /////////////////////////////// /////////////////////////////// // プログラムのリンク programID = gl.createProgram (); gl.attachShader (programID, vertexShader); gl.attachShader (programID, fragmentShader); gl.linkProgram (programID); if ( !gl.getProgramParameter(programID, gl.LINK_STATUS) ) { throw new Error (gl.getProgramInfoLog(programID)); } } </script><!-- シェーダのコンパイル。ここはJavaScriptで書く -->
<!-- やっと描画部分 --><!-- データの準備。ここもJavaScriptで書く -->
<!-- ************************************** --> <!-- ** 三角形の準備********* --> <!-- ************************************** --> <!-- ************************************** --> <script> //各変数をグローバル変数として定義 var vertexesVboID; var colorsVboID; function prepare_data(gl){ //頂点バッファ作成 var vertexes = [ -0.7, 0.7, 0.0, 0.7, 0.7, 0.0, -0.7, -0.7, 0.0, 0.7, -0.7, 0.0 ]; vertexesVboID = gl.createBuffer (); gl.bindBuffer (gl.ARRAY_BUFFER, vertexesVboID); gl.bufferData (gl.ARRAY_BUFFER, new Float32Array (vertexes), gl.STATIC_DRAW); //カラーバッファ作成 var colors = [ 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 ]; colorsVboID = gl.createBuffer (); gl.bindBuffer (gl.ARRAY_BUFFER, colorsVboID); gl.bufferData (gl.ARRAY_BUFFER, new Float32Array (colors), gl.STATIC_DRAW); } </script><!-- ここもJavaScriptで書く -->
<!-- ////////////////////////////////////// --> <!-- // 初回のみ実行 ////////////////////// --> <!-- ////////////////////////////////////// --> <!-- ////////////////////////////////////// --> <script> // 初回表示時に呼び出される window.onload = function(){ var mycanv = document.getElementById("MyCanvas"); // GLコンテキストを取得 var gl = mycanv.getContext('webgl') || mycanv.getContext('experimental-webgl'); // 画面を黒でクリア gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); prepare_shaders(gl);//シェーダの準備 prepare_data(gl);//描画データの準備 gl.useProgram(programID); var vertex_location = gl.getAttribLocation (programID, 'position'); var color_location = gl.getAttribLocation (programID, 'color'); //頂点バッファを有効にする gl.enableVertexAttribArray (vertex_location); gl.bindBuffer (gl.ARRAY_BUFFER, vertexesVboID); gl.vertexAttribPointer (vertex_location, 3, gl.FLOAT,false, 0, 0); //カラーバッファを有効にする gl.enableVertexAttribArray (color_location); gl.bindBuffer (gl.ARRAY_BUFFER, colorsVboID); gl.vertexAttribPointer (color_location, 3, gl.FLOAT,false, 0, 0); //四角形を描く gl.drawArrays (gl.TRIANGLE_STRIP, 0, 4); //バッファを無効にする gl.disableVertexAttribArray(vertex_location); gl.disableVertexAttribArray(color_location); gl.flush (); } </script></body> </html>
※注意 IE11では表示されないらしい
相変わらずJavaScriptがさっぱりわからない。というか上記<script></script>内は本当にJavaScriptなんだろうな?(ぉぃ)
昔JavaScript書くときは<script>タグにもっと色々属性付けた気がするんだけれどHTML5ではいらないのか。さっぱりわからない。
OpenGL知っている所からWebGL入門(1) – 画面クリア
OpenGL知っている所からWebGL入門(2) – 図形描画
OpenGL知っている所からWebGL入門(3) – 図形を回転
WebGLの入門はOpenGLの入門に等しいのだけどそこは大体つかめてるので、GLUTぐらいは知ってる状態でWebGLどうやるのと言う話です。
<!DOCTYPE html> <html> <head> <title>画面クリア</title> </head> <body> //HTML5のキャンバス作成 <canvas id="MyCanvas" width="640" height="480"></canvas> <script>// 初回表示時に呼び出される window.onload = function(){ var mycanv = document.getElementById("MyCanvas"); // GLコンテキストを取得 var gl = mycanv.getContext('webgl') || mycanv.getContext('experimental-webgl'); // 画面を緑でクリア gl.clearColor(0.0, 1.0, 0.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); }</script> </body> </html>
解説したいけれどJavaScript側の知識がなさ過ぎて出来ない。
OpenGL知っている所からWebGL入門(1) – 画面クリア
OpenGL知っている所からWebGL入門(2) – 図形描画
OpenGL知っている所からWebGL入門(3) – 図形を回転