HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2021
1130
youdaiHGIMG4でblender 2.83のWorkbenchと同じアルゴリズムのMatcapを実装したい12解決


youdai

リンク

2021/11/30(Tue) 13:02:09|NO.94574

●HGIMG4でblender 2.83のWorkbenchと同じアルゴリズムのMatcapを実装したい

通常のMatcapではなく、精度の高いMatcapを実装したいと思って、blender 2.83のWorkbenchのソースを参考にして、
同アルゴリズムを分析してHGIMG4に実装しました。

github blender 2.83 matcap関連のソース
https://github.com/blender/blender/blob/594f47ecd2d5367ca936cf6fc6ec8168c2b360d0/source/blender/draw/engines/workbench/shaders/workbench_matcap_lib.glsl

ですが、実際に動かしてみると、数日前に質問を投稿させて頂いた「常にテクスチャが正面を向くシェーダー」の問題と同じく、
広角透視モードでは正面を正しく向いてくれません。

自作したシェーダーを検証してみると、概念がよく分からない部分があって、
その一部が原因でシェーダーが上手く動作していないようなのです。

以下は、そのシェーダーです。

シェーダーのサンプルファイル
http://youdaizone.webcrow.jp/matcap_shader_test.zip

matcap_dev.vert


//------------------------------------------------------------------------------ // attribute attribute vec4 a_position; attribute vec3 a_normal; attribute vec3 a_tangent; attribute vec3 a_binormal; //------------------------------------------------------------------------------ // uniform // world uniform mat4 u_worldMatrix; uniform mat4 u_worldViewMatrix; uniform mat4 u_worldViewProjectionMatrix; // inverse uniform mat4 u_inverseTransposeWorldMatrix; uniform mat4 u_inverseTransposeWorldViewMatrix; // projection uniform mat4 u_projectionMatrix; // view uniform mat4 u_viewMatrix; uniform mat4 u_viewProjectionMatrix; // camera uniform vec3 u_cameraWorldPosition; uniform vec3 u_cameraViewPosition; //------------------------------------------------------------------------------ // encode /* Compact Normal Storage for small G-Buffers のエンコーディングを GLSL に移植 */ // GeForce 8800 // パフォーマンス enc 12.00, dec 15.00 // 品質 48.071 vec4 sphereMap_encode(vec3 n) { float p = sqrt(n.z * 8+8); return vec4(n.xy / p + 0.5, 0, 0); } //------------------------------------------------------------------------------ // varying varying vec4 v_normal; varying mat4 v_projectionMatrix; varying vec3 v_viewvec0; varying vec3 v_viewvec1; //------------------------------------------------------------------------------ // main void main() { vec4 positionWorldViewSpace = u_worldViewMatrix * a_position; mat3 inverseTransposeWorldViewMatrix = mat3(u_inverseTransposeWorldViewMatrix[0].xyz, u_inverseTransposeWorldViewMatrix[1].xyz, u_inverseTransposeWorldViewMatrix[2].xyz); vec3 normalVector = normalize(inverseTransposeWorldViewMatrix * a_normal); vec3 tangentVector = normalize(inverseTransposeWorldViewMatrix * a_tangent); vec3 binormalVector = normalize(inverseTransposeWorldViewMatrix * a_binormal); mat3 tangentSpaceTransformMatrix = mat3(tangentVector.x, binormalVector.x, normalVector.x, tangentVector.y, binormalVector.y, normalVector.y, tangentVector.z, binormalVector.z, normalVector.z); vec3 cameraDirection = tangentSpaceTransformMatrix * (u_cameraWorldPosition - positionWorldViewSpace.xyz); // 短い版? //cameraDirection = positionWorldViewSpace.xyz - u_cameraWorldPosition; /* viewvec について viewvecについては、そもそもカメラのベクトルなのかどうかもよく分からない。 しかし何らかの視点系ベクトルであると思われる。 */ v_viewvec0 = u_cameraWorldPosition; v_viewvec1 = cameraDirection; vec3 N = inverseTransposeWorldViewMatrix * a_normal; v_normal = sphereMap_encode(N); // 法線をエンコーディング gl_Position = u_worldViewProjectionMatrix * a_position; }

matcap_dev.frag


