草を作るチュートリアル約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) – 図形を回転
25:20~
1.別のレイヤーに移動してあるSmall Petalを選択する
この時Editモードへ行き、[U]→UnwrapでUVマップを作成する


2.まずBig Petalとマテリアルを共有し、[2]を押して共有を解除し個別のマテリアルにする。

3.Big PetalからコピーしたSmall Petalの以下の2色を修正する

リアルにするためにBig Petalの数を減らす。
花の中心のオブジェクトを選択し、Particle Systemのpetals_outerのNumberを70→50に変更
1.レンダリングのために配置換えを行う。
この時、花を回転するとHairが歪んでしまうが、確定すると元に戻る。

2.カメラの位置とパラメータを以下のように設定。ただし※の値はカメラの視線の上の+印が花の中央に来るように設定する

1.User PreferencesからImport Images as Planesアドオンを設定し、テクスチャ画像を指定すると同時にPlaneが作成されるようにする
2.Images as PlanesでできたPlaneを以下のように配置


なお今回使用する画像は

ライトやオブジェクトの位置は詳細がわからないので明るくなるように調整する必要がある。

Blender – Detailed Flower のチュートリアルを試す(1)
Blender – Detailed Flower のチュートリアルを試す(2)
Blender – Detailed Flower のチュートリアルを試す(3)
13:30
1.デフォルトのライトをSunに設定し、Strengthを2.0に設定する。

2.Backgroundに、動画ではHDR画像を使っているらしいが、どのようなものか不明なため似たような効果を得られるものをノードで設定する。
さらにその上で、Render→Film→Transparentをonにする
3.Ctrl+Bで左ドラッグし、3Dのシェーディングを行う範囲を限定する
※解除はCtrl+Alt+Bで行う
1.花の中心のオブジェクトを選択し、マテリアルを以下のように設定する
なお動画ではNode Wranglerを使っているのでCtrl+Shift+左クリックでプレビューを更新できる。使うのであればUser Preferences→Add-onsから追加する。

1.別のレイヤーに移動してあるBig Petalを選択する
この時Editモードへ行き、[U]→UnwrapでUVマップを作成する


2.花全体に戻り、マテリアルを追加し、マテリアル名を"petal"とし以下のように設定
なお使用しているテクスチャは
https://www.textures.com/download/plasterdirty0073/72072
https://www.textures.com/download/woodfine0010/19679
ただし、plasterdirty0073のほうは、動画ではtiledというのがあるのだがtextures.comの中には見当たらないので、配信者が自作した可能性がある。要は花にしたときに模様がリング状になることを目的としているので(動画22:25前後)、それに合わせてgimpなどで編集するといいかもしれない。
続く
Blender – Detailed Flower のチュートリアルを試す(1)
Blender – Detailed Flower のチュートリアルを試す(2)
Blender – Detailed Flower のチュートリアルを試す(3)
09:15
petals_outerのHairのNumberを70にする

1.Particle Systemを追加し、petals_smallと名前を付ける
2.[Z]キーを押してワイヤフレーム表示にして、[G][Z]で花の中心の一番上の点を0.050だけ移動する
3.petal_smallを生やすためのVertexGroupを作成するため、花の中心の中心付近の頂点を選択する。なお、動画でやっているようにShift+Altで選択範囲を調整すると余計な部分まで選択されるが動画では気にしていない。
4.新たにVertexGroupを作成し、名前をpetals_innerとして上で選択した頂点をassignする。
5.petals_smallの設定に戻り、VertexGroupにpetals_innerを設定する
6.調整のため選択してある点群の一番内側の円をループ選択し、Weight=0でAssignする。なお、VertexGroupを編集してもParticle Systemは自動で更新されないので、Numberを変えるなどして更新のきっかけを与えてやらないといけない。
7.さらに調整として、petal_smallが多すぎるので304まで減らす。
マテリアルに入る前に花弁をさらに編集する
12:15~
以下、数値が一切示されていないので全て目分量で編集する

続く
Blender – Detailed Flower のチュートリアルを試す(1)
Blender – Detailed Flower のチュートリアルを試す(2)
Blender – Detailed Flower のチュートリアルを試す(3)
04:30~
1.花の中心のEditモードへ行き、切り口の部分の頂点を一つ選択し、[Alt+右クリック]でループ選択する。
そして[E][Z]でZ方向に-0.196(目分量)だけエッジをコピー→押し出しを行う。
2.そのまま[A]で全頂点を選択し、[S][Z]でZ方向に0.563(目分量)縮小する
04:45
1.花の中心にHairを設定する
この時に、各花びらをpetal_big,petal_smallという名前に変更する


2.上記Hairの範囲を限定するためのVertexGroupを追加(petals_outer)する
3.上記VertexGroupに頂点を割り当てる。花の中心の一点からAlt+右クリックでループ選択し、Ctrl+Num"+"で範囲拡大し、petals_outerにAssignする

4.Hairの設定に戻り、Vertex Groupsの設定のDensityにpetals_outerを設定する

5.Hairの設定を以下のように修正
6.petal_big,petal_smallを選択し、[M]で別のレイヤーに移動する

08:30~
1.花の中心の内側から5個目のループをAlt+右クリックでループ選択し、[S]で1.102拡大する
2.内側から4番目のループを選択し、[S]で1.098拡大する

3.縁のリングをループ選択し、[S]で1.082拡大する
4.最後に、HairのRandom Sizeを0.275に設定
続く
Blender – Detailed Flower のチュートリアルを試す(1)
Blender – Detailed Flower のチュートリアルを試す(2)
Blender – Detailed Flower のチュートリアルを試す(3)