GLSL – DECODE https://decode.red/blog data decode, decoder or decoded ... design of code Mon, 15 Dec 2025 06:15:00 +0000 ja hourly 1 https://wordpress.org/?v=4.7.29 OpenGL Shading Language (4) ../../../201908161025/ Fri, 16 Aug 2019 07:08:15 +0000 ../../../?p=1025 最近またGLSLを復習する目的で過去のコードを眺めていたところ、ProcessingでGLSLを動かす環境が、WebGLのGLSLSandboxの再現しやいと思い、試してみました。

GLSL Sandbox

上記サイトのコードを使って再現します。

環境: Processing 3.3.7 / Mac
fshader.glsl

uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;
 
const float PI = 3.14159265358979;
 
const float num = 2.0;
 
void main( void ) {
 
	vec2 pos = ( gl_FragCoord.xy / resolution.xy );
	
	vec3 color = vec3(0.05, 0.05, 0.2);
	
	float r, g, b, cl;
 
	cl += 0.5/abs(length(vec2(
		pos.x-0.5+cos(time*0.3)*0.1 - mouse.x * 0.1,
		pos.y-0.5+sin(time*0.3)*0.1 + mouse.y * 0.1
		))-0.3);
	r = cl * color.x;
	g = cl * color.y;
	b = cl * color.z;
 
	gl_FragColor = vec4(r, g, b, 1.0 );
}

mouseの動きで変化をつける部分を追加しました。
このフラグメントシェーダプログラムをProcessingから呼び出します。
このブログラムと同じ階層にdataフォルダを作りその中にシェーダプログラムをおきます。
GraphicsサンプルのLowLevelGLVboSeparateなどが参考になります。
(modes/java/examples/Demos/Graphics/LowLevelGLVboSeparate)

PShader sh;

void setup() {
  size(400, 400, P2D);
  sh = loadShader("fshader.glsl");
}

void draw() {
  sh.set("time", millis() / 1000.0);
  sh.set("resolution", (float)width, (float)height);
  sh.set("mouse", (float)mouseX/(float)width, (float)mouseY/(float)height);
  shader(sh);
  rect(0, 0, width, height);
}

実行結果

当然ですが、GLSL Sandboxと違って、バーテックスシェーダも使えます。
グラフィックアートを表現する方法として、ProcessingではGLSLに頼らなくてもかなりのことはできるのですが、GLSLという並列プログラミングの手法のトレーニング環境として興味深いものと思っています。

]]>
OpenGL Shading Language (3) ../../../20170613721/ Tue, 13 Jun 2017 12:56:24 +0000 ../../../?p=721 今回はWayland EGLの環境で、前回の四面体を表示してみました。

WaylandはXに変わるディスプレイサーバで、以下の記事でFedora25(デフォルトでサポート)上でテストしました。

Wayland Display Server

ここでは、Ubuntu 16.04を使います。(インストールメモを忘れてしましまたが、下にあるコンパイルオプションからだいたい想像できると思います)

参考 :
http://blog.techlab-xe.net/archives/4637
https://github.com/techlabxe/SimpleWindowWayland/tree/base-1.1

こちらのサイトを参考にさせていただきました。
カメラが自動的移動する処理はそのままで、四面体を表示させ色をつけています。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <cmath>
#include <cstdint>
#include <vector>
#include <GLES2/gl2.h>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>

#include "WaylandCore.h"

#define WIDTH 640
#define HEIGHT 480
#define TITLE  "Wayland EGL Window"

const GLchar* srcVertexShader[] =
{
	"attribute vec4 vPosition;\n"
	"attribute vec3 vColor;\n"
	"varying vec4 color;\n"
	"uniform vec4 matPVW[4];\n"
	"void main() {\n"
		"vec4 pos;\n"
		"pos = matPVW[0] * vPosition.x;\n"
		"pos += matPVW[1]* vPosition.y;\n"
		"pos += matPVW[2]* vPosition.z;\n"
		"pos += matPVW[3]* vPosition.w;\n"
		"gl_Position = pos;\n"
		"color.rgb = vec3(vColor.r, vColor.g, vColor.b);\n"
		"color.a = 1.0;\n"
	"}"
};