//------------------------------------------------------------------------------ // quality #if defined(OPENGL_ES) || defined(GL_ES) #ifdef GL_FRAGMENT_PRECISION_HIGH precision highp float; #else precision mediump float; #endif #endif //------------------------------------------------------------------------------ // uniform uniform sampler2D u_matcapTexture; uniform float u_aspect; //------------------------------------------------------------------------------ // decode /* Compact Normal Storage for small G-Buffers のデコーディングを GLSL に移植 */ vec3 sphereMap_decode(vec2 enc) { vec2 fenc = enc * 4 - 2; float f = dot(fenc, fenc); float g = sqrt(1 - f / 4); vec3 n; n.xy = fenc * g; n.z = 1 - f / 2; return n; } //------------------------------------------------------------------------------ // matcap // blender 2.83.18 workbench matcap // I = 不明。恐らく何らかの視点系ベクトル // N = 法線。恐らく (mat3(u_inverseTransposeWorldViewMatrix) * a_normal で処理した後に指定するもの? // flipped = 反転スイッチ vec2 matcap_uv_compute(vec3 I, vec3 N, bool flipped) { // Quick creation of an orthonormal basis // Iに関係する処理 平行投影のベクトルは 0.0, 0.0, 1.0 float a = 1.0 / (1.0 + I.z); // 平行投影の場合、a = 0.5 float b = -I.x * I.y * a; // 平行投影の場合、b = 0.0 vec3 b1 = vec3(1.0 - I.x * I.x * a, b, -I.x); // 平行投影の場合、b1 = 1.0, 0.0, 0.0 vec3 b2 = vec3(b, 1.0 - I.y * I.y * a, -I.y); // 平行投影の場合、b2 = 0.0, 1.0, 0.0 // Iを処理したb1、b2とNをdot積する vec2 matcap_uv = vec2(dot(b1, N), dot(b2, N)); // 平行投影の場合、matcap_uv.x は 法線.x の方向にだけ向き、 // matcap_uv.y は 法線.y の方向にだけ向くことになる // 左右反転処理、及び、そのスイッチ if (flipped) { matcap_uv.x = -matcap_uv.x; } // センタリグ処理 return matcap_uv * 0.496 + 0.5; } // uv = 不明 // viewvec0 = 不明 // viewvec1 = 不明 // proj_mat = u_projectionMatrix /* 本来の関数は vec3 view_vector_from_screen_uv(vec2 uv, vec4 viewvec[3], mat4 proj_mat) { if (proj_mat[3][3] == 0.0) { // 広角透視モード return normalize(viewvec[0].xyz + vec3(uv, 0.0) * viewvec[1].xyz); } else { // 平行投影モード return vec3(0.0, 0.0, 1.0); } } というアルゴリズムだったが、以下のものと結果的には同じもの。 分かりやすくするために、以下のようにした。 */ // このuv、viewvec0、viewvec1に何を指定するべきかがよく分からない vec3 view_vector_from_screen_uv(vec2 uv, vec3 viewvec0, vec3 viewvec1, mat4 proj_mat) { if (proj_mat[3][3] == 0.0) { // 広角透視モード return normalize(viewvec0 + vec3(uv, 0.0) * viewvec1); } else { // 平行投影モード return vec3(0.0, 0.0, 1.0); } } //------------------------------------------------------------------------------ // varying varying vec4 v_normal; varying mat4 v_projectionMatrix; varying vec3 v_viewvec0; varying vec3 v_viewvec1; //------------------------------------------------------------------------------ // main void main() { // uv の値は、これで正しいのかよく分からない vec2 uv = gl_FragCoord.xy * vec2(u_aspect, 1.0); /* Normal and Incident vector are in viewspace. Lighting is evaluated in viewspace. */ vec3 I = view_vector_from_screen_uv(uv, v_viewvec0, v_viewvec1, v_projectionMatrix); vec3 N = sphereMap_decode(v_normal.xy); // 法線をデコーディング vec2 matcap_uv = matcap_uv_compute(I, N, 0); gl_FragColor = texture2D(u_matcapTexture, matcap_uv); }

cop.material


