learning webglで学ぶwebgl入門
DESCRIPTION
WebGL勉強会 第5回(http://atnd.org/events/11693)で発表をした時のスライドに少し、加筆したもの。WebGLをこれから勉強する方のとっかかりに成る様に意識して作成したので、発表向けというより授業向けに近い内容と成っています。TRANSCRIPT
Learning WebGLで学ぶWebGL入門
nakamura001
今回はLesson 1の内容を解説
http://learningwebgl.com/blog/?p=28
Lesson 1のURLはこちら
https://github.com/gpjt/webgl-lessons
サンプルコードはこちら
日本語に翻訳されたもの
https://sites.google.com/site/hackthewebgl/learning-webglhon-yaku/the-lessons/lesson-1
翻訳された皆様に感謝します
サンプルの内容を確認
プログラム構造の概要解説
<body onload="webGLStart();"> <a href="http://learningwebgl.com/blog/?p=28"><< Back to Lesson 1</a><br />
<canvas id="lesson01-canvas" style="border: none;" width="500" height="500"></canvas>
<br/> <a href="http://learningwebgl.com/blog/?p=28"><< Back to Lesson 1</a><br /></body>
HTML
<body onload="webGLStart();"> <a href="http://learningwebgl.com/blog/?p=28"><< Back to Lesson 1</a><br />
<canvas id="lesson01-canvas" style="border: none;" width="500" height="500"></canvas>
<br/> <a href="http://learningwebgl.com/blog/?p=28"><< Back to Lesson 1</a><br /></body>
HTML
<body onload="webGLStart();"> <a href="http://learningwebgl.com/blog/?p=28"><< Back to Lesson 1</a><br />
<canvas id="lesson01-canvas" style="border: none;" width="500" height="500"></canvas>
<br/> <a href="http://learningwebgl.com/blog/?p=28"><< Back to Lesson 1</a><br /></body>
HTML
function webGLStart() { var canvas = document.getElementById("lesson01-canvas"); initGL(canvas); initShaders(); initBuffers();
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0);
gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL);
setInterval(drawScene, 15); }
JavaScript
function webGLStart() { var canvas = document.getElementById("lesson01-canvas"); initGL(canvas); initShaders(); initBuffers();
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0);
gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL);
setInterval(drawScene, 15); }
学習のアドバイス
function webGLStart() { var canvas = document.getElementById("lesson01-canvas"); initGL(canvas); initShaders(); initBuffers();
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0);
gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL);
setInterval(drawScene, 15); }
学習のアドバイス
先頭が gl. に成っているものはほどんどがOpenGL ESのコマンドをWebGLに
移植したもの(※Learning WebGLの場合)
先頭が gl. に成っているものはほどんどがOpenGL ESのコマンドをWebGLに
移植したもの(※Learning WebGLの場合)
gl.clearColor()↓
glClearColor()
先頭が gl. に成っているものはほどんどがOpenGL ESのコマンドをWebGLに
移植したもの(※Learning WebGLの場合)
gl.clearColor()↓
glClearColor() このワードで検索
注意点glTranslate()
void glTranslated(GLdouble x, GLdouble y, GLdouble z)void glTranslatef(GLfloat x, GLfloat y, GLfloat z)
glTranslated()
glTranslatef()
OpenGLのリファレンス
http://d.hatena.ne.jp/nakamura001/20080918/1221738923
ここでOpenGLやOpenGL ESのリファレンスの一覧を紹介しています
順番に細かく説明
initGL()
function webGLStart() { var canvas = document.getElementById("lesson01-canvas"); initGL(canvas); initShaders(); initBuffers();
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0);
gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL);
setInterval(drawScene, 15); }
function webGLStart() { var canvas = document.getElementById("lesson01-canvas"); initGL(canvas); initShaders(); initBuffers();
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0);
gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL);
setInterval(drawScene, 15); }
var gl; function initGL(canvas) { try { gl = canvas.getContext("experimental-webgl"); gl.viewportWidth = canvas.width; gl.viewportHeight = canvas.height; } catch(e) { } if (!gl) { alert("Could not initialise WebGL, sorry :-("); } }
initBuffers()
function webGLStart() { var canvas = document.getElementById("lesson01-canvas"); initGL(canvas); initShaders(); initBuffers();
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0);
gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL);
setInterval(drawScene, 15); }
function webGLStart() { var canvas = document.getElementById("lesson01-canvas"); initGL(canvas); initShaders(); initBuffers();
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0);
gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL);
setInterval(drawScene, 15); }
var triangleVertexPositionBuffer; var squareVertexPositionBuffer; function initBuffers() { triangleVertexPositionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer); var vertices = [ 0.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0 ];
頂点
(X,Y,Z)の3つの情報で指定
右手座標系
XZ
Y主にOpenGLで使われる
右手座標系
XZ
Y主にOpenGLで使われる
左手座標系
X
Y
Z
主にDirectXで使われる
ディスプレイとの対応
XZ
Y
右手座標系
XZ
Y
WebGLはもちろん、OpenGLと同じ右手座標系
右手座標系
XZ
Y
WebGLはもちろん、OpenGLと同じ右手座標系
var triangleVertexPositionBuffer; var squareVertexPositionBuffer; function initBuffers() { triangleVertexPositionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer); var vertices = [ 0.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0 ];
XZ
Y
var triangleVertexPositionBuffer; var squareVertexPositionBuffer; function initBuffers() { triangleVertexPositionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer); var vertices = [ 0.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0 ];
XZ
Y
①
X
Y
①
var triangleVertexPositionBuffer; var squareVertexPositionBuffer; function initBuffers() { triangleVertexPositionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer); var vertices = [ 0.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0 ];
XZ
Y
①
X
Y
①
②
②
var triangleVertexPositionBuffer; var squareVertexPositionBuffer; function initBuffers() { triangleVertexPositionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer); var vertices = [ 0.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0 ];
XZ
Y
①
X
Y
①
②
②
③
③
var triangleVertexPositionBuffer; var squareVertexPositionBuffer; function initBuffers() { triangleVertexPositionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer); var vertices = [ 0.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0 ]; gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); triangleVertexPositionBuffer.itemSize = 3; triangleVertexPositionBuffer.numItems = 3;
頂点数
要素の数
triangleVertexPositionBuffer.itemSize = 3; triangleVertexPositionBuffer.numItems = 3;
ちなみにここで出てきた .itemSize と .numItems はこのサンプルで独自に追加されたプロパティ(メンバ変数)です。※OpenGL ES由来のプロパティでは有りません。
ここで指定した値は実際には他のメソッドの引数として使用されます。※具体的には gl.vertexAttribPointer() と gl.drawArrays() 。
注意!
squareVertexPositionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer); vertices = [ 1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, -1.0, -1.0, 0.0 ]; gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); squareVertexPositionBuffer.itemSize = 3; squareVertexPositionBuffer.numItems = 4;
四角形
drawScene()
function webGLStart() { var canvas = document.getElementById("lesson01-canvas"); initGL(canvas); initShaders(); initBuffers();
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0);
gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL);
setInterval(drawScene, 15); }
function webGLStart() { var canvas = document.getElementById("lesson01-canvas"); initGL(canvas); initShaders(); initBuffers();
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0);
gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL);
setInterval(drawScene, 15); }
function drawScene() { gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
デプスバッファ有り正しい前後関係となる
デプスバッファ無し描画した順番の表示となる
デプスバッファ
•デプスバッファを使うと複雑な前後関係を持ったモデルでも正しい順番で描画される
•デプスバッファ(深度バッファ)はDirectXではZバッファと呼ばれるもの
perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0);
詳しくは gluPerspective で検索
loadIdentity();
mvTranslate([-1.5, 0.0, -7.0]);
XZ
Y
loadIdentity();
mvTranslate([-1.5, 0.0, -7.0]);
XZ
Y
詳しくはglTranslateで検索
実際に描画する命令
gl.drawArrays(gl.TRIANGLES, 0, triangleVertexPositionBuffer.numItems);
三角形の描画
gl.drawArrays(gl.TRIANGLES, 0, triangleVertexPositionBuffer.numItems);
三角形の描画
三角形の頂点を個別に指定※複数三角形を描画可能ですが今回は1つしか指定してないので描画される三角形は1つのみです。
gl.drawArrays(gl.TRIANGLE_STRIP, 0, squareVertexPositionBuffer.numItems);
四角形の描画
gl.drawArrays(gl.TRIANGLE_STRIP, 0, squareVertexPositionBuffer.numItems);
四角形の描画
前に指定した「2頂点+新規1頂点」で三角形の頂点を指定
gl.drawArrays(gl.TRIANGLE_STRIP, 0, squareVertexPositionBuffer.numItems);
四角形の描画
前に指定した「2頂点+新規1頂点」で三角形の頂点を指定
http://d.hatena.ne.jp/nakamura001/20081231/1230719279
詳しくはこちらの「GL_TRIANGLE_STRIP」の解説を参照
initShaders()
function webGLStart() { var canvas = document.getElementById("lesson01-canvas"); initGL(canvas); initShaders(); initBuffers();
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0);
gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL);
setInterval(drawScene, 15); }
function webGLStart() { var canvas = document.getElementById("lesson01-canvas"); initGL(canvas); initShaders(); initBuffers();
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0);
gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL);
setInterval(drawScene, 15); }
var shaderProgram; function initShaders() { var fragmentShader = getShader(gl, "shader-fs"); var vertexShader = getShader(gl, "shader-vs");
shaderProgram = gl.createProgram();
シェーダ
CPUでは無く、GPUで実行される3D処理関連のプログラム
頂点やピクセルを操作する処理を記述
シェーダ
Vertex Shader(頂点シェーダ)
シェーダ
Vertex Shader(頂点シェーダ)
頂点情報を操作
シェーダ
Vertex Shader(頂点シェーダ)
頂点情報を操作
http://www.nvidia.co.jp/object/IO_20011001_6613.html分り易いVertex Shaderの解説ページ(NVIDIAのサイト)
シェーダ
Fragment Shader(フラグメント・シェーダ)
シェーダ
ピクセル情報を操作
Fragment Shader(フラグメント・シェーダ)
GLSL(OpenGL Shading Language)で検索
http://d.hatena.ne.jp/nakamura001/20090927/1254058173
参考書籍などは以前こちらで紹介しましたので宜しければ参照下さい。
より、理解する為に自分で変更してみよう
色を変えてみる<script id="shader-fs" type="x-shader/x-fragment"> #ifdef GL_ES precision highp float; #endif
void main(void) { gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); }</script>
色を変えてみる<script id="shader-fs" type="x-shader/x-fragment"> #ifdef GL_ES precision highp float; #endif
void main(void) { gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); }</script>
頂点を変更してみる triangleVertexPositionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer); var vertices = [ 0.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0 ]; gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); triangleVertexPositionBuffer.itemSize = 3; triangleVertexPositionBuffer.numItems = 3;
※四角形の方 (gl.TRIANGLE_STRIP) はちょっと頂点の指定方法が直感的で無いので最初は三角形の方の頂点を変更してみるのが良いと思います。
頂点を変更してみる triangleVertexPositionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer); var vertices = [ 0.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0 ]; gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); triangleVertexPositionBuffer.itemSize = 3; triangleVertexPositionBuffer.numItems = 3;
※四角形の方 (gl.TRIANGLE_STRIP) はちょっと頂点の指定方法が直感的で無いので最初は三角形の方の頂点を変更してみるのが良いと思います。
注意点
•描画される三角形は表裏、両面ではなく表面のみ描画される
•法線の向きが表を表す
法線
※これはイメージです。実際にはポリゴンを描画した時に法線が描画される事は有りません。
法線• 面と垂直な線
※これはイメージです。実際にはポリゴンを描画した時に法線が描画される事は有りません。
法線• 面と垂直な線 • 面の向きを表す。法線の向きが面の表
※これはイメージです。実際にはポリゴンを描画した時に法線が描画される事は有りません。
法線• 面と垂直な線 • 面の向きを表す。法線の向きが面の表• 主にライティングの計算時に利用される
※これはイメージです。実際にはポリゴンを描画した時に法線が描画される事は有りません。
法線• 面と垂直な線
これが法線
• 面の向きを表す。法線の向きが面の表• 主にライティングの計算時に利用される
※これはイメージです。実際にはポリゴンを描画した時に法線が描画される事は有りません。
右手座標系の場合はこの様に手で法線を握ったときの親指以外が指している向き(反時計回り)の順番で頂点を指定する必要が有ります。もし、逆順(時計回り)に指定すると面の向きが逆になります。
使用しているライブラリを見てみよう
使用ライブラリ
<script type="text/javascript" src="sylvester.js"></script><script type="text/javascript" src="glUtils.js"></script>
http://sylvester.jcoglan.com/Sylvesterのサイト
※sylvester.src.js が無圧縮状態のコード