const GLchar* srcFragmentShader[] =
{
	"precision mediump float; \n"
	"varying vec4 color;\n"
	"void main() {\n"
		"gl_FragColor = color;\n"
	"}"
};

struct DrawBatch {
	GLuint vb, ib;
	GLuint shader;
	int indexCount;
} drawObj;

struct VertexPosition {
	float x, y, z;
};
struct VertexColor {
	float r, g, b;
};
struct Vertex {
	VertexPosition Position;
	VertexColor   Color;
};

GLint attrPVW;

GLuint createShaderProgram(const char* srcVS[], const char* srcFS[]){
	GLuint shaderVS = glCreateShader(GL_VERTEX_SHADER);
	GLuint shaderFS = glCreateShader(GL_FRAGMENT_SHADER);

	glShaderSource(shaderVS, 1, srcVS, NULL);
	glCompileShader(shaderVS);

	glShaderSource(shaderFS, 1, srcFS, NULL);
	glCompileShader(shaderFS);

	GLuint program;
	program = glCreateProgram();
	glAttachShader(program, shaderVS);
	glAttachShader(program, shaderFS);

	glLinkProgram(program);

	return program;
}

template<typename T> GLuint createBufferObject(std::vector<T>& src, GLuint type)
{
	GLuint vbo;
	glGenBuffers( 1, &vbo );
	glBindBuffer( type, vbo );
	glBufferData( type, sizeof(T) * src.size(), src.data(), GL_STATIC_DRAW );
	return vbo;
}

void createResource() {
	drawObj.shader = createShaderProgram( srcVertexShader, srcFragmentShader );
	GLint attrPos = glGetAttribLocation( drawObj.shader, "vPosition" );
	GLint attrCol = glGetAttribLocation( drawObj.shader, "vColor" );
	
	attrPVW = glGetUniformLocation( drawObj.shader, "matPVW" );

	std::vector<uint16_t> indices = {0,1,2,1,2,3,0,2,3,0,1,3};
	std::vector<Vertex> vertices;

	GLfloat vData[] = { 
		-1.0f, -1.0f, 0.0f, //1
		 1.0f, -1.0f, 0.0f, //2
		 0.0f,  1.0f, 0.0f, //3
		 0.0f,  0.0f, 1.0f, //4
	};
	GLfloat vColor[] = {
		1.0f, 0.0f, 0.0f,
		0.0f, 1.0f, 0.0f,
		0.0f, 0.0f, 1.0f,
		1.0f, 1.0f, 1.0f,
	};
	
	for(int i=0;i<4;i++){
		Vertex v;
		v.Position.x = vData[i*3];
		v.Position.y = vData[i*3+1];
		v.Position.z = vData[i*3+2];
		v.Color.r = vColor[i*3];
		v.Color.g = vColor[i*3+1];
		v.Color.b = vColor[i*3+2];
		vertices.push_back(v);
	}
	drawObj.vb = createBufferObject(vertices, GL_ARRAY_BUFFER);
	drawObj.ib = createBufferObject(indices, GL_ELEMENT_ARRAY_BUFFER);
	drawObj.indexCount = indices.size();

	char* offset = NULL;
	int stride = sizeof(Vertex);
	
	glVertexAttribPointer(attrPos, 3, GL_FLOAT, GL_FALSE, stride, offset);
	offset += sizeof(VertexPosition);
	glVertexAttribPointer(attrCol, 3, GL_FLOAT, GL_FALSE, stride, offset);
	offset += sizeof(VertexColor);
	
	glEnableVertexAttribArray(attrPos);
	glEnableVertexAttribArray(attrCol);
}

void destroyResource() {
	glDeleteBuffers( 1, &drawObj.vb );
	glDeleteBuffers( 1, &drawObj.ib );
	glDeleteProgram( drawObj.shader );
}