material matcap_shader { u_worldMatrix = WORLD_MATRIX u_worldViewMatrix = WORLD_VIEW_MATRIX u_worldViewProjectionMatrix = WORLD_VIEW_PROJECTION_MATRIX u_inverseTransposeWorldViewMatrix = INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX u_inverseTransposeWorldMatrix = INVERSE_TRANSPOSE_WORLD_MATRIX u_projectionMatrix = PROJECTION_MATRIX u_cameraWorldPosition = CAMERA_WORLD_POSITION u_cameraViewPosition = CAMERA_VIEW_POSITION u_viewMatrix = VIEW_MATRIX u_viewProjectionMatrix = VIEW_PROJECTION_MATRIX u_matrixPalette = MATRIX_PALETTE u_aspect = 1.333333333333333 sampler u_matcapTexture { mipmap = false wrapS = REPEAT wrapT = REPEAT minFilter = LINER magFilter = LINER } renderState { cullFace = true depthTest = true } technique { pass { vertexShader = matcap_dev.vert fragmentShader = matcap_dev.frag } } } material mc : matcap_shader { sampler u_matcapTexture { path = res/m000.png } }

これは平行投影モードでは正確にMatcapなので、matcap_uv_compute自体が動作していないわけではないようです。

おそらく、view_vector_from_screen_uv関数に渡す値が間違っているのだと思います。
このuv、viewvec0、viewvec1に渡す正しい値さえ分かれば、おそらく広角透視モードでも正しく正面を向いてくれるはずだと思います。
(matcapは本来は常に正面を向くシェーダーです)

view_vector_from_screen_uv関数の関数名から想像すると、ビューベクトルからスクリーンUVに変換する関数なのだと思います。

view_vector_from_screen_uv関数のアルゴリズムは一見単純で


return normalize(viewvec0 + vec3(uv, 0.0) * viewvec1);

これだけが本体なようなもので、すぐに分かるだろうと思っていたのですが、
これがどうしてビューからスクリーンUVへの変換になるのか想像できません。

ちなみにsphereMap_encode関数、sphereMap_decode関数については、法線のクオリティを調整する関数なので、
今回の問題とはあまり関係ないと思われます。
もしこれがシェーダーの動作に問題あるようであれば、外して貰っても構いません。

仕組みが違うから同じ問題は発生しないかもと思ったのですが、これも「常にテクスチャが正面を向くシェーダー」の時と同じ問題が発生しているのでしょうか?
これが広角透視モードでも正しく正面を向くよう、アドバイスお願い致します。



この記事に返信する


youdai

リンク

2021/12/2(Thu) 00:43:46|NO.94586

viewvecの詳細らしきものを見つけました。

https://www.mail-archive.com/bf-blender-cvs@blender.org/msg129323.html

google日本語翻訳で簡易的に訳したものは以下のものです。
(viewvecs[0]は本シェーダーではviewvecs0、viewvecs[1]は本シェーダーではviewvec1です)

翻訳文--------------------------------------------------------------------------------------------------------------

view_vecs [1]は、左下隅から右上隅に向かうベクトルです。
Perspの場合:view_vecs [0] .xyとview_vecs [1] .xyはそれぞれ左下隅です
Z = 1の場合、左上隅はZ = 1の場合
view_vecs [0] .zニアクリップ距離およびview_vecs [1] .zは、ニアプレーンからファークリッププレーンまでの(符号付き)+ *距離です。

copy_v3_v3(storage-> viewvecs [0]、view_vecs [0]);

/ *差異を保存する必要があります* /
storage-> viewvecs [1] [0] = view_vecs [1] [0] --view_vecs [0] [0];
storage-> viewvecs [1] [1] = view_vecs [2] [1] --view_vecs [0] [1];
storage-> viewvecs [1] [2] = view_vecs [3] [2] --view_vecs [0] [2];

翻訳文終了--------------------------------------------------------------------------------------------------------



youdai

リンク

2021/12/2(Thu) 01:02:12|NO.94587

原文を読み直して、翻訳文を一部修正しました。

原文の一部

/** + * If ortho : view_vecs[0] is the near-bottom-left corner of the frustum and + * view_vecs[1] is the vector going from the near-bottom-left corner to + * the far-top-right corner. + * If Persp : view_vecs[0].xy and view_vecs[1].xy are respectively the bottom-left corner + * when Z = 1, and top-left corner if Z = 1. + * view_vecs[0].z the near clip distance and view_vecs[1].z is the (signed) + * distance from the near plane to the far clip plane. + */

平行投影の場合:
view_vecs [0]は錐台の左下隅にあり、view_vecs [1]は、左下隅から右上隅に向かうベクトルです。

広角透視の場合:
view_vecs [0] .xyとview_vecs [1] .xyはそれぞれ左下隅です。

Z = 1の場合、左上隅はZ = 1の場合

