OpenGLをベースにした描画系プログラミング環境は基本GLSLによるシェーダーをサポートしているしているのでそれぞれの特徴や違いを調べてみた。 いろいろ間違っているところなとあるっともうので、用紙いただきたい&指摘/修正してほしい。 グラフィックカードによって対応しているGLSLバージョンに違いがあり、それぞれ利用できる型や関数が異なるため注意。##Shaderって?
各描画のレイヤーに対して、GPUにパイプラインで処理を渡す仕組み。
主なシェーダーとしては、
・vertex shader: 頂点バッファに対して処理するためのシェーダー。
・fragmental shader: ラスタライズ後のピクセルを操作するためのシェーダー、テクスチャーの合成とかもここでやる。
・geometry shader: よくわからないけどすごそう。
などがある。
より詳しく知りたい場合は以下などで解説されているので参考に。
Introducing Shaders (openframeworks.cc)
An intro to modern OpenGL. Chapter 2.2: Shaders
https://processing.org/tutorials/pshader/
[Swift SceneKit] SCNShadableでシェーダープログラムメモ(1)
以下それぞれの環境の特徴を挙げ、その後大雑把にShaderプログラミングに共通している処理をプログラミング環境ごとに短いサンプルなどとともにまとめた。
##openFrameworks(以下oF)
・GLSL #version 120 #version 150 などversion識別子で指定 (2.0系はどうだったか)
・ofShaderクラスでシェーダーファイルのロード
##THREE.js
・THREE.ShaderMaterialオブジェクトのアトリビュートとして割り当てる
・スニペットによるインライン(?)なglsl適応
##Processing
・PShaderクラスでロード
・2.0からP3D描画内ではデフォルトでGPU描画
##SceneKit(Swift)
・SCNProgram
・SCNShadable
・mterialのshader modifierとして割り当て
・スニペットによるglsl適応
・SCNTechnique (テクニック)による複雑なシェーダー(これについては今は触れない)
##Max/PSP Jitter
・よく知らないが多分サポートしてる
##シェーダーの読み込み
oF
ofShader myShader; myShader.load("YOUR_SHADER"); あとは draw() 内で begin() end() を呼ぶ
myShader.begin(); ... myShader.end();
THREE.js
var myUniforms = { yourUniformsFloat: { type: "f", value: 1.0 }, ... }
var material = new THREE.ShaderMaterial({ uniforms: myUniforms, attributes: myAttributes, vertexShader: "MY VERTEX SHADER CODE....", fragmentShader: "MY FRAGMENTAL SHADER CODE....", }) あとはマテリアルを割り当てた 3DObject を scene に追加する
Processing
PShader myShader; myShader = loadShader("YourFrag.glsl", "YourVert.glsl"); あとは draw() 内でshader()を呼ぶ
shader(myShader);
SceneKit SCNProgram で vertex shader をロード
let program = SCNProgram() let material = SCNMaterial();
let vertexShaderURL = NSBundle.mainBundle().URLForResource("yourShader", withExtension: "vert") let vertexShader = NSString(contentsOfURL: vertexShaderURL!, encoding: NSUTF8StringEncoding, error: nil)
program.vertexShader = vertexShader material.program = program; shader modifier の場合
let surfaceModifire = "YOUR GLSL CODE...."
material.shaderModifiers = [ SCNShaderModifierEntryPointSurface: surfaceModifire ] あとはマテリアルを割り当てた SceneNode を scene に追加する
##テクスチャーの読み込み
oF ofPlanePrimitive に setUniformTexture() というテクスチャーをバインドするメソッドが用意されている。 ofPlanePrimitive は単純な UV プレーンを作ってくれる
plane.mapTexCoordsFromTexture(img.getTextureReference());
これに ofTexture をバインドする
texture.getTextureReference().bind();
shader.begin(); ... plane.draw(); ... shader.end();
glsl fragmental shader 内では sampler2DRect tex0 にバインドされる。
uniform sampler2DRect tex0;
THREE.js THREE.ShaderMaterial の uniform にセットする。
var image = new Image(); var myUniforms = { texture1: { type: "t", value: image } }
var material = new THREE.ShaderMaterial({ uniforms: myUniforms, })
Processing プリミティブな図形の描画 beginShape() - endShape() の中で、PImage で画像ロードして、 texture() 関数で割り当てる。
PImage img = loadImage("laDefense.jpg"); beginShape(); texture(img); vertex(10, 20, 0, 0); vertex(80, 5, 100, 0); vertex(95, 90, 100, 100); vertex(40, 95, 0, 100); endShape(); または PShape クラスの beginShape() - endShape() 間でセットする。
PShape sh = createShape(); sh.beginShape(QUAD_STRIP); sh.texture(tex); ... sh.endShape();
SceneKit SceneKitの場合 SCNMaterial のプロパティ content に様々な要素が指定できるようになっていて、 画像はテクスチャは NSImage か CGImageRef を渡すとテクスチャとして読み込まれるようになっている。 他にも以下の様なものを content 受け付ける。 ・色 : NSColor / CGColorRef
・画像 : NSImage / CGColorRef
・NSString / NSURL (画像ファイルの読み込み先)
・6画像のArray 上下左右にキューブマップされる
・Core Animation の layer
・SpriteKit の texture
・SpriteKit の シーン content プロパティはいくつかの方法で適応されそれぞれ効果が違う 例えば diffuse.content なら拡散反射光として適応される。
let material = SCNMaterial() let diffuseImage = NSImage(named: "myImage") material.diffuse.contents = diffuseImage content の種類については以下↓
Configuring a Material’s Visual Properties また、Texcoord を指定して読み込ませたい場合 SCNProgram でテクスチャをバインドする必要があるが今はちょっとパス。
##Uniformの更新
当然CPUでの描画処理からGPUのシェーダにユーザーが定義した変数を渡したくなるのでそのためのパイプラインのしくみが用意されている。Uniformはシェーダー内で一意の名前で定義ができる変数。任意のタイミングで更新できるがそれぞれの開発環境によって用法が違っている。
oF shader.begin(); ~ shader.end();
内でset用のメソッドを呼ぶ
setUniform1i(myUniformInt, myValue) setUniform1f(myUniformFloat, myValue) 等、uniformの型によて関数が用意されてる。
THREE.js window.requestAnimationFrame()
で呼んでいる animate() 関数内で THREE.ShaderMaterial の uniform プロパティに代入する。
uniforms.yourUniformsFloat.value = myNewValue;
Processing PShader の set() 関数を使う
myShader.set("myUniform", myNewValue);
SceneKit SCNProgramDelegate で行うもしくはマテリアルの handleBindingOfSymbol コールバックで行う。
material.handleBindingOfSymbol("myUniform") { programID, location, renderedNode, renderer in glUniform1f(GLint(location), myNewValue)) }
##Attributeの更新
Uniformとは違って、頂点バッファの頂点毎にシェーダーにパイプラインで値を渡せる、locationで指定したポインタ経由でアクセスする
oF draw() 関数の中で shader の begin() - end() 間で各頂点にアクセスするためにまず GLint型 の loction を取得する (getAttributeLocation()) 引数には vertex shader 内での atrribute名 を渡す。
セットする型によって別の glVertexAttrib 関数が用意されている。
glVertexAttrib~ は頂点バッファの一つの頂点に対してシェーダをセットする、 なのですべての頂点を更新したい場合は頂点の数分コールする。
myShader.begin(); ... GLint myLocation = myShader.getAttributeLocation("myAttribute");
for (int i = 0; i < mesh.vertices.size(); i++) { myShader.setAttribute1i(myLocation, myIntegerValue); } ... myShader.end();
THREE.js Uniform の時のように THREE.ShaderMaterial の attributes プロパティに渡すだけだが、 配列 value を geometry.vertices の lenght と同じサイズの配列で宣言してあげる必要がある。
var myAttributes = { myAttributesVector3: { type: "v3", value: [] }, ... }
// シェーダ側の v3 型に対応してる THREE.Vector3 を生成して代入 var displacement = attributes.myAttributesVector3.value;
for( var i = 0; i < geometry.vertices.length; i++ ) { displacement[i] = new THREE.Vector3( 0, 0, 0 ); }
var material = new THREE.ShaderMaterial({ uniforms: myUniforms, attributes: myAttributes, vertexShader: "MY VERTEX SHADER CODE....", fragmentShader: "MY FRAGMENTAL SHADER CODE....", }) あとは Uniform の時のように animate() 関数内で更新してあげる。
needsUpdate = true;
してあげないと更新されない、たしか。
for( var i = 0; i <myAttributes.displacement.value.length; i ++ ) { myAttributes.attributes.displacement.value[ i ] = myNewThreeVector3 ; }
attributes.displacement.needsUpdate = true;
Processing ProcessingではユーザーのカスタムなAttributeをパイプするのはできなさそう? PGLで実現している人がいた↓
http://processing342.rssing.com/chan-5887731/all_p3.html この方法はoFとさほど変わらない感じ。 vertexAttribPointer() の引数は glVertexAttribPointer と同じ?
FloatBuffer myValueBuffer;
pgl = beginPGL();
int myLocation = pgl.getAttribLocation(myShader.glProgram, "myAttribute"); pgl.enableVertexAttribArray(myLocation); pgl.vertexAttribPointer(myLocation, 4, PGL.FLOAT, false, 0, myValueBuffer);
endPGL();
SceneKit Geometry Semantic Identifiers が用意されている、
key名
形
説明
SCNGeometrySourceSemanticVertex
NSString!
--
SCNGeometrySourceSemanticNormal
NSString!
--
SCNGeometrySourceSemanticColor
NSString!
--
SCNGeometrySourceSemanticTexcoord
NSString!
--
SCNGeometrySourceSemanticVertexCrease
NSString!
--
SCNGeometrySourceSemanticEdgeCrease
NSString!
--
SCNGeometrySourceSemanticBoneWeights
NSString!
--
SCNGeometrySourceSemanticBoneIndices
NSString!
-- ちょっとまとめきれないのでの後程どうにかしたい..。