void draw()
{
	static double angle = 0.0f;
	angle += .01f;
	if( angle > 3600.0 ) {
		angle -= 3600.0;
	}
	
	glEnable(GL_DEPTH_TEST);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	
	glm::vec3 cameraPos = glm::vec3(0.0, 0.0f, 2.0f);
	glm::mat4 proj = glm::perspective<float>(30.0f, float(WIDTH)/float(HEIGHT), 1.0f, 100.0f);
	glm::mat4 view = glm::lookAt<float>(cameraPos, glm::vec3(0.0f,0.0f,0.0f), glm::vec3(0.0f,1.0f,0.0f));
	glm::mat4 world= glm::rotate( glm::mat4(1.0f), (float)angle, glm::vec3(0.0f,0.0f,1.0f));
	world= glm::rotate(world, (float) angle * 0.5f, glm::vec3(0.0f, 0.0f, 1.0f));
	world= glm::rotate(world, (float) angle * 0.5f, glm::vec3(1.0f, 0.0f, 0.0f));
	glUseProgram(drawObj.shader);

	glm::mat4 pvw = proj * view * world;
	glUniform4fv(attrPVW, 4, glm::value_ptr(pvw));
	glBindBuffer(GL_ARRAY_BUFFER, drawObj.vb);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, drawObj.ib);
	glDrawElements(GL_TRIANGLES, drawObj.indexCount, GL_UNSIGNED_SHORT, NULL);   
}

int main() {
	WaylandCore* core = new WaylandCore(WIDTH, HEIGHT, TITLE);

	fprintf(stderr, "Renderer: %s\n", (char*)glGetString(GL_RENDERER));
	fprintf(stderr, "GL_VERSION: %s\n", (char*)glGetString( GL_VERSION));
	fprintf(stderr, "GL_EXTENSIONS: %s\n", (char*)glGetString(GL_EXTENSIONS));

	createResource();
  
	while( !core->isShouldClose() ) {
		core->pollEvents();
		usleep(100*1000);
		
		glClearColor( 0, 0, 0.25f, 0.75f );
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
		
		draw();
		
		core->swapBuffers();
	}
	
	destroyResource();
	
	delete core;core = NULL;
	return 0;
}

g++ -o WaylandEglTest main.cpp WaylandCore.cpp WaylandCore_registry_handlers.cpp WaylandCore_seat_handlers.cpp -lwayland-client -lwayland-cursor -lwayland-egl -lEGL -lGLESv2 -std=c++11

main.cpp以外のWayland部分は、上記サイトにあるのを使わせていただきました。
(マウスやキーボードのイベントリスナーなども含んでいます)
参考までに、メインループの最初と最後にあるpollEvents()とswapBuffers()をWaylandCore.cppから抜粋。

void WaylandCore::pollEvents() {
  if( mDisplay == NULL || mRegistry == NULL ) {
    mShouldClose = true;
    return;
  }
  pollfd fds[] = {
    { wl_display_get_fd(mDisplay), POLLIN },
  };
  while( wl_display_prepare_read(mDisplay) != 0 )
  {
    wl_display_dispatch_pending(mDisplay);
  }
  wl_display_flush(mDisplay);
  if( poll(fds,1,0) > 0 )
  {
    wl_display_read_events(mDisplay);
    wl_display_dispatch_pending(mDisplay);
  } else {
    wl_display_cancel_read(mDisplay);
  }
}

void WaylandCore::swapBuffers()
{
  eglSwapBuffers( mEglDisplay, mSurface.eglSurface );
}

イベントのポーリングでソケット読み込みしています。IPC(Interprocess Communication)の部分が詳しく書かれています。OpneGLもそうですが、コードボリュームが多くなってしまうのは、このような細かいAPI(wl_xxx)を組みあわせるところから書くからでしょう。意味がわかれば中身を意識せず使えばいいと思われます。
(この部分、Xのようにリモートでアクセスできるかどうか、興味は膨らみます)

ここまで違う環境でシェーダープログラムを試してみると、理解がより深まった気がします。
いろいろな環境で実行できるとわかったところで、GPUの特性を生かした画像処理のシェーダプログラムにもっと比重をおいて学習したいと思います。