view_vecs [0] .zニアクリップ距離およびview_vecs [1] .zは、ニアプレーンからファークリッププレーンまでの(符号付き)+ *距離です。

copy_v3_v3(storage-> viewvecs [0]、view_vecs [0]);
/ *差異を保存する必要があります* /
storage-> viewvecs [1] [0] = view_vecs [1] [0] --view_vecs [0] [0];
storage-> viewvecs [1] [1] = view_vecs [2] [1] --view_vecs [0] [1];
storage-> viewvecs [1] [2] = view_vecs [3] [2] --view_vecs [0] [2];



砂時 計

リンク

2021/12/3(Fri) 20:19:00|NO.94595

私も全体が理解できていないので外している可能性はありますが
それっぽい動作ができましたのでご参考まで.

matcap_dev.vert varying 以下のみ

// 略 //------------------------------------------------------------------------------ // varying varying vec4 v_normal; varying mat4 v_projectionMatrix; //varying vec3 v_viewvec0; //varying vec3 v_viewvec1; //------------------------------------------------------------------------------ // main void main() { mat3 inverseTransposeWorldViewMatrix = mat3(u_inverseTransposeWorldViewMatrix[0].xyz, u_inverseTransposeWorldViewMatrix[1].xyz, u_inverseTransposeWorldViewMatrix[2].xyz); v_projectionMatrix = u_projectionMatrix; vec3 N = inverseTransposeWorldViewMatrix * a_normal; v_normal = sphereMap_encode(N); // 法線をエンコーディング gl_Position = u_worldViewProjectionMatrix * a_position; }

matcap_dev.frag varying 以下のみ

// 略 //------------------------------------------------------------------------------ // varying varying vec4 v_normal; varying mat4 v_projectionMatrix; //varying vec3 v_viewvec0; //varying vec3 v_viewvec1; //------------------------------------------------------------------------------ // main void main() { vec3 v_viewvec0 = vec3(0.0, 0.0, 0.0); vec3 v_viewvec1 = vec3(0.0, 0.0, 0.0); vec2 reso = vec2(800.0, 600.0); float aspectw = reso.x / reso.y; if (v_projectionMatrix[3][3] == 0.0) { // perspective float ang = 51.48 * 3.141592 / 180.0; float t = tan(ang * 0.5); float near = 0.1; float far = 50.0; v_viewvec1 = vec3( t * aspectw * 2.0, t * 2.0, far - near); // v_viewvec0 = vec3(-t * aspectw, -t, near); v_viewvec0 = vec3(-t * aspectw, -t, 1.0); } else { // ortho float near = 0.1; float far = 100.0; float zoom = 8.0; float h = zoom * 0.5; float w = h * aspectw; v_viewvec1 = vec3(w * 2.0, h * 2.0, far - near); v_viewvec0 = vec3(-w, -h, near); } // uv の値は、これで正しいのかよく分からない // vec2 uv = gl_FragCoord.xy * vec2(u_aspect, 1.0); vec2 uv = vec2(gl_FragCoord.x / reso.x, gl_FragCoord.y / reso.y); /* Normal and Incident vector are in viewspace. Lighting is evaluated in viewspace. */ vec3 I = view_vector_from_screen_uv(uv, v_viewvec0, v_viewvec1, v_projectionMatrix); vec3 N = sphereMap_decode(v_normal.xy); // 法線をデコーディング vec2 matcap_uv = matcap_uv_compute(I, N, false); gl_FragColor = texture2D(u_matcapTexture, matcap_uv); }

なお view_vector_from_screen_uv は、スクリーンUVからビューベクトルの計算です。



youdai

リンク

2021/12/5(Sun) 10:10:22|NO.94611

アドバイスありがとうございます。
砂時計さんのシェーダーを検証してみましたが、広角透視時にはmatcapが正面を向いていませんでした。

質問した後に自分でも様々な方法で試行錯誤してきましたが、
やはりview_vector_from_screen_uv関数の部分でとん挫してしまいました。

そこでシェーダーの趣旨を変えてblenderのworkbenchのmatcapを再現するのではなく、
「広角透視時でもmatcapに正面を向かせる」ことだけ重視したシンプルなmatcapシェーダーを作りました。

シェーダーサンプルファイル
http://youdaizone.webcrow.jp/simple_matcap.zip

シェーダーサンプルファイルに同梱した matcap_sample.blend に、
workbenchではなく、eeveeのシェーダーノードエディターで作成したmatcapのサンプルを作ってあります。
これは常に正面を向くmatcapです。これの肝はblenderでいう所の「ベクトル変換」ノードです。

これをそのままGLSLで再現したいです。

matcap_simple.vert


// 常に正面を向くmatcapシェーダー // モデルの法線や位置を調整するのではなく、空間そのものをコンバートして、それを法線へ処理したい //------------------------------------------------------------------------------ // attribute attribute vec4 a_position; attribute vec3 a_normal; //------------------------------------------------------------------------------ // uniform // world uniform mat4 u_worldMatrix; uniform mat4 u_worldViewMatrix; uniform mat4 u_worldViewProjectionMatrix; // inverse uniform mat4 u_inverseTransposeWorldMatrix; uniform mat4 u_inverseTransposeWorldViewMatrix; // view uniform mat4 u_viewMatrix; uniform mat4 u_viewProjectionMatrix; // projection uniform mat4 u_projectionMatrix; // camera uniform vec3 u_cameraWorldPosition; uniform vec3 u_cameraViewPosition; //------------------------------------------------------------------------------ // matcap // blenderでいう所の「ベクトル変換」をしたい // space0 = 変換元空間 // space1 = 変換先空間 // normal = 法線 vec3 cnv_space_normal(mat4 space0, mat4 space1, vec3 normal) { // u_worldViewProjectionMatrixなどの行列が空間ということなのかも? // 行列そのものを何らかの処理でトランスフォームしたものを // normalと乗算するのだと思われる。 // オブジェクト空間というのはu_worldMatrixのこと? // カメラ空間というのはu_viewMatrixのこと? // どうすれば空間を変換できたことになるのか分からない mat4 cnvSpace = space1 * space0; return mat3(cnvSpace) * normal; } // シンプル版 vec2 matcap_uv_compute(mat4 objectSpace, mat4 cameraSpace, vec3 normal) { // オブジェクト空間から、カメラ空間へnormalを変換 vec3 uv = cnv_space_normal(objectSpace, cameraSpace, normal); // mapping uv = uv * vec3(0.496, 0.496, 1.0); uv = uv + vec3(0.5, 0.5, 0.0); return uv.xy; } //------------------------------------------------------------------------------ // varying varying vec2 v_uv; //------------------------------------------------------------------------------ // main void main() { v_uv = matcap_uv_compute(u_worldMatrix, u_viewMatrix, a_normal); gl_Position = u_worldViewProjectionMatrix * a_position; }

matcap_simple.frag


//------------------------------------------------------------------------------ // quality #if defined(OPENGL_ES) || defined(GL_ES) #ifdef GL_FRAGMENT_PRECISION_HIGH precision highp float; #else precision mediump float; #endif #endif //------------------------------------------------------------------------------ // uniform uniform sampler2D u_matcapTexture; //------------------------------------------------------------------------------ // varying varying vec2 v_uv; //------------------------------------------------------------------------------ // main void main() { gl_FragColor = texture2D(u_matcapTexture, v_uv); }

それでよく分からないところは、blenderの「ベクトル変換」ノードをどうやってGLSLで書くかというところです。
おそらく、空間とはu_worldViewProjectionMatrix等の行列のことで、その行列に関する部分だと思うのですが、よく分かりません。

matcap_sample.blend をGLSLで再現できれば、おそらく「広角透視時でもmatcapに正面を向かせる」シェーダーができるはずです。

アドバイスお願い致します。



Rosh

リンク

2021/12/5(Sun) 10:33:25|NO.94613

自分は普段HLSLを使ってるからGLSLは良く分からないけど
attribute vec3 a_normal;
これが恐らく頂点法線ですよね?
であれば
この頂点法線をカメラから見た向きに変換するだけで良いから
uniform mat4 u_worldViewMatrix;
この行列で変換した法線ベクトルを
スフィアマップの座標に変換すれば良いのでは?



youdai

リンク

2021/12/5(Sun) 10:39:30|NO.94614

>uniform mat4 u_worldViewMatrix;
>この行列で変換した法線ベクトルを
>スフィアマップの座標に変換すれば良いのでは?

スフィアマップの座標とは何ですか?



youdai

リンク

2021/12/5(Sun) 10:51:50|NO.94615

もしかしてスフィアマップの座標とは、エンコード・デコード用に使っている関数のことでしょうか?
これは文字通りエンコード・デコード用の関数なので座標は持ちません。
この関数の中のNの法線について指摘されているのでしょうか?