Renderer: Gallium 0.4 on llvmpipe(LLVM3.8, 256bits)
GL Version : OpenGL ES 3.0 Mesa 12.0.6
EGL major : 1 minor : 4

]]>
OpenGL Shading Language (2) ../../../20170604713/ Sun, 04 Jun 2017 02:56:40 +0000 ../../../?p=713 前回のWebGLではなく、OpenGL3.3を使って今度は3D表示をしてみました。3Dになるとカメラ位置の計算で行列演算が入り、急に数学的な手法が多用されるようになります。

参考 : http://www.opengl-tutorial.org/jp/beginners-tutorials/tutorial-3-matrices/

ここにあるチュートリアルを参考にさせていただきました。
Windowsでのビルドがうまくいったので、VisualStudio 2013 でテストしました。

参考 : http://www.opengl-tutorial.org/jp/beginners-tutorials/tutorial-1-opening-a-window/

内容は、四面体の頂点の色をフラグメンシェーダに処理をさせることと、座標変換による見え方の違いを確認することです。

// Include standard headers
#include <stdio.h>
#include <stdlib.h>
#include <GL/glew.h>
#include <glfw3.h>
GLFWwindow* window;

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include <common/shader.hpp>

int main( void )
{
        // Initialise GLFW
	glfwInit();

	glfwWindowHint(GLFW_SAMPLES, 4);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 

	window = glfwCreateWindow(640, 480, "Test", NULL, NULL);

	glfwMakeContextCurrent(window);

	// Initialize GLEW
	glewExperimental = true; // Needed for core profile

	glewInit();

	// Ensure we can capture the escape key being pressed below
	glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);

	// Dark blue background
	glClearColor(0.0f, 0.0f, 0.4f, 0.0f);

	GLuint VertexArrayID;
	glGenVertexArrays(1, &VertexArrayID);
	glBindVertexArray(VertexArrayID);

	// Create and compile our GLSL program from the shaders
	GLuint programID = LoadShaders( "vertexshader.glsl", "fragmentshader.glsl" );

	// Get a handle for our "MVP" uniform
	GLuint MatrixID = glGetUniformLocation(programID, "MVP");

	// Projection matrix : 45ー Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units
	glm::mat4 Projection = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f);
	
	// Camera matrix
	glm::mat4 View       = glm::lookAt(
								glm::vec3(0.5,0.8,2), // Camera is at (4,3,3), in World Space
								glm::vec3(0,0,0), // and looks at the origin
								glm::vec3(0,1,0)  // Head is up (set to 0,-1,0 to look upside-down)
						   );
	// Model matrix : an identity matrix (model will be at the origin)
	glm::mat4 Model      = glm::mat4(1.0f);
	// Our ModelViewProjection : multiplication of our 3 matrices
	glm::mat4 MVP        = Projection * View * Model; // Remember, matrix multiplication is the other way around

	static const GLfloat g_vertex_buffer_data[] = { 
		-1.0f, -1.0f, 0.0f, //1
		 1.0f, -1.0f, 0.0f, //2
		 0.0f,  1.0f, 0.0f, //3

		-1.0f, -1.0f, 0.0f,  //1
		 1.0f, -1.0f, 0.0f,  //2
		 0.0f,  0.0f, 1.0f,  //4

		-1.0f, -1.0f, 0.0f,  //1
		 0.0f,  0.0f, 1.0f,  //4
		 0.0f,  1.0f, 0.0f,  //3

		 1.0f, -1.0f, 0.0f,  //2
		 0.0f,  1.0f, 0.0f,  //3
		 0.0f,  0.0f, 1.0f,  //4
	};

	GLuint vertexbuffer;
	glGenBuffers(1, &vertexbuffer);
	glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
	glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);

	static const GLfloat g_color_buffer_data[] = {
		1.0f, 0.0f, 0.0f,
		0.0f, 1.0f, 0.0f,
		0.0f, 0.0f, 1.0f,

		1.0f, 0.0f, 0.0f,
		0.0f, 1.0f, 0.0f,
		1.0f, 1.0f, 1.0f,

		1.0f, 0.0f, 0.0f,
		1.0f, 1.0f, 1.0f,
		0.0f, 0.0f, 1.0f,

		0.0f, 1.0f, 0.0f,
		0.0f, 0.0f, 1.0f,
		1.0f, 1.0f, 1.0f,
	};

	GLuint colorbuffer;
	glGenBuffers(1, &colorbuffer);
	glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
	glBufferData(GL_ARRAY_BUFFER, sizeof(g_color_buffer_data), g_color_buffer_data, GL_STATIC_DRAW);

	do{
		// Clear the screen
		glClear( GL_COLOR_BUFFER_BIT );

		// Use our shader
		glUseProgram(programID);

		// Send our transformation to the currently bound shader, 
		// in the "MVP" uniform
		glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);

		// 1rst attribute buffer : vertices
		glEnableVertexAttribArray(0);
		glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
		glVertexAttribPointer(
			0,                  // attribute. No particular reason for 0, but must match the layout in the shader.
			3,                  // size
			GL_FLOAT,           // type
			GL_FALSE,           // normalized?
			0,                  // stride
			(void*)0            // array buffer offset
		);

		// 2nd attribute buffer : colors
		glEnableVertexAttribArray(1);
		glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
		glVertexAttribPointer(
			1,                                // attribute. No particular reason for 1, but must match the layout in the shader.
			3,                                // size
			GL_FLOAT,                         // type
			GL_FALSE,                         // normalized?
			0,                                // stride
			(void*)0                          // array buffer offset
		);

		// Draw the triangle !
		glDrawArrays(GL_TRIANGLES, 0, 3*4); // 3 indices starting at 0 -> 1 triangle
		glDisableVertexAttribArray(0);

		// Swap buffers
		glfwSwapBuffers(window);
		glfwPollEvents();

	} // Check if the ESC key was pressed or the window was closed
	while( glfwGetKey(window, GLFW_KEY_ESCAPE ) != GLFW_PRESS &&
		   glfwWindowShouldClose(window) == 0 );

	// Cleanup VBO and shader
	glDeleteBuffers(1, &vertexbuffer);
	glDeleteProgram(programID);
	glDeleteVertexArrays(1, &VertexArrayID);

	// Close OpenGL window and terminate GLFW
	glfwTerminate();

	return 0;
}

今回はGLSLがシンプルです。バージョンがかわりattributeのかわりにinが使われるようになりましたが、基本的な文法は同じです。

バーテックスシェーダ

#version 330 core

layout(location = 0) in vec3 vPosition;
layout(location = 1) in vec3 vColor;
uniform mat4 MVP;
out vec3 fColor;

void main(){
	gl_Position =  MVP * vec4(vPosition,1);
	//gl_Position =  vec4(vPosition,1);

	fColor = vColor;
}

フラグメントシェーダ

#version 330 core

in vec3 fColor;
out vec3 color;

void main()
{
	//color = vec3(1,0,0);
	color = fColor;
}

バーテックスが持っている色情報をフラグメントにわたしています。自動的に中間値が計算され補間されます。(GPUが全部やってくれる)
シェーダ部分は別ファイルになっていて、これを読み込んで設定する関数が用意されています。
四面体をカメラ座標の変換をするものとしないもの両方出力してみました。(切り替えはバーテックスシェーダのコメントで)

座標変換なし

座標変換あり

とても基本的なテストですが、VisualStudioでセットアップできたことが、自分の開発環境的に有意義でした。(Compute Shaderに備えて・・)
頂点増やしたり、いろいろと数値を変えて試してみると理解も深まります。

]]>
OpenGL Shading Language ../../../20170530705/ Tue, 30 May 2017 14:00:31 +0000 ../../../?p=705 JavaScriptの3DライブラリThree.jsを使う機会から、そこで使われているシェーダープログラムに、興味をもつようになりました。(GPUで実行される言語なので)
Three.jsはJavaScriptなので、OpenGLではなく厳密にはWebGLと呼びますが、OpenGL ES 2.0がWebGL 1.0相当となるようです。(https://wgld.org/d/webgl2/w001.html)
今回はまずシェーディングの機能を試したかったので、このブログのロゴデータを加工してみました。

参考 : https://wgld.org/sitemap.html

このサイトはWebGLについて、とても詳しく丁寧に書かれており、大変勉強になります。Three.jsのようなライブラリは使わずに、自前のライブラリを使って書かれているので、しくみを一から勉強するのにはとても役に立ちます。ただコードのボリュームは多いので、さらにそこから必要な部分のみを切り取って使わせていただきました。このサイトにあるブラーの記事を参考に、ちょっとカッコいいエフェクトをつくってみました。

scriptタグにGPUで走る頂点シェーダ、フラグメントシェーダのプログラムを書くようになっています。(手軽すぎる・・)
組み込み変数やベクトル演算が特徴的です。attribute変数、uniform変数でJavaScriptの変数の値を受け取ります。
create_shader関数から下はそのまま引用させていただきましたが、結構なボリュームがあります。それだけOpenGLは手順が多いということですね。しかし一つずつ順を追っていくとその仕組みがよくわかるように書かれており、関数が機能ごとによくまとめられています。

<html>
<head>
<script id="vs" type="x-shader/x-vertex">
attribute vec3 position;
attribute vec2 texCoord;
uniform   mat4 mvpMatrix;
varying   vec2 vTexCoord;

void main(void){
	vTexCoord   = texCoord;
	gl_Position = mvpMatrix * vec4(position, 1.0);
}
</script>

<script id="fs" type="x-shader/x-fragment">
precision mediump float;

uniform sampler2D texture;
uniform float     strength;
varying vec2      vTexCoord;

const float num = 10.0;			//変更
const float tFrag = 1.0 / 512.0;
const float nFrag = 1.0 / num ;

void main(void){
	vec3  destColor = vec3(0.0);
	vec2  fc = vec2(gl_FragCoord.s, 512.0 - gl_FragCoord.t);
	vec2  fcc = fc - vec2(gl_FragCoord.s, 0.0);
	
	float totalWeight = 0.0;
	for(float i = 0.0; i <= num; i++){
		float percent = i * nFrag;
		float weight = percent - percent * percent;
		vec2  t = fc - fcc * percent * strength * nFrag;
		destColor += texture2D(texture, t * tFrag).rgb * weight;
		totalWeight += weight;
	}
	gl_FragColor = vec4(destColor / totalWeight, 1.0);
}
</script>

<script type="text/javascript">
var c;

onload = function(){

	var strength = 2.0;		//変更
	
	c = document.getElementById('canvas');
	c.width = 512;
	c.height = 512;
	
	// webglコンテキストを取得
	var gl = c.getContext('webgl') || c.getContext('experimental-webgl');
	
	// 正射影で板ポリゴンをレンダリングするシェーダ
	v_shader = create_shader('vs');
	f_shader = create_shader('fs');
	var oPrg = create_program(v_shader, f_shader);
	var oAttLocation = new Array();
	oAttLocation[0] = gl.getAttribLocation(oPrg, 'position');
	oAttLocation[1] = gl.getAttribLocation(oPrg, 'texCoord');
	var oAttStride = new Array();
	oAttStride[0] = 3;
	oAttStride[1] = 2;
	var oUniLocation = new Array();
	oUniLocation[0] = gl.getUniformLocation(oPrg, 'mvpMatrix');
	oUniLocation[1] = gl.getUniformLocation(oPrg, 'texture');
	oUniLocation[2] = gl.getUniformLocation(oPrg, 'strength');
	
	var position = [
		-1.0,  1.0,  0.0,
		 1.0,  1.0,  0.0,
		-1.0, -1.0,  0.0,
		 1.0, -1.0,  0.0
	];
	var texCoord = [
		0.0, 0.0,
		1.0, 0.0,
		0.0, 1.0,
		1.0, 1.0
	];
	var index = [
		0, 2, 1,
		2, 3, 1
	];
	var vPosition = create_vbo(position);
	var vTexCoord = create_vbo(texCoord);
	var vVBOList  = [vPosition, vTexCoord];
	var vIndex    = create_ibo(index);
	
	// 各種行列の生成と初期化
	var m = new matIV();
	var tmpMatrix = m.identity(m.create());
	var mvpMatrix = m.identity(m.create());

	// テクスチャ生成
	var texture1 = null;
	create_texture('decode.jpg', 1);
	
	setTimeout(render, 100);
	
	function render(){
		
		// バッファクリア
		gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
		
		// プログラムオブジェクトの選択
		gl.useProgram(oPrg);
		
		// canvas を初期化
		gl.clearDepth(1.0);
		gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
		
		// テクスチャの適用
		gl.activeTexture(gl.TEXTURE0);

		gl.bindTexture(gl.TEXTURE_2D, texture1);

		// 板ポリゴンのレンダリング
		set_attribute(vVBOList, oAttLocation, oAttStride);
		gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vIndex);
		gl.uniformMatrix4fv(oUniLocation[0], false, tmpMatrix);
		gl.uniform1i(oUniLocation[1], 0);
		gl.uniform1f(oUniLocation[2], strength);
		gl.drawElements(gl.TRIANGLES, index.length, gl.UNSIGNED_SHORT, 0);
		
		// コンテキストの再描画
		gl.flush();
	}
	
	// シェーダを生成する関数
	function create_shader(id){
		// シェーダを格納する変数
		var shader;
		// HTMLからscriptタグへの参照を取得
		var scriptElement = document.getElementById(id);
		// scriptタグが存在しない場合は抜ける
		if(!scriptElement){return;}
		// scriptタグのtype属性をチェック
		switch(scriptElement.type){
			// 頂点シェーダの場合
			case 'x-shader/x-vertex':
				shader = gl.createShader(gl.VERTEX_SHADER);
				break;
			// フラグメントシェーダの場合
			case 'x-shader/x-fragment':
				shader = gl.createShader(gl.FRAGMENT_SHADER);
				break;
			default :
				return;
		}
		// 生成されたシェーダにソースを割り当てる
		gl.shaderSource(shader, scriptElement.text);
		// シェーダをコンパイルする
		gl.compileShader(shader);
		// シェーダが正しくコンパイルされたかチェック
		if(gl.getShaderParameter(shader, gl.COMPILE_STATUS)){
			// 成功していたらシェーダを返して終了
			return shader;
		}else{
			// 失敗していたらエラーログをアラートする
			alert(gl.getShaderInfoLog(shader));
		}
	}
	
	// プログラムオブジェクトを生成しシェーダをリンクする関数
	function create_program(vs, fs){
		// プログラムオブジェクトの生成
		var program = gl.createProgram();
		// プログラムオブジェクトにシェーダを割り当てる
		gl.attachShader(program, vs);
		gl.attachShader(program, fs);
		// シェーダをリンク
		gl.linkProgram(program);
		// シェーダのリンクが正しく行なわれたかチェック
		if(gl.getProgramParameter(program, gl.LINK_STATUS)){
			// 成功していたらプログラムオブジェクトを有効にする
			gl.useProgram(program);
			// プログラムオブジェクトを返して終了
			return program;
		}else{
			// 失敗していたらエラーログをアラートする
			alert(gl.getProgramInfoLog(program));
		}
	}
	
	// VBOを生成する関数
	function create_vbo(data){
		// バッファオブジェクトの生成
		var vbo = gl.createBuffer();
		// バッファをバインドする
		gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
		// バッファにデータをセット
		gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);
		// バッファのバインドを無効化
		gl.bindBuffer(gl.ARRAY_BUFFER, null);
		// 生成した VBO を返して終了
		return vbo;
	}
	
	// VBOをバインドし登録する関数
	function set_attribute(vbo, attL, attS){
		// 引数として受け取った配列を処理する
		for(var i in vbo){
			// バッファをバインドする
			gl.bindBuffer(gl.ARRAY_BUFFER, vbo[i]);
			// attributeLocationを有効にする
			gl.enableVertexAttribArray(attL[i]);
			// attributeLocationを通知し登録する
			gl.vertexAttribPointer(attL[i], attS[i], gl.FLOAT, false, 0, 0);
		}
	}
	
	// IBOを生成する関数
	function create_ibo(data){
		// バッファオブジェクトの生成
		var ibo = gl.createBuffer();
		// バッファをバインドする
		gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
		// バッファにデータをセット
		gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Int16Array(data), gl.STATIC_DRAW);
		// バッファのバインドを無効化
		gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
		// 生成したIBOを返して終了
		return ibo;
	}
	
	// テクスチャを生成する関数
	function create_texture(source, number){
		// イメージオブジェクトの生成
		var img = new Image();
		// データのオンロードをトリガーにする
		img.onload = function(){
			// テクスチャオブジェクトの生成
			var tex = gl.createTexture();
			// テクスチャをバインドする
			gl.bindTexture(gl.TEXTURE_2D, tex);
			// テクスチャへイメージを適用
			gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
			// ミップマップを生成
			gl.generateMipmap(gl.TEXTURE_2D);
			// テクスチャパラメータの設定
			gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
			gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
			gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
			gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
			// 生成したテクスチャを変数に代入
			switch(number){
				case 1:
					texture1 = tex;
					break;
				case 2:
					texture2 = tex;
					break;
				default :
					break;
			}
			// テクスチャのバインドを無効化
			gl.bindTexture(gl.TEXTURE_2D, null);
		};
		// イメージオブジェクトのソースを指定
		img.src = source;
	}
	
	// フレームバッファをオブジェクトとして生成する関数
	function create_framebuffer(width, height){
		// フレームバッファの生成
		var frameBuffer = gl.createFramebuffer();
		// フレームバッファをWebGLにバインド
		gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
		// 深度バッファ用レンダーバッファの生成とバインド
		var depthRenderBuffer = gl.createRenderbuffer();
		gl.bindRenderbuffer(gl.RENDERBUFFER, depthRenderBuffer);
		// レンダーバッファを深度バッファとして設定
		gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
		// フレームバッファにレンダーバッファを関連付ける
		gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthRenderBuffer);
		// フレームバッファ用テクスチャの生成
		var fTexture = gl.createTexture();
		// フレームバッファ用のテクスチャをバインド
		gl.bindTexture(gl.TEXTURE_2D, fTexture);
		// フレームバッファ用のテクスチャにカラー用のメモリ領域を確保
		gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
		// テクスチャパラメータ
		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
		// フレームバッファにテクスチャを関連付ける
		gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, fTexture, 0);
		// 各種オブジェクトのバインドを解除
		gl.bindTexture(gl.TEXTURE_2D, null);
		gl.bindRenderbuffer(gl.RENDERBUFFER, null);
		gl.bindFramebuffer(gl.FRAMEBUFFER, null);
		// オブジェクトを返して終了
		return {f : frameBuffer, d : depthRenderBuffer, t : fTexture};
	}
};

function matIV(){
	this.create = function(){
		return new Float32Array(16);
	};
	this.identity = function(dest){
		dest[0]  = 1; dest[1]  = 0; dest[2]  = 0; dest[3]  = 0;
		dest[4]  = 0; dest[5]  = 1; dest[6]  = 0; dest[7]  = 0;
		dest[8]  = 0; dest[9]  = 0; dest[10] = 1; dest[11] = 0;
		dest[12] = 0; dest[13] = 0; dest[14] = 0; dest[15] = 1;
		return dest;
	};
}

</script>
</head>
<body>
<canvas id="canvas" width="512" height="512"></canvas>
</body>
</html>

num=20, strength=0

num=20, strength=2

num=10, strength=2

フラグメントシェーダでは自ピクセルの色を周辺ピクセルの色から計算して決定しています。ここでは縦方向のピクセルを対象にしてブレを起こしています。
どのピクセルを選択するかによって、ブレ方や方向を変化させることができそうです。

今回は平面だけでしたが、また掘り下げていきたいです。

]]>