>attribute vec3 a_normal;
>これが恐らく頂点法線ですよね?
>であれば
>この頂点法線をカメラから見た向きに変換するだけで良いから
>uniform mat4 u_worldViewMatrix;
>この行列で変換した法線ベクトルを
>スフィアマップの座標に変換すれば良いのでは?

u_worldViewMatrixを法線に乗算すると平行投影時にはmatcapの向きが画面を向きますが、
広角透視時では正面のオブジェクトしか画面を向きません。
u_inverseTransposeWorldViewMatrixと同じです。



砂時 計

リンク

2021/12/5(Sun) 14:40:45|NO.94622

目的を達成するまでに課題が2つあるように見えますね。

まず1つめから。
"matcap_sample.blend をGLSLで再現できれば、おそらく「広角透視時でもmatcapに正面を向かせる」シェーダーができるはずです。"について
ほんと? という点です。

matcap_sample.blend で HSP3 側と似た状況を作るために
以下のように球体を追加しカメラも設定を合わせてみてください。


追加→メッシュ→UV球 を1つ作る。
マテリアルプロパティから user_matcap を指定する。
オブジェクトデータプロパティ→ノーマル
→自動スムーズにチェックを入れる。
モディファイアプロパティで
重み付き法線を追加する。
モディファイアプロパティで
配列を追加する。数を5にする。

ビュー→視点→カメラを選択する。(または F4 を押す。)

Camera のオブジェクトプロパティで
位置を (4,-8,0),回転を (90°,0°,0°) にする。
Camera のオブジェクトプロパティで
焦点距離を28mmにする。


このようにすると
5つの球体がおおよそ描画フレームにちょうど入った状態になっています。
これがこの質問の最初の HSP3 側の状態に近いものだと思います。

このとき球面に映っているものは
私の blender の環境では正面を向いていませんが
youdai さんの環境でも正面を向かないのではないでしょうか?
※私の認識では透視投影での user_matcap の処理では
正面を向かなくて正しいと思っています。



砂時 計

リンク

2021/12/5(Sun) 17:37:18|NO.94625

前回の NO.94556 と同じ方針で実装すると以下のようになります。
例によって端にいくほど歪みますがなんとなく正面を向きます。

matcap_simple.vert varying 以下

// 略 //------------------------------------------------------------------------------ // varying varying vec2 v_uv; //------------------------------------------------------------------------------ // main void main() { vec3 uv = (u_inverseTransposeWorldViewMatrix * vec4(a_normal, 0.0)).xyz; if (u_projectionMatrix[3][3] == 0.0) { vec3 r = normalize((u_worldViewMatrix * a_position).xyz); // Y軸回り vec3 s = vec3(-r.z * uv.x + r.x * uv.z, uv.y, r.x * uv.x - r.z * uv.z); // X軸回り uv = vec3(s.x, - r.z * s.y + r.y * s.z, - r.y * s.y - r.z * s.z); } v_uv = uv.xy * 0.496 + 0.5; gl_Position = u_worldViewProjectionMatrix * a_position; }



youdai

リンク

2021/12/5(Sun) 18:14:09|NO.94626

砂時計さんへ

blenderのuser_matcapを確認してみた所、ご指摘の通りに確かに完全にはカメラに正面を向いていませんでした。
画面の淵に近づくに従って、外側を僅かに向いていることを確認しました。

user_matcap方式のmatcapのアドバイスありがとうございます。
実際にゲームシーンで使ってみましたが、自分にとっては十分な精度がありました。
非常に助かりました。

実はもう半ば諦めかけていて、今度の方式がダメならもう諦めようと思っていました。
質問して良かったです。
大変参考になりました。
ありがとうございました!



砂時 計

リンク

2021/12/6(Mon) 20:03:54|NO.94627

すでに解決となっていますが
NO.94625 はX軸Y軸上以外ではちょっと変換が怪しい感じだったので
元々のコードに登場する、Iベクトルに対する直交基底を使用する関数
matcap_uv_compute() から算出するコードを改めてまとめました。
ご参考までに。

matcap_dev.vert

//------------------------------------------------------------------------------ // attribute attribute vec4 a_position; attribute vec3 a_normal; attribute vec3 a_tangent; attribute vec3 a_binormal; //------------------------------------------------------------------------------ // uniform // world uniform mat4 u_worldMatrix; uniform mat4 u_worldViewMatrix; uniform mat4 u_worldViewProjectionMatrix; // inverse uniform mat4 u_inverseTransposeWorldMatrix; uniform mat4 u_inverseTransposeWorldViewMatrix; // projection uniform mat4 u_projectionMatrix; // view uniform mat4 u_viewMatrix; uniform mat4 u_viewProjectionMatrix; // camera uniform vec3 u_cameraWorldPosition; uniform vec3 u_cameraViewPosition; //------------------------------------------------------------------------------ // encode /* Compact Normal Storage for small G-Buffers のエンコーディングを GLSL に移植 */ // GeForce 8800 // パフォーマンス enc 12.00, dec 15.00 // 品質 48.071 vec4 sphereMap_encode(vec3 n) { float p = sqrt(n.z * 8+8); return vec4(n.xy / p + 0.5, 0, 0); } //------------------------------------------------------------------------------ // varying varying vec4 v_normal; //varying mat4 v_projectionMatrix; varying float v_proj33; //------------------------------------------------------------------------------ // main void main() { mat3 inverseTransposeWorldViewMatrix = mat3(u_inverseTransposeWorldViewMatrix[0].xyz, u_inverseTransposeWorldViewMatrix[1].xyz, u_inverseTransposeWorldViewMatrix[2].xyz); // v_projectionMatrix = u_projectionMatrix; v_proj33 = u_projectionMatrix[3][3]; vec3 N = inverseTransposeWorldViewMatrix * a_normal; v_normal = sphereMap_encode(N); // 法線をエンコーディング gl_Position = u_worldViewProjectionMatrix * a_position; }

matcap_dev.frag

//------------------------------------------------------------------------------ // quality #if defined(OPENGL_ES) || defined(GL_ES) #ifdef GL_FRAGMENT_PRECISION_HIGH precision highp float; #else precision mediump float; #endif #endif //------------------------------------------------------------------------------ // uniform uniform sampler2D u_matcapTexture; uniform float u_aspect; //------------------------------------------------------------------------------ // decode /* Compact Normal Storage for small G-Buffers のデコーディングを GLSL に移植 */ vec3 sphereMap_decode(vec2 enc) { vec2 fenc = enc * 4 - 2; float f = dot(fenc, fenc); float g = sqrt(1 - f / 4); vec3 n; n.xy = fenc * g; n.z = 1 - f / 2; return n; } //------------------------------------------------------------------------------ // matcap // blender 2.83.18 workbench matcap // I = 不明。恐らく何らかの視点系ベクトル // N = 法線。恐らく (mat3(u_inverseTransposeWorldViewMatrix) * a_normal で処理した後に指定するもの? // flipped = 反転スイッチ vec2 matcap_uv_compute(vec3 I, vec3 N, bool flipped) { // Quick creation of an orthonormal basis // Iに関係する処理 平行投影のベクトルは 0.0, 0.0, 1.0 float a = 1.0 / (1.0 + I.z); // 平行投影の場合、a = 0.5 float b = -I.x * I.y * a; // 平行投影の場合、b = 0.0 vec3 b1 = vec3(1.0 - I.x * I.x * a, b, -I.x); // 平行投影の場合、b1 = 1.0, 0.0, 0.0 vec3 b2 = vec3(b, 1.0 - I.y * I.y * a, -I.y); // 平行投影の場合、b2 = 0.0, 1.0, 0.0 // Iを処理したb1、b2とNをdot積する vec2 matcap_uv = vec2(dot(b1, N), dot(b2, N)); // 平行投影の場合、matcap_uv.x は 法線.x の方向にだけ向き、 // matcap_uv.y は 法線.y の方向にだけ向くことになる // 左右反転処理、及び、そのスイッチ if (flipped) { matcap_uv.x = -matcap_uv.x; } // センタリグ処理 return matcap_uv * 0.496 + 0.5; } //------------------------------------------------------------------------------ // varying varying vec4 v_normal; //varying mat4 v_projectionMatrix; varying float v_proj33; //------------------------------------------------------------------------------ // main void main() { // view 座標系で、カメラから点へ vec3 to_point = vec3(0.0, 0.0, -1.0); if (v_proj33 == 0.0) { // perspective vec2 reso = vec2(800.0, 600.0); float aspectw = reso.x / reso.y; vec2 uv = vec2(gl_FragCoord.x / reso.x, gl_FragCoord.y / reso.y); float ang = 51.48 * 3.141592 / 180.0; float t = tan(ang * 0.5); float near = 0.1; float far = 50.0; vec3 v_viewvec1 = vec3( t * aspectw * 2.0, t * 2.0, far - near); // vec3 v_viewvec0 = vec3(-t * aspectw, -t, near); vec3 v_viewvec0 = vec3(-t * aspectw, -t, -1.0); to_point = normalize(v_viewvec0 + vec3(uv, 0.0) * v_viewvec1); } vec3 N = sphereMap_decode(v_normal.xy); // 法線をデコーディング vec2 matcap_uv = matcap_uv_compute(-to_point, N, false); gl_FragColor = texture2D(u_matcapTexture, matcap_uv); }



youdai

リンク

2021/12/14(Tue) 19:42:51|NO.94698

砂時計さん新しいアドバイスありがとうございます。

あれから自分もblender 2.83のworkbenchのmatcapの再現を、自分なりに試行錯誤して改良を重ねました。
このシェーダーは広角透視時でも、workbenchのmatcapにかなり近いのではないかと思います。
これは自分なりのmatcapシェーダー完成版です。

matcap.vert


//------------------------------------------------------------------------------ // attribute attribute vec4 a_position; attribute vec3 a_normal; //------------------------------------------------------------------------------ // uniform // world uniform mat4 u_worldMatrix; uniform mat4 u_worldViewMatrix; uniform mat4 u_worldViewProjectionMatrix; // inverse uniform mat4 u_inverseTransposeWorldMatrix; uniform mat4 u_inverseTransposeWorldViewMatrix; // projection uniform mat4 u_projectionMatrix; // view uniform mat4 u_viewMatrix; uniform mat4 u_viewProjectionMatrix; // camera uniform vec3 u_cameraWorldPosition; uniform vec3 u_cameraViewPosition; //------------------------------------------------------------------------------ // varying varying vec2 v_uv; //------------------------------------------------------------------------------ // blender 2.83.18 workbench matcap vec2 matcap_uv_compute(vec3 I, vec3 N, bool flipped) { // Quick creation of an orthonormal basis // Iに関係する処理 平行投影のベクトルは 0.0, 0.0, 1.0 float a = 1.0 / (1.0 + I.z); // 平行投影の場合、a = 0.5 float b = -I.x * I.y * a; // 平行投影の場合、b = 0.0 vec3 b1 = vec3(1.0 - I.x * I.x * a, b, -I.x); // 平行投影の場合、b1 = 1.0, 0.0, 0.0 vec3 b2 = vec3(b, 1.0 - I.y * I.y * a, -I.y); // 平行投影の場合、b2 = 0.0, 1.0, 0.0 // Iを処理したb1、b2とNをdot積する vec2 matcap_uv = vec2(dot(b1, N), dot(b2, N)); // 平行投影の場合、matcap_uv.x は 法線.x の方向にだけ向き、 // matcap_uv.y は 法線.y の方向にだけ向くことになる // 左右反転処理、及び、そのスイッチ if (flipped) { matcap_uv.x = -matcap_uv.x; } // センタリグ処理 return matcap_uv * 0.496 + 0.5; } //------------------------------------------------------------------------------ // main void main() { vec3 viewVec; if(u_projectionMatrix[3][3] == 0.0) { vec4 positionWorldViewSpace = u_worldViewMatrix * a_position; vec3 cameraDirection = u_cameraViewPosition - positionWorldViewSpace.xyz; viewVec = normalize(cameraDirection); } else { viewVec = vec3(0.0, 0.0, 1.0); } vec3 normal = (u_inverseTransposeWorldViewMatrix * vec4(a_normal, 0.0)).xyz; v_uv = matcap_uv_compute(viewVec, normal, false); gl_Position = u_worldViewProjectionMatrix * a_position; }

matcap.frag


//------------------------------------------------------------------------------ // quality #if defined(OPENGL_ES) || defined(GL_ES) #ifdef GL_FRAGMENT_PRECISION_HIGH precision highp float; #else precision mediump float; #endif #endif //------------------------------------------------------------------------------ // uniform uniform sampler2D u_matcapTexture; //------------------------------------------------------------------------------ // varying varying vec2 v_uv; //------------------------------------------------------------------------------ // main void main() { gl_FragColor = texture2D(u_matcapTexture, v_uv); }



ONION software Copyright 1997-2023(c) All rights reserved.